diff --git a/DEPS b/DEPS index 61e6b491..39e6884 100644 --- a/DEPS +++ b/DEPS
@@ -295,19 +295,19 @@ # Three lines of non-changing comments so that # the commit queue can handle CLs rolling V8 # and whatever else without interference from each other. - 'src_internal_revision': '9cbd7389395536e4bc8d2fbcd1f0b3ab1e1adf4b', + 'src_internal_revision': '5de18f0aacc3865198ffcb45372a83f6b0b9b27b', # 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': '9d53fcefe244192b0057c3b4a41bff52c838799d', + 'skia_revision': 'ac2d5ceecb588e3be2a798896b92d27c91ad6461', # Three lines of non-changing comments so that # the commit queue can handle CLs rolling V8 # and whatever else without interference from each other. - 'v8_revision': 'bbcee50bb6db5243a0eac31ac5a4453a91239d98', + 'v8_revision': 'a1ba74a85fdfad2d8eac3857059676bc6e0fb686', # 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': '5b08297d9c01207db50f399083c05c8f168d6b88', + 'angle_revision': '00845fd649a450a00e61215f44bb4fc95d515c97', # 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': '864a235afcf4d2575b1eab8de96fbf0d84f6cda9', + 'boringssl_revision': '154bb5860a47c160c3be09003908c64c1de11bff', # 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. @@ -375,7 +375,7 @@ # Three lines of non-changing comments so that # the commit queue can handle CLs rolling CrossBench # and whatever else without interference from each other. - 'crossbench_revision': '73e0cfe22e2699bf14bedb489c0b39ba1bc03702', + 'crossbench_revision': '803813fe85b358fa2883c15cd4dec46098e1b4c3', # Three lines of non-changing comments so that # the commit queue can handle CLs rolling libFuzzer # 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': 'dfe3855e5d0b5367a598e60674766ffa1c894c71', + 'dawn_revision': '19a7153574fe7506f098586a12ac21edf0944849', # Three lines of non-changing comments so that # the commit queue can handle CLs rolling feed # and whatever else without interference from each other. @@ -523,7 +523,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. - 'compiler_rt_revision': '16c0e42913a45357f0e557dcc0b7055129c78af6', + 'compiler_rt_revision': '2fdf452fd1b09f2e0bb9fdfe8a162c86cc70dbc2', # If you change this, also update the libc++ revision in # //buildtools/deps_revisions.gni. @@ -1486,7 +1486,7 @@ 'src/clank': { 'url': Var('chrome_git') + '/clank/internal/apps.git' + '@' + - '7e5d0fb6ef7e00fd425fcf363a4f4123ee271ae2', + '1881fd29e10d5d32e8b8f6ed892c860e43dded76', 'condition': 'checkout_android and checkout_src_internal', }, @@ -1948,7 +1948,7 @@ # Tools used when building Chrome for Chrome OS. This affects both the Simple # Chrome workflow, as well as the chromeos-chrome ebuild. 'src/third_party/chromite': { - 'url': Var('chromium_git') + '/chromiumos/chromite.git' + '@' + '96096c3c83cbe537782e84dfc12981f146723f6a', + 'url': Var('chromium_git') + '/chromiumos/chromite.git' + '@' + 'bf95ef3e710a92303a9f6b450e85d27892d26c69', 'condition': 'checkout_chromeos', }, @@ -2840,11 +2840,11 @@ 'dep_type': 'cipd', }, - 'src/third_party/vulkan-deps': '{chromium_git}/vulkan-deps@0b7863549d960381696d3cd7485ac6a284122e15', + 'src/third_party/vulkan-deps': '{chromium_git}/vulkan-deps@9d021471f38e15ccd4d7087f4cf05292e6e671b0', 'src/third_party/glslang/src': '{chromium_git}/external/github.com/KhronosGroup/glslang@963588074b26326ff0426c8953c1235213309bdb', 'src/third_party/spirv-cross/src': '{chromium_git}/external/github.com/KhronosGroup/SPIRV-Cross@b8fcf307f1f347089e3c46eb4451d27f32ebc8d3', 'src/third_party/spirv-headers/src': '{chromium_git}/external/github.com/KhronosGroup/SPIRV-Headers@6d0784e9f1ab92c17eeea94821b2465c14a52be9', - 'src/third_party/spirv-tools/src': '{chromium_git}/external/github.com/KhronosGroup/SPIRV-Tools@e8864edbebe9fb9872c6c95b2363b490c6105a15', + 'src/third_party/spirv-tools/src': '{chromium_git}/external/github.com/KhronosGroup/SPIRV-Tools@f06e0f3d2e5acfe4b14e714e4103dd1ccdb237e5', 'src/third_party/vulkan-headers/src': '{chromium_git}/external/github.com/KhronosGroup/Vulkan-Headers@9c77de5c3dd216f28e407eec65ed9c0a296c1f74', 'src/third_party/vulkan-loader/src': '{chromium_git}/external/github.com/KhronosGroup/Vulkan-Loader@fefd7ed96ef9994f0080dbd078822b07d8637918', 'src/third_party/vulkan-tools/src': '{chromium_git}/external/github.com/KhronosGroup/Vulkan-Tools@ba13d38d06830f714a93c5bb159e6e4bacacf0bc', @@ -2940,7 +2940,7 @@ 'packages': [ { 'package': 'skia/tools/goldctl/linux-amd64', - 'version': 'srGbc-O6bVQmBoT6VLvP66h6Be4A-nffx4UhixUJ9w0C', + 'version': 'KN2IwZqaE82rq6fqVp3hdruYYJ84LUBLgOVTQRAGtOgC', }, ], 'dep_type': 'cipd', @@ -2972,7 +2972,7 @@ 'packages': [ { 'package': 'skia/tools/goldctl/mac-arm64', - 'version': '2br8hIWKHDAaOt77siYyRUclHBYUZX6dj8jtogL90nwC', + 'version': 'ujgOeYoS5cTXHy9h7j_PSg7YsGl9q9TmvkhaM22_Uh4C', }, ], 'dep_type': 'cipd', @@ -3027,7 +3027,7 @@ 'packages': [ { 'package': 'chromeos_internal/apps/help_app/app', - 'version': '6CJPIYRk3LvYL2TI2hdXpgt3mj7_1EbDw3FRFg1WIgEC', + 'version': 'bJ2z4HiEKwb3ilEnXeJy1UrzmBHmJdDnqhcr_UW5-T8C', }, ], 'condition': 'checkout_chromeos and checkout_src_internal', @@ -3038,7 +3038,7 @@ 'packages': [ { 'package': 'chromeos_internal/apps/media_app/app', - 'version': 'taLuHtO78kWltYP_ZiTnuCBWONLuhnxQmxNbAQWuQEQC', + 'version': 'UjL0kayMnT1EQUqhcQ8uKU0TB4PnSOklcndgabv0VVoC', }, ], 'condition': 'checkout_chromeos and checkout_src_internal', @@ -4614,7 +4614,7 @@ 'src/components/optimization_guide/internal': { 'url': Var('chrome_git') + '/chrome/components/optimization_guide.git' + '@' + - '448e864469af5010a1dbb83b15d0cbfa1dcb5f55', + '6032e32fa8b82048023e47709f1e5a22dd277791', 'condition': 'checkout_src_internal', },
diff --git a/PRESUBMIT.py b/PRESUBMIT.py index 4480406..c9b9c0df 100644 --- a/PRESUBMIT.py +++ b/PRESUBMIT.py
@@ -762,6 +762,7 @@ '^services/cert_verifier/', '^components/certificate_transparency/', '^components/media_router/common/providers/cast/certificate/', + '^components/trusted_vault/', # gRPC provides some C++ libraries that use std::shared_ptr<>. '^chromeos/ash/services/libassistant/grpc/', '^chromecast/cast_core/grpc',
diff --git a/android_webview/BUILD.gn b/android_webview/BUILD.gn index 33d8df5..ab2903e 100644 --- a/android_webview/BUILD.gn +++ b/android_webview/BUILD.gn
@@ -1117,6 +1117,7 @@ defines = [ "enable_arcore=$enable_arcore", + "enable_cardboard=$enable_cardboard", "enable_pdf=$enable_pdf", "enable_print_preview=$enable_print_preview", "enable_screen_ai_service=$enable_screen_ai_service",
diff --git a/ash/constants/ash_features.cc b/ash/constants/ash_features.cc index 50a5918..e3f99b1 100644 --- a/ash/constants/ash_features.cc +++ b/ash/constants/ash_features.cc
@@ -112,10 +112,6 @@ "AudioSelectionImprovement", base::FEATURE_DISABLED_BY_DEFAULT); -// Enables the Audio URL that is designed to help user debug or troubleshoot -// common issues on ChromeOS. -BASE_FEATURE(kAudioUrl, "AudioUrl", base::FEATURE_DISABLED_BY_DEFAULT); - // Enables the Auto Night Light feature which sets the default schedule type to // sunset-to-sunrise until the user changes it to something else. This feature // is not exposed to the end user, and is enabled only via cros_config for @@ -288,6 +284,11 @@ "BocaAdjustCaptionBubbleOnExpand", base::FEATURE_ENABLED_BY_DEFAULT); +// Enables or disables keeping the Boca SWA open when the session is ended. +BASE_FEATURE(kBocaKeepSWAOpenOnSessionEnded, + "BocaKeepSWAOpenOnSessionEnded", + base::FEATURE_ENABLED_BY_DEFAULT); + // Enables or disables enforcing sequential execution for Boca Session load. BASE_FEATURE(kBocaSequentialSessionLoad, "BocaSequentialSessionLoad", @@ -3520,6 +3521,10 @@ return base::FeatureList::IsEnabled(kBocaAdjustCaptionBubbleOnExpand); } +bool IsBocaKeepSWAOpenOnSessionEndedEnabled() { + return base::FeatureList::IsEnabled(kBocaKeepSWAOpenOnSessionEnded); +} + bool IsBocaSequentialSessionLoadEnabled() { return base::FeatureList::IsEnabled(kBocaSequentialSessionLoad); }
diff --git a/ash/constants/ash_features.h b/ash/constants/ash_features.h index 8e7c59d..f0c939d 100644 --- a/ash/constants/ash_features.h +++ b/ash/constants/ash_features.h
@@ -45,7 +45,6 @@ COMPONENT_EXPORT(ASH_CONSTANTS) BASE_DECLARE_FEATURE(kCrasSplitAlsaUsbInternal); COMPONENT_EXPORT(ASH_CONSTANTS) -COMPONENT_EXPORT(ASH_CONSTANTS) BASE_DECLARE_FEATURE(kAudioUrl); COMPONENT_EXPORT(ASH_CONSTANTS) BASE_DECLARE_FEATURE(kAutoNightLight); COMPONENT_EXPORT(ASH_CONSTANTS) BASE_DECLARE_FEATURE(kAutoScreenBrightness); COMPONENT_EXPORT(ASH_CONSTANTS) BASE_DECLARE_FEATURE(kAutocorrectFederatedPhh); @@ -103,6 +102,8 @@ BASE_DECLARE_FEATURE(kBocaClientTypeForSpeechRecognition); COMPONENT_EXPORT(ASH_CONSTANTS) BASE_DECLARE_FEATURE(kBocaAdjustCaptionBubbleOnExpand); +COMPONENT_EXPORT(ASH_CONSTANTS) +BASE_DECLARE_FEATURE(kBocaKeepSWAOpenOnSessionEnded); COMPONENT_EXPORT(ASH_CONSTANTS) BASE_DECLARE_FEATURE(kCameraSuperResSupported); COMPONENT_EXPORT(ASH_CONSTANTS) BASE_DECLARE_FEATURE(kCrosSwitcher); COMPONENT_EXPORT(ASH_CONSTANTS) BASE_DECLARE_FEATURE(kBorealisBigGl); @@ -1104,6 +1105,8 @@ COMPONENT_EXPORT(ASH_CONSTANTS) bool IsBocaAdjustCaptionBubbleOnExpandEnabled(); COMPONENT_EXPORT(ASH_CONSTANTS) +bool IsBocaKeepSWAOpenOnSessionEndedEnabled(); +COMPONENT_EXPORT(ASH_CONSTANTS) bool IsBocaSequentialSessionLoadEnabled(); COMPONENT_EXPORT(ASH_CONSTANTS) bool IsBrightnessControlInSettingsEnabled(); COMPONENT_EXPORT(ASH_CONSTANTS) bool IsCaptureModeEducationEnabled();
diff --git a/ash/webui/recorder_app_ui/resources/platforms/swa/on_device_model.ts b/ash/webui/recorder_app_ui/resources/platforms/swa/on_device_model.ts index 21b2b98e..2c8121e 100644 --- a/ash/webui/recorder_app_ui/resources/platforms/swa/on_device_model.ts +++ b/ash/webui/recorder_app_ui/resources/platforms/swa/on_device_model.ts
@@ -152,7 +152,6 @@ session.append( { maxTokens: 0, - tokenOffset: 0, input: inputPieces, }, null, @@ -160,9 +159,6 @@ session.generate( { maxOutputTokens: 0, - topK: null, - temperature: null, - responseJsonSchema: null, constraint: null, }, responseRouter.$.bindNewPipeAndPassRemote(),
diff --git a/base/test/android/javatests/src/org/chromium/base/test/transit/ScrollableFacility.java b/base/test/android/javatests/src/org/chromium/base/test/transit/ScrollableFacility.java index cd7d4a7c..8da47fc 100644 --- a/base/test/android/javatests/src/org/chromium/base/test/transit/ScrollableFacility.java +++ b/base/test/android/javatests/src/org/chromium/base/test/transit/ScrollableFacility.java
@@ -9,7 +9,6 @@ import static androidx.test.espresso.assertion.ViewAssertions.matches; import static androidx.test.espresso.matcher.ViewMatchers.isCompletelyDisplayed; -import static org.chromium.base.test.transit.ViewSpec.viewSpec; import static org.chromium.build.NullUtil.assumeNonNull; import android.view.View; @@ -49,8 +48,14 @@ /** Must populate |items| with the expected items. */ protected abstract void declareItems(ItemsBuilder items); - /** Returns the minimum number of items declared expected to be displayed screen initially. */ - protected abstract int getMinimumOnScreenItemCount(); + /** + * Returns the minimum number of items declared expected to be displayed screen initially. + * + * <p>Defaults to 2. + */ + protected int getMinimumOnScreenItemCount() { + return 2; + } @CallSuper @Override @@ -66,8 +71,8 @@ if (item.getPresence() == Presence.ABSENT || i < itemsToExpect) { switch (item.mPresence) { case Presence.ABSENT: - assert item.mOnScreenViewMatcher != null; - elements.declareNoView(item.mOnScreenViewMatcher); + assert item.mViewSpec != null; + elements.declareNoView(item.mViewSpec.getViewMatcher()); break; case Presence.PRESENT_AND_ENABLED: case Presence.PRESENT_AND_DISABLED: @@ -98,10 +103,10 @@ /** Create a new item stub which throws UnsupportedOperationException if selected. */ public Item<Void> declareStubItem( - Matcher<View> onScreenViewMatcher, @Nullable Matcher<?> offScreenDataMatcher) { + ViewSpec<View> onScreenViewSpec, @Nullable Matcher<?> offScreenDataMatcher) { Item<Void> item = new Item<>( - onScreenViewMatcher, + onScreenViewSpec, offScreenDataMatcher, Presence.PRESENT_AND_ENABLED, ItemsBuilder::unsupported); @@ -111,12 +116,12 @@ /** Create a new item which runs |selectHandler| when selected. */ public <SelectReturnT> Item<SelectReturnT> declareItem( - Matcher<View> onScreenViewMatcher, + ViewSpec<View> onScreenViewSpec, @Nullable Matcher<?> offScreenDataMatcher, Function<ItemOnScreenFacility<SelectReturnT>, SelectReturnT> selectHandler) { Item<SelectReturnT> item = new Item<>( - onScreenViewMatcher, + onScreenViewSpec, offScreenDataMatcher, Presence.PRESENT_AND_ENABLED, selectHandler); @@ -127,11 +132,11 @@ /** Create a new item which transitions to a |DestinationStationT| when selected. */ public <DestinationStationT extends Station<?>> Item<DestinationStationT> declareItemToStation( - Matcher<View> onScreenViewMatcher, + ViewSpec<View> onScreenViewSpec, @Nullable Matcher<?> offScreenDataMatcher, Callable<DestinationStationT> destinationStationFactory) { return declareItem( - onScreenViewMatcher, + onScreenViewSpec, offScreenDataMatcher, (itemOnScreenFacility) -> travelToStation(itemOnScreenFacility, destinationStationFactory)); @@ -140,11 +145,11 @@ /** Create a new item which enters a |EnteredFacilityT| when selected. */ public <EnteredFacilityT extends Facility<HostStationT>> Item<EnteredFacilityT> declareItemToFacility( - Matcher<View> onScreenViewMatcher, + ViewSpec<View> onScreenViewSpec, @Nullable Matcher<?> offScreenDataMatcher, Callable<EnteredFacilityT> destinationFacilityFactory) { return declareItem( - onScreenViewMatcher, + onScreenViewSpec, offScreenDataMatcher, (itemOnScreenFacility) -> enterFacility(itemOnScreenFacility, destinationFacilityFactory)); @@ -152,10 +157,10 @@ /** Create a new disabled item. */ public Item<Void> declareDisabledItem( - Matcher<View> onScreenViewMatcher, @Nullable Matcher<?> offScreenDataMatcher) { + ViewSpec<View> onScreenViewSpec, @Nullable Matcher<?> offScreenDataMatcher) { Item<Void> item = new Item<>( - onScreenViewMatcher, + onScreenViewSpec, offScreenDataMatcher, Presence.PRESENT_AND_DISABLED, null); @@ -165,21 +170,21 @@ /** Create a new item expected to be absent. */ public Item<Void> declareAbsentItem( - Matcher<View> onScreenViewMatcher, @Nullable Matcher<?> offScreenDataMatcher) { + ViewSpec<View> onScreenViewSpec, @Nullable Matcher<?> offScreenDataMatcher) { Item<Void> item = - new Item<>(onScreenViewMatcher, offScreenDataMatcher, Presence.ABSENT, null); + new Item<>(onScreenViewSpec, offScreenDataMatcher, Presence.ABSENT, null); mItems.add(item); return item; } /** Create a new item which may or may not be present. */ public <SelectReturnT> Item<SelectReturnT> declarePossibleItem( - Matcher<View> onScreenViewMatcher, + ViewSpec<View> onScreenViewSpec, @Nullable Matcher<?> offScreenDataMatcher, Function<ItemOnScreenFacility<SelectReturnT>, SelectReturnT> selectHandler) { Item<SelectReturnT> item = new Item<>( - onScreenViewMatcher, + onScreenViewSpec, offScreenDataMatcher, Presence.MAYBE_PRESENT, selectHandler); @@ -191,7 +196,7 @@ public <SelectReturnT> Item<SelectReturnT> declarePossibleStubItem() { Item<SelectReturnT> item = new Item<>( - /* onScreenViewMatcher= */ null, + /* onScreenViewSpec= */ null, /* offScreenDataMatcher= */ null, Presence.MAYBE_PRESENT_STUB, /* selectHandler= */ null); @@ -245,10 +250,9 @@ int MAYBE_PRESENT_STUB = 4; } - protected final @Nullable Matcher<View> mOnScreenViewMatcher; protected final @Nullable Matcher<?> mOffScreenDataMatcher; protected final @Presence int mPresence; - protected final @Nullable ViewSpec mViewSpec; + protected final @Nullable ViewSpec<View> mViewSpec; protected final ViewElement.@Nullable Options mViewElementOptions; protected @Nullable Function<ItemOnScreenFacility<SelectReturnT>, SelectReturnT> mSelectHandler; @@ -268,31 +272,35 @@ * </ul> */ protected Item( - @Nullable Matcher<View> onScreenViewMatcher, + @Nullable ViewSpec<View> onScreenViewSpec, @Nullable Matcher<?> offScreenDataMatcher, @Presence int presence, - @Nullable - Function<ItemOnScreenFacility<SelectReturnT>, SelectReturnT> - selectHandler) { + @Nullable Function<ItemOnScreenFacility<SelectReturnT>, SelectReturnT> + selectHandler) { mPresence = presence; - mOnScreenViewMatcher = onScreenViewMatcher; mOffScreenDataMatcher = offScreenDataMatcher; mSelectHandler = selectHandler; switch (mPresence) { - case Presence.ABSENT, Presence.MAYBE_PRESENT_STUB: + case Presence.ABSENT: + assert onScreenViewSpec != null; + mViewSpec = onScreenViewSpec; + mViewElementOptions = null; + break; + case Presence.MAYBE_PRESENT_STUB: + assert onScreenViewSpec == null; mViewSpec = null; mViewElementOptions = null; break; case Presence.PRESENT_AND_ENABLED: case Presence.MAYBE_PRESENT: - assert mOnScreenViewMatcher != null; - mViewSpec = viewSpec(mOnScreenViewMatcher); + assert onScreenViewSpec != null; + mViewSpec = onScreenViewSpec; mViewElementOptions = ViewElement.Options.DEFAULT; break; case Presence.PRESENT_AND_DISABLED: - assert mOnScreenViewMatcher != null; - mViewSpec = viewSpec(mOnScreenViewMatcher); + assert onScreenViewSpec != null; + mViewSpec = onScreenViewSpec; mViewElementOptions = ViewElement.expectDisabledOption(); break; default: @@ -326,8 +334,9 @@ ItemOnScreenFacility<SelectReturnT> focusedItem = new ItemOnScreenFacility<>(this); + assumeNonNull(mViewSpec); try { - onView(mOnScreenViewMatcher) + onView(mViewSpec.getViewMatcher()) .withFailureHandler(RawFailureHandler.getInstance()) .check(matches(isCompletelyDisplayed())); return mHostStation.enterFacilitySync(focusedItem, /* trigger= */ null); @@ -373,12 +382,14 @@ } else { // If there is no data matcher, use the ViewMatcher to scroll as the item should be // created but not displayed. + assumeNonNull(mViewSpec); try { - onView(mOnScreenViewMatcher).perform(ViewActions.scrollTo()); + onView(mViewSpec.getViewMatcher()).perform(ViewActions.scrollTo()); } catch (PerformException performException) { throw TravelException.newTravelException( String.format( - "Could not scroll using view matcher %s", mOnScreenViewMatcher), + "Could not scroll using view matcher %s", + mViewSpec.getViewMatcher()), performException); } }
diff --git a/build/config/rust.gni b/build/config/rust.gni index 953935d..97a0b3af 100644 --- a/build/config/rust.gni +++ b/build/config/rust.gni
@@ -226,6 +226,12 @@ } else if (current_cpu == "riscv64") { rust_abi_target = "riscv64gc-unknown-linux-gnu" cargo_target_abi = "" + } else if (current_cpu == "ppc64") { + rust_abi_target = "powerpc64le-unknown-linux-gnu" + cargo_target_abi = "" + } else if (current_cpu == "s390x") { + rust_abi_target = "s390x-unknown-linux-gnu" + cargo_target_abi = "" } else { # Best guess for other future platforms. rust_abi_target = current_cpu + "-unknown-linux-gnu"
diff --git a/build/rust/known-target-triples.txt b/build/rust/known-target-triples.txt index 3a033bf..d11c6fc 100644 --- a/build/rust/known-target-triples.txt +++ b/build/rust/known-target-triples.txt
@@ -40,3 +40,5 @@ x86_64-pc-windows-msvc x86_64-unknown-fuchsia x86_64-unknown-linux-gnu +powerpc64le-unknown-linux-gnu +s390x-unknown-linux-gnu
diff --git a/chrome/android/chrome_public_apk_tmpl.gni b/chrome/android/chrome_public_apk_tmpl.gni index 4bb9116..2a50e44 100644 --- a/chrome/android/chrome_public_apk_tmpl.gni +++ b/chrome/android/chrome_public_apk_tmpl.gni
@@ -640,17 +640,17 @@ _libarcore_dir = get_label_info(_arcore_target, "target_out_dir") + "/com_google_ar_core_java/jni" - if (android_64bit_target_cpu) { - if (_is_64_bit_browser) { - loadable_modules += - [ "$_libarcore_dir/arm64-v8a/libarcore_sdk_c.so" ] - } else { - secondary_abi_loadable_modules += - [ "$_libarcore_dir/armeabi-v7a/libarcore_sdk_c.so" ] - } + if (android_64bit_target_cpu && !_is_64_bit_browser) { + # We're targeting a 64-bit architecture but the browser is 32-bit, + # so we need to set the secondary_abi_loadable_modules appropriately: + secondary_abi_loadable_modules += [ + "$_libarcore_dir/$android_app_secondary_abi/libarcore_sdk_c.so", + ] } else { + # We're targeting a 64-bit or 32-bit architecture and the browser + # matches the ABI, so we need to set the loadable_modules: loadable_modules += - [ "$_libarcore_dir/armeabi-v7a/libarcore_sdk_c.so" ] + [ "$_libarcore_dir/$android_app_abi/libarcore_sdk_c.so" ] } } }
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/ArchivedTabsDialogCoordinator.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/ArchivedTabsDialogCoordinator.java index f0b5f3b..7c8cbc07 100644 --- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/ArchivedTabsDialogCoordinator.java +++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/ArchivedTabsDialogCoordinator.java
@@ -11,6 +11,7 @@ import android.animation.AnimatorSet; import android.animation.ObjectAnimator; import android.app.Activity; +import android.content.Context; import android.content.res.Resources; import android.text.SpannableString; import android.text.Spanned; @@ -782,34 +783,41 @@ private void updateIphPropertyModel() { if (mIphMessagePropertyModel == null) return; + mIphMessagePropertyModel.set( + MessageCardViewProperties.DESCRIPTION_TEXT, + getIphDescription(mActivity, mTabArchiveSettings)); + } - int archiveTimeDeltaDays = mTabArchiveSettings.getArchiveTimeDeltaDays(); - int autoDeleteTimeDeletaDays = mTabArchiveSettings.getAutoDeleteTimeDeltaDays(); + @VisibleForTesting + public static CharSequence getIphDescription( + Context context, TabArchiveSettings tabArchiveSettings) { + int archiveTimeDeltaDays = tabArchiveSettings.getArchiveTimeDeltaDays(); + int autoDeleteTimeDeletaDays = tabArchiveSettings.getAutoDeleteTimeDeltaDays(); String settingsTitle = - mActivity.getString(R.string.archived_tab_iph_card_subtitle_settings_title); + context.getString(R.string.archived_tab_iph_card_subtitle_settings_title); // The auto-delete section is blank when the feature param is disabled. String autoDeleteTitle = - mTabArchiveSettings.isAutoDeleteEnabled() - ? mActivity.getString( + tabArchiveSettings.isAutoDeleteEnabled() + ? context.getString( R.string.archived_tab_iph_card_subtitle_autodelete_section, autoDeleteTimeDeletaDays) : ""; String description = - mActivity.getString( + context.getString( R.string.archived_tab_iph_card_subtitle, archiveTimeDeltaDays, autoDeleteTitle, settingsTitle); + SpannableString ss = new SpannableString(description); ForegroundColorSpan fcs = - new ForegroundColorSpan(SemanticColorUtils.getDefaultTextColorAccent1(mActivity)); + new ForegroundColorSpan(SemanticColorUtils.getDefaultTextColorAccent1(context)); ss.setSpan( fcs, description.indexOf(settingsTitle), description.indexOf(settingsTitle) + settingsTitle.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); - - mIphMessagePropertyModel.set(MessageCardViewProperties.DESCRIPTION_TEXT, ss); + return ss; } private List<String> getTabGroupSyncIds() {
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabSwitcherPanePublicTransitTest.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabSwitcherPanePublicTransitTest.java index c3aa4658..223515a4 100644 --- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabSwitcherPanePublicTransitTest.java +++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabSwitcherPanePublicTransitTest.java
@@ -9,13 +9,10 @@ import static androidx.test.espresso.assertion.ViewAssertions.matches; import static androidx.test.espresso.matcher.ViewMatchers.isDisplayed; -import static org.hamcrest.Matchers.instanceOf; import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertThat; import static org.junit.Assert.assertTrue; import androidx.recyclerview.widget.LinearLayoutManager; -import androidx.recyclerview.widget.RecyclerView; import androidx.test.filters.MediumTest; import org.junit.Rule; @@ -33,7 +30,6 @@ import org.chromium.chrome.test.transit.hub.IncognitoTabSwitcherStation; import org.chromium.chrome.test.transit.hub.RegularTabSwitcherStation; import org.chromium.chrome.test.transit.hub.TabSwitcherListEditorFacility; -import org.chromium.chrome.test.transit.hub.TabSwitcherStation; import org.chromium.chrome.test.transit.ntp.IncognitoNewTabPageStation; import org.chromium.chrome.test.transit.page.PageStation; import org.chromium.chrome.test.transit.page.WebPageStation; @@ -70,15 +66,10 @@ RegularTabSwitcherStation regularTabSwitcher = incognitoTabSwitcher.closeTabAtIndex(0, RegularTabSwitcherStation.class); - onView(TabSwitcherStation.TAB_LIST_RECYCLER_VIEW.getViewMatcher()) - .check( - (v, noMatchException) -> { - if (noMatchException != null) throw noMatchException; - assertThat(v, instanceOf(RecyclerView.class)); - LinearLayoutManager layoutManager = - (LinearLayoutManager) ((RecyclerView) v).getLayoutManager(); - assertEquals(9, layoutManager.findLastVisibleItemPosition()); - }); + LinearLayoutManager layoutManager = + (LinearLayoutManager) + regularTabSwitcher.recyclerViewElement.get().getLayoutManager(); + assertEquals(9, layoutManager.findLastVisibleItemPosition()); // Go back to a tab to cleanup tab state regularTabSwitcher.selectTabAtIndex(0, WebPageStation.newBuilder());
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 dcad9dbe..3044916 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
@@ -24,14 +24,13 @@ import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; -import static org.chromium.ui.test.util.ViewUtils.onViewWaiting; - import android.view.View; import androidx.recyclerview.widget.RecyclerView; import androidx.test.espresso.Espresso; import androidx.test.espresso.UiController; import androidx.test.espresso.ViewAction; +import androidx.test.filters.LargeTest; import androidx.test.filters.MediumTest; import androidx.test.platform.app.InstrumentationRegistry; @@ -49,6 +48,7 @@ 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; import org.chromium.base.test.util.HistogramWatcher; import org.chromium.base.test.util.Restriction; import org.chromium.base.test.util.UserActionTester; @@ -67,8 +67,12 @@ import org.chromium.chrome.browser.tabmodel.TabCreator; import org.chromium.chrome.browser.tabmodel.TabModel; import org.chromium.chrome.test.ChromeJUnit4ClassRunner; -import org.chromium.chrome.test.ChromeTabbedActivityTestRule; import org.chromium.chrome.test.R; +import org.chromium.chrome.test.transit.ChromeTransitTestRules; +import org.chromium.chrome.test.transit.FreshCtaTransitTestRule; +import org.chromium.chrome.test.transit.hub.ArchivedTabsDialogStation; +import org.chromium.chrome.test.transit.hub.RegularTabSwitcherStation; +import org.chromium.chrome.test.transit.page.WebPageStation; import org.chromium.chrome.test.util.ActivityTestUtils; import org.chromium.chrome.test.util.ChromeRenderTestRule; import org.chromium.content_public.browser.LoadUrlParams; @@ -79,10 +83,12 @@ @RunWith(ChromeJUnit4ClassRunner.class) @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 ChromeTabbedActivityTestRule mActivityTestRule = new ChromeTabbedActivityTestRule(); + public FreshCtaTransitTestRule mCtaTestRule = + ChromeTransitTestRules.freshChromeTabbedActivityRule(); @Rule public ChromeRenderTestRule mRenderTestRule = @@ -102,24 +108,17 @@ private TabArchiveSettings mTabArchiveSettings; private int mTimesShown; + private WebPageStation mInitialPage; + @Before public void setUp() throws Exception { - mActivityTestRule.startMainActivityOnBlankPage(); + mInitialPage = mCtaTestRule.startOnBlankPage(); + ChromeTabbedActivity cta = mCtaTestRule.getActivity(); ThreadUtils.runOnUiThreadBlocking( () -> { - mProfile = - mActivityTestRule - .getActivity() - .getProfileProviderSupplier() - .get() - .getOriginalProfile(); - mRegularTabCreator = mActivityTestRule.getActivity().getTabCreator(false); - mRegularTabModel = - mActivityTestRule - .getActivity() - .getTabModelSelectorSupplier() - .get() - .getModel(false); + mProfile = cta.getProfileProviderSupplier().get().getOriginalProfile(); + mRegularTabCreator = cta.getTabCreator(false); + mRegularTabModel = cta.getTabModelSelectorSupplier().get().getModel(false); }); mArchivedTabModelOrchestrator = ArchivedTabModelOrchestrator.getForProfile(mProfile); @@ -146,7 +145,10 @@ @MediumTest public void testOneInactiveTab() throws Exception { addArchivedTab(new GURL("https://google.com"), "test 2"); - enterTabSwitcherAndShowDialog(1); + + RegularTabSwitcherStation tabSwitcherStation = mInitialPage.openRegularTabSwitcher(); + tabSwitcherStation.expectArchiveMessageCard().openArchivedTabsDialog(); + onView(withText("1 inactive tab")).check(matches(isDisplayed())); mRobot.resultRobot.verifyTabListEditorIsVisible().verifyAdapterHasItemCount(1); @@ -162,7 +164,10 @@ public void testTwoInactiveTabs() throws Exception { addArchivedTab(new GURL("https://google.com"), "test 1"); addArchivedTab(new GURL("https://google.com"), "test 2"); - enterTabSwitcherAndShowDialog(2); + + RegularTabSwitcherStation tabSwitcherStation = mInitialPage.openRegularTabSwitcher(); + tabSwitcherStation.expectArchiveMessageCard().openArchivedTabsDialog(); + onView(withText("2 inactive tabs")).check(matches(isDisplayed())); // The dialog isn't scrollable, so the shadow should be hidden. onView(withId(R.id.close_all_tabs_button_container_shadow)) @@ -176,7 +181,10 @@ addArchivedTab(new GURL("https://google.com"), "test 2"); mTabArchiveSettings.setShouldShowDialogIphForTesting(true); - enterTabSwitcherAndShowDialog(2); + + RegularTabSwitcherStation tabSwitcherStation = mInitialPage.openRegularTabSwitcher(); + tabSwitcherStation.expectArchiveMessageCard().openArchivedTabsDialog(); + onView(withText("2 inactive tabs")).check(matches(isDisplayed())); mRobot.resultRobot.verifyAdapterHasItemCount(3); assertEquals(1, mUserActionTester.getActionCount("Tabs.ArchivedTabsDialogIphShown")); @@ -189,7 +197,10 @@ addArchivedTab(new GURL("https://google.com"), "test 2"); mTabArchiveSettings.setShouldShowDialogIphForTesting(true); - enterTabSwitcherAndShowDialog(2); + + RegularTabSwitcherStation tabSwitcherStation = mInitialPage.openRegularTabSwitcher(); + tabSwitcherStation.expectArchiveMessageCard().openArchivedTabsDialog(); + onView(withText("2 inactive tabs")).check(matches(isDisplayed())); SettingsActivity activity = @@ -208,39 +219,43 @@ } @Test - @MediumTest + @LargeTest public void testDialogIph_CloseDialog() throws Exception { addArchivedTab(new GURL("https://google.com"), "test 1"); addArchivedTab(new GURL("https://google.com"), "test 2"); mTabArchiveSettings.setShouldShowDialogIphForTesting(true); - enterTabSwitcherAndShowDialog(2); + RegularTabSwitcherStation tabSwitcherStation = mInitialPage.openRegularTabSwitcher(); + ArchivedTabsDialogStation archivedTabsDialogStation = + tabSwitcherStation.expectArchiveMessageCard().openArchivedTabsDialog(); + dismissIphMessage(/* numOfArchivedTabs= */ 2); assertTrue(mTabArchiveSettings.shouldShowDialogIph()); assertEquals(1, mUserActionTester.getActionCount("Tabs.ArchivedTabsDialogIphDismissed")); - mRobot.actionRobot.clickToolbarNavigationButton( - R.string.accessibility_archived_tabs_dialog_back_button); + tabSwitcherStation = archivedTabsDialogStation.closeDialog(); mRobot.resultRobot.verifyTabListEditorIsHidden(); - showDialog(2); + archivedTabsDialogStation = + tabSwitcherStation.expectArchiveMessageCard().openArchivedTabsDialog(); + dismissIphMessage(/* numOfArchivedTabs= */ 2); assertTrue(mTabArchiveSettings.shouldShowDialogIph()); assertEquals(2, mUserActionTester.getActionCount("Tabs.ArchivedTabsDialogIphDismissed")); - mRobot.actionRobot.clickToolbarNavigationButton( - R.string.accessibility_archived_tabs_dialog_back_button); + tabSwitcherStation = archivedTabsDialogStation.closeDialog(); mRobot.resultRobot.verifyTabListEditorIsHidden(); - showDialog(2); + archivedTabsDialogStation = + tabSwitcherStation.expectArchiveMessageCard().openArchivedTabsDialog(); + dismissIphMessage(/* numOfArchivedTabs= */ 2); assertFalse(mTabArchiveSettings.shouldShowDialogIph()); assertEquals(3, mUserActionTester.getActionCount("Tabs.ArchivedTabsDialogIphDismissed")); - mRobot.actionRobot.clickToolbarNavigationButton( - R.string.accessibility_archived_tabs_dialog_back_button); + tabSwitcherStation = archivedTabsDialogStation.closeDialog(); mRobot.resultRobot.verifyTabListEditorIsHidden(); // After 3 dismisses, the iph message won't show again. - showDialog(2); + tabSwitcherStation.expectArchiveMessageCard().openArchivedTabsDialog(); mRobot.resultRobot.verifyAdapterHasItemCount(2); } @@ -249,7 +264,10 @@ public void testRestoreAllInactiveTabs() throws Exception { addArchivedTab(new GURL("https://google.com"), "test 1"); addArchivedTab(new GURL("https://google.com"), "test 2"); - enterTabSwitcherAndShowDialog(2); + + RegularTabSwitcherStation tabSwitcherStation = mInitialPage.openRegularTabSwitcher(); + tabSwitcherStation.expectArchiveMessageCard().openArchivedTabsDialog(); + onView(withText("2 inactive tabs")).check(matches(isDisplayed())); HistogramWatcher histogramExpectation = @@ -269,7 +287,10 @@ public void testRestoreArchivedTabsAndOpenLast() throws Exception { addArchivedTab(new GURL("https://google.com"), "test 1"); addArchivedTab(new GURL("https://google.com"), "test 2"); - enterTabSwitcherAndShowDialog(2); + + RegularTabSwitcherStation tabSwitcherStation = mInitialPage.openRegularTabSwitcher(); + tabSwitcherStation.expectArchiveMessageCard().openArchivedTabsDialog(); + onView(withText("2 inactive tabs")).check(matches(isDisplayed())); mRobot.actionRobot.clickToolbarMenuButton().clickToolbarMenuItem("Restore all"); @@ -301,8 +322,8 @@ } }); LayoutTestUtils.waitForLayout( - mActivityTestRule.getActivity().getLayoutManager(), LayoutType.BROWSING); - Tab activityTab = mActivityTestRule.getActivity().getActivityTabProvider().get(); + mCtaTestRule.getActivity().getLayoutManager(), LayoutType.BROWSING); + Tab activityTab = mCtaTestRule.getActivity().getActivityTabProvider().get(); assertEquals(mRegularTabModel.getTabAt(2), activityTab); } @@ -311,7 +332,9 @@ public void testSettings() throws Exception { addArchivedTab(new GURL("https://google.com"), "test 1"); addArchivedTab(new GURL("https://google.com"), "test 2"); - enterTabSwitcherAndShowDialog(2); + + RegularTabSwitcherStation tabSwitcherStation = mInitialPage.openRegularTabSwitcher(); + tabSwitcherStation.expectArchiveMessageCard().openArchivedTabsDialog(); SettingsActivity activity = ActivityTestUtils.waitForActivity( @@ -333,24 +356,19 @@ @Test @MediumTest public void testTurnOffArchiveThroughSettings() throws Exception { + mTabArchiveSettings.setShouldShowDialogIphForTesting(true); addArchivedTab(new GURL("https://google.com"), "test 1"); addArchivedTab(new GURL("https://google.com"), "test 2"); - enterTabSwitcherAndShowDialog(2); - SettingsActivity activity = - ActivityTestUtils.waitForActivity( - InstrumentationRegistry.getInstrumentation(), - SettingsActivity.class, - new Runnable() { - @Override - public void run() { - mRobot.actionRobot - .clickToolbarMenuButton() - .clickToolbarMenuItem("Settings"); - } - }); + RegularTabSwitcherStation tabSwitcherStation = mInitialPage.openRegularTabSwitcher(); + ArchivedTabsDialogStation archivedTabsDialogStation = + tabSwitcherStation.expectArchiveMessageCard().openArchivedTabsDialog(); + + archivedTabsDialogStation.openSettings( + () -> { + mRobot.actionRobot.clickToolbarMenuButton().clickToolbarMenuItem("Settings"); + }); mRobot.resultRobot.verifyTabListEditorIsHidden(); - ActivityTestUtils.waitForFragmentToAttach(activity, TabArchiveSettingsFragment.class); assertEquals(1, mUserActionTester.getActionCount("Tabs.OpenArchivedTabsSettingsMenuItem")); mArchivedTabModelOrchestrator.resetRescueArchivedTabsForTesting(); @@ -365,7 +383,9 @@ public void testCloseAllArchivedTabs() throws Exception { addArchivedTab(new GURL("https://google.com"), "test 1"); addArchivedTab(new GURL("https://google.com"), "test 2"); - enterTabSwitcherAndShowDialog(2); + + RegularTabSwitcherStation tabSwitcherStation = mInitialPage.openRegularTabSwitcher(); + tabSwitcherStation.expectArchiveMessageCard().openArchivedTabsDialog(); HistogramWatcher histogramExpectation = HistogramWatcher.newSingleRecordWatcher("Tabs.CloseAllArchivedTabs.TabCount", 2); @@ -385,7 +405,9 @@ public void testCloseAllArchivedTabs_Cancel() throws Exception { addArchivedTab(new GURL("https://google.com"), "test 1"); addArchivedTab(new GURL("https://google.com"), "test 2"); - enterTabSwitcherAndShowDialog(2); + + RegularTabSwitcherStation tabSwitcherStation = mInitialPage.openRegularTabSwitcher(); + tabSwitcherStation.expectArchiveMessageCard().openArchivedTabsDialog(); onView(withText("2 inactive tabs")).check(matches(isDisplayed())); onView(withText("Close all inactive tabs")).perform(click()); @@ -400,7 +422,9 @@ public void testSelectTabs() { addArchivedTab(new GURL("https://google.com"), "test 1"); addArchivedTab(new GURL("https://google.com"), "test 2"); - enterTabSwitcherAndShowDialog(2); + + RegularTabSwitcherStation tabSwitcherStation = mInitialPage.openRegularTabSwitcher(); + tabSwitcherStation.expectArchiveMessageCard().openArchivedTabsDialog(); mRobot.actionRobot.clickToolbarMenuButton().clickToolbarMenuItem("Select tabs"); assertEquals(1, mUserActionTester.getActionCount("Tabs.SelectArchivedTabsMenuItem")); @@ -428,7 +452,9 @@ public void testSelectionModeMenuItems() { addArchivedTab(new GURL("https://google.com"), "test 1"); addArchivedTab(new GURL("https://google.com"), "test 2"); - enterTabSwitcherAndShowDialog(2); + + RegularTabSwitcherStation tabSwitcherStation = mInitialPage.openRegularTabSwitcher(); + tabSwitcherStation.expectArchiveMessageCard().openArchivedTabsDialog(); mRobot.actionRobot.clickToolbarMenuButton().clickToolbarMenuItem("Select tabs"); mRobot.actionRobot.clickToolbarMenuButton(); @@ -461,7 +487,10 @@ addArchivedTab(new GURL("https://google.com"), "test 1"); addArchivedTab(new GURL("https://google.com"), "test 2"); addArchivedTab(new GURL("https://google.com"), "test 3"); - enterTabSwitcherAndShowDialog(3); + + RegularTabSwitcherStation tabSwitcherStation = mInitialPage.openRegularTabSwitcher(); + tabSwitcherStation.expectArchiveMessageCard().openArchivedTabsDialog(); + assertEquals(1, mRegularTabModel.getCount()); assertEquals(3, mArchivedTabModel.getCount()); @@ -489,7 +518,10 @@ addArchivedTab(new GURL("https://google.com"), "test 1"); addArchivedTab(new GURL("https://google.com"), "test 2"); addArchivedTab(new GURL("https://google.com"), "test 3"); - enterTabSwitcherAndShowDialog(3); + + RegularTabSwitcherStation tabSwitcherStation = mInitialPage.openRegularTabSwitcher(); + tabSwitcherStation.expectArchiveMessageCard().openArchivedTabsDialog(); + assertEquals(1, mRegularTabModel.getCount()); assertEquals(3, mArchivedTabModel.getCount()); @@ -519,7 +551,10 @@ addArchivedTab(new GURL("https://google.com"), "test 1"); addArchivedTab(new GURL("https://google.com"), "test 2"); addArchivedTab(new GURL("https://google.com"), "test 3"); - enterTabSwitcherAndShowDialog(3); + + RegularTabSwitcherStation tabSwitcherStation = mInitialPage.openRegularTabSwitcher(); + tabSwitcherStation.expectArchiveMessageCard().openArchivedTabsDialog(); + assertEquals(1, mRegularTabModel.getCount()); assertEquals(3, mArchivedTabModel.getCount()); @@ -556,14 +591,16 @@ @MediumTest public void testCloseDialogWithBackButton() throws Exception { addArchivedTab(new GURL("https://google.com"), "test 2"); - enterTabSwitcherAndShowDialog(1); + + RegularTabSwitcherStation tabSwitcherStation = mInitialPage.openRegularTabSwitcher(); + tabSwitcherStation.expectArchiveMessageCard().openArchivedTabsDialog(); ThreadUtils.runOnUiThreadBlocking( () -> { - mActivityTestRule.getActivity().getOnBackPressedDispatcher().onBackPressed(); + mCtaTestRule.getActivity().getOnBackPressedDispatcher().onBackPressed(); }); mRobot.resultRobot.verifyTabListEditorIsHidden(); - assertNull(mActivityTestRule.getActivity().findViewById(R.id.archived_tabs_dialog)); + assertNull(mCtaTestRule.getActivity().findViewById(R.id.archived_tabs_dialog)); } @Test @@ -576,7 +613,9 @@ addArchivedTab(new GURL("https://test.com"), "test 2"); assertEquals(1, mRegularTabModel.getCount()); assertEquals(2, mArchivedTabModel.getCount()); - enterTabSwitcherAndShowDialog(2); + + RegularTabSwitcherStation tabSwitcherStation = mInitialPage.openRegularTabSwitcher(); + tabSwitcherStation.expectArchiveMessageCard().openArchivedTabsDialog(); mRobot.actionRobot.clickItemAtAdapterPosition(0); mRobot.resultRobot.verifyTabListEditorIsHidden(); @@ -584,8 +623,8 @@ assertEquals(1, mArchivedTabModel.getCount()); LayoutTestUtils.waitForLayout( - mActivityTestRule.getActivity().getLayoutManager(), LayoutType.BROWSING); - Tab activityTab = mActivityTestRule.getActivity().getActivityTabProvider().get(); + mCtaTestRule.getActivity().getLayoutManager(), LayoutType.BROWSING); + Tab activityTab = mCtaTestRule.getActivity().getActivityTabProvider().get(); CriteriaHelper.pollUiThread(() -> activityTab.getId() == tabId); assertEquals(1, mUserActionTester.getActionCount("Tabs.RestoreSingleTab")); } @@ -595,7 +634,10 @@ public void testCloseArchivedTab() throws Exception { addArchivedTab(new GURL("https://google.com"), "test 1"); addArchivedTab(new GURL("https://google.com"), "test 2"); - enterTabSwitcherAndShowDialog(2); + + RegularTabSwitcherStation tabSwitcherStation = mInitialPage.openRegularTabSwitcher(); + tabSwitcherStation.expectArchiveMessageCard().openArchivedTabsDialog(); + onView(withText("2 inactive tabs")).check(matches(isDisplayed())); mRobot.actionRobot.clickViewIdAtAdapterPosition(1, R.id.action_button); @@ -609,7 +651,10 @@ public void testCloseArchivedTab_SnackbarResetForTabSwitcher() throws Exception { addArchivedTab(new GURL("https://google.com"), "test 1"); addArchivedTab(new GURL("https://google.com"), "test 2"); - enterTabSwitcherAndShowDialog(2); + + RegularTabSwitcherStation tabSwitcherStation = mInitialPage.openRegularTabSwitcher(); + tabSwitcherStation.expectArchiveMessageCard().openArchivedTabsDialog(); + onView(withText("2 inactive tabs")).check(matches(isDisplayed())); mRobot.actionRobot.clickViewIdAtAdapterPosition(1, R.id.action_button); @@ -618,7 +663,7 @@ .verifyUndoSnackbarWithTextIsShown("Closed google"); ThreadUtils.runOnUiThreadBlocking( () -> { - mActivityTestRule.getActivity().getOnBackPressedDispatcher().onBackPressed(); + mCtaTestRule.getActivity().getOnBackPressedDispatcher().onBackPressed(); }); mRobot.resultRobot.verifyTabListEditorIsHidden(); @@ -644,6 +689,7 @@ RecyclerView.ViewHolder viewHolder = recyclerView.findViewHolderForAdapterPosition(index); if (viewHolder.itemView == null) return; + viewHolder.itemView.findViewById(R.id.action_button).performClick(); } }); @@ -651,7 +697,7 @@ } @Test - @MediumTest + @LargeTest public void testContentDescription() { onView(withContentDescription(R.string.accessibility_tab_selection_editor_back_button)); onView(withContentDescription(R.string.accessibility_tab_selection_editor)); @@ -663,7 +709,10 @@ for (int i = 0; i < 50; i++) { addArchivedTab(new GURL("https://google.com?q=" + i), "test " + i); } - enterTabSwitcherAndShowDialog(50); + + RegularTabSwitcherStation tabSwitcherStation = mInitialPage.openRegularTabSwitcher(); + tabSwitcherStation.expectArchiveMessageCard().openArchivedTabsDialog(); + onView(withText("50 inactive tabs")).check(matches(isDisplayed())); mRobot.resultRobot.verifyTabListEditorIsVisible().verifyAdapterHasItemCount(50); @@ -687,11 +736,15 @@ addArchivedTab(new GURL("https://google.com"), "test 9"); addArchivedTab(new GURL("https://google.com"), "test 10"); addArchivedTab(new GURL("https://google.com"), "test 11"); - enterTabSwitcherAndShowDialog(11); + + RegularTabSwitcherStation tabSwitcherStation = mInitialPage.openRegularTabSwitcher(); + tabSwitcherStation.expectArchiveMessageCard().openArchivedTabsDialog(); + onView(withText("11 inactive tabs")).check(matches(isDisplayed())); mRobot.resultRobot.verifyTabListEditorIsVisible().verifyAdapterHasItemCount(11); // When there is more than a page of tabs, then the bottom container should have a shadow. + onView(withId(R.id.close_all_tabs_button_container_shadow)).check(matches(isDisplayed())); // When the recycler view is scrolled all the way down, the shadow should be hidden. @@ -709,15 +762,18 @@ @Restriction({DeviceFormFactor.TABLET}) @Feature({"RenderTest"}) public void testMessageResizedOnTablet() throws Exception { - ChromeTabbedActivity cta = mActivityTestRule.getActivity(); + ChromeTabbedActivity cta = mCtaTestRule.getActivity(); ActivityTestUtils.rotateActivityToOrientation(cta, ORIENTATION_PORTRAIT); addArchivedTab(new GURL("https://www.google.com/"), "test 2"); - TabUiTestHelper.enterTabSwitcher(mActivityTestRule.getActivity()); + + TabUiTestHelper.enterTabSwitcher(mCtaTestRule.getActivity()); mRenderTestRule.render( cta.findViewById(R.id.pane_frame), "archived_tabs_message_tablet_portrait"); + ActivityTestUtils.rotateActivityToOrientation(cta, ORIENTATION_LANDSCAPE); mRenderTestRule.render( cta.findViewById(R.id.pane_frame), "archived_tabs_message_tablet_landscape"); + ActivityTestUtils.clearActivityOrientation(cta); } @@ -726,14 +782,16 @@ @Restriction({DeviceFormFactor.TABLET}) @Feature({"RenderTest"}) public void testIphMessageResizedOnTablet() throws Exception { - ChromeTabbedActivity cta = mActivityTestRule.getActivity(); + ChromeTabbedActivity cta = mCtaTestRule.getActivity(); ActivityTestUtils.rotateActivityToOrientation(cta, ORIENTATION_PORTRAIT); addArchivedTab(new GURL("https://www.google1.com/"), "test 1"); addArchivedTab(new GURL("https://www.google2.com/"), "test 2"); addArchivedTab(new GURL("https://www.google3.com/"), "test 3"); addArchivedTab(new GURL("https://www.google4.com/"), "test 4"); mTabArchiveSettings.setShouldShowDialogIphForTesting(true); - enterTabSwitcherAndShowDialog(4); + + RegularTabSwitcherStation tabSwitcherStation = mInitialPage.openRegularTabSwitcher(); + tabSwitcherStation.expectArchiveMessageCard().openArchivedTabsDialog(); mRenderTestRule.render( cta.findViewById(R.id.archived_tabs_dialog), @@ -745,27 +803,6 @@ ActivityTestUtils.clearActivityOrientation(cta); } - private void enterTabSwitcherAndShowDialog(int numOfArchivedTabs) { - // Enter the tab switcher and click the message. - TabUiTestHelper.enterTabSwitcher(mActivityTestRule.getActivity()); - showDialog(numOfArchivedTabs); - } - - private void showDialog(int numOfArchivedTabs) { - String tabsText = - mActivityTestRule - .getActivity() - .getResources() - .getQuantityString( - R.plurals.archived_tab_card_title, - numOfArchivedTabs, - numOfArchivedTabs); - onViewWaiting(withText(tabsText)).perform(click()); - mRobot.resultRobot.verifyTabListEditorIsVisible(); - mTimesShown++; - assertEquals(mTimesShown, mUserActionTester.getActionCount("Tabs.ArchivedTabsDialogShown")); - } - private Tab addArchivedTab(GURL url, String title) { return ThreadUtils.runOnUiThreadBlocking( () ->
diff --git a/chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/TabSwitcherLayoutPTTest.java b/chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/TabSwitcherLayoutPTTest.java index 88878606..e29b617 100644 --- a/chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/TabSwitcherLayoutPTTest.java +++ b/chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/TabSwitcherLayoutPTTest.java
@@ -4,10 +4,8 @@ package org.chromium.chrome.browser.tasks.tab_management; -import static androidx.test.espresso.Espresso.onView; import static androidx.test.espresso.assertion.ViewAssertions.matches; import static androidx.test.espresso.matcher.ViewMatchers.isCompletelyDisplayed; -import static androidx.test.espresso.matcher.ViewMatchers.isDisplayed; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; @@ -19,11 +17,8 @@ import static org.chromium.chrome.browser.flags.ChromeFeatureList.ANDROID_ELEGANT_TEXT_HEIGHT; import static org.chromium.chrome.browser.flags.ChromeFeatureList.TAB_GROUP_PARITY_BOTTOM_SHEET_ANDROID; -import android.content.res.ColorStateList; import android.graphics.Bitmap; import android.graphics.drawable.BitmapDrawable; -import android.graphics.drawable.GradientDrawable; -import android.widget.FrameLayout; import android.widget.ImageView; import androidx.test.filters.MediumTest; @@ -58,7 +53,6 @@ import org.chromium.chrome.test.transit.hub.RegularTabSwitcherStation; import org.chromium.chrome.test.transit.hub.TabSwitcherGroupCardFacility; import org.chromium.chrome.test.transit.hub.TabSwitcherListEditorFacility; -import org.chromium.chrome.test.transit.hub.TabSwitcherStation; import org.chromium.chrome.test.transit.hub.UndoSnackbarFacility; import org.chromium.chrome.test.transit.ntp.IncognitoNewTabPageStation; import org.chromium.chrome.test.transit.ntp.RegularNewTabPageStation; @@ -68,7 +62,6 @@ import org.chromium.chrome.test.util.ChromeRenderTestRule; import org.chromium.components.embedder_support.util.UrlConstants; import org.chromium.components.tab_groups.TabGroupColorId; -import org.chromium.components.tab_groups.TabGroupColorPickerUtils; import org.chromium.ui.base.PageTransition; import java.io.IOException; @@ -435,11 +428,10 @@ editor = editor.addTabToSelection(0, firstTabId); editor = editor.addTabToSelection(1, secondTabId); NewTabGroupDialogFacility dialog = editor.openAppMenuWithEditor().groupTabs(); - dialog.pressBack(); + TabSwitcherGroupCardFacility card = dialog.pressBack(); // Verify the color icon exists and that the dialog is dismissed via another action - onView(TabSwitcherStation.TAB_GROUP_COLOR_ICON_VIEW.getViewMatcher()) - .check(matches(isDisplayed())); + card.expectColor(TabGroupColorId.GREY); watcher.assertExpected(); // Open NTP PageStation for InitialStateRule to reset @@ -477,8 +469,9 @@ dialog.pressDone(); // Assert that the expected fields are correct - tabSwitcher.expectGroupCard(List.of(firstTabId, secondTabId), "Test"); - verifyGroupCardColor(TabGroupColorId.BLUE); + tabSwitcher + .expectGroupCard(List.of(firstTabId, secondTabId), "Test") + .expectColor(TabGroupColorId.BLUE); histograms.assertExpected(); // Open NTP PageStation for InitialStateRule to reset @@ -505,10 +498,11 @@ dialog.pressDone(); // Assert that the expected fields are correct - tabSwitcher.expectGroupCard( - List.of(firstTabId, secondTabId), - TabSwitcherGroupCardFacility.DEFAULT_N_TABS_TITLE); - verifyGroupCardColor(TabGroupColorId.GREY); + tabSwitcher + .expectGroupCard( + List.of(firstTabId, secondTabId), + TabSwitcherGroupCardFacility.DEFAULT_N_TABS_TITLE) + .expectColor(TabGroupColorId.GREY); // Open NTP PageStation for InitialStateRule to reset RegularNewTabPageStation ntp = tabSwitcher.openNewTab(); @@ -536,10 +530,11 @@ dialog.pressBack(); // Assert that the expected fields are correct - tabSwitcher.expectGroupCard( - List.of(firstTabId, secondTabId), - TabSwitcherGroupCardFacility.DEFAULT_N_TABS_TITLE); - verifyGroupCardColor(TabGroupColorId.BLUE); + tabSwitcher + .expectGroupCard( + List.of(firstTabId, secondTabId), + TabSwitcherGroupCardFacility.DEFAULT_N_TABS_TITLE) + .expectColor(TabGroupColorId.BLUE); // Open NTP PageStation for InitialStateRule to reset RegularNewTabPageStation ntp = tabSwitcher.openNewTab(); @@ -570,10 +565,11 @@ dialog.pressBack(); // Assert that the expected fields are correct - tabSwitcher.expectGroupCard( - List.of(firstTabId, secondTabId), - TabSwitcherGroupCardFacility.DEFAULT_N_TABS_TITLE); - verifyGroupCardColor(TabGroupColorId.GREY); + tabSwitcher + .expectGroupCard( + List.of(firstTabId, secondTabId), + TabSwitcherGroupCardFacility.DEFAULT_N_TABS_TITLE) + .expectColor(TabGroupColorId.GREY); // Open NTP PageStation for InitialStateRule to reset RegularNewTabPageStation ntp = tabSwitcher.openNewTab(); @@ -601,8 +597,9 @@ dialog.pressBack(); // Assert that the expected fields are correct - tabSwitcher.expectGroupCard(List.of(firstTabId, secondTabId), "Test"); - verifyGroupCardColor(TabGroupColorId.BLUE); + tabSwitcher + .expectGroupCard(List.of(firstTabId, secondTabId), "Test") + .expectColor(TabGroupColorId.BLUE); // Open NTP PageStation for InitialStateRule to reset RegularNewTabPageStation ntp = tabSwitcher.openNewTab(); @@ -682,32 +679,9 @@ return tabSwitcher.leaveHubToPreviousTabViaBack(destinationBuiderFactory.get()); } - private void verifyGroupCardColor(@TabGroupColorId int color) { - onView(TabSwitcherStation.TAB_GROUP_COLOR_ICON_VIEW.getViewMatcher()) - .check( - (v, noMatchException) -> { - if (noMatchException != null) throw noMatchException; - - FrameLayout containerView = (FrameLayout) v; - FrameLayout colorView = (FrameLayout) containerView.getChildAt(0); - GradientDrawable drawable = - (GradientDrawable) colorView.getBackground(); - - assertEquals( - ColorStateList.valueOf( - TabGroupColorPickerUtils - .getTabGroupColorPickerItemColor( - mCtaTestRule.getActivity(), - color, - false)), - drawable.getColor()); - }); - } - @Test @MediumTest public void testUrlUpdatedNotCrashing_ForTabNotInCurrentModel() throws Exception { - ChromeTabbedActivity cta = mCtaTestRule.getActivity(); WebPageStation regularPage = mCtaTestRule.startOnBlankPage(); Tab regularTab = regularPage.loadedTabElement.get(); IncognitoNewTabPageStation incognitoPage = regularPage.openNewIncognitoTabFast();
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/compositor/overlays/strip/StripLayoutHelper.java b/chrome/android/java/src/org/chromium/chrome/browser/compositor/overlays/strip/StripLayoutHelper.java index 1855552..5540646c 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/compositor/overlays/strip/StripLayoutHelper.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/compositor/overlays/strip/StripLayoutHelper.java
@@ -2125,6 +2125,9 @@ * @param groupTitle The title of the group. */ private void showTabGroupContextMenuHelper(StripLayoutGroupTitle groupTitle) { + // No-op if the tab group isn't found in sync (it might have been removed from another + // device and will be cleaned up here soon). + if (groupTitle.getTabGroupId() == null) return; // Popup menu requires screen coordinates for anchor view. Get absolute position for title. RectProvider anchorRectProvider = new RectProvider(); getAnchorRect(groupTitle, anchorRectProvider);
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/features/CustomTabDimensionUtils.java b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/features/CustomTabDimensionUtils.java index a3369c8f..6cae1e7 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/features/CustomTabDimensionUtils.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/features/CustomTabDimensionUtils.java
@@ -15,8 +15,15 @@ import androidx.annotation.Px; import androidx.annotation.RequiresApi; +import org.chromium.base.MathUtils; +import org.chromium.chrome.browser.browserservices.intents.BrowserServicesIntentDataProvider; + /** Utility methods for calculating the dimensions of a Custom Tab. */ public class CustomTabDimensionUtils { + private static final int WINDOW_WIDTH_EXPANDED_CUTOFF_DP = 840; + private static final float MINIMAL_WIDTH_RATIO_EXPANDED = 0.33f; + private static final float MINIMAL_WIDTH_RATIO_MEDIUM = 0.5f; + public static int getDisplayWidth(Activity activity) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { return getDisplayWidthR(activity); @@ -46,4 +53,44 @@ display.getSize(size); return size.x; } + + /** + * Initial width of the CCT based on the screen metrics and intent data. + * + * @param activity The {@link Activity} for the screen metrics. + * @param intentDataProvider The {@link BrowserServicesIntentDataProvider} for the intent data. + * @return The initial width of the CCT. + */ + @Px + public static int getInitialWidth( + Activity activity, BrowserServicesIntentDataProvider intentDataProvider) { + int unclampedWidth = intentDataProvider.getInitialActivityWidth(); + int displayWidth = getDisplayWidth(activity); + + if (unclampedWidth <= 0) { + return getDisplayWidth(activity); + } + + int displayWidthDp = + (int) (displayWidth / activity.getResources().getDisplayMetrics().density); + return getInitialWidth(unclampedWidth, displayWidth, displayWidthDp); + } + + /** + * Initial width of the CCT based on the width values. + * + * @param unclampedWidth Unclamped width of the CCT. + * @param displayWidth Display width in pixels. + * @param displayWidthDp Display width in dps. + * @return The initial width of the CCT. + */ + @Px + public static int getInitialWidth( + @Px int unclampedWidth, @Px int displayWidth, int displayWidthDp) { + float minWidthRatio = + displayWidthDp < WINDOW_WIDTH_EXPANDED_CUTOFF_DP + ? MINIMAL_WIDTH_RATIO_MEDIUM + : MINIMAL_WIDTH_RATIO_EXPANDED; + return MathUtils.clamp(unclampedWidth, displayWidth, (int) (displayWidth * minWidthRatio)); + } }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/features/partialcustomtab/PartialCustomTabSideSheetStrategy.java b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/features/partialcustomtab/PartialCustomTabSideSheetStrategy.java index fb3a958e..24ef633 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/features/partialcustomtab/PartialCustomTabSideSheetStrategy.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/features/partialcustomtab/PartialCustomTabSideSheetStrategy.java
@@ -34,11 +34,11 @@ import androidx.browser.customtabs.CustomTabsCallback; import androidx.browser.customtabs.CustomTabsIntent; -import org.chromium.base.MathUtils; import org.chromium.base.SysUtils; import org.chromium.chrome.R; import org.chromium.chrome.browser.browserservices.intents.BrowserServicesIntentDataProvider; import org.chromium.chrome.browser.customtabs.CustomTabIntentDataProvider; +import org.chromium.chrome.browser.customtabs.features.CustomTabDimensionUtils; import org.chromium.chrome.browser.customtabs.features.toolbar.CustomTabToolbar; import org.chromium.chrome.browser.customtabs.features.toolbar.CustomTabToolbarButtonsCoordinator; import org.chromium.chrome.browser.flags.ChromeFeatureList; @@ -51,10 +51,7 @@ * class should be owned by the CustomTabActivity. */ public class PartialCustomTabSideSheetStrategy extends PartialCustomTabBaseStrategy { - private static final int WINDOW_WIDTH_EXPANDED_CUTOFF_DP = 840; private static final int SIDE_SHEET_UI_DELAY = 20; - private static final float MINIMAL_WIDTH_RATIO_EXPANDED = 0.33f; - private static final float MINIMAL_WIDTH_RATIO_MEDIUM = 0.5f; private static final NoAnimator NO_ANIMATOR = new NoAnimator(); private final @Px int mUnclampedInitialWidth; @@ -454,11 +451,8 @@ private int calculateWidth(int unclampedWidth) { int displayWidth = mVersionCompat.getDisplayWidth(); int displayWidthDp = mVersionCompat.getDisplayWidthDp(); - float minWidthRatio = - displayWidthDp < WINDOW_WIDTH_EXPANDED_CUTOFF_DP - ? MINIMAL_WIDTH_RATIO_MEDIUM - : MINIMAL_WIDTH_RATIO_EXPANDED; - return MathUtils.clamp(unclampedWidth, displayWidth, (int) (displayWidth * minWidthRatio)); + return CustomTabDimensionUtils.getInitialWidth( + unclampedWidth, displayWidth, displayWidthDp); } private float calculateElevation() {
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/features/toolbar/CustomTabToolbar.java b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/features/toolbar/CustomTabToolbar.java index 231ca84..7434e87 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/features/toolbar/CustomTabToolbar.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/features/toolbar/CustomTabToolbar.java
@@ -331,7 +331,7 @@ if (omniboxParams != null) { setOmniboxParams(omniboxParams); } - calculateToolbarWidthBeforeMeasure(activity); + calculateToolbarWidthBeforeMeasure(activity, intentDataProvider); inflateAndPositionToolbarElements(mToolbarWidth); } @@ -344,10 +344,9 @@ inflateAndPositionToolbarElements(mToolbarWidth); } - private void calculateToolbarWidthBeforeMeasure(Activity activity) { - // TODO(crbug.com/402213312): Come up with a better way to calculate the toolbar width that - // handles pCCT. - mToolbarWidth = CustomTabDimensionUtils.getDisplayWidth(activity); + private void calculateToolbarWidthBeforeMeasure( + Activity activity, BrowserServicesIntentDataProvider intentDataProvider) { + mToolbarWidth = CustomTabDimensionUtils.getInitialWidth(activity, intentDataProvider); } /**
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/hub/HubLayoutPublicTransitTest.java b/chrome/android/java/src/org/chromium/chrome/browser/hub/HubLayoutPublicTransitTest.java index 3504e8c..2c43ab85 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/hub/HubLayoutPublicTransitTest.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/hub/HubLayoutPublicTransitTest.java
@@ -4,8 +4,6 @@ package org.chromium.chrome.browser.hub; -import static org.junit.Assert.assertEquals; - import static org.chromium.base.test.transit.TransitAsserts.assertFinalDestination; import static org.chromium.chrome.browser.flags.ChromeFeatureList.START_SURFACE_RETURN_TIME; @@ -82,23 +80,21 @@ @Test @LargeTest public void testChangeTabSwitcherPanes() { - WebPageStation firstPage = mCtaTestRule.startOnBlankPage(); - IncognitoNewTabPageStation incognitoNewTabPage = firstPage.openNewIncognitoTabFast(); - IncognitoTabSwitcherStation incognitoTabSwitcher = - incognitoNewTabPage.openIncognitoTabSwitcher(); - assertEquals( - incognitoTabSwitcher, - incognitoTabSwitcher.selectPane( - PaneId.INCOGNITO_TAB_SWITCHER, IncognitoTabSwitcherStation.class)); + mCtaTestRule + .startOnBlankPage() + .openNewIncognitoTabFast() + .openIncognitoTabSwitcher(); - RegularTabSwitcherStation tabSwitcher = + RegularTabSwitcherStation regularTabSwitcher = incognitoTabSwitcher.selectPane( PaneId.TAB_SWITCHER, RegularTabSwitcherStation.class); + incognitoTabSwitcher = + regularTabSwitcher.selectPane( + PaneId.INCOGNITO_TAB_SWITCHER, IncognitoTabSwitcherStation.class); // Go back to a PageStation for BlankCTATabInitialStateRule to reset state. - WebPageStation blankTab = tabSwitcher.selectTabAtIndex(0, WebPageStation.newBuilder()); - assertFinalDestination(blankTab); + incognitoTabSwitcher.selectTabAtIndex(0, IncognitoNewTabPageStation.newBuilder()); } @Test
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/native_page/ContextMenuManager.java b/chrome/android/java/src/org/chromium/chrome/browser/native_page/ContextMenuManager.java index ca7e47da..a81da57 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/native_page/ContextMenuManager.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/native_page/ContextMenuManager.java
@@ -18,6 +18,7 @@ import org.chromium.chrome.R; import org.chromium.chrome.browser.flags.ChromeFeatureList; import org.chromium.chrome.browser.offlinepages.OfflinePageBridge; +import org.chromium.chrome.browser.suggestions.tile.TileUtils; import org.chromium.chrome.browser.ui.native_page.TouchEnabledDelegate; import org.chromium.components.browser_ui.widget.BrowserUiListMenuUtils; import org.chromium.ui.base.WindowAndroid.OnCloseContextMenuListener; @@ -376,8 +377,13 @@ GURL itemUrl = delegate.getUrl(); return itemUrl != null && OfflinePageBridge.canSavePage(itemUrl); } - case ContextMenuItemId.REMOVE: // Fall through. - case ContextMenuItemId.PIN_THIS_SHORTCUT: // Fall through. + case ContextMenuItemId.REMOVE: + return true; + case ContextMenuItemId.PIN_THIS_SHORTCUT: + { + GURL itemUrl = delegate.getUrl(); + return TileUtils.isValidCustomTileUrl(itemUrl); + } case ContextMenuItemId.EDIT_SHORTCUT: // Fall through. case ContextMenuItemId.UNPIN: return true;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/privacy_sandbox/ChromeTrackingProtectionDelegate.java b/chrome/android/java/src/org/chromium/chrome/browser/privacy_sandbox/ChromeTrackingProtectionDelegate.java index a0f64efb..8825232 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/privacy_sandbox/ChromeTrackingProtectionDelegate.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/privacy_sandbox/ChromeTrackingProtectionDelegate.java
@@ -17,9 +17,11 @@ public class ChromeTrackingProtectionDelegate implements TrackingProtectionDelegate { private final Profile mProfile; + private final TrackingProtectionSettingsBridge mTrackingProtectionSettingsBridge; public ChromeTrackingProtectionDelegate(Profile profile) { mProfile = profile; + mTrackingProtectionSettingsBridge = new TrackingProtectionSettingsBridge(profile); } @Override @@ -53,11 +55,26 @@ } @Override + public boolean isIpProtectionManaged() { + return UserPrefs.get(mProfile).isManagedPreference(Pref.IP_PROTECTION_ENABLED); + } + + @Override + public boolean isFingerprintingProtectionManaged() { + return UserPrefs.get(mProfile).isManagedPreference(Pref.FINGERPRINTING_PROTECTION_ENABLED); + } + + @Override public void setIpProtection(boolean enabled) { UserPrefs.get(mProfile).setBoolean(Pref.IP_PROTECTION_ENABLED, enabled); } @Override + public boolean isIpProtectionDisabledForEnterprise() { + return mTrackingProtectionSettingsBridge.isIpProtectionDisabledForEnterprise(); + } + + @Override public boolean isFingerprintingProtectionUxEnabled() { return ChromeFeatureList.isEnabled(ChromeFeatureList.FINGERPRINTING_PROTECTION_UX); }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/suggestions/SuggestionsConfig.java b/chrome/android/java/src/org/chromium/chrome/browser/suggestions/SuggestionsConfig.java index 5feff951..4a08ec0 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/suggestions/SuggestionsConfig.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/suggestions/SuggestionsConfig.java
@@ -29,6 +29,12 @@ /** Maximum number of custom tiles supported. In C++ backend this is `kMaxNumCustomLinks`. */ public static final int MAX_NUM_CUSTOM_LINKS = 10; + /** Maximum length of Custom Tiles name. */ + public static final int MAX_CUSTOM_TILES_NAME_LENGTH = 50; + + /** Maximum length of Custom Tiles URL. */ + public static final int MAX_CUSTOM_TILES_URL_LENGTH = 2083; + /** * Default value of referrer URL for content suggestions. It must be kept in sync with * //components/feed/feed_feature_list.cc.
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/suggestions/tile/TileGroup.java b/chrome/android/java/src/org/chromium/chrome/browser/suggestions/tile/TileGroup.java index cf116b0..425e61f 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/suggestions/tile/TileGroup.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/suggestions/tile/TileGroup.java
@@ -673,7 +673,8 @@ if (tile == null) return; GURL url = tile.getUrl(); - assignCustomLinkAndUpdateOnSuccess(url, tile.getTitle(), url); + String name = TileUtils.formatCustomTileName(tile.getTitle(), url); + assignCustomLinkAndUpdateOnSuccess(url, name, url); } @Override @@ -708,6 +709,10 @@ } private boolean addCustomLinkAndUpdateOnSuccess(String name, GURL url) { + if (!TileUtils.isValidCustomTileName(name) || !TileUtils.isValidCustomTileUrl(url)) { + return false; + } + // On success, onSiteSuggestionsAvailable() triggers. mPendingChanges.customTilesIndicator = true; boolean success = mTileGroupDelegate.addCustomLink(name, url); @@ -719,6 +724,11 @@ private boolean assignCustomLinkAndUpdateOnSuccess( GURL keyUrl, String name, @Nullable GURL url) { + if (!TileUtils.isValidCustomTileName(name) + || (url != null && !TileUtils.isValidCustomTileUrl(url))) { + return false; + } + // On success, onSiteSuggestionsAvailable() triggers. mPendingChanges.customTilesIndicator = true; boolean success = mTileGroupDelegate.assignCustomLink(keyUrl, name, url);
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/suggestions/tile/TileUtils.java b/chrome/android/java/src/org/chromium/chrome/browser/suggestions/tile/TileUtils.java index 305a9e2..236b176 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/suggestions/tile/TileUtils.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/suggestions/tile/TileUtils.java
@@ -4,12 +4,31 @@ package org.chromium.chrome.browser.suggestions.tile; +import android.text.TextUtils; + import org.chromium.chrome.browser.profiles.Profile; import org.chromium.chrome.browser.search_engines.TemplateUrlServiceFactory; +import org.chromium.chrome.browser.suggestions.SuggestionsConfig; +import org.chromium.components.embedder_support.util.UrlConstants; import org.chromium.components.search_engines.TemplateUrlService; +import org.chromium.url.GURL; + +import java.util.Arrays; +import java.util.HashSet; +import java.util.Set; /** Utility class for {@link Tile}s related queries or operations. */ public class TileUtils { + private static final Set<String> CUSTOM_TILE_SCHEMES = + new HashSet<String>( + Arrays.asList( + UrlConstants.CHROME_SCHEME, + UrlConstants.CHROME_NATIVE_SCHEME, + UrlConstants.FTP_SCHEME, + UrlConstants.HTTP_SCHEME, + UrlConstants.HTTPS_SCHEME, + UrlConstants.FILE_SCHEME)); + /** * Returns whether or not {@param tile} represents a Search query. This includes suggestions * from Repeatable Queries. @@ -20,4 +39,32 @@ return searchService != null && searchService.isSearchResultsPageFromDefaultSearchProvider(tile.getUrl()); } + + /** + * @return Custom tile name, truncated (if needed) from {@param name} if non-empty, or {@param + * url} otherwise. The result can still fail {@link isValidCustomTileName()}, e.g., when + * both {@param name} and {@param url} are empty then the result is also empty. + */ + public static String formatCustomTileName(String name, GURL url) { + String nameToUse = TextUtils.isEmpty(name) ? url.getSpec() : name; + return nameToUse.length() <= SuggestionsConfig.MAX_CUSTOM_TILES_NAME_LENGTH + ? nameToUse + : nameToUse.substring(0, SuggestionsConfig.MAX_CUSTOM_TILES_NAME_LENGTH); + } + + /** + * @return Whether {@param url} is a valid name for Custom Tiles. + */ + public static boolean isValidCustomTileName(String name) { + return !name.isEmpty() && name.length() <= SuggestionsConfig.MAX_CUSTOM_TILES_NAME_LENGTH; + } + + /** + * @return Whether {@param url} is a valid URL for usage Custom Tiles. + */ + public static boolean isValidCustomTileUrl(GURL url) { + return !GURL.isEmptyOrInvalid(url) + && url.getSpec().length() <= SuggestionsConfig.MAX_CUSTOM_TILES_URL_LENGTH + && CUSTOM_TILE_SCHEMES.contains(url.getScheme()); + } }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/suggestions/tile/tile_edit_dialog/CustomTileEditMediator.java b/chrome/android/java/src/org/chromium/chrome/browser/suggestions/tile/tile_edit_dialog/CustomTileEditMediator.java index cb6152a..c7c2c2f 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/suggestions/tile/tile_edit_dialog/CustomTileEditMediator.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/suggestions/tile/tile_edit_dialog/CustomTileEditMediator.java
@@ -4,12 +4,11 @@ package org.chromium.chrome.browser.suggestions.tile.tile_edit_dialog; -import android.text.TextUtils; - import org.chromium.build.annotations.Initializer; import org.chromium.build.annotations.NullMarked; import org.chromium.build.annotations.Nullable; import org.chromium.chrome.browser.suggestions.tile.Tile; +import org.chromium.chrome.browser.suggestions.tile.TileUtils; import org.chromium.chrome.browser.suggestions.tile.tile_edit_dialog.CustomTileEditDelegates.DialogMode; import org.chromium.chrome.browser.suggestions.tile.tile_edit_dialog.CustomTileEditDelegates.MediatorToBrowser; import org.chromium.chrome.browser.suggestions.tile.tile_edit_dialog.CustomTileEditDelegates.MediatorToView; @@ -70,7 +69,7 @@ @UrlErrorCode int urlErrorCode = validateUrl(url); boolean success = (urlErrorCode == UrlErrorCode.NONE); if (success) { - String nameToUse = TextUtils.isEmpty(name) ? url.getSpec() : name; + String nameToUse = TileUtils.formatCustomTileName(name, url); if (!mBrowserDelegate.submitChange(nameToUse, url)) { // validateUrl() should have caught the error scenario, but handle again for // robustness. @@ -125,7 +124,7 @@ // If Edit shortcut then skip duplicate checks if URL didn't change. Note that GURL.equals() // conveniently ignores trailing "/". if (mOriginalTile == null || !mOriginalTile.getUrl().equals(url)) { - if (GURL.isEmptyOrInvalid(url)) { + if (!TileUtils.isValidCustomTileUrl(url)) { return UrlErrorCode.INVALID_URL; } if (mBrowserDelegate.isUrlDuplicate(url)) {
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/suggestions/tile/tile_edit_dialog/CustomTileEditView.java b/chrome/android/java/src/org/chromium/chrome/browser/suggestions/tile/tile_edit_dialog/CustomTileEditView.java index 3a3b5ef..0eea5360 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/suggestions/tile/tile_edit_dialog/CustomTileEditView.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/suggestions/tile/tile_edit_dialog/CustomTileEditView.java
@@ -7,6 +7,7 @@ import android.content.Context; import android.content.res.Resources; import android.text.Editable; +import android.text.InputFilter; import android.util.AttributeSet; import android.widget.FrameLayout; import android.widget.TextView.BufferType; @@ -17,6 +18,7 @@ import org.chromium.build.annotations.NullMarked; import org.chromium.build.annotations.Nullable; import org.chromium.chrome.R; +import org.chromium.chrome.browser.suggestions.SuggestionsConfig; import org.chromium.chrome.browser.suggestions.tile.tile_edit_dialog.CustomTileEditDelegates.DialogMode; import org.chromium.chrome.browser.suggestions.tile.tile_edit_dialog.CustomTileEditDelegates.MediatorToView; import org.chromium.chrome.browser.suggestions.tile.tile_edit_dialog.CustomTileEditDelegates.UrlErrorCode; @@ -52,7 +54,15 @@ public void onFinishInflate() { super.onFinishInflate(); mNameField = findViewById(R.id.name_field); + mNameField.setFilters( + new InputFilter[] { + new InputFilter.LengthFilter(SuggestionsConfig.MAX_CUSTOM_TILES_NAME_LENGTH) + }); mUrlField = findViewById(R.id.url_field); + mUrlField.setFilters( + new InputFilter[] { + new InputFilter.LengthFilter(SuggestionsConfig.MAX_CUSTOM_TILES_URL_LENGTH) + }); mUrlField.addTextChangedListener( new EmptyTextWatcher() { @Override
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/tabmodel/TabPersistentStore.java b/chrome/android/java/src/org/chromium/chrome/browser/tabmodel/TabPersistentStore.java index 3d4f8a7f..fc21349 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/tabmodel/TabPersistentStore.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/tabmodel/TabPersistentStore.java
@@ -656,13 +656,14 @@ * Merge the tabs of the other Chrome instance into this instance by reading its tab metadata * file and tab state files. * - * This method should be called after a change in activity state indicates that a merge is + * <p>This method should be called after a change in activity state indicates that a merge is * necessary. #loadState() will take care of merging states on application cold start if needed. * - * If there is currently a merge or load in progress then this method will return early. + * <p>If there is currently a merge or load in progress then this method will return early. */ public void mergeState() { if (mLoadInProgress + || mTabBatchLoader != null || mPersistencePolicy.isMergeInProgress() || !mTabsToRestore.isEmpty()) { Log.d(TAG, "Tab load still in progress when merge was attempted."); @@ -1815,6 +1816,8 @@ private void loadNextTabs() { if (mDestroyed) return; + if (mTabBatchLoader != null) return; + if (mTabsToRestore.isEmpty()) { mNormalTabsRestored = null; mIncognitoTabsRestored = null; @@ -1847,7 +1850,6 @@ recordUniqueTabUrlMetrics(); cleanUpPersistentData(); onStateLoaded(); - mTabBatchLoader = null; Log.d( TAG, "Loaded tab lists; counts: " @@ -1927,6 +1929,7 @@ private class TabBatchLoader { private final LinkedList<TabRestoreDetails> mBatchedTabsToRestore; private LoadTabsTask mLoadTabsTask; + private boolean mCancelled; /** * @param tabsToRestore details of {@link Tab}s which will be read from storage @@ -1942,12 +1945,15 @@ /** Loads {@link TabState} for the batch. */ public void load() { + if (mCancelled) return; + mLoadTabsTask = new LoadTabsTask(mBatchedTabsToRestore); mLoadTabsTask.executeOnTaskRunner(mSequencedTaskRunner); } /** Cancels restoring the batch of tabs. */ public void cancel(boolean mayInterruptIfRunning) { + mCancelled = true; if (mLoadTabsTask != null) { mLoadTabsTask.cancel(mayInterruptIfRunning); } @@ -2167,6 +2173,7 @@ } } + mTabBatchLoader = null; loadNextTabs(); }
diff --git a/chrome/android/javatests/BUILD.gn b/chrome/android/javatests/BUILD.gn index 8bca428..b8dcbe9 100644 --- a/chrome/android/javatests/BUILD.gn +++ b/chrome/android/javatests/BUILD.gn
@@ -559,7 +559,6 @@ "src/org/chromium/chrome/browser/autofill/settings/AutofillPaymentMethodsFragmentCardBenefitsTest.java", "src/org/chromium/chrome/browser/autofill/settings/AutofillPaymentMethodsFragmentTest.java", "src/org/chromium/chrome/browser/autofill/settings/AutofillProfilesFragmentTest.java", - "src/org/chromium/chrome/browser/autofill/settings/AutofillServerCardEditorTest.java", "src/org/chromium/chrome/browser/autofill/settings/AutofillTestRule.java", "src/org/chromium/chrome/browser/autofill/settings/FinancialAccountsManagementFragmentTest.java", "src/org/chromium/chrome/browser/autofill/vcn/AutofillVcnEnrollBottomSheetRenderTest.java",
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/history/HistoryPaneTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/history/HistoryPaneTest.java index b15c8346..c8ee19c 100644 --- a/chrome/android/javatests/src/org/chromium/chrome/browser/history/HistoryPaneTest.java +++ b/chrome/android/javatests/src/org/chromium/chrome/browser/history/HistoryPaneTest.java
@@ -8,7 +8,6 @@ import static androidx.test.espresso.action.ViewActions.click; import static androidx.test.espresso.action.ViewActions.replaceText; import static androidx.test.espresso.assertion.ViewAssertions.matches; -import static androidx.test.espresso.matcher.ViewMatchers.isDescendantOfA; import static androidx.test.espresso.matcher.ViewMatchers.isDisplayed; import static androidx.test.espresso.matcher.ViewMatchers.withContentDescription; import static androidx.test.espresso.matcher.ViewMatchers.withId; @@ -18,7 +17,6 @@ import static org.hamcrest.CoreMatchers.containsString; import static org.chromium.base.ThreadUtils.runOnUiThreadBlocking; -import static org.chromium.chrome.test.transit.hub.HubBaseStation.HUB_PANE_SWITCHER; import static org.chromium.ui.test.util.ViewUtils.onViewWaiting; import androidx.test.filters.MediumTest; @@ -43,6 +41,8 @@ import org.chromium.chrome.test.ChromeJUnit4ClassRunner; import org.chromium.chrome.test.transit.AutoResetCtaTransitTestRule; import org.chromium.chrome.test.transit.ChromeTransitTestRules; +import org.chromium.chrome.test.transit.hub.RegularTabSwitcherStation; +import org.chromium.chrome.test.transit.hub.TabSwitcherStation; import org.chromium.chrome.test.transit.page.WebPageStation; /** Public transit tests for the Hub's history pane. */ @@ -68,8 +68,8 @@ @Test @MediumTest public void testEmptyView() { - mStartingPage.openRegularTabSwitcher(); - enterHistoryPane(); + RegularTabSwitcherStation tabSwitcher = mStartingPage.openRegularTabSwitcher(); + enterHistoryPane(tabSwitcher); onViewWaiting(withText("You’ll find your history here")).check(matches(isDisplayed())); onViewWaiting( @@ -86,11 +86,12 @@ mCtaTestRule.getTestServer().getURL("/chrome/test/data/android/navigate/one.html"); String urlTwo = mCtaTestRule.getTestServer().getURL("/chrome/test/data/android/navigate/two.html"); - mStartingPage - .loadWebPageProgrammatically(urlOne) - .loadWebPageProgrammatically(urlTwo) - .openRegularTabSwitcher(); - enterHistoryPane(); + RegularTabSwitcherStation tabSwitcher = + mStartingPage + .loadWebPageProgrammatically(urlOne) + .loadWebPageProgrammatically(urlTwo) + .openRegularTabSwitcher(); + enterHistoryPane(tabSwitcher); onViewWaiting(withText("One")).check(matches(isDisplayed())); onViewWaiting(withText("Two")).check(matches(isDisplayed())); @@ -103,11 +104,12 @@ mCtaTestRule.getTestServer().getURL("/chrome/test/data/android/navigate/one.html"); String urlTwo = mCtaTestRule.getTestServer().getURL("/chrome/test/data/android/navigate/two.html"); - mStartingPage - .loadWebPageProgrammatically(urlOne) - .loadWebPageProgrammatically(urlTwo) - .openRegularTabSwitcher(); - enterHistoryPane(); + RegularTabSwitcherStation tabSwitcher = + mStartingPage + .loadWebPageProgrammatically(urlOne) + .loadWebPageProgrammatically(urlTwo) + .openRegularTabSwitcher(); + enterHistoryPane(tabSwitcher); onViewWaiting(withText("One")).check(matches(isDisplayed())); onViewWaiting(withText("Two")).check(matches(isDisplayed())); @@ -127,11 +129,12 @@ mCtaTestRule.getTestServer().getURL("/chrome/test/data/android/navigate/one.html"); String urlTwo = mCtaTestRule.getTestServer().getURL("/chrome/test/data/android/navigate/two.html"); - mStartingPage - .loadWebPageProgrammatically(urlOne) - .loadWebPageProgrammatically(urlTwo) - .openRegularTabSwitcher(); - enterHistoryPane(); + RegularTabSwitcherStation tabSwitcher = + mStartingPage + .loadWebPageProgrammatically(urlOne) + .loadWebPageProgrammatically(urlTwo) + .openRegularTabSwitcher(); + enterHistoryPane(tabSwitcher); onViewWaiting(withText("One")).perform(click()); // When the history view is clicked, it should replace the current tab's URL. @@ -146,11 +149,12 @@ .getSpec())); } - private void enterHistoryPane() { + private void enterHistoryPane(TabSwitcherStation tabSwitcher) { onView( - allOf( - isDescendantOfA(HUB_PANE_SWITCHER.getViewMatcher()), - withContentDescription(containsString("History")))) + tabSwitcher + .paneSwitcherElement + .descendant(withContentDescription(containsString("History"))) + .getViewMatcher()) .perform(click()); }
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/webid/DigitalCredentialProviderTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/webid/DigitalCredentialProviderTest.java index 124822d..eca89c2 100644 --- a/chrome/android/javatests/src/org/chromium/chrome/browser/webid/DigitalCredentialProviderTest.java +++ b/chrome/android/javatests/src/org/chromium/chrome/browser/webid/DigitalCredentialProviderTest.java
@@ -29,10 +29,9 @@ import org.chromium.base.test.util.CriteriaNotSatisfiedException; import org.chromium.base.test.util.Features.EnableFeatures; import org.chromium.chrome.browser.flags.ChromeSwitches; +import org.chromium.chrome.browser.webid.IdentityCredentialsDelegate.DigitalCredential; import org.chromium.chrome.test.ChromeJUnit4ClassRunner; import org.chromium.chrome.test.ChromeTabbedActivityTestRule; -import org.chromium.content.browser.webid.IdentityCredentialsDelegate; -import org.chromium.content.browser.webid.IdentityCredentialsDelegate.DigitalCredential; import org.chromium.content_public.browser.ContentFeatureList; import org.chromium.content_public.browser.test.util.DOMUtils; import org.chromium.content_public.browser.test.util.JavaScriptUtils;
diff --git a/chrome/android/junit/BUILD.gn b/chrome/android/junit/BUILD.gn index 754b1555..123582c 100644 --- a/chrome/android/junit/BUILD.gn +++ b/chrome/android/junit/BUILD.gn
@@ -48,7 +48,6 @@ "$google_play_services_package:google_play_services_cast_framework_java", "$google_play_services_package:google_play_services_cast_java", "$google_play_services_package:google_play_services_gcm_java", - "$google_play_services_package:google_play_services_identity_credentials_java", "$google_play_services_package:google_play_services_tasks_java", "//base:base_java", "//base:base_java_test_support", @@ -461,6 +460,7 @@ "src/org/chromium/chrome/browser/autofill/settings/AutofillDeleteSavedCvcsConfirmationDialogTest.java", "src/org/chromium/chrome/browser/autofill/settings/AutofillLocalCardEditorTest.java", "src/org/chromium/chrome/browser/autofill/settings/AutofillLocalIbanEditorTest.java", + "src/org/chromium/chrome/browser/autofill/settings/AutofillServerCardEditorTest.java", "src/org/chromium/chrome/browser/autofill/settings/AutofillVirtualCardEnrollmentDialogTest.java", "src/org/chromium/chrome/browser/autofill/settings/AutofillVirtualCardUnenrollmentDialogTest.java", "src/org/chromium/chrome/browser/autofill/settings/CreditCardScannerManagerTest.java", @@ -985,6 +985,7 @@ "src/org/chromium/chrome/browser/suggestions/tile/TileGroupDelegateImplUnitTest.java", "src/org/chromium/chrome/browser/suggestions/tile/TileInteractionDelegateTest.java", "src/org/chromium/chrome/browser/suggestions/tile/TileRendererTest.java", + "src/org/chromium/chrome/browser/suggestions/tile/TileUtilsUnitTest.java", "src/org/chromium/chrome/browser/suggestions/tile/tile_edit_dialog/CustomTileEditCoordinatorUnitTest.java", "src/org/chromium/chrome/browser/suggestions/tile/tile_edit_dialog/CustomTileEditMediatorUnitTest.java", "src/org/chromium/chrome/browser/suggestions/tile/tile_edit_dialog/CustomTileEditViewUnitTest.java", @@ -1274,7 +1275,6 @@ "src/org/chromium/chrome/browser/usage_stats/EventTrackerTest.java", "src/org/chromium/chrome/browser/usage_stats/PageViewObserverTest.java", "src/org/chromium/chrome/browser/webauthn/AuthenticatorIncognitoConfirmationBottomsheetTest.java", - "src/org/chromium/content/browser/webid/DigitalCredentialsPresentationDelegateTest.java", ] deps = [
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/autofill/settings/AutofillServerCardEditorTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/autofill/settings/AutofillServerCardEditorTest.java similarity index 81% rename from chrome/android/javatests/src/org/chromium/chrome/browser/autofill/settings/AutofillServerCardEditorTest.java rename to chrome/android/junit/src/org/chromium/chrome/browser/autofill/settings/AutofillServerCardEditorTest.java index 51be63b2..88590d0e 100644 --- a/chrome/android/javatests/src/org/chromium/chrome/browser/autofill/settings/AutofillServerCardEditorTest.java +++ b/chrome/android/junit/src/org/chromium/chrome/browser/autofill/settings/AutofillServerCardEditorTest.java
@@ -15,28 +15,27 @@ import static androidx.test.espresso.matcher.ViewMatchers.withText; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.never; -import static org.mockito.Mockito.reset; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; -import android.app.Activity; +import android.content.Intent; import android.os.Bundle; import android.text.Spanned; import android.text.style.ClickableSpan; import android.view.View; import android.widget.TextView; -import androidx.test.espresso.Espresso; +import androidx.test.core.app.ActivityScenario; import androidx.test.espresso.NoMatchingViewException; import androidx.test.espresso.UiController; import androidx.test.espresso.ViewAction; import androidx.test.espresso.matcher.ViewMatchers.Visibility; import androidx.test.filters.MediumTest; -import androidx.test.runner.lifecycle.Stage; import org.hamcrest.Matcher; import org.hamcrest.Matchers; @@ -50,44 +49,57 @@ import org.mockito.Mock; import org.mockito.junit.MockitoJUnit; import org.mockito.junit.MockitoRule; +import org.robolectric.Shadows; +import org.robolectric.annotation.Config; +import org.robolectric.shadows.ShadowActivity; import org.chromium.base.Callback; +import org.chromium.base.ContextUtils; import org.chromium.base.ThreadUtils; -import org.chromium.base.test.util.ApplicationTestUtils; -import org.chromium.base.test.util.Batch; +import org.chromium.base.test.BaseRobolectricTestRunner; import org.chromium.base.test.util.CommandLineFlags; -import org.chromium.base.test.util.DisabledTest; import org.chromium.base.test.util.HistogramWatcher; -import org.chromium.chrome.browser.autofill.AutofillEditorBase; +import org.chromium.chrome.R; +import org.chromium.chrome.browser.ChromeStringConstants; +import org.chromium.chrome.browser.autofill.AutofillImageFetcher; +import org.chromium.chrome.browser.autofill.AutofillImageFetcherFactory; import org.chromium.chrome.browser.autofill.AutofillTestHelper; +import org.chromium.chrome.browser.autofill.PersonalDataManager; import org.chromium.chrome.browser.autofill.PersonalDataManager.CreditCard; +import org.chromium.chrome.browser.autofill.PersonalDataManagerFactory; import org.chromium.chrome.browser.flags.ChromeSwitches; +import org.chromium.chrome.browser.init.ChromeBrowserInitializer; import org.chromium.chrome.browser.profiles.Profile; +import org.chromium.chrome.browser.profiles.ProfileManager; +import org.chromium.chrome.browser.profiles.ProfileManagerUtilsJni; import org.chromium.chrome.browser.settings.SettingsActivity; -import org.chromium.chrome.browser.settings.SettingsActivityTestRule; -import org.chromium.chrome.test.ChromeJUnit4ClassRunner; -import org.chromium.chrome.test.R; +import org.chromium.chrome.browser.settings.SettingsIntentUtil; +import org.chromium.chrome.browser.settings.SettingsNavigationFactory; import org.chromium.components.autofill.VirtualCardEnrollmentLinkType; import org.chromium.components.autofill.VirtualCardEnrollmentState; import org.chromium.components.autofill.payments.LegalMessageLine; +import org.chromium.components.browser_ui.settings.SettingsNavigation; import org.chromium.url.GURL; -import java.util.concurrent.TimeoutException; - -/** Instrumentation tests for AutofillServerCardEditor. */ -@RunWith(ChromeJUnit4ClassRunner.class) -@Batch(Batch.PER_CLASS) +/** Unit tests for {@link AutofillServerCardEditor}. */ +@RunWith(BaseRobolectricTestRunner.class) +@Config(manifest = Config.NONE) @CommandLineFlags.Add({ChromeSwitches.DISABLE_FIRST_RUN_EXPERIENCE}) public class AutofillServerCardEditorTest { - @Rule public MockitoRule mMockitoRule = MockitoJUnit.rule(); - @Rule public final AutofillTestRule rule = new AutofillTestRule(); - - @Rule - public final SettingsActivityTestRule<AutofillServerCardEditor> mSettingsActivityTestRule = - new SettingsActivityTestRule<>(AutofillServerCardEditor.class); private static final long NATIVE_AUTOFILL_PAYMENTS_METHODS_DELEGATE = 100L; + @Rule public MockitoRule mMockitoRule = MockitoJUnit.rule(); + + @Mock private Profile mProfile; + @Mock private PersonalDataManager mPersonalDataManager; + @Mock private SettingsNavigation mSettingsNavigation; + @Mock private ChromeBrowserInitializer mInitializer; + @Mock private ProfileManagerUtilsJni mProfileManagerUtilsJni; + @Mock private AutofillImageFetcher mImageFetcher; + @Mock private Callback<String> mServerCardEditLinkOpenerCallback; + @Mock private AutofillPaymentMethodsDelegate.Natives mNativeMock; + private static final CreditCard SAMPLE_VIRTUAL_CARD_ENROLLED_CARD = new CreditCard( /* guid= */ "1", @@ -171,39 +183,57 @@ /* issuerId= */ "", /* productTermsUrl= */ null); - @Mock private AutofillPaymentMethodsDelegate.Natives mNativeMock; - @Mock private Callback<String> mServerCardEditLinkOpenerCallback; - - private AutofillTestHelper mAutofillTestHelper; + private ActivityScenario<SettingsActivity> mActivityScenario; + private SettingsActivity mSettingsActivity; + private AutofillServerCardEditor mCardEditor; @Before public void setUp() { - reset(mNativeMock); AutofillPaymentMethodsDelegateJni.setInstanceForTesting(mNativeMock); when(mNativeMock.init(any(Profile.class))) .thenReturn(NATIVE_AUTOFILL_PAYMENTS_METHODS_DELEGATE); - mAutofillTestHelper = new AutofillTestHelper(); + PersonalDataManagerFactory.setInstanceForTesting(mPersonalDataManager); + SettingsNavigationFactory.setInstanceForTesting(mSettingsNavigation); + ProfileManagerUtilsJni.setInstanceForTesting(mProfileManagerUtilsJni); + ChromeBrowserInitializer.setForTesting(mInitializer); + ProfileManager.setLastUsedProfileForTesting(mProfile); + AutofillImageFetcherFactory.setInstanceForTesting(mImageFetcher); } @After - public void tearDown() throws TimeoutException { - mAutofillTestHelper.clearAllDataForTesting(); - // Ensures that the native cleanup method is called before mocks are cleaned up - // on failed cases. - Activity activity = mSettingsActivityTestRule.getActivity(); - if (activity != null) { - finishAndWaitForActivity(activity); + public void tearDown() { + if (mActivityScenario != null) { + mActivityScenario.close(); } } + private void initEditor(CreditCard card) { + Bundle arguments = new Bundle(); + if (card != null) { + String guid = card.getGUID(); + arguments.putString("guid", guid); + when(mPersonalDataManager.getCreditCard(guid)).thenReturn(card); + } + + Intent intent = + SettingsIntentUtil.createIntent( + ContextUtils.getApplicationContext(), + AutofillServerCardEditor.class.getName(), + arguments); + + mActivityScenario = ActivityScenario.launch(intent); + mActivityScenario.onActivity( + activity -> { + mSettingsActivity = activity; + }); + + mCardEditor = (AutofillServerCardEditor) mSettingsActivity.getMainFragment(); + } + @Test @MediumTest - public void virtualCardEnrolled_virtualCardRemoveButtonShown() throws Exception { - mAutofillTestHelper.addServerCreditCard(SAMPLE_VIRTUAL_CARD_ENROLLED_CARD); - - SettingsActivity activity = - mSettingsActivityTestRule.startSettingsActivity( - fragmentArgs(SAMPLE_VIRTUAL_CARD_ENROLLED_CARD.getGUID())); + public void virtualCardEnrolled_virtualCardRemoveButtonShown() { + initEditor(SAMPLE_VIRTUAL_CARD_ENROLLED_CARD); onView(withId(R.id.virtual_card_ui)).check(matches(isDisplayed())); onView(withId(R.id.virtual_card_enrollment_button)) @@ -212,18 +242,12 @@ withText( R.string .autofill_card_editor_virtual_card_turn_off_button_label))); - // Ensure the activity is cleaned up. - finishAndWaitForActivity(activity); } @Test @MediumTest - public void virtualCardUnenrolledAndEligible_virtualCardAddButtonShown() throws Exception { - mAutofillTestHelper.addServerCreditCard(SAMPLE_VIRTUAL_CARD_UNENROLLED_AND_ELIGIBLE_CARD); - - SettingsActivity activity = - mSettingsActivityTestRule.startSettingsActivity( - fragmentArgs(SAMPLE_VIRTUAL_CARD_UNENROLLED_AND_ELIGIBLE_CARD.getGUID())); + public void virtualCardUnenrolledAndEligible_virtualCardAddButtonShown() { + initEditor(SAMPLE_VIRTUAL_CARD_UNENROLLED_AND_ELIGIBLE_CARD); onView(withId(R.id.virtual_card_ui)).check(matches(isDisplayed())); onView(withId(R.id.virtual_card_enrollment_button)) @@ -232,38 +256,23 @@ withText( R.string .autofill_card_editor_virtual_card_turn_on_button_label))); - // Ensure that the native delegate is cleaned up when the test has finished. - finishAndWaitForActivity(activity); } @Test @MediumTest - public void virtualCardUnenrolledAndNotEligible_virtualCardLayoutNotShown() throws Exception { - mAutofillTestHelper.addServerCreditCard( - SAMPLE_VIRTUAL_CARD_UNENROLLED_AND_NOT_ELIGIBLE_CARD); - - SettingsActivity activity = - mSettingsActivityTestRule.startSettingsActivity( - fragmentArgs( - SAMPLE_VIRTUAL_CARD_UNENROLLED_AND_NOT_ELIGIBLE_CARD.getGUID())); + public void virtualCardUnenrolledAndNotEligible_virtualCardLayoutNotShown() { + initEditor(SAMPLE_VIRTUAL_CARD_UNENROLLED_AND_NOT_ELIGIBLE_CARD); onView(withId(R.id.virtual_card_ui)) .check(matches(withEffectiveVisibility(Visibility.GONE))); - // Ensure that the native delegate is cleaned up when the test has finished. - finishAndWaitForActivity(activity); } @Test @MediumTest - @DisabledTest(message = "https://crbug.com/342333311") public void - virtualCardUnenrolledAndEligible_virtualCardAddButtonClicked_enrollAccepted_enrollmentSuccessful() - throws Exception { - mAutofillTestHelper.addServerCreditCard(SAMPLE_VIRTUAL_CARD_UNENROLLED_AND_ELIGIBLE_CARD); + virtualCardUnenrolledAndEligible_virtualCardAddButtonClicked_enrollAccepted_enrollmentSuccessful() { + initEditor(SAMPLE_VIRTUAL_CARD_UNENROLLED_AND_ELIGIBLE_CARD); - SettingsActivity activity = - mSettingsActivityTestRule.startSettingsActivity( - fragmentArgs(SAMPLE_VIRTUAL_CARD_UNENROLLED_AND_ELIGIBLE_CARD.getGUID())); // Verify that the native delegate was initialized properly. verify(mNativeMock).init(any(Profile.class)); @@ -327,8 +336,13 @@ onView(withId(R.id.virtual_card_education)).perform(clickLink()); histogram.assertExpected(); - // Go back to the settings page. - Espresso.pressBack(); + // Verify that the correct CCT is opened. + ShadowActivity shadowActivity = Shadows.shadowOf(mSettingsActivity); + Intent cctIntent = shadowActivity.getNextStartedActivity(); + assertNotNull(cctIntent); + assertEquals( + ChromeStringConstants.AUTOFILL_VIRTUAL_CARD_ENROLLMENT_SUPPORT_URL, + cctIntent.getDataString()); // Click positive button on enrollment dialog. // Verify that enrollment dialog acceptance is recorded. @@ -370,22 +384,14 @@ R.string .autofill_card_editor_virtual_card_turn_off_button_label))) .check(matches(isEnabled())); - - // Ensure that the native delegate is cleaned up when the test has finished. - finishAndWaitForActivity(activity); } @Test @MediumTest public void - virtualCardUnenrolledAndEligible_virtualCardAddButtonClicked_enrollAccepted_enrollmentFailure() - throws Exception { - mAutofillTestHelper.addServerCreditCard(SAMPLE_VIRTUAL_CARD_UNENROLLED_AND_ELIGIBLE_CARD); + virtualCardUnenrolledAndEligible_virtualCardAddButtonClicked_enrollAccepted_enrollmentFailure() { + initEditor(SAMPLE_VIRTUAL_CARD_UNENROLLED_AND_ELIGIBLE_CARD); - SettingsActivity activity = - mSettingsActivityTestRule.startSettingsActivity( - fragmentArgs(SAMPLE_VIRTUAL_CARD_UNENROLLED_AND_ELIGIBLE_CARD.getGUID())); - // Verify that the native delegate was initialized properly. verify(mNativeMock).init(any(Profile.class)); // Verify that the Virtual Card enrollment button is shown and allows enrollment. @@ -400,6 +406,7 @@ // Press the enrollment button. onView(withId(R.id.virtual_card_enrollment_button)).perform(click()); + // Verify that the Virtual Card enrollment button still shows text for enrollment and that // the button is disabled. onView(withId(R.id.virtual_card_enrollment_button)) @@ -458,20 +465,13 @@ R.string .autofill_card_editor_virtual_card_turn_on_button_label))) .check(matches(isEnabled())); - - // Ensure that the native delegate is cleaned up when the test has finished. - finishAndWaitForActivity(activity); } @Test @MediumTest - public void virtualCardUnenrolledAndEligible_virtualCardAddButtonClicked_enrollRejected() - throws Exception { - mAutofillTestHelper.addServerCreditCard(SAMPLE_VIRTUAL_CARD_UNENROLLED_AND_ELIGIBLE_CARD); + public void virtualCardUnenrolledAndEligible_virtualCardAddButtonClicked_enrollRejected() { + initEditor(SAMPLE_VIRTUAL_CARD_UNENROLLED_AND_ELIGIBLE_CARD); - SettingsActivity activity = - mSettingsActivityTestRule.startSettingsActivity( - fragmentArgs(SAMPLE_VIRTUAL_CARD_UNENROLLED_AND_ELIGIBLE_CARD.getGUID())); // Verify that the native delegate was initialized properly. verify(mNativeMock).init(any(Profile.class)); @@ -541,22 +541,14 @@ verify(mNativeMock, times(0)) .enrollOfferedVirtualCard( eq(NATIVE_AUTOFILL_PAYMENTS_METHODS_DELEGATE), any(Callback.class)); - - // Ensure that the native delegate is cleaned up when the test has finished. - finishAndWaitForActivity(activity); } @Test @MediumTest - @DisabledTest(message = "https://crbug.com/1368548") public void - virtualCardUnenrolledAndEligible_virtualCardAddButtonClicked_enrollAccepted_editorExited() - throws Exception { - mAutofillTestHelper.addServerCreditCard(SAMPLE_VIRTUAL_CARD_UNENROLLED_AND_ELIGIBLE_CARD); + virtualCardUnenrolledAndEligible_virtualCardAddButtonClicked_enrollAccepted_editorExited() { + initEditor(SAMPLE_VIRTUAL_CARD_UNENROLLED_AND_ELIGIBLE_CARD); - SettingsActivity activity = - mSettingsActivityTestRule.startSettingsActivity( - fragmentArgs(SAMPLE_VIRTUAL_CARD_UNENROLLED_AND_ELIGIBLE_CARD.getGUID())); // Verify that the native delegate was initialized properly. verify(mNativeMock).init(any(Profile.class)); @@ -600,7 +592,7 @@ onView(withId(R.id.positive_button)).perform(click()); // Exit the editor. - finishAndWaitForActivity(activity); + mActivityScenario.close(); // Verify that the native delegate is not cleaned up while the server call is in progress. verify(mNativeMock, never()).cleanup(NATIVE_AUTOFILL_PAYMENTS_METHODS_DELEGATE); @@ -626,12 +618,8 @@ @Test @MediumTest - public void virtualCardEnrolled_virtualCardRemoveButtonClicked_dialogShown() throws Exception { - mAutofillTestHelper.addServerCreditCard(SAMPLE_VIRTUAL_CARD_ENROLLED_CARD); - - SettingsActivity activity = - mSettingsActivityTestRule.startSettingsActivity( - fragmentArgs(SAMPLE_VIRTUAL_CARD_ENROLLED_CARD.getGUID())); + public void virtualCardEnrolled_virtualCardRemoveButtonClicked_dialogShown() { + initEditor(SAMPLE_VIRTUAL_CARD_ENROLLED_CARD); // Verify that the Virtual Card enrollment button is shown and allows unenrollment. onView(withId(R.id.virtual_card_ui)).check(matches(isDisplayed())); @@ -647,20 +635,14 @@ onView(withId(R.id.virtual_card_enrollment_button)).perform(click()); // Verify that the unenroll dialog is shown. onView(withText(R.string.autofill_credit_card_editor_virtual_card_unenroll_dialog_title)) + .inRoot(isDialog()) .check(matches(isDisplayed())); - // Ensure that the native delegate is cleaned up when the test has finished. - finishAndWaitForActivity(activity); } @Test @MediumTest - public void virtualCardEnrolled_virtualCardRemoveButtonClicked_unenrollCancelled() - throws Exception { - mAutofillTestHelper.addServerCreditCard(SAMPLE_VIRTUAL_CARD_ENROLLED_CARD); - - SettingsActivity activity = - mSettingsActivityTestRule.startSettingsActivity( - fragmentArgs(SAMPLE_VIRTUAL_CARD_ENROLLED_CARD.getGUID())); + public void virtualCardEnrolled_virtualCardRemoveButtonClicked_unenrollCancelled() { + initEditor(SAMPLE_VIRTUAL_CARD_ENROLLED_CARD); // Verify that the Virtual Card enrollment button is shown and allows unenrollment. onView(withId(R.id.virtual_card_ui)).check(matches(isDisplayed())); @@ -683,6 +665,7 @@ // Verify that the unenroll dialog is shown. onView(withText(R.string.autofill_credit_card_editor_virtual_card_unenroll_dialog_title)) + .inRoot(isDialog()) .check(matches(isDisplayed())); // Click the Cancel button. @@ -701,21 +684,13 @@ R.string .autofill_card_editor_virtual_card_turn_off_button_label))) .check(matches(isEnabled())); - - // Ensure that the native delegate is cleaned up when the test has finished. - finishAndWaitForActivity(activity); } @Test @MediumTest public void - virtualCardEnrolled_virtualCardRemoveButtonClicked_unenrollAccepted_unenrollmentSuccessful() - throws Exception { - mAutofillTestHelper.addServerCreditCard(SAMPLE_VIRTUAL_CARD_ENROLLED_CARD); - - SettingsActivity activity = - mSettingsActivityTestRule.startSettingsActivity( - fragmentArgs(SAMPLE_VIRTUAL_CARD_ENROLLED_CARD.getGUID())); + virtualCardEnrolled_virtualCardRemoveButtonClicked_unenrollAccepted_unenrollmentSuccessful() { + initEditor(SAMPLE_VIRTUAL_CARD_ENROLLED_CARD); // Verify that the native delegate was initialized properly. verify(mNativeMock).init(any(Profile.class)); @@ -735,6 +710,7 @@ // Verify that the unenroll dialog is shown. onView(withText(R.string.autofill_credit_card_editor_virtual_card_unenroll_dialog_title)) + .inRoot(isDialog()) .check(matches(isDisplayed())); // Click the positive button on unenrollment dialog. @@ -782,21 +758,13 @@ R.string .autofill_card_editor_virtual_card_turn_on_button_label))) .check(matches(isEnabled())); - - // Ensure that the native delegate is cleaned up when the test has finished. - finishAndWaitForActivity(activity); } @Test @MediumTest public void - virtualCardEnrolled_virtualCardRemoveButtonClicked_unenrollAccepted_unenrollmentFailure() - throws Exception { - mAutofillTestHelper.addServerCreditCard(SAMPLE_VIRTUAL_CARD_ENROLLED_CARD); - - SettingsActivity activity = - mSettingsActivityTestRule.startSettingsActivity( - fragmentArgs(SAMPLE_VIRTUAL_CARD_ENROLLED_CARD.getGUID())); + virtualCardEnrolled_virtualCardRemoveButtonClicked_unenrollAccepted_unenrollmentFailure() { + initEditor(SAMPLE_VIRTUAL_CARD_ENROLLED_CARD); // Verify that the native delegate was initialized properly. verify(mNativeMock).init(any(Profile.class)); @@ -816,6 +784,7 @@ // Verify that the unenroll dialog is shown. onView(withText(R.string.autofill_credit_card_editor_virtual_card_unenroll_dialog_title)) + .inRoot(isDialog()) .check(matches(isDisplayed())); // Click the positive button on unenrollment dialog. @@ -848,20 +817,12 @@ R.string .autofill_card_editor_virtual_card_turn_off_button_label))) .check(matches(isEnabled())); - - // Ensure that the native delegate is cleaned up when the test has finished. - finishAndWaitForActivity(activity); } @Test @MediumTest - public void virtualCardEnrolled_virtualCardRemoveButtonClicked_unenrollAccepted_editorExited() - throws Exception { - mAutofillTestHelper.addServerCreditCard(SAMPLE_VIRTUAL_CARD_ENROLLED_CARD); - - SettingsActivity activity = - mSettingsActivityTestRule.startSettingsActivity( - fragmentArgs(SAMPLE_VIRTUAL_CARD_ENROLLED_CARD.getGUID())); + public void virtualCardEnrolled_virtualCardRemoveButtonClicked_unenrollAccepted_editorExited() { + initEditor(SAMPLE_VIRTUAL_CARD_ENROLLED_CARD); // Verify that the native delegate was initialized properly. verify(mNativeMock).init(any(Profile.class)); @@ -881,6 +842,7 @@ // Verify that the unenroll dialog is shown. onView(withText(R.string.autofill_credit_card_editor_virtual_card_unenroll_dialog_title)) + .inRoot(isDialog()) .check(matches(isDisplayed())); // Click the positive button on unenrollment dialog. @@ -891,7 +853,7 @@ .perform(click()); // Exit the editor. - finishAndWaitForActivity(activity); + mActivityScenario.close(); // Verify that the native delegate is not cleaned up while the server call is in progress. verify(mNativeMock, never()).cleanup(NATIVE_AUTOFILL_PAYMENTS_METHODS_DELEGATE); @@ -910,7 +872,6 @@ booleanCallbackArgumentCaptor.getValue(); ThreadUtils.runOnUiThreadBlocking( () -> virtualCardEnrollmentUpdateResponseCallback.onResult(true)); - // Ensure that the callback is run after receiving the server response and that the native // delegate is cleaned up. verify(mNativeMock).cleanup(NATIVE_AUTOFILL_PAYMENTS_METHODS_DELEGATE); @@ -918,17 +879,13 @@ @Test @MediumTest - public void testAutofillPaymentMethodsDelegateLifecycleEvents() throws Exception { - mAutofillTestHelper.addServerCreditCard(SAMPLE_VIRTUAL_CARD_ENROLLED_CARD); - - SettingsActivity activity = - mSettingsActivityTestRule.startSettingsActivity( - fragmentArgs(SAMPLE_VIRTUAL_CARD_ENROLLED_CARD.getGUID())); + public void testAutofillPaymentMethodsDelegateLifecycleEvents() { + initEditor(SAMPLE_VIRTUAL_CARD_ENROLLED_CARD); // Verify that the native delegate was initialized properly. verify(mNativeMock).init(any(Profile.class)); - finishAndWaitForActivity(activity); + mActivityScenario.close(); // Ensure that the native delegate is cleaned up when the test has finished. verify(mNativeMock).cleanup(NATIVE_AUTOFILL_PAYMENTS_METHODS_DELEGATE); @@ -936,15 +893,11 @@ @Test @MediumTest - public void testCustomUrlForServerCardEditPage() throws Exception { - mAutofillTestHelper.addServerCreditCard(SAMPLE_VIRTUAL_CARD_ENROLLED_CARD); + public void testCustomUrlForServerCardEditPage() { + initEditor(SAMPLE_VIRTUAL_CARD_ENROLLED_CARD); - SettingsActivity activity = - mSettingsActivityTestRule.startSettingsActivity( - fragmentArgs(SAMPLE_VIRTUAL_CARD_ENROLLED_CARD.getGUID())); - mSettingsActivityTestRule - .getFragment() - .setServerCardEditLinkOpenerCallbackForTesting(mServerCardEditLinkOpenerCallback); + mCardEditor.setServerCardEditLinkOpenerCallbackForTesting( + mServerCardEditLinkOpenerCallback); // Verify that the edit card button is shown and enabled. onView(withId(R.id.edit_server_card)).check(matches(isDisplayed())); @@ -960,23 +913,16 @@ eq( "https://pay.google.com/pay?p=paymentmethods&utm_source=chrome&utm_medium=settings&utm_campaign=payment_methods&id=" + SAMPLE_VIRTUAL_CARD_ENROLLED_CARD.getInstrumentId())); - - // Ensure that the native delegate is cleaned up when the test has finished. - finishAndWaitForActivity(activity); } @Test @MediumTest @CommandLineFlags.Add({ChromeSwitches.USE_SANDBOX_WALLET_ENVIRONMENT}) - public void testCustomUrlForServerCardEditPage_sandboxEnabled() throws Exception { - mAutofillTestHelper.addServerCreditCard(SAMPLE_VIRTUAL_CARD_ENROLLED_CARD); + public void testCustomUrlForServerCardEditPage_sandboxEnabled() { + initEditor(SAMPLE_VIRTUAL_CARD_ENROLLED_CARD); - SettingsActivity activity = - mSettingsActivityTestRule.startSettingsActivity( - fragmentArgs(SAMPLE_VIRTUAL_CARD_ENROLLED_CARD.getGUID())); - mSettingsActivityTestRule - .getFragment() - .setServerCardEditLinkOpenerCallbackForTesting(mServerCardEditLinkOpenerCallback); + mCardEditor.setServerCardEditLinkOpenerCallbackForTesting( + mServerCardEditLinkOpenerCallback); // Verify that the edit card button is shown and enabled. onView(withId(R.id.edit_server_card)).check(matches(isDisplayed())); @@ -991,20 +937,6 @@ // expected URL. verify(mServerCardEditLinkOpenerCallback) .onResult(eq(kExpectedUrl + SAMPLE_VIRTUAL_CARD_ENROLLED_CARD.getInstrumentId())); - - // Ensure that the native delegate is cleaned up when the test has finished. - finishAndWaitForActivity(activity); - } - - private void finishAndWaitForActivity(Activity activity) { - activity.finish(); - ApplicationTestUtils.waitForActivityState(activity, Stage.DESTROYED); - } - - private Bundle fragmentArgs(String guid) { - Bundle args = new Bundle(); - args.putString(AutofillEditorBase.AUTOFILL_GUID, guid); - return args; } private ViewAction clickLink() {
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/suggestions/tile/TileUtilsUnitTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/suggestions/tile/TileUtilsUnitTest.java new file mode 100644 index 0000000..ef85939 --- /dev/null +++ b/chrome/android/junit/src/org/chromium/chrome/browser/suggestions/tile/TileUtilsUnitTest.java
@@ -0,0 +1,83 @@ +// Copyright 2025 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +package org.chromium.chrome.browser.suggestions.tile; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.robolectric.annotation.Config; + +import org.chromium.base.test.BaseRobolectricTestRunner; +import org.chromium.url.GURL; + +/** Unit tests for {@link TileUtils} */ +@RunWith(BaseRobolectricTestRunner.class) +@Config(manifest = Config.NONE) +public class TileUtilsUnitTest { + @Test + public void testFormatCustomTileName() { + GURL testUrl = new GURL("https://example.com"); + assertEquals("Name", TileUtils.formatCustomTileName("Name", testUrl)); + assertEquals("https://example.com/", TileUtils.formatCustomTileName("", testUrl)); + + // Relies on SuggestionsConfig.MAX_CUSTOM_TILES_NAME_LENGTH == 50. + assertEquals( + "A234567890B234567890C234567890D234567890E234567890", + TileUtils.formatCustomTileName( + "A234567890B234567890C234567890D234567890E234567890F234567890", testUrl)); + assertEquals( + "https://example.com/C234567890?234567890#234567890", + TileUtils.formatCustomTileName( + "", new GURL("https://example.com/C234567890?234567890#234567890123456"))); + + // Empty input -> empty output. The result will fail isValidCustomTileUrl(). We didn't + // bother provide a default title for this, since if the URL is empty then a Tile would be + // invalid anyway, and formatCustomTileName() would be unneeded. + assertEquals("", TileUtils.formatCustomTileName("", new GURL(""))); + } + + @Test + public void testIsValidCustomTileName() { + assertTrue(TileUtils.isValidCustomTileName("Valid Name 123")); + assertTrue(TileUtils.isValidCustomTileName("!@#$%^&*()")); + assertTrue(TileUtils.isValidCustomTileName(" ")); + assertTrue(TileUtils.isValidCustomTileName("\u9ebb\u96c0")); + assertFalse(TileUtils.isValidCustomTileName("")); + + // Rely on SuggestionsConfig.MAX_CUSTOM_TILES_NAME_LENGTH == 50. + assertTrue(TileUtils.isValidCustomTileName("A".repeat(40))); + assertFalse(TileUtils.isValidCustomTileName("A".repeat(60))); + } + + @Test + public void testIsValidCustomTileUrl() { + assertTrue(TileUtils.isValidCustomTileUrl(new GURL("https://example.com/path?query#ref"))); + assertTrue(TileUtils.isValidCustomTileUrl(new GURL("https://m.example.com"))); + assertTrue(TileUtils.isValidCustomTileUrl(new GURL("https://example.com."))); + assertTrue(TileUtils.isValidCustomTileUrl(new GURL("http://example.com:8000/"))); + assertTrue(TileUtils.isValidCustomTileUrl(new GURL("ftp://example.com/file.txt"))); + assertTrue(TileUtils.isValidCustomTileUrl(new GURL("file:///docs/info.pdf"))); + assertTrue(TileUtils.isValidCustomTileUrl(new GURL("chrome://bookmarks"))); + assertTrue(TileUtils.isValidCustomTileUrl(new GURL("chrome-native://recent-tabs"))); + assertFalse(TileUtils.isValidCustomTileUrl(new GURL("javascript://alert(3)"))); + assertFalse(TileUtils.isValidCustomTileUrl(new GURL("javascript:alert(3)"))); + assertFalse(TileUtils.isValidCustomTileUrl(new GURL("about:blank"))); + + assertFalse(TileUtils.isValidCustomTileUrl(new GURL(""))); + assertFalse(TileUtils.isValidCustomTileUrl(new GURL("Not a valid URL"))); + assertFalse(TileUtils.isValidCustomTileUrl(new GURL("://example.com"))); + + // Rely on SuggestionsConfig.MAX_CUSTOM_TILES_URL_LENGTH == 2083. + assertTrue( + TileUtils.isValidCustomTileUrl( + new GURL("https://example.com/" + "a".repeat(2000)))); + assertFalse( + TileUtils.isValidCustomTileUrl( + new GURL("https://example.com/" + "a".repeat(3000)))); + } +}
diff --git a/chrome/app/generated_resources.grd b/chrome/app/generated_resources.grd index 0fc6cf7..b6aaaba 100644 --- a/chrome/app/generated_resources.grd +++ b/chrome/app/generated_resources.grd
@@ -11535,7 +11535,7 @@ Close this view </message> <message name="IDS_SPLIT_TAB_SEPARATE_VIEWS" translateable="false" desc="The label for a menu item that unsplits the split tab when clicked."> - Seperate views + Separate views </message> </if> <if expr="use_titlecase"> @@ -11555,7 +11555,7 @@ Close This View </message> <message name="IDS_SPLIT_TAB_SEPARATE_VIEWS" translateable="false" desc="In title case: the label for a menu item that unsplits the split tab when clicked."> - Seperate Views + Separate Views </message> </if>
diff --git a/chrome/browser/BUILD.gn b/chrome/browser/BUILD.gn index 5af9213..56cab97 100644 --- a/chrome/browser/BUILD.gn +++ b/chrome/browser/BUILD.gn
@@ -750,18 +750,6 @@ "metrics/thread_watcher_report_hang.h", "metrics/ukm_background_recorder_service.cc", "metrics/ukm_background_recorder_service.h", - "metrics/usage_scenario/system_event_provider.cc", - "metrics/usage_scenario/system_event_provider.h", - "metrics/usage_scenario/tab_usage_scenario_tracker.cc", - "metrics/usage_scenario/tab_usage_scenario_tracker.h", - "metrics/usage_scenario/usage_scenario.cc", - "metrics/usage_scenario/usage_scenario.h", - "metrics/usage_scenario/usage_scenario_data_store.cc", - "metrics/usage_scenario/usage_scenario_data_store.h", - "metrics/usage_scenario/usage_scenario_tracker.cc", - "metrics/usage_scenario/usage_scenario_tracker.h", - "metrics/usage_scenario/video_capture_event_provider.cc", - "metrics/usage_scenario/video_capture_event_provider.h", "metrics/variations/chrome_variations_service_client.cc", "metrics/variations/chrome_variations_service_client.h", "metrics/variations/google_groups_manager_factory.cc", @@ -1417,7 +1405,6 @@ "segmentation_platform/segmentation_platform_service_factory.h", "segmentation_platform/ukm_database_client.cc", "segmentation_platform/ukm_database_client.h", - "send_tab_to_self/receiving_ui_handler.h", "send_tab_to_self/send_tab_to_self_client_service.cc", "send_tab_to_self/send_tab_to_self_client_service.h", "send_tab_to_self/send_tab_to_self_client_service_factory.cc", @@ -2003,6 +1990,7 @@ "//chrome/browser/search_engine_choice", "//chrome/browser/search_engine_choice:impl", "//chrome/browser/search_engines", + "//chrome/browser/send_tab_to_self", "//chrome/browser/share", "//chrome/browser/sharing:buildflags", "//chrome/browser/signin", @@ -3180,6 +3168,7 @@ "preloading/android/preloading_data_bridge.cc", "privacy/secure_dns_bridge.cc", "privacy_sandbox/android/privacy_sandbox_bridge.cc", + "privacy_sandbox/android/tracking_protection_settings_bridge.cc", "privacy_sandbox/privacy_sandbox_activity_types_factory.cc", "privacy_sandbox/privacy_sandbox_activity_types_factory.h", "privacy_sandbox/privacy_sandbox_activity_types_service.cc", @@ -3902,6 +3891,18 @@ "metrics/power/process_monitor.h", "metrics/usage_scenario/chrome_responsiveness_calculator_delegate.cc", "metrics/usage_scenario/chrome_responsiveness_calculator_delegate.h", + "metrics/usage_scenario/system_event_provider.cc", + "metrics/usage_scenario/system_event_provider.h", + "metrics/usage_scenario/tab_usage_scenario_tracker.cc", + "metrics/usage_scenario/tab_usage_scenario_tracker.h", + "metrics/usage_scenario/usage_scenario.cc", + "metrics/usage_scenario/usage_scenario.h", + "metrics/usage_scenario/usage_scenario_data_store.cc", + "metrics/usage_scenario/usage_scenario_data_store.h", + "metrics/usage_scenario/usage_scenario_tracker.cc", + "metrics/usage_scenario/usage_scenario_tracker.h", + "metrics/usage_scenario/video_capture_event_provider.cc", + "metrics/usage_scenario/video_capture_event_provider.h", "new_tab_page/chrome_colors/chrome_colors_factory.cc", "new_tab_page/chrome_colors/chrome_colors_factory.h", "new_tab_page/chrome_colors/chrome_colors_service.cc", @@ -4349,6 +4350,7 @@ "//chrome/browser/ui/media_router:impl", "//chrome/browser/ui/page_action:icon_type", "//chrome/browser/ui/privacy_sandbox", + "//chrome/browser/ui/send_tab_to_self", # TODO(413315837): Remove this dependency when chrome/browser/privacy_sandbox # gets modularized.
diff --git a/chrome/browser/about_flags.cc b/chrome/browser/about_flags.cc index 77a4ff9c..5e7c676 100644 --- a/chrome/browser/about_flags.cc +++ b/chrome/browser/about_flags.cc
@@ -5254,8 +5254,7 @@ #endif // USE_AURA {"enable-touch-drag-drop", flag_descriptions::kTouchDragDropName, flag_descriptions::kTouchDragDropDescription, kOsWin | kOsCrOS, - ENABLE_DISABLE_VALUE_TYPE(switches::kEnableTouchDragDrop, - switches::kDisableTouchDragDrop)}, + FEATURE_VALUE_TYPE(features::kTouchDragAndDrop)}, {"touch-selection-strategy", flag_descriptions::kTouchSelectionStrategyName, flag_descriptions::kTouchSelectionStrategyDescription, kOsAndroid, // TODO(mfomitchev): Add CrOS/Win/Linux support soon. @@ -8961,6 +8960,10 @@ FEATURE_VALUE_TYPE( blink::features::kPrerender2EarlyDocumentLifecycleUpdate)}, + {"trees-in-viz", flag_descriptions::kTreesInVizName, + flag_descriptions::kTreesInVizDescription, kOsAll, + FEATURE_VALUE_TYPE(features::kTreesInViz)}, + #if BUILDFLAG(IS_ANDROID) {"prerender2-new-tab-page-android", flag_descriptions::kPrerender2ForNewTabPageAndroidName, @@ -12397,6 +12400,9 @@ FEATURE_VALUE_TYPE(toast_features::kPinnedTabToastOnClose)}, #endif // !BUILDFLAG(IS_ANDROID) + {"discount-autofill", commerce::flag_descriptions::kDiscountAutofillName, + commerce::flag_descriptions::kDiscountAutofillDescription, kOsDesktop, + FEATURE_VALUE_TYPE(commerce::kDiscountAutofill)}, // Add new entries above this line. // NOTE: Adding a new flag requires adding a corresponding entry to enum
diff --git a/chrome/browser/ash/app_list/search/common/icon_constants.h b/chrome/browser/ash/app_list/search/common/icon_constants.h index 06a19e5e..ea34a92c7 100644 --- a/chrome/browser/ash/app_list/search/common/icon_constants.h +++ b/chrome/browser/ash/app_list/search/common/icon_constants.h
@@ -10,15 +10,15 @@ namespace app_list { // Result icon dimension constants. -constexpr int kFaviconDimension = 18; -constexpr int kThumbnailDimension = 28; -constexpr int kSystemIconDimension = 20; -constexpr int kAnswerCardIconDimension = 28; -constexpr int kSystemAnswerCardIconDimension = 32; -constexpr int kAppIconDimension = 32; -constexpr int kImageIconDimension = 28; -constexpr int kImageSearchWidth = 240; -constexpr int kImageSearchHeight = 160; +inline constexpr int kFaviconDimension = 18; +inline constexpr int kThumbnailDimension = 28; +inline constexpr int kSystemIconDimension = 20; +inline constexpr int kAnswerCardIconDimension = 28; +inline constexpr int kSystemAnswerCardIconDimension = 32; +inline constexpr int kAppIconDimension = 32; +inline constexpr int kImageIconDimension = 28; +inline constexpr int kImageSearchWidth = 240; +inline constexpr int kImageSearchHeight = 160; SkColor GetGenericIconColor();
diff --git a/chrome/browser/ash/app_list/search/federated_metrics_manager.h b/chrome/browser/ash/app_list/search/federated_metrics_manager.h index c47a770fb..93451af 100644 --- a/chrome/browser/ash/app_list/search/federated_metrics_manager.h +++ b/chrome/browser/ash/app_list/search/federated_metrics_manager.h
@@ -28,7 +28,7 @@ inline constexpr char kHistogramReportStatus[] = "Apps.AppList.Search.Federated.ReportStatus"; -const int kMaxLoggedQueryLengthOnStorageSuccess = 20; +inline constexpr int kMaxLoggedQueryLengthOnStorageSuccess = 20; // Records launcher search backend federated analytics. // Requires that OnDefaultSearchIsGoogleSet() is called after class creation and
diff --git a/chrome/browser/ash/app_list/search/omnibox/omnibox_util.h b/chrome/browser/ash/app_list/search/omnibox/omnibox_util.h index 65d95ce..89f76f5 100644 --- a/chrome/browser/ash/app_list/search/omnibox/omnibox_util.h +++ b/chrome/browser/ash/app_list/search/omnibox/omnibox_util.h
@@ -31,7 +31,7 @@ // The magic number 1500 is the highest score of an omnibox result. // See comments in autocomplete_provider.h. -constexpr double kMaxOmniboxScore = 1500.0; +inline constexpr double kMaxOmniboxScore = 1500.0; // Traffic annotation details for fetching Omnibox image icons. constexpr net::NetworkTrafficAnnotationTag kOmniboxTrafficAnnotation = @@ -62,7 +62,7 @@ // Some omnibox answers overtrigger on short queries. This controls the minimum // query length before they are displayed. -constexpr size_t kMinQueryLengthForCommonAnswers = 4u; +inline constexpr size_t kMinQueryLengthForCommonAnswers = 4u; // Returns the tag vector for the given text type. ash::SearchResultTags TagsForText(const std::u16string& text,
diff --git a/chrome/browser/ash/app_list/search/ranking/constants.h b/chrome/browser/ash/app_list/search/ranking/constants.h index 52dd190e..2f9b8a0 100644 --- a/chrome/browser/ash/app_list/search/ranking/constants.h +++ b/chrome/browser/ash/app_list/search/ranking/constants.h
@@ -9,22 +9,22 @@ // The maximum number of omnibox results to display if we have more results than // can fit in the UI. -constexpr int kMaxOmniboxResults = 3; +inline constexpr int kMaxOmniboxResults = 3; // The maximum number of best matches to show. -constexpr size_t kNumBestMatches = 3u; +inline constexpr size_t kNumBestMatches = 3u; // The number of top-ranked best match results to stabilize during the // post-burn-in period. Stabilized results retain their rank and are not // displaced by later-arriving results -constexpr size_t kNumBestMatchesToStabilize = 1u; +inline constexpr size_t kNumBestMatchesToStabilize = 1u; // The score threshold before we consider a result a best match. -constexpr double kBestMatchThreshold = 0.8; +inline constexpr double kBestMatchThreshold = 0.8; // The score threshold used when there's keyword ranking. // This is given by tanh(2.65 * 0.8) where 0.8 is original best match threshold. -constexpr double kBestMatchThresholdWithKeywordRanking = 0.97159407725; +inline constexpr double kBestMatchThresholdWithKeywordRanking = 0.97159407725; } // namespace app_list
diff --git a/chrome/browser/ash/app_list/search/search_controller.h b/chrome/browser/ash/app_list/search/search_controller.h index 3661e05..33136475 100644 --- a/chrome/browser/ash/app_list/search/search_controller.h +++ b/chrome/browser/ash/app_list/search/search_controller.h
@@ -50,7 +50,7 @@ class SearchEngine; // Long queries will be truncated down to this length. -constexpr int kMaxAllowedQueryLength = 500; +inline constexpr int kMaxAllowedQueryLength = 500; // A controller that collects queries from the AppListClient, dispatches them to // search providers, then ranks and publishes the results to the AppListModel.
diff --git a/chrome/browser/ash/app_list/search/search_session_metrics_manager.h b/chrome/browser/ash/app_list/search/search_session_metrics_manager.h index fdb6900d..688c5e3 100644 --- a/chrome/browser/ash/app_list/search/search_session_metrics_manager.h +++ b/chrome/browser/ash/app_list/search/search_session_metrics_manager.h
@@ -16,7 +16,7 @@ namespace app_list { -const int kMaxLoggedQueryLengthOnSessionConclusion = 20; +inline constexpr int kMaxLoggedQueryLengthOnSessionConclusion = 20; // Records launcher search backend metrics. This includes impression, // abandonment, and launch information reported by the AppListNotifier.
diff --git a/chrome/browser/ash/arc/arc_migration_constants.h b/chrome/browser/ash/arc/arc_migration_constants.h index e98e2fda..56c5c81 100644 --- a/chrome/browser/ash/arc/arc_migration_constants.h +++ b/chrome/browser/ash/arc/arc_migration_constants.h
@@ -8,10 +8,10 @@ namespace arc { // The minimum battery level to start the migration. -constexpr double kMigrationMinimumBatteryPercent = 10; +inline constexpr double kMigrationMinimumBatteryPercent = 10; // The minimum size of available space to start the migration. (50MB) -constexpr int64_t kMigrationMinimumAvailableStorage = 50LL * 1024 * 1024; +inline constexpr int64_t kMigrationMinimumAvailableStorage = 50LL * 1024 * 1024; } // namespace arc
diff --git a/chrome/browser/ash/arc/input_overlay/actions/action_move.h b/chrome/browser/ash/arc/input_overlay/actions/action_move.h index d43b1130..6283606 100644 --- a/chrome/browser/ash/arc/input_overlay/actions/action_move.h +++ b/chrome/browser/ash/arc/input_overlay/actions/action_move.h
@@ -12,7 +12,7 @@ namespace arc::input_overlay { // UI specs. -constexpr int kActionMoveMinRadius = 99; +inline constexpr int kActionMoveMinRadius = 99; class TouchInjector;
diff --git a/chrome/browser/ash/arc/input_overlay/constants.h b/chrome/browser/ash/arc/input_overlay/constants.h index c1b867a6..231520bb 100644 --- a/chrome/browser/ash/arc/input_overlay/constants.h +++ b/chrome/browser/ash/arc/input_overlay/constants.h
@@ -21,10 +21,10 @@ inline constexpr char kSystemVersionAlphaV2Plus[] = "0.2"; // The coordinates number, including Axis x and y. -constexpr int kAxisSize = 2; +inline constexpr int kAxisSize = 2; // Total key size for ActionMoveKey. -constexpr size_t kActionMoveKeysSize = 4; +inline constexpr size_t kActionMoveKeysSize = 4; // Maximum of actions size. inline constexpr size_t kMaxActionCount = 50; @@ -32,31 +32,33 @@ inline constexpr char16_t kUnknownBind[] = u"?"; // Directions from up, left, down, right. -constexpr int kDirection[kActionMoveKeysSize][kAxisSize] = {{0, -1}, - {-1, 0}, - {0, 1}, - {1, 0}}; +inline constexpr int kDirection[kActionMoveKeysSize][kAxisSize] = {{0, -1}, + {-1, 0}, + {0, 1}, + {1, 0}}; // From ActionTap AlphaV2 design. There is the label offset to touch point in // the edit mode. -constexpr int kOffsetToTouchPoint = -1; // 2 - 3(kDotOutsideStrokeThickness) +// +// 2 - 3(kDotOutsideStrokeThickness) +inline constexpr int kOffsetToTouchPoint = -1; // The space between EditingList and main window when EditingList is outside of // the game window. -constexpr int kEditingListSpaceBetweenMainWindow = 5; +inline constexpr int kEditingListSpaceBetweenMainWindow = 5; // The offset from the game window content when EditingList is inside of the // game window. -constexpr int kEditingListOffsetInsideMainWindow = 24; +inline constexpr int kEditingListOffsetInsideMainWindow = 24; // The offset from the action view list item to the editing list border. -constexpr int kEditingListInsideBorderInsets = 16; +inline constexpr int kEditingListInsideBorderInsets = 16; // Width of `EditingList`. -constexpr int kEditingListWidth = 296; +inline constexpr int kEditingListWidth = 296; // Width of `ButtonOptionsMenu` minus the triangle height. -constexpr int kButtonOptionsMenuWidth = 296; +inline constexpr int kButtonOptionsMenuWidth = 296; // Horizontal order inset for `ArrowContainer` and its children. -constexpr int kArrowContainerHorizontalBorderInset = 16; +inline constexpr int kArrowContainerHorizontalBorderInset = 16; // Arrow key move distance per key press event. inline constexpr int kArrowKeyMoveDistance = 2;
diff --git a/chrome/browser/ash/arc/input_overlay/touch_injector.h b/chrome/browser/ash/arc/input_overlay/touch_injector.h index ed0293f..37e29f31 100644 --- a/chrome/browser/ash/arc/input_overlay/touch_injector.h +++ b/chrome/browser/ash/arc/input_overlay/touch_injector.h
@@ -50,7 +50,7 @@ gfx::RectF CalculateWindowContentBounds(aura::Window* window); // Maximum default action ID. User-added actions have ID > kMaxDefaultActionID. -constexpr int kMaxDefaultActionID = 9999; +inline constexpr int kMaxDefaultActionID = 9999; // TouchInjector includes all the touch actions related to the specific window // and performs as a bridge between the ArcInputOverlayManager and the touch
diff --git a/chrome/browser/ash/arc/input_overlay/ui/action_view.h b/chrome/browser/ash/arc/input_overlay/ui/action_view.h index ee509f9..c1c950a 100644 --- a/chrome/browser/ash/arc/input_overlay/ui/action_view.h +++ b/chrome/browser/ash/arc/input_overlay/ui/action_view.h
@@ -28,7 +28,7 @@ class TouchPoint; // Represents the default label index. Default -1 means all the index. -constexpr int kDefaultLabelIndex = -1; +inline constexpr int kDefaultLabelIndex = -1; // ActionView is the view for each action. class ActionView : public views::View {
diff --git a/chrome/browser/ash/arc/session/arc_disk_space_monitor.h b/chrome/browser/ash/arc/session/arc_disk_space_monitor.h index 83528b5e..c1306d2 100644 --- a/chrome/browser/ash/arc/session/arc_disk_space_monitor.h +++ b/chrome/browser/ash/arc/session/arc_disk_space_monitor.h
@@ -16,11 +16,14 @@ // These thresholds are chosen based on UMA stats. (go/arcvm-virtio-blk-sparse) // Show a pre-stop warning notification if free disk space is lower than this. -constexpr int64_t kDiskSpaceThresholdForPreStopNotification = 1LL << 30; // 1GB +inline constexpr int64_t kDiskSpaceThresholdForPreStopNotification = + 1LL << 30; // 1GB // Stop ARC and show a post-stop warning notification if free disk space is // lower than this. -constexpr int64_t kDiskSpaceThresholdForStoppingArc = 256LL << 20; // 256MB +// +// 256MB +inline constexpr int64_t kDiskSpaceThresholdForStoppingArc = 256LL << 20; // TODO(b/233030867): Choose these values based on some logic // instead of deciding them on a hunch.
diff --git a/chrome/browser/ash/arc/session/arc_session_manager.h b/chrome/browser/ash/arc/session/arc_session_manager.h index 116eebd..5ad4f02 100644 --- a/chrome/browser/ash/arc/session/arc_session_manager.h +++ b/chrome/browser/ash/arc/session/arc_session_manager.h
@@ -52,7 +52,7 @@ // auto-resumes have been already attempted but the migration has not finished, // ARC is blocked and the user needs to manually trigger the resume by clicking // a notification. -constexpr int kArcVmDataMigrationMaxAutoResumeCount = 3; +inline constexpr int kArcVmDataMigrationMaxAutoResumeCount = 3; class ArcDataRemover; class ArcFastAppReinstallStarter;
diff --git a/chrome/browser/ash/boca/on_task/on_task_session_manager_browsertest.cc b/chrome/browser/ash/boca/on_task/on_task_session_manager_browsertest.cc index bbc1710..20b99ae 100644 --- a/chrome/browser/ash/boca/on_task/on_task_session_manager_browsertest.cc +++ b/chrome/browser/ash/boca/on_task/on_task_session_manager_browsertest.cc
@@ -72,19 +72,11 @@ std::set<std::string> notifications_shown_; }; -class OnTaskSessionManagerBrowserTest : public InProcessBrowserTest { +class OnTaskSessionManagerBrowserTestBase : public InProcessBrowserTest { protected: - OnTaskSessionManagerBrowserTest() { + OnTaskSessionManagerBrowserTestBase() { // Initialize the MockClock. boca::MockClock::Get(); - - // Enable Boca and consumer experience for testing purposes. This is used - // to set up the Boca SWA for OnTask. - scoped_feature_list_.InitWithFeatures( - /*enabled_features=*/{features::kBoca, features::kBocaConsumer, - features::kBocaLockedModeCustomCountdownDuration, - features::kOnDeviceSpeechRecognition}, - /*disabled_features=*/{}); } void SetUpOnMainThread() override { @@ -161,13 +153,30 @@ net::EmbeddedTestServer* https_server() { return &https_server_; } private: - base::test::ScopedFeatureList scoped_feature_list_; content::ContentMockCertVerifier mock_cert_verifier_; net::EmbeddedTestServer https_server_{net::EmbeddedTestServer::TYPE_HTTPS}; raw_ptr<FakeOnTaskNotificationsManagerDelegate> fake_notifications_delegate_ptr_; }; +class OnTaskSessionManagerBrowserTest + : public OnTaskSessionManagerBrowserTestBase { + protected: + OnTaskSessionManagerBrowserTest() { + // Enable Boca and consumer experience for testing purposes. This is used + // to set up the Boca SWA for OnTask. + scoped_feature_list_.InitWithFeatures( + /*enabled_features=*/{features::kBoca, features::kBocaConsumer, + features::kBocaLockedModeCustomCountdownDuration, + features::kOnDeviceSpeechRecognition, + features::kBocaKeepSWAOpenOnSessionEnded}, + /*disabled_features=*/{}); + } + + private: + base::test::ScopedFeatureList scoped_feature_list_; +}; + IN_PROC_BROWSER_TEST_F(OnTaskSessionManagerBrowserTest, ShouldOpenTabsOnBundleUpdated) { content::TestNavigationObserver navigation_observer_1((GURL(kTestUrl1))); @@ -618,7 +627,7 @@ } IN_PROC_BROWSER_TEST_F(OnTaskSessionManagerBrowserTest, - ShouldCloseBocaSWAOnSessionEnd) { + ShouldKeepBocaSWAOpenOnSessionEndWithFeatureEnabled) { const GURL boca_chrome_url = GURL(kChromeBocaAppUntrustedIndexURL); content::TestNavigationObserver navigation_observer(boca_chrome_url); navigation_observer.StartWatchingNewWebContents(); @@ -633,12 +642,11 @@ ASSERT_TRUE(boca_app_browser->IsLockedForOnTask()); // End the session. GetOnTaskSessionManager()->OnSessionEnded(kSessionId); - // Wait until the browser actually gets closed. - ui_test_utils::WaitForBrowserToClose(); + VerifyNotificationShown(kOnTaskSessionEndNotificationId, true); VerifyNotificationShown(kOnTaskBundleContentAddedNotificationId, false); VerifyNotificationShown(kOnTaskBundleContentRemovedNotificationId, false); - ASSERT_THAT(FindBocaSystemWebAppBrowser(), IsNull()); + EXPECT_EQ(FindBocaSystemWebAppBrowser(), boca_app_browser); } IN_PROC_BROWSER_TEST_F(OnTaskSessionManagerBrowserTest, @@ -788,5 +796,44 @@ browser_2->tab_strip_model()->GetActiveWebContents()->IsAudioMuted()); } +class OnTaskSessionManagerCloseSWAOnSessionEndBrowserTest + : public OnTaskSessionManagerBrowserTestBase { + protected: + OnTaskSessionManagerCloseSWAOnSessionEndBrowserTest() { + scoped_feature_list_.InitWithFeatures( + /*enabled_features=*/{features::kBoca, features::kBocaConsumer, + features::kBocaLockedModeCustomCountdownDuration, + features::kOnDeviceSpeechRecognition}, + /*disabled_features=*/{features::kBocaKeepSWAOpenOnSessionEnded}); + } + + private: + base::test::ScopedFeatureList scoped_feature_list_; +}; + +IN_PROC_BROWSER_TEST_F(OnTaskSessionManagerCloseSWAOnSessionEndBrowserTest, + ShouldCloseBocaSWAOnSessionEnd) { + const GURL boca_chrome_url = GURL(kChromeBocaAppUntrustedIndexURL); + content::TestNavigationObserver navigation_observer(boca_chrome_url); + navigation_observer.StartWatchingNewWebContents(); + + // Start OnTask session. + GetOnTaskSessionManager()->OnSessionStarted(kSessionId, + ::boca::UserIdentity()); + navigation_observer.Wait(); + + Browser* const boca_app_browser = FindBocaSystemWebAppBrowser(); + ASSERT_THAT(boca_app_browser, NotNull()); + ASSERT_TRUE(boca_app_browser->IsLockedForOnTask()); + // End the session. + GetOnTaskSessionManager()->OnSessionEnded(kSessionId); + // Wait until the browser actually gets closed. + ui_test_utils::WaitForBrowserToClose(); + VerifyNotificationShown(kOnTaskSessionEndNotificationId, true); + VerifyNotificationShown(kOnTaskBundleContentAddedNotificationId, false); + VerifyNotificationShown(kOnTaskBundleContentRemovedNotificationId, false); + ASSERT_THAT(FindBocaSystemWebAppBrowser(), IsNull()); +} + } // namespace } // namespace ash::boca
diff --git a/chrome/browser/ash/crostini/crostini_disk.h b/chrome/browser/ash/crostini/crostini_disk.h index 4ddb7c0..de103507 100644 --- a/chrome/browser/ash/crostini/crostini_disk.h +++ b/chrome/browser/ash/crostini/crostini_disk.h
@@ -33,21 +33,21 @@ namespace disk { -constexpr int64_t kGiB = 1024 * 1024 * 1024; -constexpr int64_t kDiskHeadroomBytes = 1 * kGiB; -constexpr int64_t kMinimumDiskSizeBytes = 2 * kGiB; -constexpr int64_t kRecommendedDiskSizeBytes = 10 * kGiB; +inline constexpr int64_t kGiB = 1024 * 1024 * 1024; +inline constexpr int64_t kDiskHeadroomBytes = 1 * kGiB; +inline constexpr int64_t kMinimumDiskSizeBytes = 2 * kGiB; +inline constexpr int64_t kRecommendedDiskSizeBytes = 10 * kGiB; // A number which influences the interval size and number of ticks selected for // a given range. At 400 >400 GiB gets 1 GiB ticks, smaller sizes get smaller // intervals. 400 is arbitrary, chosen because it keeps ticks at least 1px each // on sliders and feels nice. -constexpr int kGranularityFactor = 400; +inline constexpr int kGranularityFactor = 400; // The size of the download for the VM image. // As of 2020-01-10 the Termina files.zip is ~90MiB and the squashfs container // is ~330MiB. -constexpr int64_t kDownloadSizeBytes = 450ll * 1024 * 1024; // 450 MiB +inline constexpr int64_t kDownloadSizeBytes = 450ll * 1024 * 1024; // 450 MiB using OnceDiskInfoCallback = base::OnceCallback<void(std::unique_ptr<CrostiniDiskInfo> info)>;
diff --git a/chrome/browser/ash/input_method/emoji_suggester.h b/chrome/browser/ash/input_method/emoji_suggester.h index 3b6824c..2736522ea 100644 --- a/chrome/browser/ash/input_method/emoji_suggester.h +++ b/chrome/browser/ash/input_method/emoji_suggester.h
@@ -22,7 +22,7 @@ namespace ash { namespace input_method { -constexpr int kEmojiSuggesterShowSettingMaxCount = 10; +inline constexpr int kEmojiSuggesterShowSettingMaxCount = 10; // An agent to suggest emoji when the user types, and adopt or // dismiss the suggestion according to the user action.
diff --git a/chrome/browser/ash/input_method/text_utils.h b/chrome/browser/ash/input_method/text_utils.h index f813f0f..1766942d 100644 --- a/chrome/browser/ash/input_method/text_utils.h +++ b/chrome/browser/ash/input_method/text_utils.h
@@ -13,7 +13,7 @@ // TODO(crbug/1223213): Move these to a sandbox environment. namespace ash::input_method { -constexpr uint32_t kUndefined = std::numeric_limits<uint32_t>::max(); +inline constexpr uint32_t kUndefined = std::numeric_limits<uint32_t>::max(); struct Sentence { Sentence();
diff --git a/chrome/browser/ash/login/chrome_restart_request.cc b/chrome/browser/ash/login/chrome_restart_request.cc index a7beff9..805bcab 100644 --- a/chrome/browser/ash/login/chrome_restart_request.cc +++ b/chrome/browser/ash/login/chrome_restart_request.cc
@@ -113,7 +113,6 @@ ::switches::kDisableGpuCompositing, ::switches::kDisableGpuRasterization, ::switches::kDisableMojoBroker, - ::switches::kDisableTouchDragDrop, ::switches::kDisableVideoCaptureUseGpuMemoryBuffer, ::switches::kDisableYUVImageDecoding, ::switches::kEnableBlinkFeatures, @@ -122,7 +121,6 @@ ::switches::kEnableLogging, ::switches::kEnableMicrophoneMuteSwitchDeviceSwitch, ::switches::kEnableNativeGpuMemoryBuffers, - ::switches::kEnableTouchDragDrop, ::switches::kEnableUnifiedDesktop, ::switches::kEnableViewport, ::switches::kEnableHardwareOverlays,
diff --git a/chrome/browser/ash/login/helper.h b/chrome/browser/ash/login/helper.h index 9b8adb2..88f7617 100644 --- a/chrome/browser/ash/login/helper.h +++ b/chrome/browser/ash/login/helper.h
@@ -55,7 +55,7 @@ // Maximum size of user image, in which it should be saved to be properly // displayed under all possible DPI values. -const int kMaxUserImageSize = 512; +inline constexpr int kMaxUserImageSize = 512; // Returns true if lock/login should scroll user pods into view itself when // virtual keyboard is shown and disable vk overscroll.
diff --git a/chrome/browser/ash/login/login_constants.h b/chrome/browser/ash/login/login_constants.h index 950c430..f654ed4 100644 --- a/chrome/browser/ash/login/login_constants.h +++ b/chrome/browser/ash/login/login_constants.h
@@ -15,27 +15,28 @@ // enforced by `OfflineSigninLimiter` so the user will be allowed to use offline // authentication until a different reason than this policy enforces an online // login. -constexpr int kOfflineSigninTimeLimitNotSet = -1; +inline constexpr int kOfflineSigninTimeLimitNotSet = -1; // The value -2 means match the offline sign in time limit of the login screen, // so policies GaiaLockScreenOfflineSigninTimeLimitDays would get the same value // as GaiaOfflineSigninTimeLimitDays and // SamlLockScreenOfflineSigninTimeLimitDays would get the same value as // SAMLOfflineSigninTimeLimit -constexpr int kLockScreenOfflineSigninTimeLimitDaysMatchLogin = -2; +inline constexpr int kLockScreenOfflineSigninTimeLimitDaysMatchLogin = -2; -constexpr int kDefaultGaiaOfflineSigninTimeLimitDays = +inline constexpr int kDefaultGaiaOfflineSigninTimeLimitDays = kOfflineSigninTimeLimitNotSet; -constexpr int kDefaultSAMLOfflineSigninTimeLimit = base::Days(14).InSeconds(); +inline constexpr int kDefaultSAMLOfflineSigninTimeLimit = + base::Days(14).InSeconds(); -constexpr int kDefaultGaiaLockScreenOfflineSigninTimeLimitDays = +inline constexpr int kDefaultGaiaLockScreenOfflineSigninTimeLimitDays = kOfflineSigninTimeLimitNotSet; -constexpr int kDefaultSamlLockScreenOfflineSigninTimeLimitDays = +inline constexpr int kDefaultSamlLockScreenOfflineSigninTimeLimitDays = kOfflineSigninTimeLimitNotSet; // In-session password-change feature (includes password expiry notifications). const bool kDefaultSamlInSessionPasswordChangeEnabled = false; -const int kDefaultSamlPasswordExpirationAdvanceWarningDays = 14; +inline constexpr int kDefaultSamlPasswordExpirationAdvanceWarningDays = 14; // Online reauthentication on the lock screen. const bool kDefaultLockScreenReauthenticationEnabled = false; @@ -45,7 +46,7 @@ // A value of zero indicates the policy being disabled (no auto reload is // triggered). While a value greater than zero indicates automatically reloading // the authentication flow by this interval specified in minutes. -const int kDefaultAuthenticationFlowAutoReloadInterval = 0; +inline constexpr int kDefaultAuthenticationFlowAutoReloadInterval = 0; } // namespace constants } // namespace ash
diff --git a/chrome/browser/ash/net/network_diagnostics/network_diagnostics_util.h b/chrome/browser/ash/net/network_diagnostics/network_diagnostics_util.h index f68e3f2..a8a46e0 100644 --- a/chrome/browser/ash/net/network_diagnostics/network_diagnostics_util.h +++ b/chrome/browser/ash/net/network_diagnostics/network_diagnostics_util.h
@@ -21,16 +21,16 @@ namespace util { // DNS queries taking longer than 400 ms are potentially problematic. -constexpr int kDnsPotentialProblemLatencyMs = 400; +inline constexpr int kDnsPotentialProblemLatencyMs = 400; // DNS queries taking longer than 500 ms are problematic. -constexpr int kDnsProblemLatencyMs = 500; +inline constexpr int kDnsProblemLatencyMs = 500; // Generate 204 path. extern const char kGenerate204Path[]; // STUN packet header size. -constexpr int kStunHeaderSize = 20; +inline constexpr int kStunHeaderSize = 20; // Returns the Gstatic host suffix. Network diagnostic routines attach a random // prefix to |kGstaticHostSuffix| to get a complete hostname.
diff --git a/chrome/browser/ash/policy/scheduled_task_handler/device_scheduled_update_checker.h b/chrome/browser/ash/policy/scheduled_task_handler/device_scheduled_update_checker.h index 810fe3e..0726def 100644 --- a/chrome/browser/ash/policy/scheduled_task_handler/device_scheduled_update_checker.h +++ b/chrome/browser/ash/policy/scheduled_task_handler/device_scheduled_update_checker.h
@@ -111,7 +111,7 @@ // The maximum iterations allowed to start an update check timer if the // operation fails. -constexpr int kMaxStartUpdateCheckTimerRetryIterations = 5; +inline constexpr int kMaxStartUpdateCheckTimerRetryIterations = 5; // Time to call |StartUpdateCheckTimer| again in case it failed. constexpr base::TimeDelta kStartUpdateCheckTimerRetryTime = base::Minutes(1);
diff --git a/chrome/browser/ash/policy/scheduled_task_handler/os_and_policies_update_checker.h b/chrome/browser/ash/policy/scheduled_task_handler/os_and_policies_update_checker.h index 8701c9b..c4e3653c 100644 --- a/chrome/browser/ash/policy/scheduled_task_handler/os_and_policies_update_checker.h +++ b/chrome/browser/ash/policy/scheduled_task_handler/os_and_policies_update_checker.h
@@ -24,7 +24,7 @@ // The maximum iterations allowed to check for and download an update if the // operation fails. Used with |os_and_policies_update_checker_|. -constexpr int kMaxOsAndPoliciesUpdateCheckerRetryIterations = 2; +inline constexpr int kMaxOsAndPoliciesUpdateCheckerRetryIterations = 2; // Interval at which |os_and_policies_update_checker_| retries checking for and // downloading updates.
diff --git a/chrome/browser/ash/printing/oauth2/constants.h b/chrome/browser/ash/printing/oauth2/constants.h index 59f09fb..5006797b 100644 --- a/chrome/browser/ash/printing/oauth2/constants.h +++ b/chrome/browser/ash/printing/oauth2/constants.h
@@ -20,7 +20,7 @@ inline constexpr char kClientName[] = "ChromeOS"; // Max number of parallel OAuth2 sessions with one Authorization Server. -constexpr size_t kMaxNumberOfSessions = 8; +inline constexpr size_t kMaxNumberOfSessions = 8; } // namespace oauth2 } // namespace printing
diff --git a/chrome/browser/ash/smb_client/smb_constants.h b/chrome/browser/ash/smb_client/smb_constants.h index d611d3a..ba1f0476 100644 --- a/chrome/browser/ash/smb_client/smb_constants.h +++ b/chrome/browser/ash/smb_client/smb_constants.h
@@ -10,7 +10,7 @@ extern const char kSmbScheme[]; extern const char kSmbSchemePrefix[]; -constexpr int kNetBiosDiscoveryTimeoutSeconds = 1; +inline constexpr int kNetBiosDiscoveryTimeoutSeconds = 1; } // namespace ash::smb_client
diff --git a/chrome/browser/ash/system_web_apps/apps/camera_app/chrome_camera_app_ui_constants.h b/chrome/browser/ash/system_web_apps/apps/camera_app/chrome_camera_app_ui_constants.h index 14b75781..cea3168 100644 --- a/chrome/browser/ash/system_web_apps/apps/camera_app/chrome_camera_app_ui_constants.h +++ b/chrome/browser/ash/system_web_apps/apps/camera_app/chrome_camera_app_ui_constants.h
@@ -6,9 +6,9 @@ #define CHROME_BROWSER_ASH_SYSTEM_WEB_APPS_APPS_CAMERA_APP_CHROME_CAMERA_APP_UI_CONSTANTS_H_ // All window size number here are referred to inner size excluding top bar. -constexpr int kChromeCameraAppDefaultWidth = 764; -constexpr int kChromeCameraAppDefaultHeight = 412; -constexpr int kChromeCameraAppMinimumWidth = 486; -constexpr int kChromeCameraAppMinimumHeight = 412; +inline constexpr int kChromeCameraAppDefaultWidth = 764; +inline constexpr int kChromeCameraAppDefaultHeight = 412; +inline constexpr int kChromeCameraAppMinimumWidth = 486; +inline constexpr int kChromeCameraAppMinimumHeight = 412; #endif // CHROME_BROWSER_ASH_SYSTEM_WEB_APPS_APPS_CAMERA_APP_CHROME_CAMERA_APP_UI_CONSTANTS_H_
diff --git a/chrome/browser/ash/usb/cros_usb_detector.h b/chrome/browser/ash/usb/cros_usb_detector.h index 6dcab31..53a168f 100644 --- a/chrome/browser/ash/usb/cros_usb_detector.h +++ b/chrome/browser/ash/usb/cros_usb_detector.h
@@ -27,7 +27,7 @@ namespace ash { -const uint8_t kInvalidUsbPortNumber = 0xff; +inline constexpr uint8_t kInvalidUsbPortNumber = 0xff; // List of class codes to handle / not handle. // See https://www.usb.org/defined-class-codes for more information.
diff --git a/chrome/browser/bookmarks/android/javatests/src/org/chromium/chrome/browser/bookmarks/BookmarkPaneTest.java b/chrome/browser/bookmarks/android/javatests/src/org/chromium/chrome/browser/bookmarks/BookmarkPaneTest.java index 860a733a..0799aa1f 100644 --- a/chrome/browser/bookmarks/android/javatests/src/org/chromium/chrome/browser/bookmarks/BookmarkPaneTest.java +++ b/chrome/browser/bookmarks/android/javatests/src/org/chromium/chrome/browser/bookmarks/BookmarkPaneTest.java
@@ -19,7 +19,6 @@ import static org.chromium.base.ThreadUtils.runOnUiThreadBlocking; import static org.chromium.chrome.browser.tasks.tab_management.TabUiTestHelper.enterTabSwitcher; -import static org.chromium.chrome.test.transit.hub.HubBaseStation.HUB_PANE_SWITCHER; import static org.chromium.ui.test.util.ViewUtils.onViewWaiting; import androidx.test.filters.MediumTest; @@ -150,7 +149,7 @@ private void enterBookmarkPane() { onView( allOf( - isDescendantOfA(HUB_PANE_SWITCHER.getViewMatcher()), + isDescendantOfA(withId(R.id.pane_switcher)), withContentDescription(containsString("Bookmarks")))) .perform(click()); }
diff --git a/chrome/browser/chrome_content_browser_client.cc b/chrome/browser/chrome_content_browser_client.cc index 911f1eb..2ee8a27d 100644 --- a/chrome/browser/chrome_content_browser_client.cc +++ b/chrome/browser/chrome_content_browser_client.cc
@@ -421,6 +421,7 @@ #include "ui/base/l10n/l10n_util.h" #include "ui/base/page_transition_types.h" #include "ui/base/resource/resource_bundle.h" +#include "ui/base/ui_base_features.h" #include "ui/color/color_provider.h" #include "ui/color/color_provider_key.h" #include "ui/gfx/color_utils.h" @@ -4774,6 +4775,9 @@ prefs->GetBoolean(prefs::kWebXRImmersiveArEnabled); #endif + web_prefs->touch_drag_drop_enabled = + base::FeatureList::IsEnabled(features::kTouchDragAndDrop); + for (auto& parts : extra_parts_) { parts->OverrideWebPreferences(web_contents, main_frame_site, web_prefs); }
diff --git a/chrome/browser/component_updater/pki_metadata_component_installer_browsertest.cc b/chrome/browser/component_updater/pki_metadata_component_installer_browsertest.cc index 92cf8b0..188e055 100644 --- a/chrome/browser/component_updater/pki_metadata_component_installer_browsertest.cc +++ b/chrome/browser/component_updater/pki_metadata_component_installer_browsertest.cc
@@ -29,8 +29,8 @@ #include "content/public/browser/ssl_status.h" #include "content/public/test/browser_test.h" #include "content/public/test/browser_test_utils.h" -#include "crypto/ec_private_key.h" -#include "crypto/sha2.h" +#include "crypto/hash.h" +#include "crypto/keypair.h" #include "net/cert/test_root_certs.h" #include "net/cert/x509_certificate.h" #include "net/dns/mock_host_resolver.h" @@ -64,6 +64,76 @@ return (t - base::Time::UnixEpoch()).InSeconds(); } +// A CTLog generates a log identity private key, then computes and +// caches several properties from that key that are needed in test cases. +class CTLog { + public: + CTLog(std::string_view name, + base::Time start, + base::Time end, + chrome_browser_certificate_transparency::CTLog::LogType type) + : name_(name), start_(start), end_(end), type_(type) {} + + std::string_view name() const { return name_; } + base::Time start() const { return start_; } + base::Time end() const { return end_; } + chrome_browser_certificate_transparency::CTLog::LogType type() const { + return type_; + } + + base::span<const uint8_t> spki() const { return spki_; } + std::string_view spki_base64() const { return spki_base64_; } + + // Even though the id is just a span of bytes, so this should theoretically + // return a base::span<const uint8_t> referencing the data we've cached, all the + // call sites want it as a string. + std::string id() const { return std::string(base::as_string_view(id_)); } + std::string_view id_base64() const { return id_base64_; } + + bssl::UniquePtr<EVP_PKEY> key() { return bssl::UpRef(private_key_.key()); } + + private: + const std::string name_; + const base::Time start_; + const base::Time end_; + const chrome_browser_certificate_transparency::CTLog::LogType type_; + + // The generated private key and things derived from it. Note that the private + // key itself can't be const, because returning a reference to it in key() + // above requires mutating its inner refcount. + crypto::keypair::PrivateKey private_key_{ + crypto::keypair::PrivateKey::GenerateEcP256()}; + const std::vector<uint8_t> spki_{private_key_.ToSubjectPublicKeyInfo()}; + const std::string spki_base64_{base::Base64Encode(spki_)}; + const std::array<uint8_t, crypto::hash::kSha256Size> id_{ + crypto::hash::Sha256(spki_)}; + const std::string id_base64_{base::Base64Encode(id_)}; +}; + +void AddLogToCTConfig(chrome_browser_certificate_transparency::CTConfig* config, + const CTLog& log) { + chrome_browser_certificate_transparency::CTLog* entry = + config->mutable_log_list()->add_logs(); + entry->set_log_id(log.id_base64()); + entry->set_key(log.spki_base64()); + entry->set_purpose(chrome_browser_certificate_transparency::CTLog::PROD); + entry->set_log_type(log.type()); + entry->mutable_temporal_interval()->mutable_start()->set_seconds( + SecondsSinceEpoch(log.start())); + entry->mutable_temporal_interval()->mutable_end()->set_seconds( + SecondsSinceEpoch(log.end())); + chrome_browser_certificate_transparency::CTLog_State* log_state = + entry->add_state(); + log_state->set_current_state( + chrome_browser_certificate_transparency::CTLog::USABLE); + log_state->mutable_state_start()->set_seconds(SecondsSinceEpoch(log.start())); + chrome_browser_certificate_transparency::CTLog_OperatorChange* + operator_history = entry->add_operator_history(); + operator_history->set_name(log.name()); + operator_history->mutable_operator_start()->set_seconds( + SecondsSinceEpoch(log.start())); +} + } // namespace namespace component_updater { @@ -234,29 +304,14 @@ } IN_PROC_BROWSER_TEST_P(PKIMetadataComponentUpdaterTest, TestCTUpdate) { - const std::string kLog1OperatorName = "log operator 1"; - std::unique_ptr<crypto::ECPrivateKey> log1_private_key = - crypto::ECPrivateKey::Create(); - std::vector<uint8_t> log1_spki; - ASSERT_TRUE(log1_private_key->ExportPublicKey(&log1_spki)); - const std::string log1_spki_base64 = base::Base64Encode(log1_spki); - const std::string log1_id = - crypto::SHA256HashString(std::string(log1_spki.begin(), log1_spki.end())); - const std::string log1_id_base64 = base::Base64Encode(log1_id); + const base::Time kLogStart = base::Time::Now() - base::Days(1); + const base::Time kLogEnd = base::Time::Now() + base::Days(1); - const std::string kLog2OperatorName = "log operator 2"; - std::unique_ptr<crypto::ECPrivateKey> log2_private_key = - crypto::ECPrivateKey::Create(); - std::vector<uint8_t> log2_spki; - ASSERT_TRUE(log2_private_key->ExportPublicKey(&log2_spki)); - const std::string log2_spki_base64 = base::Base64Encode(log2_spki); - const std::string log2_id = - crypto::SHA256HashString(std::string(log2_spki.begin(), log2_spki.end())); - const std::string log2_id_base64 = base::Base64Encode(log2_id); - - const int64_t kLogStart = - SecondsSinceEpoch(base::Time::Now() - base::Days(1)); - const int64_t kLogEnd = SecondsSinceEpoch(base::Time::Now() + base::Days(1)); + CTLog log1("log operator 1", kLogStart, kLogEnd, + chrome_browser_certificate_transparency::CTLog::RFC6962); + CTLog log2( + "log operator 2", kLogStart, kLogEnd, + chrome_browser_certificate_transparency::CTLog::LOG_TYPE_UNSPECIFIED); // Make the test root be interpreted as a known root so that CT will be // required. @@ -273,10 +328,10 @@ // updates cause verifier caches and socket pool invalidation, so that the // next request for the same host will use the updated CT state. server_config.dns_names = {"example.com"}; - server_config.embedded_scts.emplace_back( - log1_id, bssl::UpRef(log1_private_key->key()), base::Time::Now()); - server_config.embedded_scts.emplace_back( - log2_id, bssl::UpRef(log2_private_key->key()), base::Time::Now()); + server_config.embedded_scts.emplace_back(log1.id(), log1.key(), + base::Time::Now()); + server_config.embedded_scts.emplace_back(log2.id(), log2.key(), + base::Time::Now()); https_server_ok.SetSSLConfig(server_config); https_server_ok.ServeFilesFromSourceDirectory("chrome/test/data"); @@ -302,43 +357,9 @@ CTEnforcement::kDisabledByProto); ct_config.mutable_log_list()->mutable_timestamp()->set_seconds( SecondsSinceEpoch(base::Time::Now())); - { - chrome_browser_certificate_transparency::CTLog* log = - ct_config.mutable_log_list()->add_logs(); - log->set_log_id(log1_id_base64); - log->set_key(log1_spki_base64); - log->set_purpose(chrome_browser_certificate_transparency::CTLog::PROD); - log->set_log_type(chrome_browser_certificate_transparency::CTLog::RFC6962); - log->mutable_temporal_interval()->mutable_start()->set_seconds(kLogStart); - log->mutable_temporal_interval()->mutable_end()->set_seconds(kLogEnd); - chrome_browser_certificate_transparency::CTLog_State* log_state = - log->add_state(); - log_state->set_current_state( - chrome_browser_certificate_transparency::CTLog::USABLE); - log_state->mutable_state_start()->set_seconds(kLogStart); - chrome_browser_certificate_transparency::CTLog_OperatorChange* - operator_history = log->add_operator_history(); - operator_history->set_name(kLog1OperatorName); - operator_history->mutable_operator_start()->set_seconds(kLogStart); - } - { - chrome_browser_certificate_transparency::CTLog* log = - ct_config.mutable_log_list()->add_logs(); - log->set_log_id(log2_id_base64); - log->set_key(log2_spki_base64); - log->set_purpose(chrome_browser_certificate_transparency::CTLog::PROD); - log->mutable_temporal_interval()->mutable_start()->set_seconds(kLogStart); - log->mutable_temporal_interval()->mutable_end()->set_seconds(kLogEnd); - chrome_browser_certificate_transparency::CTLog_State* log_state = - log->add_state(); - log_state->set_current_state( - chrome_browser_certificate_transparency::CTLog::USABLE); - log_state->mutable_state_start()->set_seconds(kLogStart); - chrome_browser_certificate_transparency::CTLog_OperatorChange* - operator_history = log->add_operator_history(); - operator_history->set_name(kLog2OperatorName); - operator_history->mutable_operator_start()->set_seconds(kLogStart); - } + AddLogToCTConfig(&ct_config, log1); + AddLogToCTConfig(&ct_config, log2); + { base::ScopedAllowBlockingForTesting allow_blocking; ASSERT_TRUE(PKIMetadataComponentInstallerService::GetInstance() @@ -367,14 +388,16 @@ log->add_state(); log_state->set_current_state( chrome_browser_certificate_transparency::CTLog::RETIRED); - log_state->mutable_state_start()->set_seconds(kLogStart + 1); + log_state->mutable_state_start()->set_seconds( + SecondsSinceEpoch(kLogStart) + 1); } { chrome_browser_certificate_transparency::CTLog_State* log_state = log->add_state(); log_state->set_current_state( chrome_browser_certificate_transparency::CTLog::USABLE); - log_state->mutable_state_start()->set_seconds(kLogStart); + log_state->mutable_state_start()->set_seconds( + SecondsSinceEpoch(kLogStart)); } } { @@ -406,29 +429,10 @@ void PKIMetadataComponentUpdaterTest::DoTestAtLeastOneRFC6962LogPolicy( chrome_browser_certificate_transparency::CTLog::LogType log_type, bool expect_ct_error_with_static_ct_api_enforcement) { - const std::string kLog1OperatorName = "log operator 1"; - std::unique_ptr<crypto::ECPrivateKey> log1_private_key = - crypto::ECPrivateKey::Create(); - std::vector<uint8_t> log1_spki; - ASSERT_TRUE(log1_private_key->ExportPublicKey(&log1_spki)); - const std::string log1_spki_base64 = base::Base64Encode(log1_spki); - const std::string log1_id = - crypto::SHA256HashString(std::string(log1_spki.begin(), log1_spki.end())); - const std::string log1_id_base64 = base::Base64Encode(log1_id); - - const std::string kLog2OperatorName = "log operator 2"; - std::unique_ptr<crypto::ECPrivateKey> log2_private_key = - crypto::ECPrivateKey::Create(); - std::vector<uint8_t> log2_spki; - ASSERT_TRUE(log2_private_key->ExportPublicKey(&log2_spki)); - const std::string log2_spki_base64 = base::Base64Encode(log2_spki); - const std::string log2_id = - crypto::SHA256HashString(std::string(log2_spki.begin(), log2_spki.end())); - const std::string log2_id_base64 = base::Base64Encode(log2_id); - - const int64_t kLogStart = - SecondsSinceEpoch(base::Time::Now() - base::Days(1)); - const int64_t kLogEnd = SecondsSinceEpoch(base::Time::Now() + base::Days(1)); + const base::Time kLogStart = base::Time::Now() - base::Days(1); + const base::Time kLogEnd = base::Time::Now() + base::Days(1); + CTLog log1("log operator 1", kLogStart, kLogEnd, log_type); + CTLog log2("log operator 2", kLogStart, kLogEnd, log_type); // Make the test root be interpreted as a known root so that CT will be // required. @@ -445,10 +449,10 @@ // updates cause verifier caches and socket pool invalidation, so that the // next request for the same host will use the updated CT state. server_config.dns_names = {"example.com"}; - server_config.embedded_scts.emplace_back( - log1_id, bssl::UpRef(log1_private_key->key()), base::Time::Now()); - server_config.embedded_scts.emplace_back( - log2_id, bssl::UpRef(log2_private_key->key()), base::Time::Now()); + server_config.embedded_scts.emplace_back(log1.id(), log1.key(), + base::Time::Now()); + server_config.embedded_scts.emplace_back(log2.id(), log2.key(), + base::Time::Now()); https_server_ok.SetSSLConfig(server_config); https_server_ok.ServeFilesFromSourceDirectory("chrome/test/data"); @@ -475,44 +479,9 @@ CTEnforcement::kDisabledByProto); ct_config.mutable_log_list()->mutable_timestamp()->set_seconds( SecondsSinceEpoch(base::Time::Now())); - { - chrome_browser_certificate_transparency::CTLog* log = - ct_config.mutable_log_list()->add_logs(); - log->set_log_id(log1_id_base64); - log->set_key(log1_spki_base64); - log->set_purpose(chrome_browser_certificate_transparency::CTLog::PROD); - log->set_log_type(log_type); - log->mutable_temporal_interval()->mutable_start()->set_seconds(kLogStart); - log->mutable_temporal_interval()->mutable_end()->set_seconds(kLogEnd); - chrome_browser_certificate_transparency::CTLog_State* log_state = - log->add_state(); - log_state->set_current_state( - chrome_browser_certificate_transparency::CTLog::USABLE); - log_state->mutable_state_start()->set_seconds(kLogStart); - chrome_browser_certificate_transparency::CTLog_OperatorChange* - operator_history = log->add_operator_history(); - operator_history->set_name(kLog1OperatorName); - operator_history->mutable_operator_start()->set_seconds(kLogStart); - } - { - chrome_browser_certificate_transparency::CTLog* log = - ct_config.mutable_log_list()->add_logs(); - log->set_log_id(log2_id_base64); - log->set_key(log2_spki_base64); - log->set_purpose(chrome_browser_certificate_transparency::CTLog::PROD); - log->set_log_type(log_type); - log->mutable_temporal_interval()->mutable_start()->set_seconds(kLogStart); - log->mutable_temporal_interval()->mutable_end()->set_seconds(kLogEnd); - chrome_browser_certificate_transparency::CTLog_State* log_state = - log->add_state(); - log_state->set_current_state( - chrome_browser_certificate_transparency::CTLog::USABLE); - log_state->mutable_state_start()->set_seconds(kLogStart); - chrome_browser_certificate_transparency::CTLog_OperatorChange* - operator_history = log->add_operator_history(); - operator_history->set_name(kLog2OperatorName); - operator_history->mutable_operator_start()->set_seconds(kLogStart); - } + AddLogToCTConfig(&ct_config, log1); + AddLogToCTConfig(&ct_config, log2); + { base::ScopedAllowBlockingForTesting allow_blocking; ASSERT_TRUE(PKIMetadataComponentInstallerService::GetInstance() @@ -1094,40 +1063,16 @@ IN_PROC_BROWSER_TEST_P(PKIMetadataComponentCtAndCrsUpdaterTest, TestChromeRootStoreConstraintsSct) { - const std::string kLog1OperatorName = "log operator 1"; - std::unique_ptr<crypto::ECPrivateKey> log1_private_key = - crypto::ECPrivateKey::Create(); - std::vector<uint8_t> log1_spki; - ASSERT_TRUE(log1_private_key->ExportPublicKey(&log1_spki)); - const std::string log1_spki_base64 = base::Base64Encode(log1_spki); - const std::string log1_id = - crypto::SHA256HashString(std::string(log1_spki.begin(), log1_spki.end())); - const std::string log1_id_base64 = base::Base64Encode(log1_id); - - const std::string kLog2OperatorName = "log operator 2"; - std::unique_ptr<crypto::ECPrivateKey> log2_private_key = - crypto::ECPrivateKey::Create(); - std::vector<uint8_t> log2_spki; - ASSERT_TRUE(log2_private_key->ExportPublicKey(&log2_spki)); - const std::string log2_spki_base64 = base::Base64Encode(log2_spki); - const std::string log2_id = - crypto::SHA256HashString(std::string(log2_spki.begin(), log2_spki.end())); - const std::string log2_id_base64 = base::Base64Encode(log2_id); - - const std::string kUnknownLogOperatorName = "unknown log operator"; - std::unique_ptr<crypto::ECPrivateKey> unknown_log_private_key = - crypto::ECPrivateKey::Create(); - std::vector<uint8_t> unknown_log_spki; - ASSERT_TRUE(unknown_log_private_key->ExportPublicKey(&unknown_log_spki)); - const std::string unknown_log_spki_base64 = - base::Base64Encode(unknown_log_spki); - const std::string unknown_log_id = crypto::SHA256HashString( - std::string(unknown_log_spki.begin(), unknown_log_spki.end())); - const std::string unknown_log_id_base64 = base::Base64Encode(unknown_log_id); - - const int64_t kLogStart = - SecondsSinceEpoch(base::Time::Now() - base::Days(1)); - const int64_t kLogEnd = SecondsSinceEpoch(base::Time::Now() + base::Days(1)); + const base::Time kLogStart = base::Time::Now() - base::Days(1); + const base::Time kLogEnd = base::Time::Now() + base::Days(1); + CTLog log1("log operator 1", kLogStart, kLogEnd, + chrome_browser_certificate_transparency::CTLog::RFC6962); + CTLog log2( + "log operator 2", kLogStart, kLogEnd, + chrome_browser_certificate_transparency::CTLog::LOG_TYPE_UNSPECIFIED); + CTLog unknown_log( + "unknown log operator", kLogStart, kLogEnd, + chrome_browser_certificate_transparency::CTLog::LOG_TYPE_UNSPECIFIED); const base::Time kSctTime0UnknownLog = base::Time::Now() - base::Minutes(30); const base::Time kSctTime1 = base::Time::Now() - base::Minutes(20); @@ -1138,13 +1083,10 @@ net::EmbeddedTestServer https_server_ok(net::EmbeddedTestServer::TYPE_HTTPS); net::EmbeddedTestServer::ServerCertificateConfig server_config; server_config.dns_names = {"*.example.com"}; - server_config.embedded_scts.emplace_back( - log1_id, bssl::UpRef(log1_private_key->key()), kSctTime1); - server_config.embedded_scts.emplace_back( - log2_id, bssl::UpRef(log2_private_key->key()), kSctTime2); - server_config.embedded_scts.emplace_back( - unknown_log_id, bssl::UpRef(unknown_log_private_key->key()), - kSctTime0UnknownLog); + server_config.embedded_scts.emplace_back(log1.id(), log1.key(), kSctTime1); + server_config.embedded_scts.emplace_back(log2.id(), log2.key(), kSctTime2); + server_config.embedded_scts.emplace_back(unknown_log.id(), unknown_log.key(), + kSctTime0UnknownLog); https_server_ok.SetSSLConfig(server_config); https_server_ok.ServeFilesFromSourceDirectory("chrome/test/data"); @@ -1180,43 +1122,9 @@ CTEnforcement::kDisabledByProto); ct_config.mutable_log_list()->mutable_timestamp()->set_seconds( SecondsSinceEpoch(base::Time::Now())); - { - chrome_browser_certificate_transparency::CTLog* log = - ct_config.mutable_log_list()->add_logs(); - log->set_log_id(log1_id_base64); - log->set_key(log1_spki_base64); - log->set_purpose(chrome_browser_certificate_transparency::CTLog::PROD); - log->set_log_type(chrome_browser_certificate_transparency::CTLog::RFC6962); - log->mutable_temporal_interval()->mutable_start()->set_seconds(kLogStart); - log->mutable_temporal_interval()->mutable_end()->set_seconds(kLogEnd); - chrome_browser_certificate_transparency::CTLog_State* log_state = - log->add_state(); - log_state->set_current_state( - chrome_browser_certificate_transparency::CTLog::USABLE); - log_state->mutable_state_start()->set_seconds(kLogStart); - chrome_browser_certificate_transparency::CTLog_OperatorChange* - operator_history = log->add_operator_history(); - operator_history->set_name(kLog1OperatorName); - operator_history->mutable_operator_start()->set_seconds(kLogStart); - } - { - chrome_browser_certificate_transparency::CTLog* log = - ct_config.mutable_log_list()->add_logs(); - log->set_log_id(log2_id_base64); - log->set_key(log2_spki_base64); - log->set_purpose(chrome_browser_certificate_transparency::CTLog::PROD); - log->mutable_temporal_interval()->mutable_start()->set_seconds(kLogStart); - log->mutable_temporal_interval()->mutable_end()->set_seconds(kLogEnd); - chrome_browser_certificate_transparency::CTLog_State* log_state = - log->add_state(); - log_state->set_current_state( - chrome_browser_certificate_transparency::CTLog::USABLE); - log_state->mutable_state_start()->set_seconds(kLogStart); - chrome_browser_certificate_transparency::CTLog_OperatorChange* - operator_history = log->add_operator_history(); - operator_history->set_name(kLog2OperatorName); - operator_history->mutable_operator_start()->set_seconds(kLogStart); - } + AddLogToCTConfig(&ct_config, log1); + AddLogToCTConfig(&ct_config, log2); + { base::ScopedAllowBlockingForTesting allow_blocking; ASSERT_TRUE(PKIMetadataComponentInstallerService::GetInstance()
diff --git a/chrome/browser/extensions/api/bookmark_manager_private/bookmark_manager_private_api.cc b/chrome/browser/extensions/api/bookmark_manager_private/bookmark_manager_private_api.cc index 3bef5db..0106c95 100644 --- a/chrome/browser/extensions/api/bookmark_manager_private/bookmark_manager_private_api.cc +++ b/chrome/browser/extensions/api/bookmark_manager_private/bookmark_manager_private_api.cc
@@ -657,7 +657,10 @@ ExtensionTabUtil::OpenTabParams options; options.url = node->url().spec(); - options.active = params->active; + if (params->params.has_value()) { + options.active = params->params.value().active; + options.split = params->params.value().split; + } options.bookmark_id = node->id(); auto result =
diff --git a/chrome/browser/extensions/api/bookmark_manager_private/bookmark_manager_private_api_unittest.cc b/chrome/browser/extensions/api/bookmark_manager_private/bookmark_manager_private_api_unittest.cc index cbceebd..7332397 100644 --- a/chrome/browser/extensions/api/bookmark_manager_private/bookmark_manager_private_api_unittest.cc +++ b/chrome/browser/extensions/api/bookmark_manager_private/bookmark_manager_private_api_unittest.cc
@@ -109,7 +109,7 @@ TEST_F(BookmarkManagerPrivateApiUnitTest, RunOpenInNewTabFunction) { auto new_tab_function = base::MakeRefCounted<BookmarkManagerPrivateOpenInNewTabFunction>(); - std::string args = base::StringPrintf(R"(["%s", false])", node_id().c_str()); + std::string args = base::StringPrintf(R"(["%s"])", node_id().c_str()); ASSERT_TRUE( api_test_utils::RunFunction(new_tab_function.get(), args, profile())); @@ -122,7 +122,7 @@ base::MakeRefCounted<BookmarkManagerPrivateOpenInNewTabFunction>(); std::string node_id = base::NumberToString(model()->bookmark_bar_node()->id()); - std::string args = base::StringPrintf(R"(["%s", false])", node_id.c_str()); + std::string args = base::StringPrintf(R"(["%s"])", node_id.c_str()); EXPECT_EQ("Cannot open a folder in a new tab.", api_test_utils::RunFunctionAndReturnError(new_tab_function.get(), args, profile()));
diff --git a/chrome/browser/extensions/api/i18n/i18n_apitest.cc b/chrome/browser/extensions/api/i18n/i18n_apitest.cc index 4ac46af7..0383dbe 100644 --- a/chrome/browser/extensions/api/i18n/i18n_apitest.cc +++ b/chrome/browser/extensions/api/i18n/i18n_apitest.cc
@@ -8,16 +8,13 @@ #include "base/threading/thread_restrictions.h" #include "chrome/browser/extensions/extension_apitest.h" #include "content/public/test/browser_test.h" +#include "extensions/buildflags/buildflags.h" #include "extensions/common/extension.h" #include "extensions/test/result_catcher.h" #include "extensions/test/test_extension_dir.h" #include "net/test/embedded_test_server/embedded_test_server.h" -#if !BUILDFLAG(IS_ANDROID) -#include "chrome/browser/ui/browser.h" -#include "chrome/test/base/ui_test_utils.h" -#endif // BUILDFLAG(IS_ANDROID) - +static_assert(BUILDFLAG(ENABLE_EXTENSIONS_CORE)); namespace extensions { using ExtensionI18nTest = ExtensionApiTest; @@ -27,9 +24,6 @@ ASSERT_TRUE(RunExtensionTest("i18n")) << message_; } -#if !BUILDFLAG(IS_ANDROID) -// TODO(crbug.com/391920604): Port to desktop Android once ReloadExtension() is -// supported by ExtensionApiTest. IN_PROC_BROWSER_TEST_F(ExtensionI18nTest, I18NUpdate) { ASSERT_TRUE(embedded_test_server()->Start()); // Create an Extension whose messages.json file will be updated. @@ -51,12 +45,12 @@ ResultCatcher catcher; // Test that the messages.json file is loaded and the i18n message is loaded. - ASSERT_TRUE(ui_test_utils::NavigateToURL( - browser(), embedded_test_server()->GetURL("/extensions/test_file.html"))); + ASSERT_TRUE(NavigateToURL( + embedded_test_server()->GetURL("/extensions/test_file.html"))); EXPECT_TRUE(catcher.GetNextResult()); std::u16string title; - ui_test_utils::GetCurrentTabTitle(browser(), &title); + GetCurrentTabTitle(&title); EXPECT_EQ(u"FIRSTMESSAGE", title); // Change messages.json file and reload extension. @@ -66,14 +60,13 @@ ReloadExtension(extension->id()); // Check that the i18n message is also changed. - ASSERT_TRUE(ui_test_utils::NavigateToURL( - browser(), embedded_test_server()->GetURL("/extensions/test_file.html"))); + ASSERT_TRUE(NavigateToURL( + embedded_test_server()->GetURL("/extensions/test_file.html"))); EXPECT_TRUE(catcher.GetNextResult()); - ui_test_utils::GetCurrentTabTitle(browser(), &title); + GetCurrentTabTitle(&title); EXPECT_EQ(u"SECONDMESSAGE", title); } -#endif // !BUILDFLAG(IS_ANDROID) // detectLanguage has some custom hooks that handle the asynchronous response // manually, so explicitly test that it stays working as expected with promises.
diff --git a/chrome/browser/extensions/api/printing/printing_apitest.cc b/chrome/browser/extensions/api/printing/printing_apitest.cc index cd1585f..95ebaaf 100644 --- a/chrome/browser/extensions/api/printing/printing_apitest.cc +++ b/chrome/browser/extensions/api/printing/printing_apitest.cc
@@ -2,6 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +#include <optional> #include <string> #include "chrome/browser/extensions/api/printing/print_job_submitter.h" @@ -11,6 +12,7 @@ #include "chrome/browser/ui/browser.h" #include "content/public/test/browser_test.h" #include "extensions/test/test_extension_dir.h" +#include "printing/printing_features.h" #include "testing/gtest/include/gtest/gtest.h" namespace extensions { @@ -22,25 +24,45 @@ } // namespace -class PrintingApiTestBase : public ExtensionApiTest, - public testing::WithParamInterface<ExtensionType> { +// TODO(crbug.com/308709702): Remove the bool param from this as soon as +// the `kPrintingMarginsAndScale` feature is enabled by default. At the moment, +// this is used to run the same test with and without the feature enabled. +class PrintingApiTestBase + : public ExtensionApiTest, + public testing::WithParamInterface<std::tuple<bool, ExtensionType>> { public: + void SetUp() override { + if (GetEnableMarginAndScale()) { + feature_list_.InitAndEnableFeature( + printing::features::kApiPrintingMarginsAndScale); + } else { + feature_list_.InitAndDisableFeature( + printing::features::kApiPrintingMarginsAndScale); + } + ExtensionApiTest::SetUp(); + } + void SetUpOnMainThread() override { ExtensionApiTest::SetUpOnMainThread(); PrintJobSubmitter::SkipConfirmationDialogForTesting(); } protected: - ExtensionType GetExtensionType() const { return GetParam(); } + bool GetEnableMarginAndScale() const { return std::get<0>(GetParam()); } + ExtensionType GetExtensionType() const { return std::get<1>(GetParam()); } - void RunTest(const char* html_test_page) { + void RunTest(const char* html_test_page, bool expect_success = true) { auto dir = CreatePrintingExtension(GetExtensionType()); auto run_options = GetExtensionType() == ExtensionType::kChromeApp ? RunOptions{.custom_arg = html_test_page, .launch_as_platform_app = true} : RunOptions({.extension_url = html_test_page}); - ASSERT_TRUE(RunExtensionTest(dir->UnpackedPath(), run_options, {})); + ASSERT_EQ(RunExtensionTest(dir->UnpackedPath(), run_options, {}), + expect_success); } + + private: + base::test::ScopedFeatureList feature_list_; }; class PrintingApiTest : public PrintingApiTestBase { @@ -85,6 +107,12 @@ AddPrinterWithSemanticCaps(kId, kName, ConstructPrinterCapabilities()); RunTest("get_printer_info.html"); + + // Expect failure/success depending on whether the feature is enabled or not. + // TODO(crbug.com/308709702): Remove this and merge two files once the feature + // is enabled by default. + const bool expect_success = GetEnableMarginAndScale(); + RunTest("get_printer_info_margin_and_scale.html", expect_success); } // Verifies that: @@ -102,6 +130,59 @@ RunTest("submit_job.html"); } +IN_PROC_BROWSER_TEST_P(PrintingApiTest, SubmitJobWithMarginsAndScale) { + ASSERT_TRUE(StartEmbeddedTestServer()); + + AddPrinterWithSemanticCaps(kId, kName, ConstructPrinterCapabilities()); + + RunTest("submit_job_margins_and_scale.html"); +} + +IN_PROC_BROWSER_TEST_P(PrintingApiTest, SubmitJobWithUnsupportedMargins) { + ASSERT_TRUE(StartEmbeddedTestServer()); + + // If the feature is disabled, the test must succeed regardless of the margins + // used. + // TODO(crbug.com/308709702): Remove this and expect the test to always fail + // once the feature is enabled by default as provided margins in this test + // are not supported by the setup printer. + const bool expect_success = !GetEnableMarginAndScale(); + + auto caps = ConstructPrinterCapabilities(); + std::vector<printing::PrinterSemanticCapsAndDefaults::Paper> papers; + // Override papers with custom margins. + for (const auto& paper : caps->papers) { + papers.emplace_back(paper.display_name(), paper.vendor_id(), + paper.size_um(), paper.printable_area_um(), + paper.max_height_um(), paper.has_borderless_variant(), + printing::PaperMargins(2340, 1234, 1234, 1234)); + } + caps->papers = std::move(papers); + AddPrinterWithSemanticCaps(kId, kName, std::move(caps)); + + RunTest("submit_job_margins_and_scale.html", expect_success); +} + +IN_PROC_BROWSER_TEST_P(PrintingApiTest, SubmitJobWithUnsupportedScale) { + ASSERT_TRUE(StartEmbeddedTestServer()); + + // If the feature is disabled, the test must succeed regardless of the scale + // used. + // TODO(crbug.com/308709702): Remove this and expect the test to always fail + // once the feature is enabled by default as provided scale in this test + // is not supported by the setup printer. + const bool expect_success = !GetEnableMarginAndScale(); + + auto caps = ConstructPrinterCapabilities(); + // Override with custom scaling type different from defined in the js/html + // file of the test. + caps->print_scaling_types = {printing::mojom::PrintScalingType::kFill}; + caps->print_scaling_type_default = printing::mojom::PrintScalingType::kFill; + AddPrinterWithSemanticCaps(kId, kName, std::move(caps)); + + RunTest("submit_job_margins_and_scale.html", expect_success); +} + // As above, but tests using promise based API calls. IN_PROC_BROWSER_TEST_P(PrintingPromiseApiTest, SubmitJob) { ASSERT_TRUE(StartEmbeddedTestServer()); @@ -111,6 +192,14 @@ RunTest("submit_job_promise.html"); } +IN_PROC_BROWSER_TEST_P(PrintingPromiseApiTest, SubmitJobWithMarginsAndScale) { + ASSERT_TRUE(StartEmbeddedTestServer()); + + AddPrinterWithSemanticCaps(kId, kName, ConstructPrinterCapabilities()); + + RunTest("submit_job_promise_margins_and_scale.html"); +} + // Verifies that: // a) Cancel job request works smoothly. // b) OnJobStatusChanged() events are dispatched correctly. @@ -130,16 +219,20 @@ RunTest("get_print_job_status.html"); } -INSTANTIATE_TEST_SUITE_P(/**/, - PrintingApiTest, - testing::Values(ExtensionType::kChromeApp, - ExtensionType::kExtensionMV2, - ExtensionType::kExtensionMV3)); +INSTANTIATE_TEST_SUITE_P( + /**/, + PrintingApiTest, + testing::Combine(testing::Bool(), + testing::Values(ExtensionType::kChromeApp, + ExtensionType::kExtensionMV2, + ExtensionType::kExtensionMV3))); // We only run the promise based tests for MV3 extensions as promise based API // calls are only exposed to MV3. -INSTANTIATE_TEST_SUITE_P(/**/, - PrintingPromiseApiTest, - testing::Values(ExtensionType::kExtensionMV3)); +INSTANTIATE_TEST_SUITE_P( + /**/, + PrintingPromiseApiTest, + testing::Combine(testing::Bool(), + testing::Values(ExtensionType::kExtensionMV3))); } // namespace extensions
diff --git a/chrome/browser/extensions/api/printing/printing_test_utils.cc b/chrome/browser/extensions/api/printing/printing_test_utils.cc index 94d3088..e086bb49 100644 --- a/chrome/browser/extensions/api/printing/printing_test_utils.cc +++ b/chrome/browser/extensions/api/printing/printing_test_utils.cc
@@ -243,7 +243,7 @@ {kCustomPaperWidth, kCustomPaperHeight}, /*printable_area_um=*/{kCustomPaperWidth, kCustomPaperHeight}, /*max_height_um=*/kCustomPaperMaxHeight, - /*has_borderless_variant=*/false, + /*has_borderless_variant=*/true, /*supported_margins_um=*/kDefaultPaperMargins); capabilities->default_paper = iso_a4_paper; capabilities->papers = {std::move(iso_a4_paper), std::move(na_letter_paper), @@ -255,6 +255,12 @@ /*name=*/printing::kIppMediaSource, /*localized_name=*/"", printing::AdvancedCapability::Type::kString, /*default_value=*/"auto", /*values=*/std::move(media_source_vals)); + capabilities->print_scaling_types = { + printing::mojom::PrintScalingType::kFit, + printing::mojom::PrintScalingType::kAuto, + }; + capabilities->print_scaling_type_default = + printing::mojom::PrintScalingType::kAuto; return capabilities; }
diff --git a/chrome/browser/extensions/extension_browsertest.cc b/chrome/browser/extensions/extension_browsertest.cc index 8416aec9..37936aec 100644 --- a/chrome/browser/extensions/extension_browsertest.cc +++ b/chrome/browser/extensions/extension_browsertest.cc
@@ -847,6 +847,20 @@ return observer.last_navigation_succeeded(); } +bool ExtensionBrowserTest::GetCurrentTabTitle(std::u16string* title) { + content::WebContents* web_contents = GetActiveWebContents(); + if (!web_contents) { + return false; + } + content::NavigationEntry* last_entry = + web_contents->GetController().GetActiveEntry(); + if (!last_entry) { + return false; + } + title->assign(last_entry->GetTitleForDisplay()); + return true; +} + content::WebContents* ExtensionBrowserTest::PlatformOpenURLOffTheRecord( Profile* profile, const GURL& url) {
diff --git a/chrome/browser/extensions/extension_browsertest.h b/chrome/browser/extensions/extension_browsertest.h index d79dfc70..8695d51 100644 --- a/chrome/browser/extensions/extension_browsertest.h +++ b/chrome/browser/extensions/extension_browsertest.h
@@ -237,6 +237,9 @@ // navigation finishes. Returns true on success. [[nodiscard]] bool NavigateToURL(const GURL& url); + // Puts the current tab title in |title|. Returns true on success. + bool GetCurrentTabTitle(std::u16string* title); + // Opens `url` in an incognito browser window with the incognito profile of // `profile`, blocking until the navigation finishes. Returns the WebContents // for `url`.
diff --git a/chrome/browser/extensions/extension_tab_util.cc b/chrome/browser/extensions/extension_tab_util.cc index 8ec335e9..c2ac508 100644 --- a/chrome/browser/extensions/extension_tab_util.cc +++ b/chrome/browser/extensions/extension_tab_util.cc
@@ -297,6 +297,10 @@ // will override this default. bool active = params.active.value_or(true); + // Default to unsplit for the new tab. The presence of the 'split' property + // will override this default. + bool split = params.split.value_or(false); + // Default to not pinning the tab. Setting the 'pinned' property to true // will override this default. bool pinned = params.pinned.value_or(false); @@ -375,6 +379,11 @@ if (active) navigate_params.navigated_or_inserted_contents->SetInitialFocus(); + if (split) { + tab_strip->AddToNewSplit({new_index}, + split_tabs::SplitTabLayout::kVertical); + } + ExtensionTabUtil::ScrubTabBehavior scrub_tab_behavior = ExtensionTabUtil::GetScrubTabBehavior( function->extension(), function->source_context_type(),
diff --git a/chrome/browser/extensions/extension_tab_util.h b/chrome/browser/extensions/extension_tab_util.h index 8785b77..c79667d 100644 --- a/chrome/browser/extensions/extension_tab_util.h +++ b/chrome/browser/extensions/extension_tab_util.h
@@ -106,6 +106,7 @@ std::optional<int> opener_tab_id; std::optional<std::string> url; std::optional<bool> active; + std::optional<bool> split; std::optional<bool> pinned; std::optional<int> index; std::optional<int> bookmark_id;
diff --git a/chrome/browser/flag-metadata.json b/chrome/browser/flag-metadata.json index cf221ff7..4786e1f4 100644 --- a/chrome/browser/flag-metadata.json +++ b/chrome/browser/flag-metadata.json
@@ -2281,6 +2281,14 @@ "expiry_milestone": 145 }, { + "name": "discount-autofill", + "owners": [ + "jennycho@google.com", + "promotions-eng@google.com" + ], + "expiry_milestone": 150 + }, + { "name": "discount-on-navigation", "owners": [ "meiliang@chromium.org", @@ -9367,6 +9375,15 @@ "expiry_milestone": 142 }, { + "name": "trees-in-viz", + "owners": [ + "zmo@chromium.org", + "vmiura@chromium.org", + "chrome-gpu-team@google.com" + ], + "expiry_milestone": 144 + }, + { "name": "ui-debug-tools", "owners": [ "//ui/views/OWNERS" ], "expiry_milestone": -1
diff --git a/chrome/browser/flag_descriptions.cc b/chrome/browser/flag_descriptions.cc index 3dde30f..7c7d0fa2 100644 --- a/chrome/browser/flag_descriptions.cc +++ b/chrome/browser/flag_descriptions.cc
@@ -3306,6 +3306,12 @@ "Allows prerendering pages to execute more lifecycle updates, such as " "prepaint, before activation"; +const char kTreesInVizName[] = "Trees in viz"; +const char kTreesInVizDescription[] = + "Enables the renderer to send a CC LayerTree to the viz/gpu process " + "instead of a CompositorFrame. This allows viz to generate and submit " + "the CompositorFrame directly."; + const char kPrerender2ForNewTabPageAndroidName[] = "Enable prerendering on New Tab Page Android"; const char kPrerender2ForNewTabPageAndroidDescription[] =
diff --git a/chrome/browser/flag_descriptions.h b/chrome/browser/flag_descriptions.h index 1aff2eb8..90d11f0 100644 --- a/chrome/browser/flag_descriptions.h +++ b/chrome/browser/flag_descriptions.h
@@ -1910,6 +1910,9 @@ extern const char kPrerender2EarlyDocumentLifecycleUpdateName[]; extern const char kPrerender2EarlyDocumentLifecycleUpdateDescription[]; +extern const char kTreesInVizName[]; +extern const char kTreesInVizDescription[]; + extern const char kPrerender2ForNewTabPageAndroidName[]; extern const char kPrerender2ForNewTabPageAndroidDescription[];
diff --git a/chrome/browser/glic/fre/glic_fre_controller.cc b/chrome/browser/glic/fre/glic_fre_controller.cc index 74a8600..1f4fcc8 100644 --- a/chrome/browser/glic/fre/glic_fre_controller.cc +++ b/chrome/browser/glic/fre/glic_fre_controller.cc
@@ -116,9 +116,8 @@ prefs::kGlicCompletedFre, static_cast<int>(prefs::FreStatus::kIncomplete)); - if (auth_controller_.CheckAuthBeforeShowSync( - base::BindOnce(&GlicFreController::ShowFreDialogAfterAuthCheck, - GetWeakPtr(), browser->AsWeakPtr()))) { + if (auth_controller_.CheckAuthBeforeShowSync(base::BindOnce( + &GlicFreController::OpenFreDialogInNewTab, GetWeakPtr(), browser))) { ShowFreDialogAfterAuthCheck(browser->AsWeakPtr()); } else { // Sign-in required and handled by AuthController. In this case, do not
diff --git a/chrome/browser/glic/host/glic.mojom b/chrome/browser/glic/host/glic.mojom index daf15cd..e92cb4c 100644 --- a/chrome/browser/glic/host/glic.mojom +++ b/chrome/browser/glic/host/glic.mojom
@@ -282,7 +282,7 @@ // Note: This must be kept in sync with the corresponding enum (same name) in // glic_api.ts -// Next version: 6 +// Next version: 7 [Stable, Extensible] enum ScrollToErrorReason { // Default error reason. Used when the browser doesn't support ScrollTo, the @@ -311,6 +311,9 @@ // The search range starting from DOMNodeId did not result in a valid range. [MinVersion=5] kSearchRangeInvalid, + + // Page context access is disabled. + [MinVersion=6] kTabContextPermissionDisabled, }; // A single suggestion.
diff --git a/chrome/browser/glic/host/glic_annotation_manager.cc b/chrome/browser/glic/host/glic_annotation_manager.cc index 690066f..516c1a2 100644 --- a/chrome/browser/glic/host/glic_annotation_manager.cc +++ b/chrome/browser/glic/host/glic_annotation_manager.cc
@@ -10,9 +10,11 @@ #include "base/state_transitions.h" #include "base/strings/escape.h" #include "chrome/browser/glic/glic_keyed_service.h" +#include "chrome/browser/glic/glic_pref_names.h" #include "chrome/browser/glic/host/glic.mojom.h" #include "chrome/common/chrome_features.h" #include "components/optimization_guide/content/browser/page_content_proto_provider.h" +#include "components/prefs/pref_service.h" #include "components/shared_highlighting/core/common/text_fragment.h" #include "content/public/browser/render_frame_host.h" #include "content/public/browser/web_contents.h" @@ -102,6 +104,13 @@ // selector will set `node_id`. CHECK(text_fragment.has_value() || node_id.has_value()); + if (!service_->profile()->GetPrefs()->GetBoolean( + prefs::kGlicTabContextEnabled)) { + std::move(callback).Run( + mojom::ScrollToErrorReason::kTabContextPermissionDisabled); + return; + } + auto focused_tab_data = service_->GetFocusedTabData(); content::Page* focused_primary_page = nullptr; if (focused_tab_data.focus()) { @@ -196,18 +205,26 @@ std::move(agent_host_pending_receiver)), scroll_to_callback_(std::move(callback)), page_(page.GetWeakPtr()) { + GlicKeyedService* service = annotation_manager_->service_; + CHECK(service); // Using base::Unretained is safe here because `this` owns the subscription. tab_change_subscription_ = - annotation_manager_->service_->AddFocusedTabChangedCallback( - base::BindRepeating(&AnnotationTask::OnFocusedTabChanged, - base::Unretained(this))); + service->AddFocusedTabChangedCallback(base::BindRepeating( + &AnnotationTask::OnFocusedTabChanged, base::Unretained(this))); // Using base::Unretained is safe because `this` owns the receiver. annotation_agent_host_receiver_.set_disconnect_handler(base::BindOnce( &AnnotationTask::RemoteDisconnected, base::Unretained(this))); // Listens to the panel-closing notification. - annotation_manager_->service_->window_controller().AddStateObserver(this); + service->window_controller().AddStateObserver(this); + + pref_change_registrar_.Init(service->profile()->GetPrefs()); + // base::Unretained is safe because `this` owns `pref_change_registrar_`. + pref_change_registrar_.Add( + prefs::kGlicTabContextEnabled, + base::BindRepeating(&AnnotationTask::OnTabContextPermissionChanged, + base::Unretained(this))); } GlicAnnotationManager::AnnotationTask::~AnnotationTask() { @@ -280,6 +297,25 @@ tab_change_subscription_ = base::CallbackListSubscription(); content::WebContentsObserver::Observe(nullptr); annotation_manager_->service_->window_controller().RemoveStateObserver(this); + pref_change_registrar_.Reset(); +} + +void GlicAnnotationManager::AnnotationTask::FailTaskOrDropAnnotation( + mojom::ScrollToErrorReason reason) { + switch (state_) { + case State::kRunning: { + FailTask(reason); + break; + } + case State::kActive: { + DropAnnotation(); + break; + } + case State::kFailed: + case State::kInactive: { + NOTREACHED(); + } + } } void GlicAnnotationManager::AnnotationTask::DidFinishAttachment( @@ -336,19 +372,17 @@ if (panel_state.kind != mojom::PanelState_Kind::kHidden) { return; } - switch (state_) { - case State::kRunning: { - FailTask(mojom::ScrollToErrorReason::kFocusedTabChangedOrNavigated); - break; - } - case State::kActive: { - DropAnnotation(); - break; - } - case State::kFailed: - case State::kInactive: { - break; - } + FailTaskOrDropAnnotation( + mojom::ScrollToErrorReason::kFocusedTabChangedOrNavigated); +} + +void GlicAnnotationManager::AnnotationTask::OnTabContextPermissionChanged( + const std::string& pref_name) { + CHECK_EQ(pref_name, prefs::kGlicTabContextEnabled); + if (!annotation_manager_->service_->profile()->GetPrefs()->GetBoolean( + prefs::kGlicTabContextEnabled)) { + FailTaskOrDropAnnotation( + mojom::ScrollToErrorReason::kTabContextPermissionDisabled); } }
diff --git a/chrome/browser/glic/host/glic_annotation_manager.h b/chrome/browser/glic/host/glic_annotation_manager.h index 159accc..9f55f24 100644 --- a/chrome/browser/glic/host/glic_annotation_manager.h +++ b/chrome/browser/glic/host/glic_annotation_manager.h
@@ -100,6 +100,11 @@ void DropAnnotation(); void ResetConnections(); + // Fails the task with `reason` if it's still running, otherwise drops the + // active annotation. Should only be called if the task is running or a + // highlight is active, and will fail a CHECK otherwise. + void FailTaskOrDropAnnotation(mojom::ScrollToErrorReason reason); + // blink::mojom::AnnotationAgentHost overrides. void DidFinishAttachment( const gfx::Rect& document_relative_rect, @@ -115,6 +120,9 @@ // GlicFocusedTabManager::FocusedTabChangedCallback void OnFocusedTabChanged(FocusedTabData focused_tab_data); + // `pref_change_registrar_` callback. + void OnTabContextPermissionChanged(const std::string& pref_name); + // Uniquely owns `this`. base::raw_ref<GlicAnnotationManager> annotation_manager_; @@ -134,6 +142,9 @@ // while the task is running. Cleared after the task completes/fails. base::CallbackListSubscription tab_change_subscription_; + // Used to subscribe to tab context permission changes. + PrefChangeRegistrar pref_change_registrar_; + // Current state of the task, see documentation for `State`. State state_ = State::kRunning; };
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 8a02dfe..c06d0acc 100644 --- a/chrome/browser/glic/host/glic_annotation_manager_interactive_uitest.cc +++ b/chrome/browser/glic/host/glic_annotation_manager_interactive_uitest.cc
@@ -32,8 +32,9 @@ namespace glic::test { DEFINE_LOCAL_ELEMENT_IDENTIFIER_VALUE(kActiveTabId); -DEFINE_LOCAL_CUSTOM_ELEMENT_EVENT_TYPE(kScrollToRequestReceived); +DEFINE_LOCAL_CUSTOM_ELEMENT_EVENT_TYPE(kAnnotationAgentDisconnectedByRemote); DEFINE_LOCAL_CUSTOM_ELEMENT_EVENT_TYPE(kScrollStarted); +DEFINE_LOCAL_CUSTOM_ELEMENT_EVENT_TYPE(kScrollToRequestReceived); constexpr char kActivateSurfaceIncompatibilityNotice[] = "Programmatic window activation does not work on the Weston reference " @@ -74,8 +75,16 @@ host_remote_.Bind(std::move(pending_host_remote)); agent_receiver_.Bind(std::move(agent_receiver)); - agent_receiver_.set_disconnect_handler( - base::BindLambdaForTesting([&]() { agent_disconnected_ = true; })); + agent_receiver_.set_disconnect_handler(base::BindLambdaForTesting([&]() { + agent_disconnected_ = true; + ui::TrackedElement* el = + ui::ElementTracker::GetElementTracker()->GetElementInAnyContext( + kBrowserViewElementId); + if (el) { + ui::ElementTracker::GetFrameworkDelegate()->NotifyCustomEvent( + el, kAnnotationAgentDisconnectedByRemote); + } + })); auto* const el = ui::ElementTracker::GetElementTracker()->GetElementInAnyContext( @@ -295,6 +304,13 @@ })); } + auto SetTabContextPermission(bool enable) { + return Steps(Do([this, enable]() { + browser()->profile()->GetPrefs()->SetBoolean( + glic::prefs::kGlicTabContextEnabled, enable); + })); + } + // Checks if the currently focused tab (according to GlicFocusedTabManager) is // `web_contents_id`, or waits until it is. Set `web_contents_id` to // std::nullopt to wait until no tab is in focus. @@ -408,23 +424,25 @@ }; IN_PROC_BROWSER_TEST_F(GlicAnnotationManagerUiTest, ScrollToExactText) { - RunTestSequence(InstrumentTab(kActiveTabId), - NavigateWebContents( - kActiveTabId, embedded_test_server()->GetURL( - "/scrollable_page_with_content.html")), - OpenGlicWindow(GlicWindowMode::kDetached), - ScrollTo(ExactTextSelector("Some text")), - WaitForJsResult(kActiveTabId, "() => did_scroll")); + RunTestSequence( + InstrumentTab(kActiveTabId), + NavigateWebContents( + kActiveTabId, + embedded_test_server()->GetURL("/scrollable_page_with_content.html")), + OpenGlicWindow(GlicWindowMode::kDetached), SetTabContextPermission(true), + ScrollTo(ExactTextSelector("Some text")), + WaitForJsResult(kActiveTabId, "() => did_scroll")); } IN_PROC_BROWSER_TEST_F(GlicAnnotationManagerUiTest, ScrollToTextFragment) { - RunTestSequence(InstrumentTab(kActiveTabId), - NavigateWebContents( - kActiveTabId, embedded_test_server()->GetURL( - "/scrollable_page_with_content.html")), - OpenGlicWindow(GlicWindowMode::kDetached), - ScrollTo(TextFragmentSelector("Some", "text")), - WaitForJsResult(kActiveTabId, "() => did_scroll")); + RunTestSequence( + InstrumentTab(kActiveTabId), + NavigateWebContents( + kActiveTabId, + embedded_test_server()->GetURL("/scrollable_page_with_content.html")), + OpenGlicWindow(GlicWindowMode::kDetached), SetTabContextPermission(true), + ScrollTo(TextFragmentSelector("Some", "text")), + WaitForJsResult(kActiveTabId, "() => did_scroll")); } IN_PROC_BROWSER_TEST_F(GlicAnnotationManagerUiTest, NoMatchFound) { @@ -433,7 +451,7 @@ NavigateWebContents( kActiveTabId, embedded_test_server()->GetURL("/scrollable_page_with_content.html")), - OpenGlicWindow(GlicWindowMode::kDetached), + OpenGlicWindow(GlicWindowMode::kDetached), SetTabContextPermission(true), ScrollToExpectingError(ExactTextSelector("Text does not exist"), mojom::ScrollToErrorReason::kNoMatchFound)); } @@ -447,6 +465,7 @@ kActiveTabId, embedded_test_server()->GetURL("/scrollable_page_with_content.html")), OpenGlicWindow(GlicWindowMode::kDetached), // + SetTabContextPermission(true), // InsertFakeAnnotationService(), ScrollToAsync(ExactTextSelector("does not matter")), WaitForEvent(kBrowserViewElementId, kScrollToRequestReceived), @@ -466,7 +485,8 @@ kActiveTabId, embedded_test_server()->GetURL("/scrollable_page_with_content.html")), OpenGlicWindow(GlicWindowMode::kDetached), // - InsertFakeAnnotationService(), // + SetTabContextPermission(true), + InsertFakeAnnotationService(), // ScrollToAsync(ExactTextSelector("does not matter")), WaitForEvent(kBrowserViewElementId, kScrollToRequestReceived), PressButton(kNewTabButtonElementId), @@ -481,8 +501,9 @@ InstrumentTab(kActiveTabId), NavigateWebContents(kActiveTabId, GURL("chrome://settings")), OpenGlicWindow(GlicWindowMode::kDetached), // - WaitUntilGlicFocusedTabIs(std::nullopt), // - InsertFakeAnnotationService(), // + SetTabContextPermission(true), + WaitUntilGlicFocusedTabIs(std::nullopt), // + InsertFakeAnnotationService(), // ScrollToExpectingError(ExactTextSelector("does not matter"), mojom::ScrollToErrorReason::kNoFocusedTab)); } @@ -496,6 +517,7 @@ kActiveTabId, embedded_test_server()->GetURL("/scrollable_page_with_content.html")), OpenGlicWindow(GlicWindowMode::kDetached), // + SetTabContextPermission(true), // InsertFakeAnnotationService(), ScrollToAsync(ExactTextSelector("Some text")), WaitForEvent(kBrowserViewElementId, kScrollToRequestReceived), @@ -523,7 +545,8 @@ kActiveTabId, embedded_test_server()->GetURL("/scrollable_page_with_content.html")), OpenGlicWindow(GlicWindowMode::kDetached), // - InsertFakeAnnotationService(), // + SetTabContextPermission(true), + InsertFakeAnnotationService(), // ScrollToAsync(ExactTextSelector("does not matter")), WaitForEvent(kBrowserViewElementId, kScrollToRequestReceived), Do([&]() { fake_service()->NotifyAttachment( @@ -546,8 +569,9 @@ kActiveTabId, embedded_test_server()->GetURL("/scrollable_page_with_content.html")), OpenGlicWindow(GlicWindowMode::kDetached), // - FocusWebContents(kGlicContentsElementId), // - InsertFakeAnnotationService(), // + SetTabContextPermission(true), + FocusWebContents(kGlicContentsElementId), // + InsertFakeAnnotationService(), // ScrollToAsync(ExactTextSelector("does not matter")), WaitForEvent(kBrowserViewElementId, kScrollToRequestReceived), Do([&]() { fake_service()->NotifyAttachment( @@ -568,7 +592,8 @@ NavigateWebContents(kActiveTabId, embedded_test_server()->GetURL("/title1.html")), OpenGlicWindow(GlicWindowMode::kDetached), // - InsertFakeAnnotationService(), // + SetTabContextPermission(true), + InsertFakeAnnotationService(), // ScrollToAsync(ExactTextSelector("does not matter")), WaitForEvent(kBrowserViewElementId, kScrollToRequestReceived), // Do([&]() { @@ -593,7 +618,8 @@ NavigateWebContents(kActiveTabId, embedded_test_server()->GetURL("/title1.html")), OpenGlicWindow(GlicWindowMode::kDetached), // - InsertFakeAnnotationService(), // + SetTabContextPermission(true), + InsertFakeAnnotationService(), // ScrollToAsync(ExactTextSelector("does not matter")), WaitForEvent(kBrowserViewElementId, kScrollToRequestReceived), // Do([&]() { @@ -611,16 +637,17 @@ IN_PROC_BROWSER_TEST_F(GlicAnnotationManagerUiTest, TwoSuccessfulScrollToCalls) { - RunTestSequence(InstrumentTab(kActiveTabId), // - NavigateWebContents( - kActiveTabId, embedded_test_server()->GetURL( - "/scrollable_page_with_content.html")), - OpenGlicWindow(GlicWindowMode::kDetached), - ScrollTo(ExactTextSelector("Some text")), - WaitForJsResult(kActiveTabId, "() => did_scroll"), - ExecuteJs(kActiveTabId, "() => { did_scroll = false; }"), - ScrollTo(ExactTextSelector("Go Down")), - WaitForJsResult(kActiveTabId, "() => did_scroll")); + RunTestSequence( + InstrumentTab(kActiveTabId), // + NavigateWebContents( + kActiveTabId, + embedded_test_server()->GetURL("/scrollable_page_with_content.html")), + OpenGlicWindow(GlicWindowMode::kDetached), SetTabContextPermission(true), + ScrollTo(ExactTextSelector("Some text")), + WaitForJsResult(kActiveTabId, "() => did_scroll"), + ExecuteJs(kActiveTabId, "() => { did_scroll = false; }"), + ScrollTo(ExactTextSelector("Go Down")), + WaitForJsResult(kActiveTabId, "() => did_scroll")); } IN_PROC_BROWSER_TEST_F(GlicAnnotationManagerUiTest, @@ -630,7 +657,7 @@ NavigateWebContents( kActiveTabId, embedded_test_server()->GetURL("/scrollable_page_with_content.html")), - OpenGlicWindow(GlicWindowMode::kDetached), + OpenGlicWindow(GlicWindowMode::kDetached), SetTabContextPermission(true), InsertFakeAnnotationService(), // ScrollToAsync(ExactTextSelector("does not matter")), WaitForEvent(kBrowserViewElementId, kScrollToRequestReceived), // @@ -647,27 +674,29 @@ } IN_PROC_BROWSER_TEST_F(GlicAnnotationManagerUiTest, WithDocumentId) { - RunTestSequence(InstrumentTab(kActiveTabId), - NavigateWebContents( - kActiveTabId, embedded_test_server()->GetURL( - "/scrollable_page_with_content.html")), - OpenGlicWindow(GlicWindowMode::kDetached), - GetPageContextFromFocusedTab(), // - ScrollToWithDocumentId(ExactTextSelector("Some text")), - WaitForJsResult(kActiveTabId, "() => did_scroll")); + RunTestSequence( + InstrumentTab(kActiveTabId), + NavigateWebContents( + kActiveTabId, + embedded_test_server()->GetURL("/scrollable_page_with_content.html")), + OpenGlicWindow(GlicWindowMode::kDetached), SetTabContextPermission(true), + GetPageContextFromFocusedTab(), // + ScrollToWithDocumentId(ExactTextSelector("Some text")), + WaitForJsResult(kActiveTabId, "() => did_scroll")); } IN_PROC_BROWSER_TEST_F(GlicAnnotationManagerUiTest, WithUnknownDocumentId) { - RunTestSequence(InstrumentTab(kActiveTabId), - NavigateWebContents( - kActiveTabId, embedded_test_server()->GetURL( - "/scrollable_page_with_content.html")), - OpenGlicWindow(GlicWindowMode::kDetached), - GetPageContextFromFocusedTab(), // - ScrollToWithDocumentIdExpectingError( - ExactTextSelector("Some text"), - mojom::ScrollToErrorReason::kNoMatchingDocument, - base::UnguessableToken().Create().ToString())); + RunTestSequence( + InstrumentTab(kActiveTabId), + NavigateWebContents( + kActiveTabId, + embedded_test_server()->GetURL("/scrollable_page_with_content.html")), + OpenGlicWindow(GlicWindowMode::kDetached), SetTabContextPermission(true), + GetPageContextFromFocusedTab(), // + ScrollToWithDocumentIdExpectingError( + ExactTextSelector("Some text"), + mojom::ScrollToErrorReason::kNoMatchingDocument, + base::UnguessableToken().Create().ToString())); } IN_PROC_BROWSER_TEST_F(GlicAnnotationManagerUiTest, @@ -677,7 +706,7 @@ NavigateWebContents( kActiveTabId, embedded_test_server()->GetURL("/scrollable_page_with_content.html")), - OpenGlicWindow(GlicWindowMode::kDetached), + OpenGlicWindow(GlicWindowMode::kDetached), SetTabContextPermission(true), GetPageContextFromFocusedTab(), // NavigateWebContents(kActiveTabId, embedded_test_server()->GetURL("/title1.html")), @@ -692,7 +721,7 @@ NavigateWebContents( kActiveTabId, embedded_test_server()->GetURL("/scrollable_page_with_content.html")), - OpenGlicWindow(GlicWindowMode::kDetached), + OpenGlicWindow(GlicWindowMode::kDetached), SetTabContextPermission(true), ExecuteJs(kActiveTabId, "() => { document.getElementById('text').tabIndex = 0; }"), ScrollTo(ExactTextSelector("Some text")), @@ -708,15 +737,16 @@ NodeIdCallback range_start_id_cb = base::BindOnce( &GlicAnnotationManagerUiTest::GetRootDomNodeIdFromAnnotatedPageContent, base::Unretained(this)); - RunTestSequence(InstrumentTab(kActiveTabId), - NavigateWebContents( - kActiveTabId, embedded_test_server()->GetURL( - "/scrollable_page_with_content.html")), - OpenGlicWindow(GlicWindowMode::kDetached), - GetPageContextFromFocusedTab(), // - ScrollToWithDocumentId(ExactTextSelector( - "Some text", std::move(range_start_id_cb))), - WaitForJsResult(kActiveTabId, "() => did_scroll")); + RunTestSequence( + InstrumentTab(kActiveTabId), + NavigateWebContents( + kActiveTabId, + embedded_test_server()->GetURL("/scrollable_page_with_content.html")), + OpenGlicWindow(GlicWindowMode::kDetached), SetTabContextPermission(true), + GetPageContextFromFocusedTab(), // + ScrollToWithDocumentId( + ExactTextSelector("Some text", std::move(range_start_id_cb))), + WaitForJsResult(kActiveTabId, "() => did_scroll")); } // Search the text fragment from the range with the start node id which is @@ -726,15 +756,16 @@ NodeIdCallback range_start_id_cb = base::BindOnce( &GlicAnnotationManagerUiTest::GetRootDomNodeIdFromAnnotatedPageContent, base::Unretained(this)); - RunTestSequence(InstrumentTab(kActiveTabId), - NavigateWebContents( - kActiveTabId, embedded_test_server()->GetURL( - "/scrollable_page_with_content.html")), - OpenGlicWindow(GlicWindowMode::kDetached), - GetPageContextFromFocusedTab(), // - ScrollToWithDocumentId(TextFragmentSelector( - "Some", "text", std::move(range_start_id_cb))), - WaitForJsResult(kActiveTabId, "() => did_scroll")); + RunTestSequence( + InstrumentTab(kActiveTabId), + NavigateWebContents( + kActiveTabId, + embedded_test_server()->GetURL("/scrollable_page_with_content.html")), + OpenGlicWindow(GlicWindowMode::kDetached), SetTabContextPermission(true), + GetPageContextFromFocusedTab(), // + ScrollToWithDocumentId( + TextFragmentSelector("Some", "text", std::move(range_start_id_cb))), + WaitForJsResult(kActiveTabId, "() => did_scroll")); } // If the start node id is not from `annotated_page_content_`, throw an invalid @@ -744,15 +775,16 @@ NodeIdCallback invalid_id_cb = base::BindOnce( &GlicAnnotationManagerUiTest::GetInvalidDomNodeIdFromAnnotatedPageContent, base::Unretained(this)); - RunTestSequence(InstrumentTab(kActiveTabId), - NavigateWebContents( - kActiveTabId, embedded_test_server()->GetURL( - "/scrollable_page_with_content.html")), - OpenGlicWindow(GlicWindowMode::kDetached), - GetPageContextFromFocusedTab(), // - ScrollToWithDocumentIdExpectingError( - ExactTextSelector("Some text", std::move(invalid_id_cb)), - mojom::ScrollToErrorReason::kSearchRangeInvalid)); + RunTestSequence( + InstrumentTab(kActiveTabId), + NavigateWebContents( + kActiveTabId, + embedded_test_server()->GetURL("/scrollable_page_with_content.html")), + OpenGlicWindow(GlicWindowMode::kDetached), SetTabContextPermission(true), + GetPageContextFromFocusedTab(), // + ScrollToWithDocumentIdExpectingError( + ExactTextSelector("Some text", std::move(invalid_id_cb)), + mojom::ScrollToErrorReason::kSearchRangeInvalid)); } IN_PROC_BROWSER_TEST_F(GlicAnnotationManagerUiTest, NodeIdSelector) { @@ -764,26 +796,28 @@ "p#text") .value(); }); - RunTestSequence(InstrumentTab(kActiveTabId), - NavigateWebContents( - kActiveTabId, embedded_test_server()->GetURL( - "/scrollable_page_with_content.html")), - OpenGlicWindow(GlicWindowMode::kDetached), - GetPageContextFromFocusedTab(), // - ScrollToWithDocumentId(NodeIdSelector(std::move(text_node)))); + RunTestSequence( + InstrumentTab(kActiveTabId), + NavigateWebContents( + kActiveTabId, + embedded_test_server()->GetURL("/scrollable_page_with_content.html")), + OpenGlicWindow(GlicWindowMode::kDetached), SetTabContextPermission(true), + GetPageContextFromFocusedTab(), // + ScrollToWithDocumentId(NodeIdSelector(std::move(text_node)))); } IN_PROC_BROWSER_TEST_F(GlicAnnotationManagerUiTest, NodeIdSelectorWithInvalidNode) { - RunTestSequence(InstrumentTab(kActiveTabId), - NavigateWebContents( - kActiveTabId, embedded_test_server()->GetURL( - "/scrollable_page_with_content.html")), - OpenGlicWindow(GlicWindowMode::kDetached), - GetPageContextFromFocusedTab(), // - ScrollToWithDocumentIdExpectingError( - NodeIdSelector(base::BindOnce([]() { return -1; })), - mojom::ScrollToErrorReason::kNoMatchFound)); + RunTestSequence( + InstrumentTab(kActiveTabId), + NavigateWebContents( + kActiveTabId, + embedded_test_server()->GetURL("/scrollable_page_with_content.html")), + OpenGlicWindow(GlicWindowMode::kDetached), SetTabContextPermission(true), + GetPageContextFromFocusedTab(), // + ScrollToWithDocumentIdExpectingError( + NodeIdSelector(base::BindOnce([]() { return -1; })), + mojom::ScrollToErrorReason::kNoMatchFound)); } IN_PROC_BROWSER_TEST_F(GlicAnnotationManagerUiTest, @@ -794,7 +828,8 @@ kActiveTabId, embedded_test_server()->GetURL("/scrollable_page_with_content.html")), OpenGlicWindow(GlicWindowMode::kDetached), // - InsertFakeAnnotationService(), // + SetTabContextPermission(true), + InsertFakeAnnotationService(), // ScrollToAsync(ExactTextSelector("does not matter")), WaitForEvent(kBrowserViewElementId, kScrollToRequestReceived), // Do([&]() { @@ -814,7 +849,8 @@ kActiveTabId, embedded_test_server()->GetURL("/scrollable_page_with_content.html")), OpenGlicWindow(GlicWindowMode::kDetached), // - InsertFakeAnnotationService(), // + SetTabContextPermission(true), + InsertFakeAnnotationService(), // ScrollToAsync(ExactTextSelector("does not matter")), WaitForEvent(kBrowserViewElementId, kScrollToRequestReceived), // CloseGlicWindow(), // @@ -848,7 +884,8 @@ kActiveTabId, embedded_test_server()->GetURL("/scrollable_page_with_content.html")), OpenGlicWindow(GlicWindowMode::kDetached), // - InsertFakeAnnotationService(), // + SetTabContextPermission(true), + InsertFakeAnnotationService(), // ScrollToAsync(ExactTextSelector("does not matter")), WaitForEvent(kBrowserViewElementId, kScrollToRequestReceived), // Do([&]() { @@ -861,6 +898,59 @@ "Annotations should be dropped")); } +IN_PROC_BROWSER_TEST_F(GlicAnnotationManagerUiTest, + TabContextPermissionDisabledBeforeRequest) { + RunTestSequence( + InstrumentTab(kActiveTabId), + NavigateWebContents( + kActiveTabId, + embedded_test_server()->GetURL("/scrollable_page_with_content.html")), + OpenGlicWindow(GlicWindowMode::kDetached), + ScrollToExpectingError( + ExactTextSelector("Text does not exist"), + mojom::ScrollToErrorReason::kTabContextPermissionDisabled)); +} + +IN_PROC_BROWSER_TEST_F(GlicAnnotationManagerUiTest, + TabContextPermissionDisabledDuringScrollToRequest) { + RunTestSequence( + InstrumentTab(kActiveTabId), // + NavigateWebContents( + kActiveTabId, + embedded_test_server()->GetURL("/scrollable_page_with_content.html")), + OpenGlicWindow(GlicWindowMode::kDetached), // + SetTabContextPermission(true), + InsertFakeAnnotationService(), // + ScrollToAsync(ExactTextSelector("does not matter")), + WaitForEvent(kBrowserViewElementId, kScrollToRequestReceived), // + SetTabContextPermission(false), + WaitForScrollToError( + mojom::ScrollToErrorReason::kTabContextPermissionDisabled) // + ); +} + +IN_PROC_BROWSER_TEST_F(GlicAnnotationManagerUiTest, + HighlightIsDroppedWhenTabContextPermissionIsDisabled) { + RunTestSequence( + InstrumentTab(kActiveTabId), // + NavigateWebContents( + kActiveTabId, + embedded_test_server()->GetURL("/scrollable_page_with_content.html")), + OpenGlicWindow(GlicWindowMode::kDetached), // + SetTabContextPermission(true), + InsertFakeAnnotationService(), // + ScrollToAsync(ExactTextSelector("does not matter")), + WaitForEvent(kBrowserViewElementId, kScrollToRequestReceived), // + Do([&]() { + fake_service()->NotifyAttachment( + gfx::Rect(20, 20), blink::mojom::AttachmentResult::kSuccess); + }), + SetTabContextPermission(false), + WaitForEvent(kBrowserViewElementId, kAnnotationAgentDisconnectedByRemote), + Check([&]() { return !fake_service()->HighlightIsActive(); }, + "Annotations should be dropped")); +} + class GlicAnnotationManagerWithScrollToDisabledUiTest : public InteractiveGlicTest { public: @@ -902,20 +992,22 @@ NavigateWebContents( kActiveTabId, embedded_test_server()->GetURL("/scrollable_page_with_content.html")), - OpenGlicWindow(GlicWindowMode::kDetached), + OpenGlicWindow(GlicWindowMode::kDetached), // + SetTabContextPermission(true), // ScrollToExpectingError(ExactTextSelector("Some text"), mojom::ScrollToErrorReason::kNotSupported)); } IN_PROC_BROWSER_TEST_F(GlicAnnotationManagerWithEnforceDocumentIdUiTest, SucceedsWithDocumentId) { - RunTestSequence( - InstrumentTab(kActiveTabId), - NavigateWebContents( - kActiveTabId, - embedded_test_server()->GetURL("/scrollable_page_with_content.html")), - OpenGlicWindow(GlicWindowMode::kDetached), GetPageContextFromFocusedTab(), - ScrollToWithDocumentId(ExactTextSelector("Some text"))); + RunTestSequence(InstrumentTab(kActiveTabId), + NavigateWebContents( + kActiveTabId, embedded_test_server()->GetURL( + "/scrollable_page_with_content.html")), + OpenGlicWindow(GlicWindowMode::kDetached), // + SetTabContextPermission(true), // + GetPageContextFromFocusedTab(), + ScrollToWithDocumentId(ExactTextSelector("Some text"))); } } // namespace glic::test
diff --git a/chrome/browser/glic/host/glic_api_uitest.cc b/chrome/browser/glic/host/glic_api_uitest.cc index 998dba19..8b599cd5 100644 --- a/chrome/browser/glic/host/glic_api_uitest.cc +++ b/chrome/browser/glic/host/glic_api_uitest.cc
@@ -942,6 +942,11 @@ ExecuteJsTest(); } +IN_PROC_BROWSER_TEST_F(GlicApiTestWithOneTab, + testScrollToFindsTextNoTabContextPermission) { + ExecuteJsTest(); +} + IN_PROC_BROWSER_TEST_F(GlicApiTestWithOneTab, testScrollToNoMatchFound) { ExecuteJsTest(); }
diff --git a/chrome/browser/metrics/usage_scenario/tab_usage_scenario_tracker.cc b/chrome/browser/metrics/usage_scenario/tab_usage_scenario_tracker.cc index a88131b..35b46e6 100644 --- a/chrome/browser/metrics/usage_scenario/tab_usage_scenario_tracker.cc +++ b/chrome/browser/metrics/usage_scenario/tab_usage_scenario_tracker.cc
@@ -11,16 +11,12 @@ #include "content/public/browser/render_process_host.h" #include "content/public/browser/visibility.h" #include "content/public/browser/web_contents.h" -#include "extensions/buildflags/buildflags.h" +#include "extensions/browser/script_injection_tracker.h" #include "extensions/common/extension_id.h" #include "services/metrics/public/cpp/ukm_source_id.h" #include "ui/display/screen.h" #include "url/origin.h" -#if BUILDFLAG(ENABLE_EXTENSIONS) -#include "extensions/browser/script_injection_tracker.h" -#endif - namespace metrics { namespace { @@ -37,9 +33,6 @@ extensions::ExtensionIdSet GetExtensionsThatRanContentScriptsInWebContents( content::WebContents* contents) { -#if !BUILDFLAG(ENABLE_EXTENSIONS) - return {}; -#else content::RenderFrameHost* main_frame = contents->GetPrimaryMainFrame(); if (!main_frame) { // WebContents is being destroyed. @@ -85,7 +78,6 @@ GetExtensionsThatRanContentScriptsInProcess(*process)); } return extensions; -#endif } } // namespace @@ -351,9 +343,8 @@ web_contents->GetVisibility() == content::Visibility::VISIBLE); // If |web_contents| is tracked in the list of visible WebContents then a // synthetic visibility change event should be emitted. - if (iter != visible_tabs_.end()) { + if (iter != visible_tabs_.end()) OnTabBecameHidden(&iter); - } // Remove |web_contents| from the list of contents playing video. If // necessary, the data store was already informed that a video stopped playing
diff --git a/chrome/browser/metrics/usage_scenario/tab_usage_scenario_tracker_browsertest.cc b/chrome/browser/metrics/usage_scenario/tab_usage_scenario_tracker_browsertest.cc index aab54d4..c34db7e2 100644 --- a/chrome/browser/metrics/usage_scenario/tab_usage_scenario_tracker_browsertest.cc +++ b/chrome/browser/metrics/usage_scenario/tab_usage_scenario_tracker_browsertest.cc
@@ -9,7 +9,6 @@ #include <memory> -#include "base/location.h" #include "base/memory/raw_ptr.h" #include "base/test/bind.h" #include "base/test/run_until.h" @@ -20,32 +19,6 @@ #include "build/build_config.h" #include "chrome/browser/metrics/tab_stats/tab_stats_tracker.h" #include "chrome/browser/metrics/usage_scenario/usage_scenario_data_store.h" -#include "chrome/browser/profiles/profile.h" -#include "content/public/browser/media_player_id.h" -#include "content/public/browser/navigation_controller.h" -#include "content/public/browser/visibility.h" -#include "content/public/browser/web_contents.h" -#include "content/public/browser/web_contents_observer.h" -#include "content/public/common/content_features.h" -#include "content/public/test/browser_test.h" -#include "content/public/test/browser_test_utils.h" -#include "net/dns/mock_host_resolver.h" -#include "net/test/embedded_test_server/http_request.h" -#include "net/test/embedded_test_server/http_response.h" -#include "services/metrics/public/cpp/ukm_source_id.h" -#include "testing/gmock/include/gmock/gmock.h" -#include "testing/gtest/include/gtest/gtest.h" -#include "ui/display/screen.h" -#include "url/gurl.h" -#include "url/origin.h" - -#if BUILDFLAG(IS_ANDROID) -#include "chrome/browser/android/tab_android.h" -#include "chrome/browser/ui/android/tab_model/tab_model.h" -#include "chrome/browser/ui/android/tab_model/tab_model_list.h" -#include "chrome/browser/ui/android/tab_model/tab_model_test_helper.h" -#include "chrome/test/base/android/android_browser_test.h" -#else #include "chrome/browser/resource_coordinator/tab_lifecycle_unit_external.h" #include "chrome/browser/resource_coordinator/tab_lifecycle_unit_source.h" #include "chrome/browser/ui/browser.h" @@ -53,7 +26,20 @@ #include "chrome/browser/ui/tabs/tab_enums.h" #include "chrome/test/base/in_process_browser_test.h" #include "chrome/test/base/ui_test_utils.h" -#endif +#include "content/public/browser/media_player_id.h" +#include "content/public/browser/visibility.h" +#include "content/public/browser/web_contents.h" +#include "content/public/browser/web_contents_observer.h" +#include "content/public/common/content_features.h" +#include "content/public/test/browser_test.h" +#include "content/public/test/browser_test_utils.h" +#include "content/public/test/content_browser_test_utils.h" +#include "net/dns/mock_host_resolver.h" +#include "net/test/embedded_test_server/http_request.h" +#include "net/test/embedded_test_server/http_response.h" +#include "services/metrics/public/cpp/ukm_source_id.h" +#include "testing/gmock/include/gmock/gmock.h" +#include "testing/gtest/include/gtest/gtest.h" namespace metrics { @@ -61,15 +47,11 @@ constexpr base::TimeDelta kInterval = base::Minutes(2); -#if !BUILDFLAG(IS_ANDROID) -// TODO(crbug.com/412634171): Enable this when discarding is supported on -// Android. void DiscardTab(content::WebContents* contents) { resource_coordinator::TabLifecycleUnitSource::GetTabLifecycleUnitExternal( contents) ->DiscardTab(mojom::LifecycleUnitDiscardReason::URGENT); } -#endif // A WebContentsObserver that allows waiting for some media to start or stop // playing fullscreen. @@ -155,36 +137,9 @@ base::RunLoop audio_stopped_playing_loop_; }; -// Android Auto trybots report 2 displays, so -// `time_playing_video_full_screen_single_monitor` will never be recorded there. -void ExpectTimePlayingVideoFullScreenSingleMonitor( - base::TimeDelta expected_time, - const UsageScenarioDataStore::IntervalData& interval_data, - const base::Location& location = base::Location::Current()) { - SCOPED_TRACE(location.ToString()); - auto* screen = display::Screen::GetScreen(); - ASSERT_TRUE(screen); - SCOPED_TRACE(::testing::Message() << screen->GetNumDisplays() << " displays"); - if (screen->GetNumDisplays() == 1) { - EXPECT_EQ(expected_time, - interval_data.time_playing_video_full_screen_single_monitor); - } else { - EXPECT_EQ(base::TimeDelta(), - interval_data.time_playing_video_full_screen_single_monitor); - } -} - -using TabStripInterface = TabStatsTracker::TabStripInterface; - -#if BUILDFLAG(IS_ANDROID) -using PlatformBrowserTest = AndroidBrowserTest; -#else -using PlatformBrowserTest = InProcessBrowserTest; -#endif - } // namespace -class TabUsageScenarioTrackerBrowserTest : public PlatformBrowserTest { +class TabUsageScenarioTrackerBrowserTest : public InProcessBrowserTest { public: TabUsageScenarioTrackerBrowserTest() : data_store_(&tick_clock_) { // Ensure that |tick_clock_.NowTicks()| doesn't return 0 the first time it @@ -201,35 +156,13 @@ // This is required for the fullscreen video tests. embedded_test_server()->ServeFilesFromSourceDirectory( base::FilePath(FILE_PATH_LITERAL("content/test/data"))); - PlatformBrowserTest::SetUp(); + InProcessBrowserTest::SetUp(); } void SetUpOnMainThread() override { - PlatformBrowserTest::SetUpOnMainThread(); + InProcessBrowserTest::SetUpOnMainThread(); host_resolver()->AddRule("*", "127.0.0.1"); ASSERT_TRUE(embedded_test_server()->Start()); - -#if BUILDFLAG(IS_ANDROID) - ASSERT_FALSE(TabModelList::models().empty()); - tab_strip_ = std::make_unique<TabStripInterface>( - TabModelList::models().front().get()); - - // The initial tab of the main TabModel will be in an inconsistent state, - // since its WebContents is created and navigates to an initial URL - // asynchronously. Wait for it to finish loading before observing it with - // TabUsageScenarioTracker. - ASSERT_EQ(tab_strip_->tab_model()->GetTabCount(), 1); - TabAndroid* initial_tab = tab_strip_->tab_model()->GetTabAt(0); - TabAndroidLoadedWaiter waiter(initial_tab); - ASSERT_TRUE(waiter.Wait()); - - // Make sure the tab isn't still playing audio from a previous test. - ASSERT_FALSE(initial_tab->web_contents()->IsCurrentlyAudible()); -#else - ASSERT_TRUE(browser()); - tab_strip_ = std::make_unique<TabStripInterface>(browser()); -#endif - tab_stats_tracker_ = TabStatsTracker::GetInstance(); ASSERT_TRUE(tab_stats_tracker_); tab_usage_scenario_tracker_ = @@ -239,81 +172,22 @@ } void TearDownOnMainThread() override { - tab_strip_.reset(); tab_stats_tracker_->RemoveObserver(tab_usage_scenario_tracker_.get()); tab_usage_scenario_tracker_.reset(); tab_stats_tracker_ = nullptr; - PlatformBrowserTest::TearDownOnMainThread(); + InProcessBrowserTest::TearDownOnMainThread(); } - TabStripInterface& tab_strip() { return *tab_strip_; } - protected: - // Methods to manipulate Browser + TabStripModel (on desktop) or TabModel (on - // Android). - -#if BUILDFLAG(IS_ANDROID) - - void NavigateNewTabToUrl(content::WebContents* contents, - const GURL& url, - bool wait_until_complete = true) { - // Navigate to `url` as a render-initiated navigation, so that it isn't - // considered a user interaction. - content::NavigationController::LoadURLParams load_params(url); - load_params.initiator_origin = url::Origin(); - load_params.is_renderer_initiated = true; - if (wait_until_complete) { - content::NavigateToURLBlockUntilNavigationsComplete(contents, load_params, - 1); - } else { - contents->GetController().LoadURLWithParams(load_params); - } - } - - bool AddTabToTabStrip(TabStripInterface& tab_strip, - const GURL& url = GURL("about:blank")) { - content::WebContents* active_contents = tab_strip.GetActiveWebContents(); - if (!active_contents) { - ADD_FAILURE() << "No active WebContents"; - return false; - } - - // Create the WebContents hidden so that there's no visibility notification - // until it's added to the tab strip. - content::WebContents::CreateParams create_params(tab_strip.GetProfile()); - create_params.initially_hidden = true; - content::WebContents* new_contents = - content::WebContents::Create(create_params).release(); - - // CreateTab works with both OwningTestTabModel and the initial tab strip, - // which is a production TabModel. - tab_strip.tab_model()->CreateTab( - TabAndroid::FromWebContents(active_contents), new_contents, - /*select=*/true); - - NavigateNewTabToUrl(new_contents, url); - return true; - } - -#else // !BUILDFLAG(IS_ANDROID) - - bool AddTabToTabStrip(TabStripInterface& tab_strip, const GURL& url) { - return AddTabAtIndexToBrowser(tab_strip.browser(), 1, url, - ui::PAGE_TRANSITION_TYPED); - } - -#endif // !BUILDFLAG(IS_ANDROID) - base::SimpleTestTickClock tick_clock_; raw_ptr<TabStatsTracker> tab_stats_tracker_{nullptr}; UsageScenarioDataStoreImpl data_store_; std::unique_ptr<TabUsageScenarioTracker> tab_usage_scenario_tracker_; - std::unique_ptr<TabStripInterface> tab_strip_; }; IN_PROC_BROWSER_TEST_F(TabUsageScenarioTrackerBrowserTest, BasicNavigations) { // Test with only one visible tab and one top level navigation. - auto* content0 = tab_strip().GetWebContentsAt(0); + auto* content0 = browser()->tab_strip_model()->GetWebContentsAt(0); EXPECT_TRUE(content::NavigateToURL( content0, embedded_test_server()->GetURL("/title1.html"))); tick_clock_.Advance(kInterval); @@ -328,8 +202,9 @@ interval_data.time_playing_video_full_screen_single_monitor.is_zero()); EXPECT_TRUE(interval_data.time_with_open_webrtc_connection.is_zero()); EXPECT_TRUE(interval_data.time_playing_video_in_visible_tab.is_zero()); - EXPECT_EQ(tab_strip() - .GetActiveWebContents() + EXPECT_EQ(browser() + ->tab_strip_model() + ->GetActiveWebContents() ->GetPrimaryMainFrame() ->GetPageUkmSourceId(), interval_data.source_id_for_longest_visible_origin); @@ -338,13 +213,14 @@ // Add a second tab that will become the visible one. tick_clock_.Advance(kInterval); - ASSERT_TRUE(AddTabToTabStrip(tab_strip(), - embedded_test_server()->GetURL("/title2.html"))); - auto* contents1 = tab_strip().GetActiveWebContents(); - EXPECT_EQ(content::Visibility::VISIBLE, - tab_strip().GetActiveWebContents()->GetVisibility()); + ASSERT_TRUE(AddTabAtIndex(1, embedded_test_server()->GetURL("/title2.html"), + ui::PAGE_TRANSITION_LINK)); + auto* contents1 = browser()->tab_strip_model()->GetActiveWebContents(); + EXPECT_EQ( + content::Visibility::VISIBLE, + browser()->tab_strip_model()->GetActiveWebContents()->GetVisibility()); EXPECT_EQ(content::Visibility::HIDDEN, - tab_strip().GetWebContentsAt(0)->GetVisibility()); + browser()->tab_strip_model()->GetWebContentsAt(0)->GetVisibility()); tick_clock_.Advance(kInterval * 2); interval_data = data_store_.ResetIntervalData(); EXPECT_EQ(2U, interval_data.max_tab_count); @@ -362,15 +238,17 @@ interval_data.source_id_for_longest_visible_origin_duration); // Activate the first tab and close it. - tab_strip().ActivateTabAtForTesting(0); + browser()->tab_strip_model()->ActivateTabAt(0); tick_clock_.Advance(kInterval * 2); - auto expected_source_id = tab_strip() - .GetActiveWebContents() + auto expected_source_id = browser() + ->tab_strip_model() + ->GetActiveWebContents() ->GetPrimaryMainFrame() ->GetPageUkmSourceId(); - size_t previous_tab_count = tab_strip().GetTabCount(); - tab_strip().CloseTabAtForTesting(0); - EXPECT_EQ(previous_tab_count - 1, tab_strip().GetTabCount()); + int previous_tab_count = browser()->tab_strip_model()->count(); + browser()->tab_strip_model()->CloseWebContentsAt( + 0, TabCloseTypes::CLOSE_USER_GESTURE); + EXPECT_EQ(previous_tab_count - 1, browser()->tab_strip_model()->count()); tick_clock_.Advance(kInterval); interval_data = data_store_.ResetIntervalData(); EXPECT_EQ(2U, interval_data.max_tab_count); @@ -408,13 +286,14 @@ } IN_PROC_BROWSER_TEST_F(TabUsageScenarioTrackerBrowserTest, TabCrash) { - ASSERT_TRUE(AddTabToTabStrip(tab_strip(), - embedded_test_server()->GetURL("/title2.html"))); + ASSERT_TRUE(AddTabAtIndex(1, embedded_test_server()->GetURL("/title2.html"), + ui::PAGE_TRANSITION_LINK)); EXPECT_EQ(content::Visibility::VISIBLE, - tab_strip().GetWebContentsAt(1)->GetVisibility()); + browser()->tab_strip_model()->GetWebContentsAt(1)->GetVisibility()); tick_clock_.Advance(kInterval); - auto expected_source_id = tab_strip() - .GetActiveWebContents() + auto expected_source_id = browser() + ->tab_strip_model() + ->GetActiveWebContents() ->GetPrimaryMainFrame() ->GetPageUkmSourceId(); @@ -434,16 +313,8 @@ // Induce a crash in the active tab. tick_clock_.Advance(kInterval); - content::CrashTab(tab_strip().GetWebContentsAt(1)); - EXPECT_TRUE(tab_strip().GetWebContentsAt(1)->IsCrashed()); -#if BUILDFLAG(IS_ANDROID) - // On Android, the Sad Tab overlay is handled in the Java layer and doesn't - // trigger in this test. Fake it by hiding the active tab. The test is still - // useful to validate that a tab being hidden during an interval updates - // `source_id_for_longest_visible_origin_duration` correctly, although it - // can't validate that a crashed tab actually becomes hidden on Android. - tab_strip().GetWebContentsAt(1)->WasHidden(); -#endif + content::CrashTab(browser()->tab_strip_model()->GetWebContentsAt(1)); + EXPECT_TRUE(browser()->tab_strip_model()->GetWebContentsAt(1)->IsCrashed()); tick_clock_.Advance(kInterval); interval_data = data_store_.ResetIntervalData(); EXPECT_EQ(2U, interval_data.max_tab_count); @@ -460,10 +331,6 @@ interval_data.source_id_for_longest_visible_origin_duration); } -#if !BUILDFLAG(IS_ANDROID) - -// TODO(crbug.com/412634171): Enable this when discarding is supported on -// Android. class TabUsageScenarioTrackerDiscardBrowserTest : public TabUsageScenarioTrackerBrowserTest, public ::testing::WithParamInterface<bool> { @@ -477,25 +344,17 @@ base::test::ScopedFeatureList scoped_feature_list_; }; -INSTANTIATE_TEST_SUITE_P( - , - TabUsageScenarioTrackerDiscardBrowserTest, - ::testing::Values(false, true), - [](const ::testing::TestParamInfo< - TabUsageScenarioTrackerDiscardBrowserTest::ParamType>& info) { - return info.param ? "RetainedWebContents" : "UnretainedWebContents"; - }); - IN_PROC_BROWSER_TEST_P(TabUsageScenarioTrackerDiscardBrowserTest, TabDiscard) { - ASSERT_TRUE(AddTabToTabStrip(tab_strip(), - embedded_test_server()->GetURL("/title2.html"))); + ASSERT_TRUE(AddTabAtIndex(1, embedded_test_server()->GetURL("/title2.html"), + ui::PAGE_TRANSITION_LINK)); EXPECT_EQ(content::Visibility::VISIBLE, - tab_strip().GetWebContentsAt(1)->GetVisibility()); + browser()->tab_strip_model()->GetWebContentsAt(1)->GetVisibility()); tick_clock_.Advance(kInterval); auto interval_data = data_store_.ResetIntervalData(); - auto expected_source_id = tab_strip() - .GetActiveWebContents() + auto expected_source_id = browser() + ->tab_strip_model() + ->GetActiveWebContents() ->GetPrimaryMainFrame() ->GetPageUkmSourceId(); EXPECT_EQ(2U, interval_data.max_tab_count); @@ -513,7 +372,7 @@ // Induce a discard of the active tab. tick_clock_.Advance(kInterval * 2); - DiscardTab(tab_strip().GetWebContentsAt(1)); + DiscardTab(browser()->tab_strip_model()->GetWebContentsAt(1)); tick_clock_.Advance(kInterval); interval_data = data_store_.ResetIntervalData(); EXPECT_EQ(2U, interval_data.max_tab_count); @@ -531,10 +390,11 @@ // Do a navigation on the discarded tab. EXPECT_TRUE( - content::NavigateToURL(tab_strip().GetWebContentsAt(1), + content::NavigateToURL(browser()->tab_strip_model()->GetWebContentsAt(1), embedded_test_server()->GetURL("/title2.html"))); - expected_source_id = tab_strip() - .GetWebContentsAt(1) + expected_source_id = browser() + ->tab_strip_model() + ->GetWebContentsAt(1) ->GetPrimaryMainFrame() ->GetPageUkmSourceId(); tick_clock_.Advance(kInterval); @@ -553,14 +413,15 @@ interval_data.source_id_for_longest_visible_origin_duration); // Same tests but with this time the discarded tab is hidden. - tab_strip().ActivateTabAtForTesting(0); + browser()->tab_strip_model()->ActivateTabAt(0); EXPECT_EQ(content::Visibility::VISIBLE, - tab_strip().GetWebContentsAt(0)->GetVisibility()); + browser()->tab_strip_model()->GetWebContentsAt(0)->GetVisibility()); tick_clock_.Advance(kInterval * 2); - DiscardTab(tab_strip().GetWebContentsAt(1)); + DiscardTab(browser()->tab_strip_model()->GetWebContentsAt(1)); tick_clock_.Advance(kInterval); - expected_source_id = tab_strip() - .GetWebContentsAt(0) + expected_source_id = browser() + ->tab_strip_model() + ->GetWebContentsAt(0) ->GetPrimaryMainFrame() ->GetPageUkmSourceId(); interval_data = data_store_.ResetIntervalData(); @@ -579,7 +440,7 @@ // Do a navigation on the discarded tab. EXPECT_TRUE( - content::NavigateToURL(tab_strip().GetWebContentsAt(1), + content::NavigateToURL(browser()->tab_strip_model()->GetWebContentsAt(1), embedded_test_server()->GetURL("/title2.html"))); tick_clock_.Advance(kInterval); interval_data = data_store_.ResetIntervalData(); @@ -602,16 +463,16 @@ // Start a video in a tab and discard it while it's playing, ensure that // things are tracked properly. EXPECT_TRUE( - content::NavigateToURL(tab_strip().GetWebContentsAt(0), + content::NavigateToURL(browser()->tab_strip_model()->GetWebContentsAt(0), embedded_test_server()->GetURL("/title2.html"))); tick_clock_.Advance(kInterval); - ASSERT_TRUE(AddTabToTabStrip( - tab_strip(), - embedded_test_server()->GetURL("/media/session/media-session.html"))); + ASSERT_TRUE(AddTabAtIndex( + 1, embedded_test_server()->GetURL("/media/session/media-session.html"), + ui::PAGE_TRANSITION_LINK)); EXPECT_EQ(content::Visibility::VISIBLE, - tab_strip().GetWebContentsAt(1)->GetVisibility()); + browser()->tab_strip_model()->GetWebContentsAt(1)->GetVisibility()); - auto* media_contents = tab_strip().GetWebContentsAt(1); + auto* media_contents = browser()->tab_strip_model()->GetWebContentsAt(1); MediaWaiter media_waiter(media_contents); EXPECT_TRUE(content::ExecJs( media_contents, "document.getElementById('long-video-loop').play();")); @@ -624,7 +485,7 @@ // Discard the tab, the data store's visible tab video timer should be reset // by the tracker. tick_clock_.Advance(kInterval * 2); - DiscardTab(tab_strip().GetWebContentsAt(1)); + DiscardTab(browser()->tab_strip_model()->GetWebContentsAt(1)); ASSERT_TRUE(base::test::RunUntil([&]() { return !data_store_.TrackingPlayingVideoInActiveTabForTesting(); })); @@ -649,15 +510,16 @@ // Play full screen video in a tab and discard it while it's playing, ensure // that things are tracked properly. EXPECT_TRUE( - content::NavigateToURL(tab_strip().GetWebContentsAt(0), + content::NavigateToURL(browser()->tab_strip_model()->GetWebContentsAt(0), embedded_test_server()->GetURL("/title2.html"))); tick_clock_.Advance(kInterval); - ASSERT_TRUE(AddTabToTabStrip( - tab_strip(), embedded_test_server()->GetURL("/media/fullscreen.html"))); + ASSERT_TRUE( + AddTabAtIndex(1, embedded_test_server()->GetURL("/media/fullscreen.html"), + ui::PAGE_TRANSITION_LINK)); EXPECT_EQ(content::Visibility::VISIBLE, - tab_strip().GetWebContentsAt(1)->GetVisibility()); + browser()->tab_strip_model()->GetWebContentsAt(1)->GetVisibility()); - auto* fullscreen_contents = tab_strip().GetWebContentsAt(1); + auto* fullscreen_contents = browser()->tab_strip_model()->GetWebContentsAt(1); FullscreenEventsWaiter fullscreen_waiter(fullscreen_contents); EXPECT_TRUE( content::ExecJs(fullscreen_contents, "makeFullscreen('small_video')")); @@ -671,7 +533,7 @@ // Discard the tab, the data store's full screen video timer should be reset // by the tracker. tick_clock_.Advance(kInterval * 2); - DiscardTab(tab_strip().GetWebContentsAt(1)); + DiscardTab(browser()->tab_strip_model()->GetWebContentsAt(1)); ASSERT_TRUE(base::test::RunUntil([&]() { return !data_store_.TrackingPlayingFullScreenVideoSingleMonitorForTesting(); })); @@ -681,7 +543,8 @@ EXPECT_EQ(1U, interval_data.max_visible_window_count); EXPECT_EQ(2U, interval_data.top_level_navigation_count); EXPECT_EQ(0U, interval_data.tabs_closed_during_interval); - ExpectTimePlayingVideoFullScreenSingleMonitor(kInterval * 2, interval_data); + EXPECT_EQ(kInterval * 2, + interval_data.time_playing_video_full_screen_single_monitor); EXPECT_TRUE(interval_data.time_with_open_webrtc_connection.is_zero()); EXPECT_TRUE(interval_data.time_playing_video_in_visible_tab.is_zero()); EXPECT_EQ(expected_source_id, @@ -690,11 +553,9 @@ interval_data.source_id_for_longest_visible_origin_duration); } -#endif // !BUILDFLAG(IS_ANDROID) - IN_PROC_BROWSER_TEST_F(TabUsageScenarioTrackerBrowserTest, FullScreenVideo) { // Play fullscreen video in a tab, ensure that things are tracked properly. - auto* contents = tab_strip().GetWebContentsAt(0); + auto* contents = browser()->tab_strip_model()->GetWebContentsAt(0); FullscreenEventsWaiter waiter(contents); EXPECT_TRUE(content::NavigateToURL( contents, embedded_test_server()->GetURL("/media/fullscreen.html"))); @@ -711,7 +572,8 @@ EXPECT_EQ(1U, interval_data.max_visible_window_count); EXPECT_EQ(1U, interval_data.top_level_navigation_count); EXPECT_EQ(0U, interval_data.tabs_closed_during_interval); - ExpectTimePlayingVideoFullScreenSingleMonitor(kInterval, interval_data); + EXPECT_EQ(kInterval, + interval_data.time_playing_video_full_screen_single_monitor); EXPECT_TRUE(interval_data.time_with_open_webrtc_connection.is_zero()); // The |time_playing_video_in_visible_tab| value is not currently being // tracked. @@ -724,7 +586,7 @@ IN_PROC_BROWSER_TEST_F(TabUsageScenarioTrackerBrowserTest, VisibleTabVideo) { // Play video in a tab, ensure that things are tracked properly. - auto* contents = tab_strip().GetWebContentsAt(0); + auto* contents = browser()->tab_strip_model()->GetWebContentsAt(0); MediaWaiter waiter(contents); EXPECT_TRUE(content::NavigateToURL( contents, @@ -748,14 +610,7 @@ interval_data.time_playing_video_full_screen_single_monitor.is_zero()); EXPECT_TRUE(interval_data.time_with_open_webrtc_connection.is_zero()); EXPECT_EQ(kInterval, interval_data.time_playing_video_in_visible_tab); - - // TODO(crbug.com/412634171): Android (especially desktop Android) sometimes - // plays audio immediately on loading media-session.html. Find out why and - // reenable this expectation. -#if !BUILDFLAG(IS_ANDROID) EXPECT_TRUE(interval_data.time_playing_audio.is_zero()); -#endif - EXPECT_EQ(expected_source_id, interval_data.source_id_for_longest_visible_origin); EXPECT_EQ(kInterval, @@ -764,7 +619,7 @@ IN_PROC_BROWSER_TEST_F(TabUsageScenarioTrackerBrowserTest, TabAudio) { // Play audio in a tab, ensure that things are tracked properly. - auto* contents = tab_strip().GetWebContentsAt(0); + auto* contents = browser()->tab_strip_model()->GetWebContentsAt(0); MediaWaiter waiter(contents); EXPECT_TRUE(content::NavigateToURL( contents, @@ -800,28 +655,31 @@ // Play fullscreen video in a tab and close it while it's playing, ensure that // things are tracked properly. EXPECT_TRUE( - content::NavigateToURL(tab_strip().GetWebContentsAt(0), + content::NavigateToURL(browser()->tab_strip_model()->GetWebContentsAt(0), embedded_test_server()->GetURL("/title2.html"))); tick_clock_.Advance(kInterval); - ASSERT_TRUE(AddTabToTabStrip( - tab_strip(), embedded_test_server()->GetURL("/media/fullscreen.html"))); - auto* contents = tab_strip().GetWebContentsAt(1); + ASSERT_TRUE( + AddTabAtIndex(1, embedded_test_server()->GetURL("/media/fullscreen.html"), + ui::PAGE_TRANSITION_LINK)); + auto* contents = browser()->tab_strip_model()->GetWebContentsAt(1); FullscreenEventsWaiter waiter(contents); EXPECT_TRUE(content::ExecJs(contents, "makeFullscreen('small_video')")); waiter.Wait(true); tick_clock_.Advance(kInterval * 2); auto expected_source_id = contents->GetPrimaryMainFrame()->GetPageUkmSourceId(); - size_t previous_tab_count = tab_strip().GetTabCount(); - tab_strip().CloseTabAtForTesting(1); - EXPECT_EQ(previous_tab_count - 1, tab_strip().GetTabCount()); + int previous_tab_count = browser()->tab_strip_model()->count(); + browser()->tab_strip_model()->CloseWebContentsAt( + 1, TabCloseTypes::CLOSE_USER_GESTURE); + EXPECT_EQ(previous_tab_count - 1, browser()->tab_strip_model()->count()); auto interval_data = data_store_.ResetIntervalData(); EXPECT_EQ(2U, interval_data.max_tab_count); EXPECT_EQ(1U, interval_data.max_visible_window_count); EXPECT_EQ(2U, interval_data.top_level_navigation_count); EXPECT_EQ(1U, interval_data.tabs_closed_during_interval); - ExpectTimePlayingVideoFullScreenSingleMonitor(kInterval * 2, interval_data); + EXPECT_EQ(kInterval * 2, + interval_data.time_playing_video_full_screen_single_monitor); EXPECT_TRUE(interval_data.time_with_open_webrtc_connection.is_zero()); EXPECT_TRUE(interval_data.time_playing_video_in_visible_tab.is_zero()); EXPECT_EQ(expected_source_id, @@ -831,8 +689,9 @@ tick_clock_.Advance(kInterval); interval_data = data_store_.ResetIntervalData(); - expected_source_id = tab_strip() - .GetActiveWebContents() + expected_source_id = browser() + ->tab_strip_model() + ->GetActiveWebContents() ->GetPrimaryMainFrame() ->GetPageUkmSourceId(); EXPECT_EQ(1U, interval_data.max_tab_count); @@ -853,7 +712,7 @@ FullScreenVideoCrash) { // Play fullscreen video in a tab and make the tab crash, ensure that things // are tracked properly. - auto* contents = tab_strip().GetWebContentsAt(0); + auto* contents = browser()->tab_strip_model()->GetWebContentsAt(0); EXPECT_TRUE(content::NavigateToURL( contents, embedded_test_server()->GetURL("/media/fullscreen.html"))); FullscreenEventsWaiter waiter(contents); @@ -869,7 +728,8 @@ EXPECT_EQ(1U, interval_data.max_visible_window_count); EXPECT_EQ(1U, interval_data.top_level_navigation_count); EXPECT_EQ(0U, interval_data.tabs_closed_during_interval); - ExpectTimePlayingVideoFullScreenSingleMonitor(kInterval, interval_data); + EXPECT_EQ(kInterval, + interval_data.time_playing_video_full_screen_single_monitor); EXPECT_TRUE(interval_data.time_with_open_webrtc_connection.is_zero()); EXPECT_TRUE(interval_data.time_playing_video_in_visible_tab.is_zero()); EXPECT_EQ(expected_source_id, @@ -900,29 +760,19 @@ InitialVisibleNotification) { // This test causes a WebContents::OnVisibilityChanged(VISIBLE) signal to be // emitted for a tab that was already visible when adding it. -#if BUILDFLAG(IS_ANDROID) - OwningTestTabModel tab_model2(tab_strip().GetProfile()); - TabAndroid* new_tab = tab_model2.AddEmptyTab(0); - - // Don't wait for the navigation, to mimic BROWSER_TEST_WAIT_FOR_BROWSER. - NavigateNewTabToUrl(new_tab->web_contents(), - embedded_test_server()->GetURL("/title2.html"), - /*wait_until_complete=*/false); - - TabStripInterface tab_strip2(&tab_model2); -#else ui_test_utils::NavigateToURLWithDisposition( - tab_strip().browser(), embedded_test_server()->GetURL("/title2.html"), + browser(), embedded_test_server()->GetURL("/title2.html"), WindowOpenDisposition::NEW_WINDOW, ui_test_utils::BROWSER_TEST_WAIT_FOR_BROWSER); - TabStripInterface tab_strip2(BrowserList::GetInstance()->get(1)); -#endif + Browser* browser2 = BrowserList::GetInstance()->get(1); - size_t previous_browser1_tab_count = tab_strip().GetTabCount(); - size_t previous_browser2_tab_count = tab_strip2.GetTabCount(); - tab_strip2.CloseTabAtForTesting(0); - EXPECT_EQ(previous_browser1_tab_count, tab_strip().GetTabCount()); - EXPECT_EQ(previous_browser2_tab_count - 1, tab_strip2.GetTabCount()); + int previous_browser1_tab_count = browser()->tab_strip_model()->count(); + int previous_browser2_tab_count = browser2->tab_strip_model()->count(); + browser2->tab_strip_model()->CloseWebContentsAt( + 0, TabCloseTypes::CLOSE_USER_GESTURE); + EXPECT_EQ(previous_browser1_tab_count, browser()->tab_strip_model()->count()); + EXPECT_EQ(previous_browser2_tab_count - 1, + browser2->tab_strip_model()->count()); tick_clock_.Advance(kInterval); auto interval_data = data_store_.ResetIntervalData(); @@ -935,8 +785,9 @@ interval_data.time_playing_video_full_screen_single_monitor.is_zero()); EXPECT_TRUE(interval_data.time_with_open_webrtc_connection.is_zero()); EXPECT_TRUE(interval_data.time_playing_video_in_visible_tab.is_zero()); - EXPECT_EQ(tab_strip() - .GetActiveWebContents() + EXPECT_EQ(browser() + ->tab_strip_model() + ->GetActiveWebContents() ->GetPrimaryMainFrame() ->GetPageUkmSourceId(), interval_data.source_id_for_longest_visible_origin); @@ -944,4 +795,13 @@ interval_data.source_id_for_longest_visible_origin_duration); } +INSTANTIATE_TEST_SUITE_P( + , + TabUsageScenarioTrackerDiscardBrowserTest, + ::testing::Values(false, true), + [](const ::testing::TestParamInfo< + TabUsageScenarioTrackerDiscardBrowserTest::ParamType>& info) { + return info.param ? "RetainedWebContents" : "UnretainedWebContents"; + }); + } // namespace metrics
diff --git a/chrome/browser/permissions/prediction_based_permission_ui_selector.cc b/chrome/browser/permissions/prediction_based_permission_ui_selector.cc index c1404adf..adc62b3 100644 --- a/chrome/browser/permissions/prediction_based_permission_ui_selector.cc +++ b/chrome/browser/permissions/prediction_based_permission_ui_selector.cc
@@ -29,6 +29,7 @@ #include "components/permissions/permission_request.h" #include "components/permissions/permission_uma_util.h" #include "components/permissions/permission_util.h" +#include "components/permissions/prediction_service/permissions_aiv3_handler.h" #include "components/permissions/prediction_service/prediction_common.h" #include "components/permissions/prediction_service/prediction_service.h" #include "components/permissions/prediction_service/prediction_service_messages.pb.h" @@ -36,6 +37,7 @@ #include "components/prefs/pref_service.h" #include "components/safe_browsing/core/common/safe_browsing_prefs.h" #include "components/unified_consent/pref_names.h" +#include "content/public/browser/render_widget_host_view.h" #include "content/public/browser/web_contents.h" #include "mojo/public/cpp/bindings/callback_helpers.h" @@ -47,7 +49,9 @@ namespace { using ::permissions::PermissionRequest; +using ::permissions::PermissionRequestRelevance; using ::permissions::PermissionsAiv1Handler; +using ::permissions::PermissionsAiv3Handler; using ::permissions::PredictionModelHandlerProvider; using ::permissions::PredictionRequestFeatures; using QuietUiReason = PredictionBasedPermissionUiSelector::QuietUiReason; @@ -196,7 +200,7 @@ } void PredictionBasedPermissionUiSelector:: - InquireAiOnDeviceAndServerModelIfAvailable( + InquireOnDeviceAiv1AndServerModelIfAvailable( content::RenderFrameHost* rfh, PredictionRequestFeatures features, permissions::RequestType request_type) { @@ -207,6 +211,31 @@ weak_ptr_factory_.GetWeakPtr(), std::move(features), request_type)); } +void PredictionBasedPermissionUiSelector:: + InquireOnDeviceAiv3AndServerModelIfAvailable( + content::RenderWidgetHostView* host_view, + PredictionRequestFeatures features, + permissions::RequestType request_type) { + if (!host_view) { + VLOG(1) << "[CPSS] On device AIv3 model unavailable"; + std::move(callback_).Run(Decision::UseNormalUiAndShowNoWarning()); + return; + } + + if (snapshot_for_testing_.has_value()) { + PredictionBasedPermissionUiSelector::OnSnapshotTakenForOnDeviceModel( + std::move(features), request_type, snapshot_for_testing_.value()); + return; + } + + // TODO(crbug.com/382447738) Add time measurement metrics + host_view->CopyFromSurface( + gfx::Rect(), gfx::Size(), + base::BindOnce( + &PredictionBasedPermissionUiSelector::OnSnapshotTakenForOnDeviceModel, + weak_ptr_factory_.GetWeakPtr(), std::move(features), request_type)); +} + void PredictionBasedPermissionUiSelector::SelectUiToUse( content::WebContents* web_contents, permissions::PermissionRequest* request, @@ -257,10 +286,15 @@ switch (prediction_source) { case PredictionSource::kOnDeviceAiv1AndServerSideModel: - InquireAiOnDeviceAndServerModelIfAvailable( + InquireOnDeviceAiv1AndServerModelIfAvailable( web_contents->GetPrimaryMainFrame(), std::move(features), request->request_type()); return; + case PredictionSource::kOnDeviceAiv3AndServerSideModel: + InquireOnDeviceAiv3AndServerModelIfAvailable( + web_contents->GetRenderWidgetHostView(), std::move(features), + request->request_type()); + return; case PredictionSource::kServerSideCpssV3Model: return InquireServerModel(features, request->request_type(), /*record_source=*/true); @@ -286,13 +320,13 @@ if (PredictionModelHandlerProvider* prediction_model_handler_provider = PredictionModelHandlerProviderFactory::GetForBrowserContext( profile_)) { - if (PermissionsAiv1Handler* aiv1_model_handler = + if (PermissionsAiv1Handler* aiv1_handler = prediction_model_handler_provider->GetPermissionsAiv1Handler()) { - VLOG(1) << "[PermissionsAIv1] Inquire model."; - aiv1_model_handler->InquireAiOnDeviceModel( + VLOG(1) << "[PermissionsAIv1] Inquire model"; + aiv1_handler->InquireAiOnDeviceModel( std::move(inner_text), request_type, base::BindRepeating(&PredictionBasedPermissionUiSelector:: - AiOnDeviceModelExecutionCallback, + OnDeviceAiv1ModelExecutionCallback, weak_ptr_factory_.GetWeakPtr(), std::move(features), request_type)); return; @@ -300,7 +334,37 @@ } VLOG(1) << "[PermissionsAIv1] On device AI model session unavailable"; } else { - VLOG(1) << "[PermissionsAIv1] The page's contnet too short or empty"; + VLOG(1) << "[PermissionsAIv1] The page's content is too short or empty"; + } + InquireServerModel(features, request_type, /*record_source=*/true); +} + +void PredictionBasedPermissionUiSelector::OnSnapshotTakenForOnDeviceModel( + PredictionRequestFeatures features, + permissions::RequestType request_type, + const SkBitmap& snapshot) { + VLOG(1) << "[PermissionsAIv3] On device AI prediction requested"; + if (snapshot.drawsNothing()) { + VLOG(1) << "[PermissionsAIv3] The page's snapshot is empty"; + } else { + if (PredictionModelHandlerProvider* prediction_model_handler_provider = + PredictionModelHandlerProviderFactory::GetForBrowserContext( + profile_)) { + if (PermissionsAiv3Handler* aiv3_handler = + prediction_model_handler_provider->GetPermissionsAiv3Handler( + request_type)) { + VLOG(1) << "[PermissionsAIv3] Inquire model"; + + aiv3_handler->ExecuteModel( + base::BindRepeating(&PredictionBasedPermissionUiSelector:: + OnDeviceAiv3ModelExecutionCallback, + weak_ptr_factory_.GetWeakPtr(), + std::move(features), request_type), + std::make_unique<SkBitmap>(snapshot)); + return; + } + } + VLOG(1) << "[PermissionsAIv3] On device AI model session unavailable"; } InquireServerModel(features, request_type, /*record_source=*/true); } @@ -321,7 +385,7 @@ return last_request_grant_likelihood_; } -std::optional<permissions::PermissionRequestRelevance> +std::optional<PermissionRequestRelevance> PredictionBasedPermissionUiSelector::PermissionRequestRelevanceForUKM() { return last_permission_request_relevance_; } @@ -351,11 +415,11 @@ features.experiment_id = 0; - if (base::FeatureList::IsEnabled(permissions::features::kPermissionsAIv1)) { + if (base::FeatureList::IsEnabled(permissions::features::kPermissionsAIv1) || + base::FeatureList::IsEnabled(permissions::features::kPermissionsAIv3)) { // Init `permission_relevance` here to avoid a crash during // `ConvertToProtoRelevance` execution. - features.permission_relevance = - permissions::PermissionRequestRelevance::kUnspecified; + features.permission_relevance = PermissionRequestRelevance::kUnspecified; } base::Time cutoff = base::Time::Now() - kPermissionActionCutoffAge; @@ -378,7 +442,7 @@ return features; } -void PredictionBasedPermissionUiSelector::AiOnDeviceModelExecutionCallback( +void PredictionBasedPermissionUiSelector::OnDeviceAiv1ModelExecutionCallback( PredictionRequestFeatures features, permissions::RequestType request_type, std::optional<PermissionsAiResponse> response) { @@ -387,8 +451,8 @@ if (response.has_value()) { last_permission_request_relevance_ = response.value().is_permission_relevant() - ? permissions::PermissionRequestRelevance::kVeryHigh - : permissions::PermissionRequestRelevance::kVeryLow; + ? PermissionRequestRelevance::kVeryHigh + : PermissionRequestRelevance::kVeryLow; VLOG(1) << "[PermissionsAIv1]: Permission request is " << (response.value().is_permission_relevant() ? "relevant" : "not relevant"); @@ -396,7 +460,7 @@ permissions::PermissionPredictionSource::ONDEVICE_AI_AND_SERVER_SIDE); } else { last_permission_request_relevance_ = - permissions::PermissionRequestRelevance::kUnspecified; + PermissionRequestRelevance::kUnspecified; } features.permission_relevance = last_permission_request_relevance_.value(); permissions::PermissionUmaUtil::RecordPermissionRequestRelevance( @@ -405,6 +469,33 @@ /*record_source=*/!response.has_value()); } +void PredictionBasedPermissionUiSelector::OnDeviceAiv3ModelExecutionCallback( + PredictionRequestFeatures features, + permissions::RequestType request_type, + const std::optional<PermissionRequestRelevance>& relevance) { + VLOG(1) << "[PermissionsAIv3]: AI model execution callback called " + << (relevance.has_value() ? "with value" : "without value"); + if (relevance.has_value()) { + VLOG(1) << "[PermissionsAIv3]: PermissionRequest has a relevance of " + << static_cast<int>(relevance.value()); + last_permission_request_relevance_ = relevance.value(); + features.permission_relevance = relevance.value(); + // TODO(crbug.com/382447738) refactor this function to also encode the model + // version + permissions::PermissionUmaUtil::RecordPermissionRequestRelevance( + features.permission_relevance); + } else { + last_permission_request_relevance_ = + PermissionRequestRelevance::kUnspecified; + } + + // We get Unspecified only if the model was not executed; so we call the + // server side model as if we never inquired the on-device model before. + InquireServerModel(features, request_type, + /*record_source=*/ + !(relevance == PermissionRequestRelevance::kUnspecified)); +} + void PredictionBasedPermissionUiSelector::LookupResponseReceived( base::TimeTicks model_inquire_start_time, bool is_on_device, @@ -429,6 +520,7 @@ last_request_grant_likelihood_ = response->prediction(0).grant_likelihood().discretized_likelihood(); + // TODO(crbug.com/382447738) No holdback for PermissionAIv3 if (ShouldHoldBack(is_on_device, request_type)) { VLOG(1) << "[CPSS] Prediction service decision held back"; was_decision_held_back_ = true; @@ -508,6 +600,12 @@ #endif // BUILDFLAG(IS_ANDROID) } if (use_server_side) { + // Aiv3 takes priority over Aiv1 if both are enabled. +#if BUILDFLAG(BUILD_WITH_TFLITE_LIB) + if (base::FeatureList::IsEnabled(permissions::features::kPermissionsAIv3)) { + return PredictionSource::kOnDeviceAiv3AndServerSideModel; + } +#endif // BUILDFLAG(BUILD_WITH_TFLITE_LIB) if (base::FeatureList::IsEnabled(permissions::features::kPermissionsAIv1)) { return PredictionSource::kOnDeviceAiv1AndServerSideModel; } @@ -530,3 +628,9 @@ return PredictionSource::kNoCpssModel; } + +void PredictionBasedPermissionUiSelector::set_snapshot_for_testing( + SkBitmap snapshot) { + CHECK_IS_TEST(); + snapshot_for_testing_ = snapshot; +}
diff --git a/chrome/browser/permissions/prediction_based_permission_ui_selector.h b/chrome/browser/permissions/prediction_based_permission_ui_selector.h index c448cd7..d573853 100644 --- a/chrome/browser/permissions/prediction_based_permission_ui_selector.h +++ b/chrome/browser/permissions/prediction_based_permission_ui_selector.h
@@ -22,6 +22,8 @@ #include "components/permissions/permission_ui_selector.h" #include "components/permissions/prediction_service/prediction_request_features.h" #include "components/permissions/request_type.h" +#include "components/unified_consent/pref_names.h" +#include "content/public/browser/render_widget_host_view.h" class PredictionServiceRequest; class Profile; @@ -41,6 +43,7 @@ kServerSideCpssV3Model, kOnDeviceCpssV1Model, kOnDeviceAiv1AndServerSideModel, + kOnDeviceAiv3AndServerSideModel, }; using PredictionGrantLikelihood = permissions::PermissionUmaUtil::PredictionGrantLikelihood; @@ -71,6 +74,11 @@ std::optional<bool> WasSelectorDecisionHeldback() override; + std::optional<permissions::PermissionRequestRelevance> + get_permission_request_relevance_for_testing(); + + void set_snapshot_for_testing(SkBitmap snapshot); + private: FRIEND_TEST_ALL_PREFIXES( PredictionBasedPermissionUiExpectedPredictionSourceTest, @@ -82,11 +90,20 @@ FRIEND_TEST_ALL_PREFIXES(PredictionBasedPermissionUiSelectorTest, HoldbackDecisionTest); - void AiOnDeviceModelExecutionCallback( + // Callback for the Aiv1ModelHandler, with the first to parameters being + // curryed to be used for the server side model call. + void OnDeviceAiv1ModelExecutionCallback( permissions::PredictionRequestFeatures features, permissions::RequestType request_type, std::optional<optimization_guide::proto::PermissionsAiResponse> response); + // Callback for the Aiv3ModelHandler, with the first to parameters being + // curryed to be used for the server side model call. + void OnDeviceAiv3ModelExecutionCallback( + permissions::PredictionRequestFeatures features, + permissions::RequestType request_type, + const std::optional<permissions::PermissionRequestRelevance>& relevance); + permissions::PredictionRequestFeatures BuildPredictionRequestFeatures( permissions::PermissionRequest* request); void LookupResponseReceived( @@ -103,25 +120,55 @@ likelihood_override_for_testing_ = mock_likelihood; } + // Part of the AIv1 model execution chain; provided as a curryed callback to + // be submitted to the logic that fetches the current page content text for + // the AIv1 model. The first two parameters are set by the callee, to be used + // by the server side model later. void OnGetInnerTextForOnDeviceModel( permissions::PredictionRequestFeatures features, permissions::RequestType request_type, std::unique_ptr<content_extraction::InnerTextResult> result); + // Part of the AIv3 model execution chain; provided as a curryed callback to + // be submitted to the logic that fetches a snapshot that serves as the input + // for the AIv3 model. The first two parameters are set by the callee, to be + // used by the server side model later. + void OnSnapshotTakenForOnDeviceModel( + permissions::PredictionRequestFeatures features, + permissions::RequestType request_type, + const SkBitmap& screenshot); + bool ShouldHoldBack(bool is_on_device, permissions::RequestType request_type); void InquireServerModel( const permissions::PredictionRequestFeatures& features, permissions::RequestType request_type, bool record_source); + void InquireCpssV1OnDeviceModelIfAvailable( const permissions::PredictionRequestFeatures& features, permissions::RequestType request_type); - void InquireAiOnDeviceAndServerModelIfAvailable( + + // As the first part of the AIv3 model execution chain, this function triggers + // AIv3 input collection and model execution, with its output being input of + // the follow-up CPSSv3 server side model execution. If the AIv3 model is not + // available or is executed with an error, only the server side model will get + // called. + void InquireOnDeviceAiv1AndServerModelIfAvailable( content::RenderFrameHost* rfh, permissions::PredictionRequestFeatures features, permissions::RequestType request_type); + // As the first part of the AIv3 model execution chain, this function triggers + // AIv3 input collection and model execution, with its output being input of + // the follow-up CPSSv3 server side model execution. If the AIv3 model is not + // available or is executed with an error, only the server side model will get + // called. + void InquireOnDeviceAiv3AndServerModelIfAvailable( + content::RenderWidgetHostView* host_view, + permissions::PredictionRequestFeatures features, + permissions::RequestType request_type); + raw_ptr<Profile> profile_; std::unique_ptr<PredictionServiceRequest> request_; std::optional<PredictionGrantLikelihood> last_request_grant_likelihood_; @@ -134,6 +181,8 @@ DecisionMadeCallback callback_; + std::optional<SkBitmap> snapshot_for_testing_; + // Used to asynchronously call the callback during on device model execution. base::WeakPtrFactory<PredictionBasedPermissionUiSelector> weak_ptr_factory_{ this};
diff --git a/chrome/browser/permissions/prediction_based_permission_ui_selector_unittest.cc b/chrome/browser/permissions/prediction_based_permission_ui_selector_unittest.cc index 93daf11..68eea03 100644 --- a/chrome/browser/permissions/prediction_based_permission_ui_selector_unittest.cc +++ b/chrome/browser/permissions/prediction_based_permission_ui_selector_unittest.cc
@@ -305,6 +305,19 @@ /*disabled_features=*/{}, /*expected_prediction_source=*/ PredictionSource::kOnDeviceAiv1AndServerSideModel}, + {/*test_name=*/"UsePermissionsAiv3OnDesktop", + /*enabled_features=*/ + {BASIC_CPSS_FEATURES, permissions::features::kPermissionsAIv3}, + /*disabled_features=*/{}, + /*expected_prediction_source=*/ + PredictionSource::kOnDeviceAiv3AndServerSideModel}, + {/*test_name=*/"UsePermissionsAiv3OverAiv1OnDesktop", + /*enabled_features=*/ + {BASIC_CPSS_FEATURES, permissions::features::kPermissionsAIv1, + permissions::features::kPermissionsAIv3}, + /*disabled_features=*/{}, + /*expected_prediction_source=*/ + PredictionSource::kOnDeviceAiv3AndServerSideModel}, #endif }), /*name_generator=*/
diff --git a/chrome/browser/permissions/prediction_model_handler_provider.cc b/chrome/browser/permissions/prediction_model_handler_provider.cc index 105e6320..91a22bd 100644 --- a/chrome/browser/permissions/prediction_model_handler_provider.cc +++ b/chrome/browser/permissions/prediction_model_handler_provider.cc
@@ -4,6 +4,7 @@ #include "chrome/browser/permissions/prediction_model_handler_provider.h" +#include "base/check_is_test.h" #include "base/notreached.h" #include "base/task/sequenced_task_runner.h" #include "base/task/task_traits.h" @@ -12,6 +13,7 @@ #include "chrome/browser/permissions/permissions_aiv1_handler.h" #include "components/optimization_guide/core/optimization_guide_model_provider.h" #include "components/permissions/features.h" +#include "components/permissions/prediction_service/permissions_aiv3_handler.h" #include "components/permissions/request_type.h" #if BUILDFLAG(BUILD_WITH_TFLITE_LIB) @@ -34,6 +36,21 @@ optimization_guide, optimization_guide::proto::OptimizationTarget:: OPTIMIZATION_TARGET_GEOLOCATION_PERMISSION_PREDICTIONS); + + if (base::FeatureList::IsEnabled(permissions::features::kPermissionsAIv3)) { + notification_aiv3_handler_ = std::make_unique<PermissionsAiv3Handler>( + optimization_guide, + optimization_guide::proto::OptimizationTarget:: + OPTIMIZATION_TARGET_NOTIFICATION_IMAGE_PERMISSION_RELEVANCE, + RequestType::kNotifications); + + geolocation_aiv3_handler_ = std::make_unique<PermissionsAiv3Handler>( + optimization_guide, + optimization_guide::proto::OptimizationTarget:: + OPTIMIZATION_TARGET_GEOLOCATION_IMAGE_PERMISSION_RELEVANCE, + RequestType::kGeolocation); + } + #endif // BUILDFLAG(BUILD_WITH_TFLITE_LIB) if (base::FeatureList::IsEnabled(permissions::features::kPermissionsAIv1)) { @@ -62,5 +79,34 @@ NOTREACHED(); } } + +PermissionsAiv3Handler* +PredictionModelHandlerProvider::GetPermissionsAiv3Handler( + RequestType request_type) { + switch (request_type) { + case RequestType::kNotifications: + return notification_aiv3_handler_.get(); + case RequestType::kGeolocation: + return geolocation_aiv3_handler_.get(); + default: + NOTREACHED(); + } +} + +void PredictionModelHandlerProvider::set_permissions_aiv3_handler_for_testing( + RequestType request_type, + std::unique_ptr<PermissionsAiv3Handler> aiv3_handler) { + CHECK_IS_TEST(); + switch (request_type) { + case RequestType::kNotifications: + notification_aiv3_handler_ = std::move(aiv3_handler); + break; + case RequestType::kGeolocation: + geolocation_aiv3_handler_ = std::move(aiv3_handler); + break; + default: + NOTREACHED(); + } +} #endif // BUILDFLAG(BUILD_WITH_TFLITE_LIB) } // namespace permissions
diff --git a/chrome/browser/permissions/prediction_model_handler_provider.h b/chrome/browser/permissions/prediction_model_handler_provider.h index 857469d..7422621 100644 --- a/chrome/browser/permissions/prediction_model_handler_provider.h +++ b/chrome/browser/permissions/prediction_model_handler_provider.h
@@ -18,6 +18,7 @@ class PredictionModelHandler; class PermissionsAiv1Handler; +class PermissionsAiv3Handler; class PredictionModelHandlerProvider : public KeyedService { public: @@ -33,6 +34,10 @@ #if BUILDFLAG(BUILD_WITH_TFLITE_LIB) PredictionModelHandler* GetPredictionModelHandler(RequestType request_type); + PermissionsAiv3Handler* GetPermissionsAiv3Handler(RequestType request_type); + void set_permissions_aiv3_handler_for_testing( + RequestType request_type, + std::unique_ptr<PermissionsAiv3Handler> handler); #endif // BUILDFLAG(BUILD_WITH_TFLITE_LIB) private: @@ -41,6 +46,8 @@ std::unique_ptr<PredictionModelHandler> notification_prediction_model_handler_; std::unique_ptr<PredictionModelHandler> geolocation_prediction_model_handler_; + std::unique_ptr<PermissionsAiv3Handler> notification_aiv3_handler_; + std::unique_ptr<PermissionsAiv3Handler> geolocation_aiv3_handler_; #endif // BUILDFLAG(BUILD_WITH_TFLITE_LIB) }; } // namespace permissions
diff --git a/chrome/browser/permissions/prediction_service_browsertest.cc b/chrome/browser/permissions/prediction_service_browsertest.cc index 7210d40b2..3d11fbc 100644 --- a/chrome/browser/permissions/prediction_service_browsertest.cc +++ b/chrome/browser/permissions/prediction_service_browsertest.cc
@@ -2,21 +2,29 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +#include "components/permissions/prediction_service/prediction_service.h" + +#include <vector> + #include "base/base_paths.h" #include "base/files/file.h" #include "base/files/file_path.h" #include "base/files/file_util.h" +#include "base/memory/raw_ptr.h" #include "base/path_service.h" #include "base/run_loop.h" #include "base/task/thread_pool/thread_pool_instance.h" #include "chrome/browser/optimization_guide/browser_test_util.h" #include "chrome/browser/optimization_guide/optimization_guide_keyed_service.h" #include "chrome/browser/optimization_guide/optimization_guide_keyed_service_factory.h" +#include "chrome/browser/permissions/prediction_based_permission_ui_selector.h" #include "chrome/browser/permissions/prediction_model_handler_provider.h" #include "chrome/browser/permissions/prediction_model_handler_provider_factory.h" +#include "chrome/browser/permissions/prediction_service_factory.h" #include "chrome/browser/profiles/profile.h" #include "chrome/browser/profiles/profile_key.h" #include "chrome/browser/ui/browser.h" +#include "chrome/common/chrome_features.h" #include "chrome/test/base/in_process_browser_test.h" #include "chrome/test/base/ui_test_utils.h" #include "components/content_settings/core/common/pref_names.h" @@ -25,12 +33,16 @@ #include "components/optimization_guide/core/optimization_guide_features.h" #include "components/optimization_guide/core/optimization_guide_test_util.h" #include "components/optimization_guide/core/test_model_info_builder.h" +#include "components/optimization_guide/core/test_optimization_guide_model_provider.h" #include "components/optimization_guide/proto/models.pb.h" #include "components/permissions/features.h" #include "components/permissions/permission_request_manager.h" #include "components/permissions/permission_uma_util.h" #include "components/permissions/permission_util.h" +#include "components/permissions/prediction_service/permissions_aiv3_handler.h" #include "components/permissions/prediction_service/prediction_model_handler.h" +#include "components/permissions/prediction_service/prediction_request_features.h" +#include "components/permissions/prediction_service/prediction_service_messages.pb.h" #include "components/permissions/request_type.h" #include "components/permissions/test/mock_permission_prompt_factory.h" #include "components/permissions/test/mock_permission_request.h" @@ -39,44 +51,137 @@ #include "content/public/test/browser_test_utils.h" #include "net/dns/mock_host_resolver.h" #include "net/test/embedded_test_server/embedded_test_server.h" +#include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" namespace permissions { namespace { + +using ::base::TimeTicks; +using ::base::test::FeatureRef; +using ::base::test::FeatureRefAndParams; +using ::optimization_guide::proto::OptimizationTarget; +using ::permissions::GeneratePredictionsResponse; +using ::permissions::PermissionRequestRelevance; +using ::permissions::PermissionsAiv3Handler; +using ::permissions::PredictionRequestFeatures; +using ::permissions::PredictionService; +using ::testing::_; +using ::testing::Eq; +using ::testing::Field; +using ::testing::Invoke; +using ::testing::WithArg; + +constexpr OptimizationTarget kCpssV1OptTargetNotification = + OptimizationTarget::OPTIMIZATION_TARGET_NOTIFICATION_PERMISSION_PREDICTIONS; + +constexpr OptimizationTarget kAiv3OptTargetNotification = OptimizationTarget:: + OPTIMIZATION_TARGET_NOTIFICATION_IMAGE_PERMISSION_RELEVANCE; + constexpr auto kLikelihoodUnspecified = PermissionUmaUtil::PredictionGrantLikelihood:: PermissionPrediction_Likelihood_DiscretizedLikelihood_DISCRETIZED_LIKELIHOOD_UNSPECIFIED; + +// This is the only server side reply that will tirgger quiet UI at the moment. constexpr auto kLikelihoodVeryUnlikely = PermissionUmaUtil::PredictionGrantLikelihood:: PermissionPrediction_Likelihood_DiscretizedLikelihood_VERY_UNLIKELY; -// The model returns a constant value of 0.5; its meaning is defined by the -// max_likely threshold we use in the signature_model_executor to differentiate -// between very unlikely and unspecified. -base::FilePath& ModelFilePath() { - static base::NoDestructor<base::FilePath> file_path([]() { - base::FilePath source_root_dir; - base::PathService::Get(base::DIR_SRC_TEST_DATA_ROOT, &source_root_dir); - return source_root_dir.AppendASCII("chrome") - .AppendASCII("test") - .AppendASCII("data") - .AppendASCII("permissions") - .AppendASCII("signature_model_ret_0.5.tflite"); - }()); - return *file_path; +// A CPSSv1 model that returns a constant value of 0.5; +// its meaning is defined by the max_likely threshold we use in the +// signature_model_executor to differentiate between +// 'very unlikely' and 'unspecified'. +constexpr std::string_view kZeroDotFiveReturnSignatureModel = + "signature_model_ret_0.5.tflite"; + +// An AIv3 model that returns a constant value of 0 which will be converted into +// a 'very unlikely' for notifications and geolocation permission request. +constexpr std::string_view kZeroReturnAiv3Model = "aiv3_ret_0.tflite"; + +// An AIv3 model that returns a constant value of 1 which will be converted into +// a 'very likely' for notifications and geolocation permission request. +constexpr std::string_view kOneReturnAiv3Model = "aiv3_ret_1.tflite"; + +// Non existing model file. +constexpr std::string_view kNotExistingModel = "does_not_exist.tflite"; + +base::FilePath ModelFilePath(std::string_view file_name) { + base::FilePath source_root_dir; + base::PathService::Get(base::DIR_SRC_TEST_DATA_ROOT, &source_root_dir); + return source_root_dir.AppendASCII("chrome") + .AppendASCII("test") + .AppendASCII("data") + .AppendASCII("permissions") + .AppendASCII(file_name); } + +class PredictionServiceMock : public PredictionService { + public: + PredictionServiceMock() : PredictionService(nullptr) {} + MOCK_METHOD(void, + StartLookup, + (const PredictionRequestFeatures& entity, + LookupRequestCallback request_callback, + LookupResponseCallback response_callback), + (override)); +}; + +class PermissionsAiv3HandlerFake : public PermissionsAiv3Handler { + public: + PermissionsAiv3HandlerFake( + optimization_guide::OptimizationGuideModelProvider* model_provider, + optimization_guide::proto::OptimizationTarget optimization_target, + RequestType request_type) + : PermissionsAiv3Handler(model_provider, + optimization_target, + request_type) {} + + void OnModelUpdated( + optimization_guide::proto::OptimizationTarget optimization_target, + base::optional_ref<const optimization_guide::ModelInfo> model_info) + override { + PermissionsAiv3Handler::OnModelUpdated(optimization_target, model_info); + if (model_info.has_value()) { + model_load_run_loop_for_testing_.Quit(); + } + } + + void ExecuteModelWrapper( + PermissionsAiv3Handler::ExecutionCallback callback, + const std::optional<PermissionsAiv3Encoder::ModelOutput>& output) { + std::move(callback).Run(output); + model_execute_run_loop_for_testing_.Quit(); + } + + void ExecuteModel( + PermissionsAiv3Handler::ExecutionCallback callback, + std::unique_ptr<PermissionsAiv3Encoder::ModelInput> snapshot) override { + PermissionsAiv3Handler::ExecuteModel( + base::BindOnce(&PermissionsAiv3HandlerFake::ExecuteModelWrapper, + weak_ptr_factory_.GetWeakPtr(), std::move(callback)), + std::move(snapshot)); + model_execute_run_loop_for_testing_.Run(); + } + + void WaitForModelLoadForTesting() { model_load_run_loop_for_testing_.Run(); } + + private: + base::RunLoop model_execute_run_loop_for_testing_ = + base::RunLoop(base::RunLoop::Type::kNestableTasksAllowed); + base::RunLoop model_load_run_loop_for_testing_; + base::WeakPtrFactory<PermissionsAiv3HandlerFake> weak_ptr_factory_{this}; +}; + } // namespace class PredictionServiceBrowserTestBase : public InProcessBrowserTest { public: - PredictionServiceBrowserTestBase() { - scoped_feature_list_.InitWithFeaturesAndParameters( - {{features::kPermissionOnDeviceNotificationPredictions, {}}, - {optimization_guide::features::kOptimizationHints, {}}, - {optimization_guide::features::kRemoteOptimizationGuideFetching, {}}, - {features::kCpssUseTfliteSignatureRunner, {}}}, - {permissions::features::kPermissionsAIv1}); + explicit PredictionServiceBrowserTestBase( + const std::vector<FeatureRefAndParams>& enabled_features = {}, + const std::vector<FeatureRef>& disabled_features = {}) { + scoped_feature_list_.InitWithFeaturesAndParameters(enabled_features, + disabled_features); } ~PredictionServiceBrowserTestBase() override = default; @@ -119,10 +224,20 @@ ->GetPredictionModelHandler(RequestType::kNotifications); } - void TriggerPromptAndVerifyUI( + PredictionBasedPermissionUiSelector* + prediction_based_permission_ui_selector() { + return static_cast<PredictionBasedPermissionUiSelector*>( + GetPermissionRequestManager() + ->get_permission_ui_selectors_for_testing() + .back() + .get()); + } + + void TriggerPromptAndVerifyUi( std::string test_url, PermissionAction permission_action, bool should_expect_quiet_ui, + std::optional<PermissionRequestRelevance> expected_relevance, std::optional<PermissionUmaUtil::PredictionGrantLikelihood> expected_prediction_likelihood) { auto* manager = GetPermissionRequestManager(); @@ -130,9 +245,12 @@ ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), url)); MockPermissionRequest req(RequestType::kNotifications); manager->AddRequest(GetActiveMainFrame(), &req); + bubble_factory()->WaitForPermissionBubble(); EXPECT_EQ(should_expect_quiet_ui, manager->ShouldCurrentRequestUseQuietUI()); + EXPECT_EQ(expected_relevance, + manager->permission_request_relevance_for_testing()); EXPECT_EQ(expected_prediction_likelihood, manager->prediction_grant_likelihood_for_testing()); if (permission_action == PermissionAction::DISMISSED) { @@ -142,14 +260,19 @@ } } + protected: + OptimizationGuideKeyedService* opt_guide() { + return OptimizationGuideKeyedServiceFactory::GetForProfile( + browser()->profile()); + } + private: std::unique_ptr<MockPermissionPromptFactory> mock_permission_prompt_factory_; base::test::ScopedFeatureList scoped_feature_list_; }; -using PredictionServiceBrowserTest = PredictionServiceBrowserTestBase; - -IN_PROC_BROWSER_TEST_F(PredictionServiceBrowserTest, PredictionServiceEnabled) { +IN_PROC_BROWSER_TEST_F(PredictionServiceBrowserTestBase, + PredictionServiceEnabled) { EXPECT_TRUE(prediction_model_handler()); } @@ -157,21 +280,63 @@ std::string test_name; float holdback_probability; // At the moment, we define everything that the signature model returns that - // is above that threshold as very unlikely, and everything below that will - // return unspecified. + // is above that threshold as very unlikely, and everything below that + // will return unspecified. float max_likely_threshold; bool should_expect_quiet_ui; std::optional<PermissionUmaUtil::PredictionGrantLikelihood> expected_prediction_likelihood; }; -class ParametrizedPredictionServiceBrowserTest +class SignatureModelPredictionServiceBrowserTest : public PredictionServiceBrowserTestBase, - public testing::WithParamInterface<HoldbackProbabilityTestCase> {}; + public testing::WithParamInterface<HoldbackProbabilityTestCase> { + public: + SignatureModelPredictionServiceBrowserTest() + : PredictionServiceBrowserTestBase(/*enabled_features=*/ + {{features:: + kPermissionOnDeviceNotificationPredictions, + {}}, + {optimization_guide::features:: + kOptimizationHints, + {}}, + {optimization_guide::features:: + kRemoteOptimizationGuideFetching, + {}}, + {features:: + kCpssUseTfliteSignatureRunner, + {}}}, + /*disabled_features=*/ + {permissions::features:: + kPermissionsAIv1, + permissions::features:: + kPermissionsAIv3}) {} + + void TriggerCpssV1AndVerifyUi( + PermissionAction permission_action, + bool should_expect_quiet_ui, + std::optional<PermissionRequestRelevance> expected_relevance, + std::optional<PermissionUmaUtil::PredictionGrantLikelihood> + expected_prediction_likelihood) { + // We need 4 prompts for the CPSS to kick in on the next prompt. + // This behaviour is defined by kRequestedPermissionMinimumHistoricalActions + std::string test_urls[] = {"a.test", "b.test", "c.test", "d.test"}; + for (std::string test_url : test_urls) { + TriggerPromptAndVerifyUi(test_url, PermissionAction::GRANTED, + /*should_expect_quiet_ui=*/false, + /*expected_relevance=*/std::nullopt, + /*expected_prediction_likelihood=*/std::nullopt); + } + TriggerPromptAndVerifyUi(/*test_url=*/"e.test", permission_action, + should_expect_quiet_ui, expected_relevance, + expected_prediction_likelihood); + EXPECT_EQ(5, bubble_factory()->show_count()); + } +}; INSTANTIATE_TEST_SUITE_P( HoldbackProbabilityTest, - ParametrizedPredictionServiceBrowserTest, + SignatureModelPredictionServiceBrowserTest, testing::ValuesIn<HoldbackProbabilityTestCase>({ { /*test_name=*/"TestUnspecifiedLikelihoodAndNoHoldback" @@ -208,11 +373,11 @@ }), /*name_generator=*/ [](const testing::TestParamInfo< - ParametrizedPredictionServiceBrowserTest::ParamType>& info) { + SignatureModelPredictionServiceBrowserTest::ParamType>& info) { return info.param.test_name; }); -IN_PROC_BROWSER_TEST_P(ParametrizedPredictionServiceBrowserTest, +IN_PROC_BROWSER_TEST_P(SignatureModelPredictionServiceBrowserTest, CheckHoldbackProbabilitiesForDifferentSignatureModels) { ASSERT_TRUE(prediction_model_handler()); @@ -229,29 +394,211 @@ "type.googleapis.com/" "optimization_guide.protos.WebPermissionPredictionsModelMetadata"); - OptimizationGuideKeyedServiceFactory::GetForProfile(browser()->profile()) - ->OverrideTargetModelForTesting( - optimization_guide::proto:: - OPTIMIZATION_TARGET_NOTIFICATION_PERMISSION_PREDICTIONS, - optimization_guide::TestModelInfoBuilder() - .SetModelFilePath(ModelFilePath()) - .SetModelMetadata(any) - .Build()); + opt_guide()->OverrideTargetModelForTesting( + kCpssV1OptTargetNotification, + optimization_guide::TestModelInfoBuilder() + .SetModelFilePath(ModelFilePath(kZeroDotFiveReturnSignatureModel)) + .SetModelMetadata(any) + .Build()); prediction_model_handler()->WaitForModelLoadForTesting(); ASSERT_TRUE(embedded_test_server()->Start()); - // We need 4 prompts for the cpss to kick in on the next prompt. - std::string test_urls[] = {"a.test", "b.test", "c.test", "d.test"}; - for (std::string test_url : test_urls) { - TriggerPromptAndVerifyUI(test_url, PermissionAction::GRANTED, - /*should_expect_quiet_ui=*/false, std::nullopt); - } - TriggerPromptAndVerifyUI("e.test", PermissionAction::DISMISSED, + TriggerCpssV1AndVerifyUi(PermissionAction::DISMISSED, GetParam().should_expect_quiet_ui, + /*expected_relevance=*/std::nullopt, GetParam().expected_prediction_likelihood); - EXPECT_EQ(5, bubble_factory()->show_count()); +} + +struct Aiv3ModelTestCase { + std::string test_name; + std::string_view model_name; + // This is defined by the output of the AIv3 model (and the defined + // thresholds). It will be used as input to the server-side model + PermissionRequestRelevance expected_relevance; + // This is the output of the server-side model (that we mock for this + // test). + // It should define the decision shared with the permission request + // manager. + PermissionUmaUtil::PredictionGrantLikelihood prediction_service_likelihood; + bool should_expect_quiet_ui; +}; + +class Aiv3ModelPredictionServiceBrowserTest + : public PredictionServiceBrowserTestBase, + public testing::WithParamInterface<Aiv3ModelTestCase> { + public: + Aiv3ModelPredictionServiceBrowserTest() + : PredictionServiceBrowserTestBase(/*enabled_features=*/ + { + {permissions::features:: + kPermissionPredictionsV2, + {{permissions::feature_params:: + kPermissionPredictionsV2HoldbackChance + .name, + "0"}}}, + {permissions::features:: + kPermissionOnDeviceNotificationPredictions, + {}}, + {permissions::features:: + kPermissionOnDeviceGeolocationPredictions, + {}}, + {::features:: + kQuietNotificationPrompts, + {}}, + {permissions::features:: + kPermissionsAIv3, + {}}, + }, + /*disabled_features=*/{}) { + PredictionServiceFactory::GetInstance()->set_prediction_service_for_testing( + &prediction_service_); + } + + void SetUpOnMainThread() override { + PredictionServiceBrowserTestBase::SetUpOnMainThread(); + + browser()->profile()->GetPrefs()->SetBoolean( + unified_consent::prefs::kUrlKeyedAnonymizedDataCollectionEnabled, true); + + // Only one model_handler can be registered for the same optimization target + // at the same time. Registering happens in the constructor, deregistering + // in the destructor of each ModelHandler. We can either deregister + // explicitly in the opt_guide service or just destroy the object. Either + // way, we need to do this before we create our fake handler. + model_handler_provider()->set_permissions_aiv3_handler_for_testing( + RequestType::kNotifications, nullptr); + + std::unique_ptr<PermissionsAiv3HandlerFake> notification_model_handler = + std::make_unique<PermissionsAiv3HandlerFake>( + opt_guide(), + /*optimization_target=*/kAiv3OptTargetNotification, + /*request_type=*/RequestType::kNotifications); + notification_model_handler_ = notification_model_handler.get(); + + model_handler_provider()->set_permissions_aiv3_handler_for_testing( + RequestType::kNotifications, std::move(notification_model_handler)); + } + + void TearDownOnMainThread() override { + PredictionServiceBrowserTestBase::TearDownOnMainThread(); + notification_model_handler_ = nullptr; + } + + void PushModelFileToModelExecutor(const base::FilePath& model_file_path) { + opt_guide()->OverrideTargetModelForTesting( + kAiv3OptTargetNotification, optimization_guide::TestModelInfoBuilder() + .SetModelFilePath(model_file_path) + .Build()); + notification_model_handler_->WaitForModelLoadForTesting(); + } + + PermissionsAiv3Handler* aiv3_model_handler() { + return PredictionModelHandlerProviderFactory::GetForBrowserContext( + browser()->profile()) + ->GetPermissionsAiv3Handler(RequestType::kNotifications); + } + + PredictionServiceMock& prediction_service() { return prediction_service_; } + + private: + PredictionModelHandlerProvider* model_handler_provider() { + return PredictionModelHandlerProviderFactory::GetForBrowserContext( + browser()->profile()); + } + + raw_ptr<PermissionsAiv3HandlerFake> notification_model_handler_; + PredictionServiceMock prediction_service_; +}; + +INSTANTIATE_TEST_SUITE_P( + Aiv3ModelTest, + Aiv3ModelPredictionServiceBrowserTest, + testing::ValuesIn<Aiv3ModelTestCase>({ + { + /*test_name=*/"OnDeviceVeryLowAndServerSideUnspecifiedResponse" + "ReturnsDefaultUI", + /*test_name=*/kZeroReturnAiv3Model, + /*expected_relevance=*/PermissionRequestRelevance::kVeryLow, + /*prediction_service_likelihood=*/kLikelihoodUnspecified, + /*should_expect_quiet_ui=*/false, + }, + { + /*test_name=*/"OnDeviceVeryLowAndServerSideVeryUnlikelyResponse" + "ReturnsQuietUI", + /*test_name=*/kZeroReturnAiv3Model, + /*expected_relevance=*/PermissionRequestRelevance::kVeryLow, + /*prediction_service_likelihood=*/kLikelihoodVeryUnlikely, + /*should_expect_quiet_ui=*/true, + }, + { + /*test_name=*/"OnDeviceVeryHighAndServerSideUnspecifiedResponse" + "ReturnsDefaultUI", + /*test_name=*/kOneReturnAiv3Model, + /*expected_relevance=*/PermissionRequestRelevance::kVeryHigh, + /*prediction_service_likelihood=*/kLikelihoodUnspecified, + /*should_expect_quiet_ui=*/false, + }, + { + /*test_name=*/"OnDeviceVeryHighAndServerSideVeryUnlikelyResponse" + "ReturnsQuietUI", + /*test_name=*/kOneReturnAiv3Model, + /*expected_relevance=*/PermissionRequestRelevance::kVeryHigh, + /*prediction_service_likelihood=*/kLikelihoodVeryUnlikely, + /*should_expect_quiet_ui=*/true, + }, + { + /*test_name=*/"FailingAiv3ModelStillResultsInValid" + "ServerSideExecution", + /*test_name=*/kNotExistingModel, + /*expected_relevance=*/PermissionRequestRelevance::kUnspecified, + /*prediction_service_likelihood=*/kLikelihoodVeryUnlikely, + /*should_expect_quiet_ui=*/true, + }, + }), + /*name_generator=*/ + [](const testing::TestParamInfo< + Aiv3ModelPredictionServiceBrowserTest::ParamType>& info) { + return info.param.test_name; + }); + +IN_PROC_BROWSER_TEST_P(Aiv3ModelPredictionServiceBrowserTest, + TestAiv3Workflow) { + ASSERT_TRUE(aiv3_model_handler()); + PushModelFileToModelExecutor(ModelFilePath(GetParam().model_name)); + ASSERT_TRUE(embedded_test_server()->Start()); + + SkBitmap bitmap; + bitmap.allocN32Pixels(64, 64); + bitmap.eraseColor(SkColorSetRGB(0x1E, 0x1C, 0x0F)); + prediction_based_permission_ui_selector()->set_snapshot_for_testing(bitmap); + + GeneratePredictionsResponse prediction_service_response; + prediction_service_response.mutable_prediction() + ->Add() + ->mutable_grant_likelihood() + ->set_discretized_likelihood(GetParam().prediction_service_likelihood); + + // The server side model fetches a lot of history actions, but we are only + // interested in the permission relevance field, that is populated by the + // on-device model. + auto expected_relevance = + Field(&PredictionRequestFeatures::permission_relevance, + Eq(GetParam().expected_relevance)); + EXPECT_CALL(prediction_service(), StartLookup(expected_relevance, _, _)) + .WillRepeatedly(WithArg<2>(Invoke( + [&](PredictionService::LookupResponseCallback response_callback) { + std::move(response_callback) + .Run(/*lookup_successful=*/true, + /*response_from_cache=*/true, prediction_service_response); + }))); + TriggerPromptAndVerifyUi( + /*test_url=*/"test.a", PermissionAction::DISMISSED, + GetParam().should_expect_quiet_ui, GetParam().expected_relevance, + GetParam().prediction_service_likelihood); + + EXPECT_EQ(1, bubble_factory()->show_count()); } } // namespace permissions
diff --git a/chrome/browser/permissions/prediction_service_factory.cc b/chrome/browser/permissions/prediction_service_factory.cc index 14a93fe..5d714d2 100644 --- a/chrome/browser/permissions/prediction_service_factory.cc +++ b/chrome/browser/permissions/prediction_service_factory.cc
@@ -4,16 +4,24 @@ #include "chrome/browser/permissions/prediction_service_factory.h" +#include "base/check_is_test.h" #include "base/no_destructor.h" #include "chrome/browser/browser_process.h" #include "chrome/browser/profiles/profile.h" #include "components/permissions/prediction_service/prediction_service.h" #include "services/network/public/cpp/cross_thread_pending_shared_url_loader_factory.h" +namespace { +using ::permissions::PredictionService; +} + // static permissions::PredictionService* PredictionServiceFactory::GetForProfile( Profile* profile) { - return static_cast<permissions::PredictionService*>( + if (GetInstance()->prediction_service_for_testing_.has_value()) { + return GetInstance()->prediction_service_for_testing_.value(); + } + return static_cast<PredictionService*>( GetInstance()->GetServiceForBrowserContext(profile, true)); } @@ -23,6 +31,12 @@ return instance.get(); } +void PredictionServiceFactory::set_prediction_service_for_testing( + permissions::PredictionService* service) { + CHECK_IS_TEST(); + prediction_service_for_testing_ = service; +} + PredictionServiceFactory::PredictionServiceFactory() : ProfileKeyedServiceFactory( "PredictionService",
diff --git a/chrome/browser/permissions/prediction_service_factory.h b/chrome/browser/permissions/prediction_service_factory.h index a862a27..59ca8c87 100644 --- a/chrome/browser/permissions/prediction_service_factory.h +++ b/chrome/browser/permissions/prediction_service_factory.h
@@ -25,6 +25,9 @@ PredictionServiceFactory(const PredictionServiceFactory&) = delete; PredictionServiceFactory& operator=(const PredictionServiceFactory&) = delete; + void set_prediction_service_for_testing( + permissions::PredictionService* service); + private: friend base::NoDestructor<PredictionServiceFactory>; @@ -34,6 +37,9 @@ // BrowserContextKeyedServiceFactory std::unique_ptr<KeyedService> BuildServiceInstanceForBrowserContext( content::BrowserContext* context) const override; + + std::optional<permissions::PredictionService*> + prediction_service_for_testing_; }; #endif // CHROME_BROWSER_PERMISSIONS_PREDICTION_SERVICE_FACTORY_H_
diff --git a/chrome/browser/prefs/BUILD.gn b/chrome/browser/prefs/BUILD.gn index 4b2f1238..fa5fc43 100644 --- a/chrome/browser/prefs/BUILD.gn +++ b/chrome/browser/prefs/BUILD.gn
@@ -279,6 +279,7 @@ "//chrome/browser/search_engine_choice", "//chrome/browser/search_engines", "//chrome/browser/ui/commerce", + "//chrome/browser/ui/send_tab_to_self", "//chrome/browser/ui/tabs", "//chrome/browser/ui/tabs:tab_strip", "//chrome/browser/ui/views/side_panel",
diff --git a/chrome/browser/privacy_sandbox/android/BUILD.gn b/chrome/browser/privacy_sandbox/android/BUILD.gn index db8a4c39..714ab48 100644 --- a/chrome/browser/privacy_sandbox/android/BUILD.gn +++ b/chrome/browser/privacy_sandbox/android/BUILD.gn
@@ -6,7 +6,10 @@ import("//third_party/jni_zero/jni_zero.gni") generate_jni("jni_headers") { - sources = [ "java/src/org/chromium/chrome/browser/privacy_sandbox/PrivacySandboxBridge.java" ] + sources = [ + "java/src/org/chromium/chrome/browser/privacy_sandbox/PrivacySandboxBridge.java", + "java/src/org/chromium/chrome/browser/privacy_sandbox/TrackingProtectionSettingsBridge.java", + ] } android_library("java") { @@ -41,6 +44,7 @@ "java/src/org/chromium/chrome/browser/privacy_sandbox/TopicsHeaderPreference.java", "java/src/org/chromium/chrome/browser/privacy_sandbox/TopicsManageFragment.java", "java/src/org/chromium/chrome/browser/privacy_sandbox/TopicsUtils.java", + "java/src/org/chromium/chrome/browser/privacy_sandbox/TrackingProtectionSettingsBridge.java", ] deps = [ ":java_resources", @@ -107,6 +111,7 @@ "javatests/src/org/chromium/chrome/browser/privacy_sandbox/PrivacySandboxSettingsFragmentTest.java", "javatests/src/org/chromium/chrome/browser/privacy_sandbox/TopicsFragmentTest.java", "javatests/src/org/chromium/chrome/browser/privacy_sandbox/TopicsManageFragmentTest.java", + "javatests/src/org/chromium/chrome/browser/privacy_sandbox/TrackingProtectionSettingsBridgeTest.java", ] deps = [ ":java",
diff --git a/chrome/browser/privacy_sandbox/android/java/src/org/chromium/chrome/browser/privacy_sandbox/TrackingProtectionSettingsBridge.java b/chrome/browser/privacy_sandbox/android/java/src/org/chromium/chrome/browser/privacy_sandbox/TrackingProtectionSettingsBridge.java new file mode 100644 index 0000000..73dd104 --- /dev/null +++ b/chrome/browser/privacy_sandbox/android/java/src/org/chromium/chrome/browser/privacy_sandbox/TrackingProtectionSettingsBridge.java
@@ -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. + +package org.chromium.chrome.browser.privacy_sandbox; + +import org.jni_zero.NativeMethods; + +import org.chromium.build.annotations.NullMarked; +import org.chromium.chrome.browser.profiles.Profile; + +/** Bridge, providing access to the native-side TrackingProtectionSettings API. */ +@NullMarked +public class TrackingProtectionSettingsBridge { + private final Profile mProfile; + + public TrackingProtectionSettingsBridge(Profile profile) { + mProfile = profile; + } + + public boolean isIpProtectionDisabledForEnterprise() { + return TrackingProtectionSettingsBridgeJni.get() + .isIpProtectionDisabledForEnterprise(mProfile); + } + + @NativeMethods + public interface Natives { + boolean isIpProtectionDisabledForEnterprise(Profile profile); + } +}
diff --git a/chrome/browser/privacy_sandbox/android/javatests/src/org/chromium/chrome/browser/privacy_sandbox/TrackingProtectionSettingsBridgeTest.java b/chrome/browser/privacy_sandbox/android/javatests/src/org/chromium/chrome/browser/privacy_sandbox/TrackingProtectionSettingsBridgeTest.java new file mode 100644 index 0000000..abaaf80 --- /dev/null +++ b/chrome/browser/privacy_sandbox/android/javatests/src/org/chromium/chrome/browser/privacy_sandbox/TrackingProtectionSettingsBridgeTest.java
@@ -0,0 +1,49 @@ +// 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.privacy_sandbox; + +import static org.junit.Assert.assertFalse; + +import androidx.test.filters.SmallTest; + +import org.junit.Before; +import org.junit.ClassRule; +import org.junit.Test; +import org.junit.runner.RunWith; + +import org.chromium.base.ThreadUtils; +import org.chromium.base.test.util.Batch; +import org.chromium.chrome.browser.profiles.ProfileManager; +import org.chromium.chrome.test.ChromeBrowserTestRule; +import org.chromium.chrome.test.ChromeJUnit4ClassRunner; + +/** Tests for TrackingProtectionSettingsBridge. */ +@RunWith(ChromeJUnit4ClassRunner.class) +@Batch(Batch.PER_CLASS) +public class TrackingProtectionSettingsBridgeTest { + @ClassRule + public static final ChromeBrowserTestRule sBrowserTestRule = new ChromeBrowserTestRule(); + + private TrackingProtectionSettingsBridge mTrackingProtectionSettingsBridge; + + @Before + public void setUp() { + mTrackingProtectionSettingsBridge = + ThreadUtils.runOnUiThreadBlocking( + () -> + new TrackingProtectionSettingsBridge( + ProfileManager.getLastUsedRegularProfile())); + } + + @Test + @SmallTest + public void isIpProtectionDisabledForEnterpriseIsPresent() { + ThreadUtils.runOnUiThreadBlocking( + () -> + assertFalse( + mTrackingProtectionSettingsBridge + .isIpProtectionDisabledForEnterprise())); + } +}
diff --git a/chrome/browser/privacy_sandbox/android/tracking_protection_settings_bridge.cc b/chrome/browser/privacy_sandbox/android/tracking_protection_settings_bridge.cc new file mode 100644 index 0000000..5c7ee02bd0 --- /dev/null +++ b/chrome/browser/privacy_sandbox/android/tracking_protection_settings_bridge.cc
@@ -0,0 +1,29 @@ +// Copyright 2025 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/android/scoped_java_ref.h" +#include "chrome/browser/privacy_sandbox/tracking_protection_settings_factory.h" +#include "chrome/browser/profiles/profile.h" +#include "components/privacy_sandbox/tracking_protection_settings.h" + +// Must come after all headers that specialize FromJniType() / ToJniType(). +#include "chrome/browser/privacy_sandbox/android/jni_headers/TrackingProtectionSettingsBridge_jni.h" + +using base::android::JavaParamRef; + +namespace { +privacy_sandbox::TrackingProtectionSettings* GetTrackingProtectionSettings( + const base::android::JavaRef<jobject>& j_profile) { + return TrackingProtectionSettingsFactory::GetForProfile( + Profile::FromJavaObject(j_profile)); +} +} // namespace + +jboolean +JNI_TrackingProtectionSettingsBridge_IsIpProtectionDisabledForEnterprise( + JNIEnv* env, + const JavaParamRef<jobject>& j_profile) { + return GetTrackingProtectionSettings(j_profile) + ->IsIpProtectionDisabledForEnterprise(); +}
diff --git a/chrome/browser/readaloud/android/BUILD.gn b/chrome/browser/readaloud/android/BUILD.gn index 9040bb8..c0876b5e 100644 --- a/chrome/browser/readaloud/android/BUILD.gn +++ b/chrome/browser/readaloud/android/BUILD.gn
@@ -230,6 +230,7 @@ "//chrome/browser/commerce/price_tracking/android:java", "//chrome/browser/device:java", "//chrome/browser/device:junit_test_support", + "//chrome/browser/feature_engagement:java", "//chrome/browser/flags:java", "//chrome/browser/fullscreen/android:java", "//chrome/browser/language/android:base_module_java",
diff --git a/chrome/browser/readaloud/android/java/src/org/chromium/chrome/browser/readaloud/ReadAloudController.java b/chrome/browser/readaloud/android/java/src/org/chromium/chrome/browser/readaloud/ReadAloudController.java index 003e456..9bc3da3 100644 --- a/chrome/browser/readaloud/android/java/src/org/chromium/chrome/browser/readaloud/ReadAloudController.java +++ b/chrome/browser/readaloud/android/java/src/org/chromium/chrome/browser/readaloud/ReadAloudController.java
@@ -45,6 +45,7 @@ import org.chromium.build.annotations.Nullable; import org.chromium.chrome.browser.browser_controls.BottomControlsStacker; import org.chromium.chrome.browser.device.DeviceConditions; +import org.chromium.chrome.browser.feature_engagement.TrackerFactory; import org.chromium.chrome.browser.fullscreen.FullscreenManager; import org.chromium.chrome.browser.fullscreen.FullscreenOptions; import org.chromium.chrome.browser.layouts.LayoutManager; @@ -1518,6 +1519,7 @@ @Override public void setPlaybackModeAndApplyToPlayback(PlaybackMode mode) { + TrackerFactory.getTrackerForProfile(getProfile()).notifyEvent("read_aloud_playback_mode_clicked"); ReadAloudPrefs.setPlaybackMode(getPrefService(), mode); if (mActivePlaybackTabSupplier.get() != null && mPlayback != null) {
diff --git a/chrome/browser/readaloud/android/java/src/org/chromium/chrome/browser/readaloud/ReadAloudControllerUnitTest.java b/chrome/browser/readaloud/android/java/src/org/chromium/chrome/browser/readaloud/ReadAloudControllerUnitTest.java index 95f3388..99ec12f 100644 --- a/chrome/browser/readaloud/android/java/src/org/chromium/chrome/browser/readaloud/ReadAloudControllerUnitTest.java +++ b/chrome/browser/readaloud/android/java/src/org/chromium/chrome/browser/readaloud/ReadAloudControllerUnitTest.java
@@ -65,6 +65,8 @@ import org.chromium.chrome.browser.browser_controls.BottomControlsStacker; import org.chromium.chrome.browser.device.DeviceConditions; import org.chromium.chrome.browser.device.ShadowDeviceConditions; +import org.chromium.components.feature_engagement.Tracker; +import org.chromium.chrome.browser.feature_engagement.TrackerFactory; import org.chromium.chrome.browser.flags.ChromeFeatureList; import org.chromium.chrome.browser.fullscreen.FullscreenManager; import org.chromium.chrome.browser.fullscreen.FullscreenOptions; @@ -190,6 +192,7 @@ @Mock private NativePage mNativePage; @Mock private LayoutStateProvider mLayoutStateProvider; @Mock private FullscreenManager mFullscreenManager; + @Mock private Tracker mTracker; private GlobalRenderFrameHostId mGlobalRenderFrameHostId = new GlobalRenderFrameHostId(1, 1); public UserActionTester mUserActionTester; private HistogramWatcher mHighlightingEnabledOnStartupHistogram; @@ -219,6 +222,7 @@ @Before public void setUp() { + TrackerFactory.setTrackerForTests(mTracker); mDefaultLocale = Locale.getDefault(); mLayoutStateProviderSupplier.set(mLayoutStateProvider); @@ -1736,6 +1740,8 @@ // Set mode and restart. mController.setPlaybackModeAndApplyToPlayback(PlaybackMode.OVERVIEW); + verify(mTracker).notifyEvent(eq("read_aloud_playback_mode_clicked")); + // Pref is updated. verify(mPrefService) .setInteger(eq("readaloud.playback_mode"), eq(PlaybackMode.OVERVIEW.getValue()));
diff --git a/chrome/browser/renderer_context_menu/link_to_text_menu_observer_interactive_uitest.cc b/chrome/browser/renderer_context_menu/link_to_text_menu_observer_interactive_uitest.cc index 828380c..22d8dc1e 100644 --- a/chrome/browser/renderer_context_menu/link_to_text_menu_observer_interactive_uitest.cc +++ b/chrome/browser/renderer_context_menu/link_to_text_menu_observer_interactive_uitest.cc
@@ -89,9 +89,7 @@ public: LinkToTextMenuObserverTest() { scoped_features_.InitWithFeatures( - {toast_features::kLinkToHighlightCopiedToast, - toast_features::kToastFramework}, - {}); + {toast_features::kLinkToHighlightCopiedToast}, {}); } void SetUpOnMainThread() override {
diff --git a/chrome/browser/renderer_context_menu/render_view_context_menu_browsertest.cc b/chrome/browser/renderer_context_menu/render_view_context_menu_browsertest.cc index 4241131e..d19b088e 100644 --- a/chrome/browser/renderer_context_menu/render_view_context_menu_browsertest.cc +++ b/chrome/browser/renderer_context_menu/render_view_context_menu_browsertest.cc
@@ -214,7 +214,6 @@ {media::kContextMenuSaveVideoFrameAs, media::kContextMenuSearchForVideoFrame, toast_features::kLinkCopiedToast, toast_features::kImageCopiedToast, - toast_features::kToastFramework, toast_features::kVideoFrameCopiedToast}, {}); }
diff --git a/chrome/browser/resources/BUILD.gn b/chrome/browser/resources/BUILD.gn index ecc7094..7bf89390 100644 --- a/chrome/browser/resources/BUILD.gn +++ b/chrome/browser/resources/BUILD.gn
@@ -46,6 +46,7 @@ "new_tab_page/untrusted:resources", "new_tab_page_instant:resources", "new_tab_page_third_party:resources", + "new_tab_shared:resources", "omnibox_popup:resources", "password_manager:resources", "privacy_sandbox:resources",
diff --git a/chrome/browser/resources/bookmarks/bookmark_manager_api_proxy.ts b/chrome/browser/resources/bookmarks/bookmark_manager_api_proxy.ts index 8b61f59..9ed38991 100644 --- a/chrome/browser/resources/bookmarks/bookmark_manager_api_proxy.ts +++ b/chrome/browser/resources/bookmarks/bookmark_manager_api_proxy.ts
@@ -3,6 +3,11 @@ // found in the LICENSE file. import type {ChromeEvent} from '/tools/typescript/definitions/chrome_event.js'; +export interface OpenInNewTabParams { + active?: boolean; + split?: boolean; +} + export interface BookmarkManagerApiProxy { onDragEnter: ChromeEvent<(p1: chrome.bookmarkManagerPrivate.DragData) => void>; @@ -14,7 +19,7 @@ removeTrees(idList: string[]): Promise<void>; canPaste(parentId: string): Promise<boolean>; openInNewWindow(idList: string[], incognito: boolean): void; - openInNewTab(id: string, active: boolean): void; + openInNewTab(id: string, params?: OpenInNewTabParams): void; openInNewTabGroup(idList: string[]): void; cut(idList: string[]): Promise<void>; paste(parentId: string, selectedIdList?: string[]): Promise<void>; @@ -44,11 +49,12 @@ } openInNewWindow(idList: string[], incognito: boolean) { - return chrome.bookmarkManagerPrivate.openInNewWindow(idList, incognito); + chrome.bookmarkManagerPrivate.openInNewWindow(idList, incognito); } - openInNewTab(id: string, active: boolean) { - return chrome.bookmarkManagerPrivate.openInNewTab(id, active); + openInNewTab(id: string, params: OpenInNewTabParams) { + chrome.bookmarkManagerPrivate.openInNewTab( + id, {active: params.active, split: params.split}); } openInNewTabGroup(idList: string[]) {
diff --git a/chrome/browser/resources/bookmarks/bookmarks.ts b/chrome/browser/resources/bookmarks/bookmarks.ts index 87e30696..0f98b33 100644 --- a/chrome/browser/resources/bookmarks/bookmarks.ts +++ b/chrome/browser/resources/bookmarks/bookmarks.ts
@@ -8,7 +8,7 @@ export {changeFolderOpen, clearSearch, createBookmark, deselectItems, editBookmark, moveBookmark, removeBookmark, reorderChildren, selectFolder, SelectFolderAction, selectItem, SelectItemsAction, setSearchResults, setSearchTerm, StartSearchAction, updateAnchor} from './actions.js'; export {setDebouncerForTesting} from './api_listener.js'; export {BookmarksAppElement, HIDE_FOCUS_RING_ATTRIBUTE} from './app.js'; -export {BookmarkManagerApiProxy, BookmarkManagerApiProxyImpl} from './bookmark_manager_api_proxy.js'; +export {BookmarkManagerApiProxy, BookmarkManagerApiProxyImpl, OpenInNewTabParams} from './bookmark_manager_api_proxy.js'; export {BookmarksApiProxy, BookmarksApiProxyImpl, Query} from './bookmarks_api_proxy.js'; export {BrowserProxy, BrowserProxyImpl} from './browser_proxy.js'; export {BookmarksCommandManagerElement} from './command_manager.js';
diff --git a/chrome/browser/resources/bookmarks/command_manager.ts b/chrome/browser/resources/bookmarks/command_manager.ts index 7f5e9b3..72fc989 100644 --- a/chrome/browser/resources/bookmarks/command_manager.ts +++ b/chrome/browser/resources/bookmarks/command_manager.ts
@@ -257,6 +257,7 @@ case Command.OPEN_NEW_GROUP: case Command.OPEN_NEW_TAB: case Command.OPEN_NEW_WINDOW: + case Command.OPEN_SPLIT_VIEW: return itemIds.size > 0; case Command.ADD_BOOKMARK: case Command.ADD_FOLDER: @@ -286,6 +287,8 @@ return this.expandIds_(itemIds).length > 0 && state.prefs.incognitoAvailability !== IncognitoAvailability.DISABLED; + case Command.OPEN_SPLIT_VIEW: + return this.expandIds_(itemIds).length === 1; case Command.SORT: return this.canChangeList_() && state.nodes[state.selectedFolder]!.children!.length > 1; @@ -400,6 +403,7 @@ case Command.OPEN_NEW_GROUP: case Command.OPEN_NEW_TAB: case Command.OPEN_NEW_WINDOW: + case Command.OPEN_SPLIT_VIEW: this.openBookmarkIds_(this.expandIds_(itemIds), command); break; case Command.OPEN: @@ -520,27 +524,35 @@ command === Command.OPEN || command === Command.OPEN_NEW_TAB || command === Command.OPEN_NEW_WINDOW || command === Command.OPEN_INCOGNITO || + command === Command.OPEN_SPLIT_VIEW || command === Command.OPEN_NEW_GROUP); if (ids.length === 0) { return; } + if (command === Command.OPEN_SPLIT_VIEW) { + assert(ids.length === 1); + } + const openBookmarkIdsCallback = function() { const incognito = command === Command.OPEN_INCOGNITO; if (command === Command.OPEN_NEW_WINDOW || incognito) { BookmarkManagerApiProxyImpl.getInstance().openInNewWindow( ids, incognito); + } else if (command === Command.OPEN_SPLIT_VIEW) { + BookmarkManagerApiProxyImpl.getInstance().openInNewTab( + ids.shift()!, {active: false, split: true}); } else if (command === Command.OPEN_NEW_GROUP) { BookmarkManagerApiProxyImpl.getInstance().openInNewTabGroup(ids); } else { if (command === Command.OPEN) { BookmarkManagerApiProxyImpl.getInstance().openInNewTab( - ids.shift()!, /*active=*/ true); + ids.shift()!, {active: true, split: false}); } ids.forEach(function(id) { BookmarkManagerApiProxyImpl.getInstance().openInNewTab( - id, /*active=*/ false); + id, {active: false, split: false}); }); } }; @@ -652,6 +664,9 @@ case Command.HELP_CENTER: label = 'menuHelpCenter'; break; + case Command.OPEN_SPLIT_VIEW: + label = 'menuOpenSplitView'; + break; } if (label !== null) { return loadTimeData.getString(label); @@ -701,7 +716,7 @@ switch (this.menuSource_) { case MenuSource.ITEM: case MenuSource.TREE: - return [ + const commands = [ Command.EDIT, Command.SHOW_IN_FOLDER, Command.DELETE, @@ -715,6 +730,10 @@ Command.OPEN_NEW_TAB, Command.OPEN_NEW_WINDOW, ]; + if (loadTimeData.getBoolean('splitViewEnabled')) { + commands.push(Command.OPEN_SPLIT_VIEW); + } + return commands; case MenuSource.TOOLBAR: return [ Command.SORT,
diff --git a/chrome/browser/resources/bookmarks/constants.ts b/chrome/browser/resources/bookmarks/constants.ts index b38e604..7eaef0c 100644 --- a/chrome/browser/resources/bookmarks/constants.ts +++ b/chrome/browser/resources/bookmarks/constants.ts
@@ -46,10 +46,12 @@ OPEN_BOOKMARK = 21, OPEN_FOLDER = 22, - OPEN_NEW_GROUP = 23, + OPEN_SPLIT_VIEW = 23, + + OPEN_NEW_GROUP = 24, // Append new values to the end of the enum. - MAX_VALUE = 24, + MAX_VALUE = 25, } /**
diff --git a/chrome/browser/resources/glic/glic_api/glic_api.ts b/chrome/browser/resources/glic/glic_api/glic_api.ts index 1fa08dc..0ec4349 100644 --- a/chrome/browser/resources/glic/glic_api/glic_api.ts +++ b/chrome/browser/resources/glic/glic_api/glic_api.ts
@@ -1054,6 +1054,11 @@ * The search range starting from DOMNodeId did not result in a valid range. */ SEARCH_RANGE_INVALID = 6, + + /** + * Tab context permission was disabled. + */ + TAB_CONTEXT_PERMISSION_DISABLED = 7, } /**
diff --git a/chrome/browser/resources/new_tab_page/BUILD.gn b/chrome/browser/resources/new_tab_page/BUILD.gn index 8f0fef1d..f830341 100644 --- a/chrome/browser/resources/new_tab_page/BUILD.gn +++ b/chrome/browser/resources/new_tab_page/BUILD.gn
@@ -81,6 +81,7 @@ "//tools/typescript/definitions/pending.d.ts", ] ts_deps = [ + "//chrome/browser/resources/new_tab_shared:build_ts", "//third_party/lit/v3_0:build_ts", "//third_party/polymer/v3_0:library", "//ui/webui/resources/cr_components/color_change_listener:build_ts", @@ -93,6 +94,12 @@ "//ui/webui/resources/mojo:build_ts", ] + ts_path_mappings = + [ "chrome://new-tab-page/shared/*|" + + rebase_path( + "$root_gen_dir/chrome/browser/resources/new_tab_shared/tsc/*", + target_gen_dir) ] + webui_context_type = "trusted" optimize = optimize_webui if (optimize) { @@ -114,6 +121,11 @@ "chrome://resources/mojo/skia/public/mojom/skcolor.mojom-webui.js", "chrome://resources/mojo/url/mojom/url.mojom-webui.js", ] + mojo_js_files + optimize_webui_external_paths = + [ "chrome://new-tab-page/shared|" + + rebase_path( + "$root_gen_dir/chrome/browser/resources/new_tab_shared/tsc", + root_build_dir) ] } }
diff --git a/chrome/browser/resources/new_tab_page/app.css b/chrome/browser/resources/new_tab_page/app.css index 2b3c866..ed9bb37 100644 --- a/chrome/browser/resources/new_tab_page/app.css +++ b/chrome/browser/resources/new_tab_page/app.css
@@ -161,159 +161,6 @@ margin-top: 16px; } -#customizeButtons { - display: flex; - flex-wrap: wrap; - gap: 8px; - bottom: 16px; - position: fixed; -} - -#wallpaperSearchButton { - --cr-hover-background-color: - var(--color-new-tab-page-wallpaper-search-button-background-hovered); - --cr-button-text-color: - var(--color-new-tab-page-wallpaper-search-button-foreground); - --cr-button-background-color: - var(--color-new-tab-page-wallpaper-search-button-background); -} - -#customizeButton { - --cr-hover-background-color: - var(--color-new-tab-page-button-background-hovered); - --cr-button-text-color: var(--color-new-tab-page-button-foreground); - --cr-button-background-color: var(--color-new-tab-page-button-background); -} - -:host([show-background-image_]) #customizeButton, -:host([show-background-image_]) #wallpaperSearchButton { - --cr-hover-background-color: - var(--ntp-protected-icon-background-color-hovered); - --cr-button-text-color: white; - --cr-button-background-color: var(--ntp-protected-icon-background-color); -} - -#customizeButton:has(help-bubble) { - /* help-bubble parent needs z-index to overlay ntp-iframe */ - z-index: 1001; -} - -:host-context([dir='ltr']) #customizeButtons { - right: 16px; -} - -:host-context([dir='rtl']) #customizeButtons { - left: 16px; -} - -.customize-button { - --cr-button-height: 32px; - border: none; - border-radius: calc(.5 * var(--cr-button-height)); - box-shadow: 0 3px 6px rgba(0, 0, 0, .16), 0 1px 2px rgba(0, 0, 0, .23); - font-weight: 400; - min-width: 32px; - padding-inline-end: 16px; - padding-inline-start: 16px; -} - -:host([show-background-image_]) #customizeButton, -:host([show-wallpaper-search-button_]) #customizeButton, -:host([show-wallpaper-search_]) #wallpaperSearchButton { - box-shadow: none; - padding-inline-end: 0; - padding-inline-start: 8px; -} - -:host-context(.focus-outline-visible) .customize-button:focus { - box-shadow: var(--ntp-focus-shadow); -} - -.customize-icon { - --cr-icon-button-margin-start: 0; - --cr-icon-color: var(--cr-button-text-color); - --cr-icon-ripple-margin: 0; - --cr-icon-ripple-size: 16px; - --cr-icon-size: 100%; -} - -#wallpaperSearchIcon { - --cr-icon-image: url(icons/sparkle.svg); -} - -#customizeIcon { - --cr-icon-image: url(icons/icon_pencil.svg); -} - -@media (max-width: 550px) { - .customize-button { - padding-inline-end: 0; - padding-inline-start: 8px; - } - - .customize-text { - display: none; - } -} - -@media (max-width: 1110px) { - :host([modules-shown-to-user]) .customize-text { - display: none; - } - - :host([modules-shown-to-user]) .customize-button { - padding-inline-end: 0; - padding-inline-start: 8px; - } -} - -@media (max-width: 970px) { - :host([modules-shown-to-user]) .customize-button { - padding-inline-end: 0; - padding-inline-start: 8px; - } - - :host([modules-shown-to-user]) .customize-text { - display: none; - } -} - -:host([wallpaper-search-button-animation-enabled_]) - #wallpaperSearchButton { - animation: 750ms forwards grow-container, 500ms forwards 300ms color-text; - color: rgba(0,0,0,0); - opacity: 0; - transform-origin: right; -} - -@keyframes grow-container { - from { - opacity: 0; - transform: scale(0.8); - } - to { - opacity: 100%; - transform: scale(1); - } -} - -/* The text inherits the color of the button, -while the icon has a color rule of it's own. */ -@keyframes color-text { - from { color: rgba(0,0,0,0); } - to { color: var(--cr-button-text-color); } -} - -:host([wallpaper-search-button-animation-enabled_]) - #wallpaperSearchIcon { - animation: 2.5s 350ms spin-icon; -} - -@keyframes spin-icon { - from { transform:rotate(0deg); } - to { transform:rotate(720deg); } -} - #themeAttribution { align-self: flex-start; bottom: 16px;
diff --git a/chrome/browser/resources/new_tab_page/app.html b/chrome/browser/resources/new_tab_page/app.html index 1218fff..cb2b52cc2 100644 --- a/chrome/browser/resources/new_tab_page/app.html +++ b/chrome/browser/resources/new_tab_page/app.html
@@ -78,31 +78,20 @@ ${this.backgroundImageAttribution2_} </div> </a> - <div id="customizeButtons"> - ${this.showWallpaperSearchButton_ ? html` - <cr-button id="wallpaperSearchButton" - class="customize-button" @click="${this.onWallpaperSearchClick_}" - title="$i18n{customizeThisPageWallpaperSearch}" - aria-pressed="${this.showWallpaperSearch_}"> - <div id="wallpaperSearchIcon" - class="customize-icon cr-icon" slot="prefix-icon"></div> - <div id="wallpaperSearchText" class="customize-text" - ?hidden="${this.showWallpaperSearch_}"> - $i18n{wallpaperSearchButton} - </div> - </cr-button> - ` : ''} - <cr-button id="customizeButton" class="customize-button" - @click="${this.onCustomizeClick_}" title="$i18n{customizeThisPage}" - aria-pressed="${this.showCustomize_}"> - <div id="customizeIcon" - class="customize-icon cr-icon" slot="prefix-icon"></div> - <div id="customizeText" class="customize-text" - ?hidden="${!this.showCustomizeChromeText_}"> - $i18n{customizeButton} - </div> - </cr-button> - </div> + <!-- TODO(crbug.com/409296433) Hide ntp-customize-buttons on new-tab-page when + footer is enabled and visible. --> + <ntp-customize-buttons id="customizeButtons" + ?modules-shown-to-user="${this.modulesShownToUser}" + ?show-background-image="${this.showBackgroundImage_}" + ?show-customize="${this.showCustomize_}" + ?show-customize-chrome-text="${this.showCustomizeChromeText_}" + ?show-wallpaper-search="${this.showWallpaperSearch_}" + ?show-wallpaper-search-button="${this.showWallpaperSearchButton_}" + ?wallpaper-search-button-animation-enabled="${this.wallpaperSearchButtonAnimationEnabled_}" + @customize-click="${this.onCustomizeClick_}" + @show-customize-change="${this.onShowCustomizeChange_}" + @wallpaper-search-click="${this.onWallpaperSearchClick_}"> + </ntp-customize-buttons> ${this.showThemeAttribution_() ? html` <div id="themeAttribution"> <div>$i18n{themeCreatedBy}</div>
diff --git a/chrome/browser/resources/new_tab_page/app.ts b/chrome/browser/resources/new_tab_page/app.ts index b8ebd9c8..2073384 100644 --- a/chrome/browser/resources/new_tab_page/app.ts +++ b/chrome/browser/resources/new_tab_page/app.ts
@@ -5,9 +5,11 @@ import './iframe.js'; import './logo.js'; import '/strings.m.js'; +import 'chrome://new-tab-page/shared/customize_buttons/customize_buttons.js'; import 'chrome://resources/cr_components/searchbox/searchbox.js'; import 'chrome://resources/cr_elements/cr_button/cr_button.js'; +import type {CustomizeButtonsElement} from 'chrome://new-tab-page/shared/customize_buttons/customize_buttons.js'; import {ColorChangeUpdater} from 'chrome://resources/cr_components/color_change_listener/colors_css_updater.js'; import {HelpBubbleMixinLit} from 'chrome://resources/cr_components/help_bubble/help_bubble_mixin_lit.js'; import type {CrToastElement} from 'chrome://resources/cr_elements/cr_toast/cr_toast.js'; @@ -129,6 +131,7 @@ export interface AppElement { $: { + customizeButtons: CustomizeButtonsElement, oneGoogleBarClipPath: HTMLElement, logo: LogoElement, }; @@ -156,10 +159,7 @@ showCustomize_: {type: Boolean}, showCustomizeChromeText_: {type: Boolean}, - showWallpaperSearch_: { - type: Boolean, - reflect: true, - }, + showWallpaperSearch_: {type: Boolean}, selectedCustomizeDialogPage_: {type: String}, showVoiceSearchOverlay_: {type: Boolean}, @@ -232,19 +232,11 @@ scrolledToTop_: {type: Boolean}, - wallpaperSearchButtonAnimationEnabled_: { - type: Boolean, - reflect: true, - }, + wallpaperSearchButtonAnimationEnabled_: {type: Boolean}, - wallpaperSearchButtonEnabled_: { - type: Boolean, - }, + wallpaperSearchButtonEnabled_: {type: Boolean}, - showWallpaperSearchButton_: { - type: Boolean, - reflect: true, - }, + showWallpaperSearchButton_: {type: Boolean}, }; } @@ -275,7 +267,6 @@ loadTimeData.getBoolean('oneGoogleBarEnabled'); protected accessor shortcutsEnabled_: boolean = loadTimeData.getBoolean('shortcutsEnabled'); - private modulesFreShown: boolean; protected accessor middleSlotPromoEnabled_: boolean = loadTimeData.getBoolean('middleSlotPromoEnabled'); protected accessor modulesEnabled_: boolean = @@ -290,7 +281,7 @@ protected accessor lazyRender_: boolean; protected accessor scrolledToTop_: boolean = document.documentElement.scrollTop <= 0; - private accessor wallpaperSearchButtonAnimationEnabled_: boolean = + protected accessor wallpaperSearchButtonAnimationEnabled_: boolean = loadTimeData.getBoolean('wallpaperSearchButtonAnimationEnabled'); protected accessor wallpaperSearchButtonEnabled_: boolean = loadTimeData.getBoolean('wallpaperSearchButtonEnabled'); @@ -606,7 +597,8 @@ // completed. document.documentElement.setAttribute('lazy-loaded', String(true)); this.registerHelpBubble( - CUSTOMIZE_CHROME_BUTTON_ELEMENT_ID, '#customizeButton', {fixed: true}); + CUSTOMIZE_CHROME_BUTTON_ELEMENT_ID, + ['ntp-customize-buttons', '#customizeButton'], {fixed: true}); this.pageHandler_.maybeShowFeaturePromo(IphFeature.kCustomizeChrome); if (this.showWallpaperSearchButton_) { this.pageHandler_.incrementWallpaperSearchButtonShownCount(); @@ -703,6 +695,10 @@ this.backgroundManager_.setShowBackgroundImage(this.showBackgroundImage_); } + protected onShowCustomizeChange_(e: CustomEvent<boolean>) { + this.showCustomize_ = e.detail; + } + private onThemeChange_() { if (this.theme_) { this.backgroundManager_.setBackgroundColor(this.theme_.backgroundColor); @@ -986,14 +982,15 @@ case $$(this, '#modules'): recordClick(NtpElement.MODULE); return; - case $$(this, '#customizeButton'): + case $$(this.$.customizeButtons, '#customizeButton'): recordClick(NtpElement.CUSTOMIZE_BUTTON); return; - case $$(this, '#wallpaperSearchButton'): + case $$(this.$.customizeButtons, '#wallpaperSearchButton'): recordClick(NtpElement.WALLPAPER_SEARCH_BUTTON); return; } } + recordClick(NtpElement.OTHER); }
diff --git a/chrome/browser/resources/new_tab_page/lazy_load.ts b/chrome/browser/resources/new_tab_page/lazy_load.ts index fb9470a..61fedbab 100644 --- a/chrome/browser/resources/new_tab_page/lazy_load.ts +++ b/chrome/browser/resources/new_tab_page/lazy_load.ts
@@ -15,6 +15,7 @@ import './modules/module_descriptors.js'; import 'chrome://resources/cr_components/most_visited/most_visited.js'; +export {CustomizeButtonsElement} from 'chrome://new-tab-page/shared/customize_buttons/customize_buttons.js'; export {PageImageServiceBrowserProxy} from 'chrome://resources/cr_components/page_image_service/browser_proxy.js'; export {LensErrorType, LensFormElement, LensSubmitType} from './lens_form.js'; export {LensUploadDialogAction, LensUploadDialogElement, LensUploadDialogError} from './lens_upload_dialog.js';
diff --git a/chrome/browser/resources/new_tab_page/new_tab_page.ts b/chrome/browser/resources/new_tab_page/new_tab_page.ts index 258da3d..cbe26b1 100644 --- a/chrome/browser/resources/new_tab_page/new_tab_page.ts +++ b/chrome/browser/resources/new_tab_page/new_tab_page.ts
@@ -9,6 +9,7 @@ * things tests need. */ +export {CustomizeButtonsElement} from 'chrome://new-tab-page/shared/customize_buttons/customize_buttons.js'; export {SearchboxElement} from 'chrome://resources/cr_components/searchbox/searchbox.js'; export {SearchboxBrowserProxy} from 'chrome://resources/cr_components/searchbox/searchbox_browser_proxy.js'; export {SearchboxIconElement} from 'chrome://resources/cr_components/searchbox/searchbox_icon.js';
diff --git a/chrome/browser/resources/new_tab_shared/BUILD.gn b/chrome/browser/resources/new_tab_shared/BUILD.gn new file mode 100644 index 0000000..f07d4f3 --- /dev/null +++ b/chrome/browser/resources/new_tab_shared/BUILD.gn
@@ -0,0 +1,27 @@ +# Copyright 2025 The Chromium Authors +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +import("//ui/webui/resources/tools/build_webui.gni") + +assert(!is_android) + +build_webui("build") { + grd_prefix = "new_tab_shared" + grd_resource_path_prefix = "shared" + + ts_files = [ + "customize_buttons/customize_buttons.ts", + "customize_buttons/customize_buttons.html.ts", + ] + css_files = [ "customize_buttons/customize_buttons.css" ] + + ts_composite = true + ts_deps = [ + "//third_party/lit/v3_0:build_ts", + "//ui/webui/resources/cr_elements:build_ts", + "//ui/webui/resources/js:build_ts", + ] + + webui_context_type = "trusted" +}
diff --git a/chrome/browser/resources/new_tab_shared/COMMON_METADATA b/chrome/browser/resources/new_tab_shared/COMMON_METADATA new file mode 100644 index 0000000..5093cd5 --- /dev/null +++ b/chrome/browser/resources/new_tab_shared/COMMON_METADATA
@@ -0,0 +1,6 @@ +monorail: { + component: "UI>Browser>NewTabPage" +} +buganizer_public: { + component_id: 1457163 +}
diff --git a/chrome/browser/resources/new_tab_shared/DIR_METADATA b/chrome/browser/resources/new_tab_shared/DIR_METADATA new file mode 100644 index 0000000..faa5c91 --- /dev/null +++ b/chrome/browser/resources/new_tab_shared/DIR_METADATA
@@ -0,0 +1 @@ +mixins: "//chrome/browser/resources/new_tab_shared/COMMON_METADATA" \ No newline at end of file
diff --git a/chrome/browser/resources/new_tab_shared/OWNERS b/chrome/browser/resources/new_tab_shared/OWNERS new file mode 100644 index 0000000..03917c3 --- /dev/null +++ b/chrome/browser/resources/new_tab_shared/OWNERS
@@ -0,0 +1 @@ +file://components/search/OWNERS \ No newline at end of file
diff --git a/chrome/browser/resources/new_tab_shared/customize_buttons/customize_buttons.css b/chrome/browser/resources/new_tab_shared/customize_buttons/customize_buttons.css new file mode 100644 index 0000000..3dd72dd6 --- /dev/null +++ b/chrome/browser/resources/new_tab_shared/customize_buttons/customize_buttons.css
@@ -0,0 +1,164 @@ +/* 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. */ + +/* #css_wrapper_metadata_start + * #type=style-lit + * #import=chrome://resources/cr_elements/cr_shared_style_lit.css.js + * #import=chrome://resources/cr_elements/cr_icons_lit.css.js + * #scheme=relative + * #include=cr-shared-style-lit cr-icons-lit + * #css_wrapper_metadata_end */ + +#customizeButtons { + display: flex; + flex-wrap: wrap; + gap: 8px; + bottom: 16px; + position: fixed; +} + +#wallpaperSearchButton { + --cr-hover-background-color: + var(--color-new-tab-page-wallpaper-search-button-background-hovered); + --cr-button-text-color: + var(--color-new-tab-page-wallpaper-search-button-foreground); + --cr-button-background-color: + var(--color-new-tab-page-wallpaper-search-button-background); +} + +#customizeButton { + --cr-hover-background-color: + var(--color-new-tab-page-button-background-hovered); + --cr-button-text-color: var(--color-new-tab-page-button-foreground); + --cr-button-background-color: var(--color-new-tab-page-button-background); +} + +:host([show-background-image]) #customizeButton, +:host([show-background-image]) #wallpaperSearchButton { + --cr-hover-background-color: + var(--ntp-protected-icon-background-color-hovered); + --cr-button-text-color: white; + --cr-button-background-color: var(--ntp-protected-icon-background-color); +} + +#customizeButton:has(help-bubble) { + /* help-bubble parent needs z-index to overlay ntp-iframe */ + z-index: 1001; +} + +:host-context([dir='ltr']) #customizeButtons { + right: 16px; +} + +:host-context([dir='rtl']) #customizeButtons { + left: 16px; +} + +.customize-button { + --cr-button-height: 32px; + border: none; + border-radius: calc(.5 * var(--cr-button-height)); + box-shadow: 0 3px 6px rgba(0, 0, 0, .16), 0 1px 2px rgba(0, 0, 0, .23); + font-weight: 400; + min-width: 32px; + padding-inline-end: 16px; + padding-inline-start: 16px; +} + +:host([show-background-image]) #customizeButton, +:host([show-wallpaper-search-button]) #customizeButton, +:host([show-wallpaper-search]) #wallpaperSearchButton { + box-shadow: none; + padding-inline-end: 0; + padding-inline-start: 8px; +} + +:host-context(.focus-outline-visible) .customize-button:focus { + box-shadow: var(--ntp-focus-shadow); +} + +.customize-icon { + --cr-icon-button-margin-start: 0; + --cr-icon-color: var(--cr-button-text-color); + --cr-icon-ripple-margin: 0; + --cr-icon-ripple-size: 16px; + --cr-icon-size: 100%; +} + +#wallpaperSearchIcon { + --cr-icon-image: url(icons/sparkle.svg); +} + +#customizeIcon { + --cr-icon-image: url(icons/icon_pencil.svg); +} + +@media (max-width: 550px) { + .customize-button { + padding-inline-end: 0; + padding-inline-start: 8px; + } + + .customize-text { + display: none; + } +} + +@media (max-width: 1110px) { + :host([modules-shown-to-user]) .customize-text { + display: none; + } + + :host([modules-shown-to-user]) .customize-button { + padding-inline-end: 0; + padding-inline-start: 8px; + } +} + +@media (max-width: 970px) { + :host([modules-shown-to-user]) .customize-button { + padding-inline-end: 0; + padding-inline-start: 8px; + } + + :host([modules-shown-to-user]) .customize-text { + display: none; + } +} + +:host([wallpaper-search-button-animation-enabled]) + #wallpaperSearchButton { + animation: 750ms forwards grow-container, 500ms forwards 300ms color-text; + color: rgba(0,0,0,0); + opacity: 0; + transform-origin: right; +} + +@keyframes grow-container { + from { + opacity: 0; + transform: scale(0.8); + } + to { + opacity: 100%; + transform: scale(1); + } +} + +/* The text inherits the color of the button, +while the icon has a color rule of it's own. */ +@keyframes color-text { + from { color: rgba(0,0,0,0); } + to { color: var(--cr-button-text-color); } +} + +:host([wallpaper-search-button-animation-enabled]) + #wallpaperSearchIcon { + animation: 2.5s 350ms spin-icon; +} + +@keyframes spin-icon { + from { transform:rotate(0deg); } + to { transform:rotate(720deg); } +}
diff --git a/chrome/browser/resources/new_tab_shared/customize_buttons/customize_buttons.html.ts b/chrome/browser/resources/new_tab_shared/customize_buttons/customize_buttons.html.ts new file mode 100644 index 0000000..5faa994 --- /dev/null +++ b/chrome/browser/resources/new_tab_shared/customize_buttons/customize_buttons.html.ts
@@ -0,0 +1,39 @@ +// Copyright 2025 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import {html} from 'chrome://resources/lit/v3_0/lit.rollup.js'; + +import type {CustomizeButtonsElement} from './customize_buttons.js'; + +export function getHtml(this: CustomizeButtonsElement) { + // clang-format off + return html`<!--_html_template_start_--> +<div id="customizeButtons"> + ${this.showWallpaperSearchButton ? html` + <cr-button id="wallpaperSearchButton" + class="customize-button" @click="${this.onWallpaperSearchClick_}" + title="$i18n{customizeThisPageWallpaperSearch}" + aria-pressed="${this.showWallpaperSearch}"> + <div id="wallpaperSearchIcon" + class="customize-icon cr-icon" slot="prefix-icon"></div> + <div id="wallpaperSearchText" class="customize-text" + ?hidden="${this.showWallpaperSearch}"> + $i18n{wallpaperSearchButton} + </div> + </cr-button> + ` : ''} + <cr-button id="customizeButton" class="customize-button" + @click="${this.onCustomizeClick_}" title="$i18n{customizeThisPage}" + aria-pressed="${this.showCustomize}"> + <div id="customizeIcon" + class="customize-icon cr-icon" slot="prefix-icon"></div> + <div id="customizeText" class="customize-text" + ?hidden="${!this.showCustomizeChromeText}"> + $i18n{customizeButton} + </div> + </cr-button> +</div> +<!--_html_template_end_-->`; + // clang-format on +}
diff --git a/chrome/browser/resources/new_tab_shared/customize_buttons/customize_buttons.ts b/chrome/browser/resources/new_tab_shared/customize_buttons/customize_buttons.ts new file mode 100644 index 0000000..fb223b9 --- /dev/null +++ b/chrome/browser/resources/new_tab_shared/customize_buttons/customize_buttons.ts
@@ -0,0 +1,84 @@ +// Copyright 2025 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'chrome://resources/cr_elements/cr_button/cr_button.js'; + +import {FocusOutlineManager} from 'chrome://resources/js/focus_outline_manager.js'; +import {CrLitElement} from 'chrome://resources/lit/v3_0/lit.rollup.js'; + +import {getCss} from './customize_buttons.css.js'; +import {getHtml} from './customize_buttons.html.js'; + +export class CustomizeButtonsElement extends CrLitElement { + static get is() { + return 'ntp-customize-buttons'; + } + + static override get styles() { + return getCss(); + } + + override render() { + return getHtml.bind(this)(); + } + + static override get properties() { + return { + modulesShownToUser: { + reflect: true, + type: Boolean, + }, + showBackgroundImage: { + reflect: true, + type: Boolean, + }, + showCustomize: { + notify: true, + type: Boolean, + }, + showCustomizeChromeText: {type: Boolean}, + showWallpaperSearch: { + reflect: true, + type: Boolean, + }, + showWallpaperSearchButton: { + reflect: true, + type: Boolean, + }, + wallpaperSearchButtonAnimationEnabled: { + reflect: true, + type: Boolean, + }, + }; + } + + protected accessor modulesShownToUser: boolean = false; + protected accessor showBackgroundImage: boolean = false; + protected accessor showCustomize: boolean = false; + protected accessor showCustomizeChromeText: boolean = false; + protected accessor showWallpaperSearch: boolean = false; + protected accessor showWallpaperSearchButton: boolean = false; + + override connectedCallback() { + super.connectedCallback(); + + FocusOutlineManager.forDocument(document); + } + + protected onCustomizeClick_() { + this.fire('customize-click'); + } + + protected onWallpaperSearchClick_() { + this.fire('wallpaper-search-click'); + } +} + +declare global { + interface HTMLElementTagNameMap { + 'ntp-customize-buttons': CustomizeButtonsElement; + } +} + +customElements.define(CustomizeButtonsElement.is, CustomizeButtonsElement);
diff --git a/chrome/browser/resources/on_device_internals/tools.ts b/chrome/browser/resources/on_device_internals/tools.ts index ee5f33f0..3c9658f 100644 --- a/chrome/browser/resources/on_device_internals/tools.ts +++ b/chrome/browser/resources/on_device_internals/tools.ts
@@ -289,8 +289,6 @@ this.session_.append( { maxTokens: 0, - // TODO(crbug.com/416009528): This field is deprecated. Remove it. - tokenOffset: 0, input: {pieces: textToInputPieces(this.contextText_)}, }, null); @@ -408,17 +406,12 @@ clonedSession.append( { maxTokens: 0, - // TODO(crbug.com/416009528): This field is deprecated. Remove it. - tokenOffset: 0, input: {pieces: pieces}, }, null); clonedSession.generate( { maxOutputTokens: 0, - topK: null, - temperature: null, - responseJsonSchema: null, constraint: null, }, this.responseRouter_.$.bindNewPipeAndPassRemote());
diff --git a/chrome/browser/resources/print_preview/BUILD.gn b/chrome/browser/resources/print_preview/BUILD.gn index 3f5fca2d..274790b 100644 --- a/chrome/browser/resources/print_preview/BUILD.gn +++ b/chrome/browser/resources/print_preview/BUILD.gn
@@ -95,7 +95,7 @@ "ui/copies_settings.css", "ui/destination_dialog_style.css", "ui/destination_list_item_style.css", - "ui/destination_select_style.css", + "ui/destination_select.css", "ui/dpi_settings.css", "ui/duplex_settings.css", "ui/header.css",
diff --git a/chrome/browser/resources/print_preview/ui/app.html b/chrome/browser/resources/print_preview/ui/app.html index d2c513c9..6fe7769b 100644 --- a/chrome/browser/resources/print_preview/ui/app.html +++ b/chrome/browser/resources/print_preview/ui/app.html
@@ -49,8 +49,7 @@ destination-state="{{destinationState_}}" controls-managed="[[controlsManaged_]]" destination="[[destination_]]" error="{{error_}}" is-pdf="[[!documentSettings_.isModifiable]]" - page-count="[[documentSettings_.pageCount]]" - settings="[[settings]]" state="[[state]]" + page-count="[[documentSettings_.pageCount]]" state="[[state]]" on-focus="onSidebarFocus_" on-destination-changed="onDestinationChanged_" on-destination-capabilities-changed="onDestinationCapabilitiesChanged_"
diff --git a/chrome/browser/resources/print_preview/ui/destination_select_style.css b/chrome/browser/resources/print_preview/ui/destination_select.css similarity index 69% rename from chrome/browser/resources/print_preview/ui/destination_select_style.css rename to chrome/browser/resources/print_preview/ui/destination_select.css index c3649d0..4e651a3 100644 --- a/chrome/browser/resources/print_preview/ui/destination_select_style.css +++ b/chrome/browser/resources/print_preview/ui/destination_select.css
@@ -1,12 +1,16 @@ -/* Copyright 2022 The Chromium Authors +/* 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. */ /* #css_wrapper_metadata_start - * #type=style - * #import=chrome://resources/cr_elements/cr_shared_style.css.js + * #type=style-lit + * #import=chrome://resources/cr_elements/cr_hidden_style_lit.css.js + * #import=chrome://resources/cr_elements/cr_shared_style_lit.css.js * #import=chrome://resources/cr_elements/cr_shared_vars.css.js - * #include=cr-shared-style + * #import=chrome://resources/cr_elements/md_select_lit.css.js + * #import=./print_preview_shared_lit.css.js + * #import=./throbber_lit.css.js + * #include=cr-shared-style-lit print-preview-shared-lit throbber-lit md-select-lit cr-hidden-style-lit * #css_wrapper_metadata_end */ :host {
diff --git a/chrome/browser/resources/print_preview/ui/destination_select.html b/chrome/browser/resources/print_preview/ui/destination_select.html index aa5a352e..1d9fd944 100644 --- a/chrome/browser/resources/print_preview/ui/destination_select.html +++ b/chrome/browser/resources/print_preview/ui/destination_select.html
@@ -1,33 +1,28 @@ -<style include="print-preview-shared throbber md-select destination-select-style - cr-hidden-style"> -</style> <print-preview-settings-section> <span id="destination-label" slot="title">$i18n{destinationLabel}</span> <div slot="controls"> - <div class="throbber-container" hidden$="[[loaded]]"> + <div class="throbber-container" ?hidden="${this.loaded}"> <div class="throbber"></div> </div> <select class="md-select" aria-labelledby="destination-label" - hidden$="[[!loaded]]" - style="background-image: - [[getBackgroundImages_(selectedValue, destination, - noDestinations, dark)]];" - disabled$="[[disabled]]" on-change="onSelectChange"> - <template is="dom-repeat" items="[[recentDestinationList]]"> - <option value="[[item.key]]" - selected$="[[isSelected_(item.key, selectedValue)]]"> - [[item.displayName]] + ?hidden="${!this.loaded}" ?disabled="${this.disabled}" + .style="background-image:${this.getBackgroundImages_()};" + @change="${this.onSelectChange}"> + ${this.recentDestinationList.map(item => html` + <option value="${item.key}" ?selected="${this.isSelected_(item.key)}"> + ${item.displayName} </option> - </template> - <option value="[[pdfDestinationKey_]]" hidden$="[[pdfPrinterDisabled]]" - selected$="[[isSelected_(pdfDestinationKey_, selectedValue)]]"> + `)} + <option value="${this.pdfDestinationKey_}" + ?hidden="${this.pdfPrinterDisabled}" + ?selected="${this.isSelected_(this.pdfDestinationKey_)}"> $i18n{printToPDF} </option> - <option value="noDestinations" hidden$="[[!noDestinations]]" - selected$="[[noDestinations]]"> + <option value="noDestinations" ?hidden="${!this.noDestinations}" + ?selected="${this.noDestinations}"> $i18n{noDestinationsMessage} </option> - <option value="seeMore" aria-label$="[[i18n(seeMoreDestinationsLabel)]]"> + <option value="seeMore" aria-label="$i18n{seeMoreDestinationsLabel}"> $i18n{seeMore} </option> </select>
diff --git a/chrome/browser/resources/print_preview/ui/destination_select.ts b/chrome/browser/resources/print_preview/ui/destination_select.ts index a58570ad..ca41538 100644 --- a/chrome/browser/resources/print_preview/ui/destination_select.ts +++ b/chrome/browser/resources/print_preview/ui/destination_select.ts
@@ -2,36 +2,23 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -/** - * Note: Chrome OS uses print-preview-destination-select-cros rather than the - * element in this file. Ensure any fixes for cross platform bugs work on both - * Chrome OS and non-Chrome OS. - */ - -import 'chrome://resources/cr_elements/cr_hidden_style.css.js'; -import 'chrome://resources/cr_elements/cr_shared_vars.css.js'; -import 'chrome://resources/cr_elements/md_select.css.js'; import 'chrome://resources/js/util.js'; -import './destination_select_style.css.js'; import './icons.html.js'; -import './print_preview_shared.css.js'; -import './throbber.css.js'; import '/strings.m.js'; import {IconsetMap} from 'chrome://resources/cr_elements/cr_icon/iconset_map.js'; -import {I18nMixin} from 'chrome://resources/cr_elements/i18n_mixin.js'; import {assert} from 'chrome://resources/js/assert.js'; -import {PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js'; +import {CrLitElement} from 'chrome://resources/lit/v3_0/lit.rollup.js'; import type {Destination} from '../data/destination.js'; import {PDF_DESTINATION_KEY} from '../data/destination.js'; import {getSelectDropdownBackground} from '../print_preview_utils.js'; -import {getTemplate} from './destination_select.html.js'; -import {SelectMixin} from './select_mixin.js'; +import {getCss} from './destination_select.css.js'; +import {getHtml} from './destination_select.html.js'; +import {SelectMixinLit} from './select_mixin_lit.js'; -const PrintPreviewDestinationSelectElementBase = - I18nMixin(SelectMixin(PolymerElement)); +const PrintPreviewDestinationSelectElementBase = SelectMixinLit(CrLitElement); export class PrintPreviewDestinationSelectElement extends PrintPreviewDestinationSelectElementBase { @@ -39,59 +26,38 @@ return 'print-preview-destination-select'; } - static get template() { - return getTemplate(); + static override get styles() { + return getCss(); } - static get properties() { + override render() { + return getHtml.bind(this)(); + } + + static override get properties() { return { - dark: { - type: Boolean, - value: false, - }, - - destination: Object, - - disabled: { - type: Boolean, - value: false, - }, - - loaded: { - type: Boolean, - value: false, - }, - - noDestinations: { - type: Boolean, - value: false, - }, - - pdfPrinterDisabled: { - type: Boolean, - value: false, - }, - - recentDestinationList: Array, - - pdfDestinationKey_: { - type: String, - value: PDF_DESTINATION_KEY, - }, + dark: {type: Boolean}, + destination: {type: Object}, + disabled: {type: Boolean}, + loaded: {type: Boolean}, + noDestinations: {type: Boolean}, + pdfPrinterDisabled: {type: Boolean}, + recentDestinationList: {type: Array}, + pdfDestinationKey_: {type: String}, }; } - declare dark: boolean; - declare destination: Destination; - declare disabled: boolean; - declare loaded: boolean; - declare noDestinations: boolean; - declare pdfPrinterDisabled: boolean; - declare recentDestinationList: Destination[]; - declare private pdfDestinationKey_: string; + accessor dark: boolean = false; + accessor destination: Destination; + accessor disabled: boolean = false; + accessor loaded: boolean = false; + accessor noDestinations: boolean = false; + accessor pdfPrinterDisabled: boolean = false; + accessor recentDestinationList: Destination[] = []; + protected accessor pdfDestinationKey_: string = PDF_DESTINATION_KEY; override focus() { - this.shadowRoot!.querySelector<HTMLElement>('.md-select')!.focus(); + this.shadowRoot.querySelector<HTMLElement>('.md-select')!.focus(); } /** Sets the select to the current value of |destination|. */ @@ -139,7 +105,7 @@ * @return An inline svg corresponding to the icon for the current * destination and the image for the dropdown arrow. */ - private getBackgroundImages_(): string { + protected getBackgroundImages_(): string { const icon = this.getDestinationIcon_(); if (!icon) { return ''; @@ -157,24 +123,24 @@ } override onProcessSelectChange(value: string) { - this.dispatchEvent(new CustomEvent( - 'selected-option-change', - {bubbles: true, composed: true, detail: value})); + this.fire('selected-option-change', value); } /** * Return the options currently visible to the user for testing purposes. */ getVisibleItemsForTest(): NodeListOf<HTMLOptionElement> { - return this.shadowRoot!.querySelectorAll<HTMLOptionElement>( + return this.shadowRoot.querySelectorAll<HTMLOptionElement>( 'option:not([hidden])'); } - private isSelected_(destinationKey: string): boolean { + protected isSelected_(destinationKey: string): boolean { return this.selectedValue === destinationKey; } } +export type DestinationSelectElement = PrintPreviewDestinationSelectElement; + declare global { interface HTMLElementTagNameMap { 'print-preview-destination-select': PrintPreviewDestinationSelectElement;
diff --git a/chrome/browser/resources/print_preview/ui/destination_settings.ts b/chrome/browser/resources/print_preview/ui/destination_settings.ts index 18c175ab8..0733a3b4 100644 --- a/chrome/browser/resources/print_preview/ui/destination_settings.ts +++ b/chrome/browser/resources/print_preview/ui/destination_settings.ts
@@ -109,7 +109,7 @@ accessor firstLoad: boolean; accessor state: State; protected accessor destinationStore_: DestinationStore|null = null; - protected accessor displayedDestinations_: Destination[]; + protected accessor displayedDestinations_: Destination[] = []; private accessor isDialogOpen_: boolean = false; protected accessor noDestinations_: boolean = false; protected accessor pdfPrinterDisabled_: boolean;
diff --git a/chrome/browser/resources/print_preview/ui/sidebar.html b/chrome/browser/resources/print_preview/ui/sidebar.html index d9a588f..3cd8c296 100644 --- a/chrome/browser/resources/print_preview/ui/sidebar.html +++ b/chrome/browser/resources/print_preview/ui/sidebar.html
@@ -17,20 +17,20 @@ </print-preview-destination-settings> <print-preview-pages-settings page-count="${this.pageCount}" ?disabled="${this.controlsDisabled_}" - ?hidden="${!this.settings?.pages.available || false}" class="settings-section"> + ?hidden="${!this.settingsAvailable_.pages}" class="settings-section"> </print-preview-pages-settings> <print-preview-copies-settings .capability="${this.destinationCapabilities_?.printer.copies || null}" ?disabled="${this.controlsDisabled_}" - ?hidden="${!this.settings?.copies.available || false}" class="settings-section"> + ?hidden="${!this.settingsAvailable_.copies}" class="settings-section"> </print-preview-copies-settings> <print-preview-layout-settings ?disabled="${this.controlsDisabled_}" - ?hidden="${!this.settings?.layout.available || false}" class="settings-section"> + ?hidden="${!this.settingsAvailable_.layout}" class="settings-section"> </print-preview-layout-settings> <print-preview-color-settings ?disabled="${this.controlsDisabled_}" - ?hidden="${!this.settings?.color.available || false}" class="settings-section"> + ?hidden="${!this.settingsAvailable_.color}" class="settings-section"> </print-preview-color-settings> <print-preview-more-settings ?settings-expanded-by-user="${this.settingsExpandedByUser_}" @@ -43,43 +43,43 @@ <print-preview-media-size-settings .capability="${this.destinationCapabilities_?.printer.media_size || null}" ?disabled="${this.controlsDisabled_}" - ?hidden="${!this.settings?.mediaSize.available || false}" + ?hidden="${!this.settingsAvailable_.mediaSize}" class="settings-section"> </print-preview-media-size-settings> <print-preview-pages-per-sheet-settings ?disabled="${this.controlsDisabled_}" - ?hidden="${!this.settings?.pagesPerSheet.available || false}" + ?hidden="${!this.settingsAvailable_.pagesPerSheet}" class="settings-section"> </print-preview-pages-per-sheet-settings> <print-preview-margins-settings .state="${this.state}" ?disabled="${this.controlsDisabled_}" - ?hidden="${!this.settings?.margins.available || false}" + ?hidden="${!this.settingsAvailable_.margins}" class="settings-section"> </print-preview-margins-settings> <print-preview-dpi-settings .capability="${this.destinationCapabilities_?.printer.dpi || null}" ?disabled="${this.controlsDisabled_}" - ?hidden="${!this.settings?.dpi.available || false}" class="settings-section"> + ?hidden="${!this.settingsAvailable_.dpi}" class="settings-section"> </print-preview-dpi-settings> <print-preview-scaling-settings ?disabled="${this.controlsDisabled_}" ?is-pdf="${this.isPdf}" - ?hidden="${!this.settings?.scaling.available || false}" + ?hidden="${!this.settingsAvailable_.scaling}" class="settings-section"> </print-preview-scaling-settings> <print-preview-duplex-settings ?disabled="${this.controlsDisabled_}" ?dark="${this.inDarkMode}" - ?hidden="${!this.settings?.duplex.available || false}" + ?hidden="${!this.settingsAvailable_.duplex}" class="settings-section"> </print-preview-duplex-settings> <print-preview-other-options-settings ?disabled="${this.controlsDisabled_}" - ?hidden="${!this.settings?.otherOptions.available || false}" + ?hidden="${!this.settingsAvailable_.otherOptions}" class="settings-section"> </print-preview-other-options-settings> <print-preview-advanced-options-settings .destination="${this.destination}" ?disabled="${this.controlsDisabled_}" - ?hidden="${!this.settings?.vendorItems.available || false}" + ?hidden="${!this.settingsAvailable_.vendorItems}" class="settings-section"> </print-preview-advanced-options-settings> <print-preview-link-container .destination="${this.destination}"
diff --git a/chrome/browser/resources/print_preview/ui/sidebar.ts b/chrome/browser/resources/print_preview/ui/sidebar.ts index e5e71e1..7b9f3e0 100644 --- a/chrome/browser/resources/print_preview/ui/sidebar.ts +++ b/chrome/browser/resources/print_preview/ui/sidebar.ts
@@ -115,7 +115,7 @@ isInAppKioskMode_: {type: Boolean}, settingsExpandedByUser_: {type: Boolean}, shouldShowMoreSettings_: {type: Boolean}, - settings: {type: Object}, + settingsAvailable_: {type: Object}, }; } @@ -127,18 +127,28 @@ accessor isPdf: boolean; accessor pageCount: number; accessor state: State; - accessor settings: Settings|null = null; + protected accessor settingsAvailable_: Record<keyof Settings, boolean>; protected accessor controlsDisabled_: boolean; protected accessor firstLoad_: boolean = true; protected accessor isInAppKioskMode_: boolean = false; protected accessor settingsExpandedByUser_: boolean = false; protected accessor shouldShowMoreSettings_: boolean; + constructor() { + super(); + + this.settingsAvailable_ = {} as Record<keyof Settings, boolean>; + for (const key of SETTINGS_TO_OBSERVE) { + this.settingsAvailable_[key] = true; + } + } + override connectedCallback() { super.connectedCallback(); for (const key of SETTINGS_TO_OBSERVE) { - this.addSettingObserver(`${key}.available`, () => { + this.addSettingObserver(`${key}.available`, (value: boolean) => { + this.settingsAvailable_[key] = value; this.updateShouldShowMoreSettings_(); this.requestUpdate(); }); @@ -185,7 +195,7 @@ // available sections exceeds the maximum number to show. this.shouldShowMoreSettings_ = SETTINGS_TO_OBSERVE.reduce((count, setting) => { - return this.getSetting(setting).available ? count + 1 : count; + return this.settingsAvailable_[setting] ? count + 1 : count; }, 1) > MAX_SECTIONS_TO_SHOW; }
diff --git a/chrome/browser/resources/side_panel/read_anything/app.ts b/chrome/browser/resources/side_panel/read_anything/app.ts index f650e925..2112914 100644 --- a/chrome/browser/resources/side_panel/read_anything/app.ts +++ b/chrome/browser/resources/side_panel/read_anything/app.ts
@@ -26,11 +26,9 @@ import {SpeechController} from './read_aloud/speech_controller.js'; import type {SpeechListener} from './read_aloud/speech_controller.js'; import {PauseActionSource, SpeechEngineState} from './read_aloud/speech_model.js'; -import type {SpeechPlayingState} from './read_aloud/speech_model.js'; import {VoicePackController} from './read_aloud/voice_pack_controller.js'; import type {VoiceLanguageListener} from './read_aloud/voice_pack_controller.js'; import {WordBoundaries} from './read_aloud/word_boundaries.js'; -import type {WordBoundaryState} from './read_aloud/word_boundaries.js'; import {ReadAnythingLogger, TimeFrom} from './read_anything_logger.js'; import type {ReadAnythingToolbarElement} from './read_anything_toolbar.js'; import type {SpeechBrowserProxy} from './speech_browser_proxy.js'; @@ -165,12 +163,6 @@ // has been set. This is null if the id has not been set. firstTextNodeSetForReadAloud: number|null = null; - // With minor page changes, we redistill or redraw sometimes and end up losing - // our reading position if read aloud has started. This keeps track of the - // last position so we can check if it's still in the new page. - private lastReadingId_: number|null = null; - private lastReadingOffset_: number|null = null; - constructor() { super(); this.constructorTime = Date.now(); @@ -578,32 +570,11 @@ // If the previous reading position still exists and we haven't reached the // end of speech, keep that spot. if (previousSpeechPlayingState.hasSpeechBeenTriggered) { - this.setPreviousReadingPositionIfExists_( + this.speechController_.setPreviousReadingPositionIfExists( previousWordBoundaryState, previousSpeechPlayingState); } } - private setPreviousReadingPositionIfExists_( - previousWordBoundaryState: WordBoundaryState, - previousSpeechPlayingState: SpeechPlayingState) { - if (this.lastReadingId_ === null || this.lastReadingOffset_ === null) { - return; - } - - if (this.nodeStore_.getDomNode(this.lastReadingId_)) { - this.movePlaybackToNode_(this.lastReadingId_, this.lastReadingOffset_); - this.speechController_.setState(previousSpeechPlayingState); - this.wordBoundaries_.state = {...previousWordBoundaryState}; - // Since we're setting the reading position after a content update when - // we're paused, redraw the highlight after moving the traversal state to - // the right spot above. - this.highlightCurrentGranularity(chrome.readingMode.getCurrentText()); - } else { - this.lastReadingId_ = null; - this.lastReadingOffset_ = null; - } - } - async onImageDownloaded(nodeId: number) { const data = chrome.readingMode.getImageBitmap(nodeId); const element = this.nodeStore_.getDomNode(nodeId); @@ -736,7 +707,8 @@ // toggled on or off to ensure the entire granularity segment is // highlighted. if (shouldRehighlightCurrentNodes && originallyHadHighlights) { - this.highlightCurrentGranularity(chrome.readingMode.getCurrentText()); + this.speechController_.highlightCurrentGranularity( + chrome.readingMode.getCurrentText()); } this.loadImages_(); } @@ -1062,7 +1034,8 @@ // updateContent, such as via a preference change, rehighlight the nodes // after a pause. if (!playedFromSelection) { - this.highlightCurrentGranularity(chrome.readingMode.getCurrentText()); + this.speechController_.highlightCurrentGranularity( + chrome.readingMode.getCurrentText()); } return; @@ -1165,7 +1138,7 @@ // Iterate through the nodes asynchronously so that we can show the spinner // in the toolbar while we move up to the selection. setTimeout(() => { - this.movePlaybackToNode_(startingNodeId, startingOffset); + this.speechController_.movePlaybackToNode(startingNodeId, startingOffset); // Set everything to previous and then play the next granularity, which // includes the selection. this.highlighter_.resetPreviousHighlight(); @@ -1177,27 +1150,6 @@ return true; } - private movePlaybackToNode_(nodeId: number, offset: number): void { - let currentTextIds = chrome.readingMode.getCurrentText(); - let hasCurrentText = currentTextIds.length > 0; - // Since a node could spread across multiple granularities, we use the - // offset to determine if the selected text is in this granularity or if - // we have to move to the next one. - let startOfSelectionIsInCurrentText = currentTextIds.includes(nodeId) && - chrome.readingMode.getCurrentTextEndIndex(nodeId) > offset; - while (hasCurrentText && !startOfSelectionIsInCurrentText) { - this.highlightCurrentGranularity( - currentTextIds, /*scrollIntoView=*/ false, - /*shouldUpdateSentenceHighlight=*/ true, - /*shouldSetLastReadingPos=*/ false); - chrome.readingMode.movePositionToNextGranularity(); - currentTextIds = chrome.readingMode.getCurrentText(); - hasCurrentText = currentTextIds.length > 0; - startOfSelectionIsInCurrentText = currentTextIds.includes(nodeId) && - chrome.readingMode.getCurrentTextEndIndex(nodeId) > offset; - } - } - highlightAndPlayInterruptedMessage(): boolean { return this.highlightAndPlayMessage(/* isInterrupted = */ true); } @@ -1254,7 +1206,7 @@ this.playText(utteranceText); } - this.highlightCurrentGranularity(axNodeIds); + this.speechController_.highlightCurrentGranularity(axNodeIds); return true; } @@ -1268,20 +1220,6 @@ return this.highlightAndPlayMessage(isInterrupted, isMovingBackward); } - // Highlights or rehighlights the current granularity, sentence or word. - highlightCurrentGranularity( - axNodeIds: number[], scrollIntoView: boolean = true, - shouldUpdateSentenceHighlight: boolean = true, - shouldSetLastReadingPos: boolean = true) { - if (shouldSetLastReadingPos && axNodeIds.length && axNodeIds[0]) { - this.lastReadingId_ = axNodeIds[0]; - this.lastReadingOffset_ = - chrome.readingMode.getCurrentTextStartIndex(this.lastReadingId_); - } - this.highlighter_.highlightCurrentGranularity( - axNodeIds, scrollIntoView, shouldUpdateSentenceHighlight); - } - // Gets the accessible text boundary for the given string. getAccessibleTextLength(utteranceText: string): number { // Splicing on commas won't work for all locales, but since this is a @@ -1332,24 +1270,7 @@ this.handleSpeechSynthesisError(error, utteranceText); }; - message.addEventListener('boundary', (event) => { - // Some voices may give sentence boundaries, but we're only concerned - // with word boundaries in boundary event because we're speaking text at - // the sentence granularity level, so we'll retrieve these boundaries in - // message.onEnd instead. - if (event.name === 'word') { - this.wordBoundaries_.updateBoundary(event.charIndex, event.charLength); - - // No need to update the highlight on word boundary events if - // highlighting is off or if sentence highlighting is used. - // Therefore, we don't need to pass in axIds because these are - // calculated downstream. - this.highlightCurrentGranularity( - [], /* scrollIntoView= */ true, - /*shouldUpdateSentenceHighlight= */ false); - } - }); - + this.speechController_.setOnBoundary(message); this.speechController_.setOnSpeechSynthesisUtteranceStart(message); message.onend = () => { @@ -1373,19 +1294,6 @@ } }; - const voice = this.voicePackController_.getCurrentVoiceOrDefault(); - if (!voice) { - // TODO: crbug.com/40927698 - Handle when no voices are available. - return; - } - - // This should only be false in tests where we can't properly construct an - // actual SpeechSynthesisVoice object even though the test voices pass the - // type checking of method signatures. - if (voice instanceof SpeechSynthesisVoice) { - message.voice = voice; - } - this.speechController_.speakMessage(message); } @@ -1607,7 +1515,8 @@ // Rehighlight the new granularity. if (changedHighlight !== chrome.readingMode.noHighlighting) { - this.highlightCurrentGranularity(chrome.readingMode.getCurrentText()); + this.speechController_.highlightCurrentGranularity( + chrome.readingMode.getCurrentText()); } // Log these highlight granularity changes when the phrase menu is shown.
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 0aeb6fb..9f02745 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
@@ -5,12 +5,17 @@ import {loadTimeData} from '//resources/js/load_time_data.js'; import {getCurrentSpeechRate} from '../common.js'; +import {NodeStore} from '../node_store.js'; import {ReadAnythingLogger} from '../read_anything_logger.js'; import type {SpeechBrowserProxy} from '../speech_browser_proxy.js'; import {SpeechBrowserProxyImpl} from '../speech_browser_proxy.js'; +import {ReadAloudHighlighter} from './highlighter.js'; import {PauseActionSource, SpeechEngineState, SpeechModel} from './speech_model.js'; import type {SpeechPlayingState} from './speech_model.js'; +import {VoicePackController} from './voice_pack_controller.js'; +import {WordBoundaries} from './word_boundaries.js'; +import type {WordBoundaryState} from './word_boundaries.js'; export interface SpeechListener { onPause(): void; @@ -24,6 +29,12 @@ private model_: SpeechModel = new SpeechModel(); private speech_: SpeechBrowserProxy = SpeechBrowserProxyImpl.getInstance(); private logger_: ReadAnythingLogger = ReadAnythingLogger.getInstance(); + private nodeStore_: NodeStore = NodeStore.getInstance(); + private voicePackController_: VoicePackController = + VoicePackController.getInstance(); + private wordBoundaries_: WordBoundaries = WordBoundaries.getInstance(); + private highlighter_: ReadAloudHighlighter = + ReadAloudHighlighter.getInstance(); private listeners_: SpeechListener[] = []; constructor() { @@ -177,7 +188,40 @@ }; } + setOnBoundary(message: SpeechSynthesisUtterance) { + message.onboundary = (event) => { + // Some voices may give sentence boundaries, but we're only concerned + // with word boundaries in boundary event because we're speaking text at + // the sentence granularity level, so we'll retrieve these boundaries in + // message.onEnd instead. + if (event.name === 'word') { + this.wordBoundaries_.updateBoundary(event.charIndex, event.charLength); + + // No need to update the highlight on word boundary events if + // highlighting is off or if sentence highlighting is used. + // Therefore, we don't need to pass in axIds because these are + // calculated downstream. + this.highlightCurrentGranularity( + [], /* scrollIntoView= */ true, + /*shouldUpdateSentenceHighlight= */ false); + } + }; + } + speakMessage(message: SpeechSynthesisUtterance) { + const voice = this.voicePackController_.getCurrentVoiceOrDefault(); + if (!voice) { + // TODO: crbug.com/40927698 - Handle when no voices are available. + return; + } + + // This should only be false in tests where we can't properly construct an + // actual SpeechSynthesisVoice object even though the test voices pass the + // type checking of method signatures. + if (voice instanceof SpeechSynthesisVoice) { + message.voice = voice; + } + if (this.model_.getEngineState() === SpeechEngineState.NONE) { this.setEngineState(SpeechEngineState.LOADING); } @@ -242,6 +286,63 @@ } } + setPreviousReadingPositionIfExists( + previousWordBoundaryState: WordBoundaryState, + previousSpeechPlayingState: SpeechPlayingState) { + const lastPosition = this.model_.getLastPosition(); + if (!lastPosition) { + return; + } + + if (this.nodeStore_.getDomNode(lastPosition.nodeId)) { + this.movePlaybackToNode(lastPosition.nodeId, lastPosition.offset); + this.setState(previousSpeechPlayingState); + this.wordBoundaries_.state = {...previousWordBoundaryState}; + // Since we're setting the reading position after a content update when + // we're paused, redraw the highlight after moving the traversal state to + // the right spot above. + this.highlightCurrentGranularity(chrome.readingMode.getCurrentText()); + } else { + this.model_.setLastPosition(null); + } + } + + movePlaybackToNode(nodeId: number, offset: number): void { + let currentTextIds = chrome.readingMode.getCurrentText(); + let hasCurrentText = currentTextIds.length > 0; + // Since a node could spread across multiple granularities, we use the + // offset to determine if the selected text is in this granularity or if + // we have to move to the next one. + let startOfSelectionIsInCurrentText = currentTextIds.includes(nodeId) && + chrome.readingMode.getCurrentTextEndIndex(nodeId) > offset; + while (hasCurrentText && !startOfSelectionIsInCurrentText) { + this.highlightCurrentGranularity( + currentTextIds, /*scrollIntoView=*/ false, + /*shouldUpdateSentenceHighlight=*/ true, + /*shouldSetLastReadingPos=*/ false); + chrome.readingMode.movePositionToNextGranularity(); + currentTextIds = chrome.readingMode.getCurrentText(); + hasCurrentText = currentTextIds.length > 0; + startOfSelectionIsInCurrentText = currentTextIds.includes(nodeId) && + chrome.readingMode.getCurrentTextEndIndex(nodeId) > offset; + } + } + + // Highlights or rehighlights the current granularity, sentence or word. + highlightCurrentGranularity( + axNodeIds: number[], scrollIntoView: boolean = true, + shouldUpdateSentenceHighlight: boolean = true, + shouldSetLastReadingPos: boolean = true) { + if (shouldSetLastReadingPos && axNodeIds.length && axNodeIds[0]) { + this.model_.setLastPosition({ + nodeId: axNodeIds[0], + offset: chrome.readingMode.getCurrentTextStartIndex(axNodeIds[0]), + }); + } + this.highlighter_.highlightCurrentGranularity( + axNodeIds, scrollIntoView, shouldUpdateSentenceHighlight); + } + private isSpeechActiveChanged(isSpeechActive: boolean) { this.listeners_.forEach(l => l.onIsSpeechActiveChange()); chrome.readingMode.onSpeechPlayingStateChanged(isSpeechActive);
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 55033f28..709bb37 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
@@ -44,6 +44,11 @@ LOADED, } +interface ReadingPosition { + nodeId: number; + offset: number; +} + export class SpeechModel { private speechPlayingState_: SpeechPlayingState = { isSpeechTreeInitialized: false, @@ -57,6 +62,11 @@ private speechEngineState_: SpeechEngineState = SpeechEngineState.NONE; private previewVoicePlaying_: SpeechSynthesisVoice|null = null; + // With minor page changes, we redistill or redraw sometimes and end up losing + // our reading position if read aloud has started. This keeps track of the + // last position so we can check if it's still in the new page. + private lastReadingPosition_: ReadingPosition|null = null; + reset(): void { this.speechPlayingState_ = { isSpeechTreeInitialized: false, @@ -70,6 +80,14 @@ this.previewVoicePlaying_ = null; } + getLastPosition(): ReadingPosition|null { + return this.lastReadingPosition_; + } + + setLastPosition(position: ReadingPosition|null) { + this.lastReadingPosition_ = position; + } + getEngineState(): SpeechEngineState { return this.speechEngineState_; }
diff --git a/chrome/browser/resources/tab_search/split_view/app.ts b/chrome/browser/resources/tab_search/split_view/app.ts index f8a4231..d7f9a87f 100644 --- a/chrome/browser/resources/tab_search/split_view/app.ts +++ b/chrome/browser/resources/tab_search/split_view/app.ts
@@ -117,6 +117,8 @@ } else { this.allInvisibleTabs_.push(tabData); } + this.allInvisibleTabs_ = + this.allInvisibleTabs_.filter(tab => !(tab.tab as Tab).visible); this.sortTabs_(); }
diff --git a/chrome/browser/send_tab_to_self/BUILD.gn b/chrome/browser/send_tab_to_self/BUILD.gn new file mode 100644 index 0000000..95c6be95 --- /dev/null +++ b/chrome/browser/send_tab_to_self/BUILD.gn
@@ -0,0 +1,7 @@ +# Copyright 2025 The Chromium Authors +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +source_set("send_tab_to_self") { + sources = [ "receiving_ui_handler.h" ] +}
diff --git a/chrome/browser/ui/BUILD.gn b/chrome/browser/ui/BUILD.gn index 0647eac..0e9fa60 100644 --- a/chrome/browser/ui/BUILD.gn +++ b/chrome/browser/ui/BUILD.gn
@@ -3681,9 +3681,6 @@ "plus_addresses/views/plus_address_creation_controller_desktop.cc", "plus_addresses/views/plus_address_creation_dialog_delegate.cc", "plus_addresses/views/plus_address_creation_dialog_delegate.h", - "send_tab_to_self/send_tab_to_self_bubble.h", - "send_tab_to_self/send_tab_to_self_toolbar_icon_controller.cc", - "send_tab_to_self/send_tab_to_self_toolbar_icon_controller.h", "sharing_hub/screenshot/screenshot_captured_bubble_controller.cc", "sharing_hub/screenshot/screenshot_captured_bubble_controller.h", "sharing_hub/sharing_hub_bubble_controller.h", @@ -3787,8 +3784,10 @@ "views/autofill/payments/payments_window_user_consent_dialog_view.h", "views/autofill/payments/promo_code_label_button.cc", "views/autofill/payments/promo_code_label_button.h", - "views/autofill/payments/save_and_fill_dialog_views.cc", - "views/autofill/payments/save_and_fill_dialog_views.h", + "views/autofill/payments/save_and_fill_dialog.cc", + "views/autofill/payments/save_and_fill_dialog.h", + "views/autofill/payments/save_and_fill_view_desktop.cc", + "views/autofill/payments/save_and_fill_view_desktop.h", "views/autofill/payments/save_card_bubble_views.cc", "views/autofill/payments/save_card_bubble_views.h", "views/autofill/payments/save_card_manage_cards_bubble_views.cc", @@ -5016,6 +5015,8 @@ "//chrome/browser/ui/promos:utils", "//chrome/browser/ui/qrcode_generator", "//chrome/browser/ui/qrcode_generator:impl", + "//chrome/browser/ui/send_tab_to_self", + "//chrome/browser/ui/send_tab_to_self:impl", "//chrome/browser/ui/views", "//chrome/browser/ui/views/bubble", "//chrome/browser/ui/views/download", @@ -5075,6 +5076,7 @@ # Any circular includes must depend on the target "//chrome/browser:browser_public_dependencies". allow_circular_includes_from += [ "//chrome/browser/ui/qrcode_generator:impl", + "//chrome/browser/ui/send_tab_to_self:impl", "//chrome/browser/ui/dialogs:impl", "//chrome/browser/ui/views", "//chrome/browser/ui/views/bubble",
diff --git a/chrome/browser/ui/android/digital_credentials/java/src/org/chromium/chrome/browser/ui/android/digital_credentials/DigitalIdentitySafetyInterstitialIntegrationTest.java b/chrome/browser/ui/android/digital_credentials/java/src/org/chromium/chrome/browser/ui/android/digital_credentials/DigitalIdentitySafetyInterstitialIntegrationTest.java index 2d9f260..9d0d405 100644 --- a/chrome/browser/ui/android/digital_credentials/java/src/org/chromium/chrome/browser/ui/android/digital_credentials/DigitalIdentitySafetyInterstitialIntegrationTest.java +++ b/chrome/browser/ui/android/digital_credentials/java/src/org/chromium/chrome/browser/ui/android/digital_credentials/DigitalIdentitySafetyInterstitialIntegrationTest.java
@@ -34,9 +34,9 @@ import org.chromium.chrome.browser.digital_credentials.DigitalIdentityInterstitialClosedReason; import org.chromium.chrome.browser.flags.ChromeSwitches; import org.chromium.chrome.browser.webid.DigitalIdentityProvider; +import org.chromium.chrome.browser.webid.IdentityCredentialsDelegate; import org.chromium.chrome.test.ChromeJUnit4ClassRunner; import org.chromium.chrome.test.ChromeTabbedActivityTestRule; -import org.chromium.content.browser.webid.IdentityCredentialsDelegate; import org.chromium.content_public.browser.ContentFeatureList; import org.chromium.content_public.browser.test.util.DOMUtils; import org.chromium.content_public.browser.test.util.JavaScriptUtils;
diff --git a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/mostvisited/MostVisitedTilesProcessor.java b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/mostvisited/MostVisitedTilesProcessor.java index f227bf1f..6b7b8330 100644 --- a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/mostvisited/MostVisitedTilesProcessor.java +++ b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/mostvisited/MostVisitedTilesProcessor.java
@@ -127,11 +127,10 @@ super.populateModel(input, match, model, matchIndex); List<ListItem> tileList = model.get(BaseCarouselSuggestionViewProperties.TILES); - String title = - TextUtils.isEmpty(match.getDisplayText()) + TextUtils.isEmpty(match.getDescription()) ? match.getUrl().getHost() - : match.getDisplayText(); + : match.getDescription(); int tileIndex = tileList.size(); var tileModel =
diff --git a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/mostvisited/MostVisitedTilesProcessorUnitTest.java b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/mostvisited/MostVisitedTilesProcessorUnitTest.java index eb3e117..2c51ce15 100644 --- a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/mostvisited/MostVisitedTilesProcessorUnitTest.java +++ b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/mostvisited/MostVisitedTilesProcessorUnitTest.java
@@ -138,7 +138,7 @@ ? OmniboxSuggestionType.TILE_REPEATABLE_QUERY : OmniboxSuggestionType.TILE_MOST_VISITED_SITE) .setIsSearch(tile.isSearch) - .setDisplayText(tile.title) + .setDescription(tile.title) .setUrl(tile.url) .build(); mProcessor.populateModel(mInput, match, mPropertyModel, placement);
diff --git a/chrome/browser/ui/android/strings/android_chrome_strings.grd b/chrome/browser/ui/android/strings/android_chrome_strings.grd index 0f5fb17..44b8d5b 100644 --- a/chrome/browser/ui/android/strings/android_chrome_strings.grd +++ b/chrome/browser/ui/android/strings/android_chrome_strings.grd
@@ -6413,16 +6413,16 @@ Continue as <ph name="NAME">%1$s<ex>Albus (or Albus Dumbledore)</ex></ph> </message> <message name="IDS_ACCOUNT_SELECTION_CONTENT_DESCRIPTION" desc="Accessibility string read when the Account Selection bottom sheet is opened. It describes the bottom sheet where a user can pick an account." is_accessibility_with_no_ui="true"> - Sign in bottom sheet. + Sign in bottom sheet </message> <message name="IDS_ACCOUNT_SELECTION_SHEET_HALF_HEIGHT" desc="Accessibility string read when the Account Selection bottom sheet showing a list of the user's accounts is opened at half height. The sheet will occupy the bottom half the screen." is_accessibility_with_no_ui="true"> - Sign in bottom sheet opened at half height. + Sign in bottom sheet opened at half height </message> <message name="IDS_ACCOUNT_SELECTION_SHEET_FULL_HEIGHT" desc="Accessibility string read when the Account Selection bottom sheet showing a list of the user's accounts is opened at full height. The sheet will occupy the entire screen." is_accessibility_with_no_ui="true"> - Sign in bottom sheet opened at full height. + Sign in bottom sheet opened at full height </message> <message name="IDS_ACCOUNT_SELECTION_SHEET_CLOSED" desc="Accessibility string read when the Account Selection bottom sheet showing a list of the user's accounts is closed." is_accessibility_with_no_ui="true"> - Sign in bottom sheet is closed. + Sign in bottom sheet is closed </message> <message name="IDS_IDP_SIGNIN_STATUS_MISMATCH_DIALOG_BODY" desc="Body for mismatch dialog which is shown to prompt the user to sign in to a website using an account from an identity provider."> You can use your <ph name="IDENTITY_PROVIDER_ETLD_PLUS_ONE">%1$s<ex>idp.com</ex></ph> account on this site. To continue, sign in to <ph name="IDENTITY_PROVIDER_ETLD_PLUS_ONE">%1$s<ex>idp.com</ex></ph>.
diff --git a/chrome/browser/ui/android/webid/internal/java/res/layout/account_selection_header_item.xml b/chrome/browser/ui/android/webid/internal/java/res/layout/account_selection_header_item.xml index 845b80c..43598bc 100644 --- a/chrome/browser/ui/android/webid/internal/java/res/layout/account_selection_header_item.xml +++ b/chrome/browser/ui/android/webid/internal/java/res/layout/account_selection_header_item.xml
@@ -15,13 +15,15 @@ android:layout_marginTop="4dp" android:layout_marginBottom="8dp" android:layout_gravity="top" - android:orientation="vertical"> + android:orientation="vertical" + android:importantForAccessibility="no"> <LinearLayout android:id="@+id/header" android:descendantFocusability="afterDescendants" android:layout_width="match_parent" android:layout_height="wrap_content" - android:focusable="true" + android:focusable="false" + android:importantForAccessibility="no" android:orientation="vertical"> <LinearLayout android:descendantFocusability="afterDescendants"
diff --git a/chrome/browser/ui/android/webid/internal/java/src/org/chromium/chrome/browser/ui/android/webid/AccountSelectionViewTest.java b/chrome/browser/ui/android/webid/internal/java/src/org/chromium/chrome/browser/ui/android/webid/AccountSelectionViewTest.java index d6db023..f05b55f 100644 --- a/chrome/browser/ui/android/webid/internal/java/src/org/chromium/chrome/browser/ui/android/webid/AccountSelectionViewTest.java +++ b/chrome/browser/ui/android/webid/internal/java/src/org/chromium/chrome/browser/ui/android/webid/AccountSelectionViewTest.java
@@ -551,6 +551,16 @@ } } + @Test + public void testContentDescription() { + mModel.set( + ItemProperties.CONTINUE_BUTTON, + buildContinueButton(mAnaAccount, mIdpMetadata, HeaderType.SIGN_IN)); + // Check that there is not a period in the content description since one will be appended. + assertEquals( + "Sign in bottom sheet", mBottomSheetContent.getSheetContentDescription(mContext)); + } + private RecyclerView getAccounts() { return mContentView.findViewById(R.id.sheet_item_list); }
diff --git a/chrome/browser/ui/ash/input_method/candidate_window_constants.h b/chrome/browser/ui/ash/input_method/candidate_window_constants.h index 1675759..e606e04e 100644 --- a/chrome/browser/ui/ash/input_method/candidate_window_constants.h +++ b/chrome/browser/ui/ash/input_method/candidate_window_constants.h
@@ -10,7 +10,7 @@ // We'll use a bigger font size, so Chinese characters are more readable // in the candidate window. -const int kFontSizeDelta = 2; +inline constexpr int kFontSizeDelta = 2; // Currently the infolist window only supports Japanese font. inline constexpr char kJapaneseFontName[] = "Noto Sans CJK JP"; @@ -18,19 +18,19 @@ // The minimum width of candidate labels in the vertical candidate // window. We use this value to prevent the candidate window from being // too narrow when all candidates are short. -const int kMinCandidateLabelWidth = 100; +inline constexpr int kMinCandidateLabelWidth = 100; // The maximum width of candidate labels in the vertical candidate // window. We use this value to prevent the candidate window from being // too wide when one of candidates are long. -const int kMaxCandidateLabelWidth = 500; +inline constexpr int kMaxCandidateLabelWidth = 500; // The minimum width of preedit area. We use this value to prevent the // candidate window from being too narrow when candidate lists are not shown. -const int kMinPreeditAreaWidth = 134; +inline constexpr int kMinPreeditAreaWidth = 134; // The width of the infolist indicator icon in the candidate window. -const int kInfolistIndicatorIconWidth = 4; +inline constexpr int kInfolistIndicatorIconWidth = 4; // The padding size of the infolist indicator icon in the candidate window. -const int kInfolistIndicatorIconPadding = 2; +inline constexpr int kInfolistIndicatorIconPadding = 2; } // namespace ime } // namespace ui
diff --git a/chrome/browser/ui/ash/input_method/completion_suggestion_view.h b/chrome/browser/ui/ash/input_method/completion_suggestion_view.h index f73d56aa..39dab859 100644 --- a/chrome/browser/ui/ash/input_method/completion_suggestion_view.h +++ b/chrome/browser/ui/ash/input_method/completion_suggestion_view.h
@@ -28,16 +28,16 @@ // Font-related constants inline constexpr char kFontStyle[] = "Roboto"; -constexpr int kAnnotationFontSize = 10; -constexpr int kIndexFontSize = 10; +inline constexpr int kAnnotationFontSize = 10; +inline constexpr int kIndexFontSize = 10; // Style-related constants -constexpr int kAnnotationBorderThickness = 1; -constexpr int kAnnotationCornerRadius = 2; -constexpr int kPadding = 8; -constexpr int kAnnotationPaddingLeft = 12; -constexpr int kAnnotationPaddingBottom = 16; -constexpr int kAnnotationPaddingTop = 6; +inline constexpr int kAnnotationBorderThickness = 1; +inline constexpr int kAnnotationCornerRadius = 2; +inline constexpr int kPadding = 8; +inline constexpr int kAnnotationPaddingLeft = 12; +inline constexpr int kAnnotationPaddingBottom = 16; +inline constexpr int kAnnotationPaddingTop = 6; inline constexpr char kTabKey[] = "tab"; constexpr cros_styles::ColorName kButtonHighlightColor = cros_styles::ColorName::kRippleColor;
diff --git a/chrome/browser/ui/ash/sharesheet/sharesheet_constants.h b/chrome/browser/ui/ash/sharesheet/sharesheet_constants.h index 47327fe1..655e6e8 100644 --- a/chrome/browser/ui/ash/sharesheet/sharesheet_constants.h +++ b/chrome/browser/ui/ash/sharesheet/sharesheet_constants.h
@@ -16,43 +16,43 @@ // LayoutProvider. // Sizes are in px. -constexpr int kDefaultBubbleWidth = 416; -constexpr int kSpacing = 24; +inline constexpr int kDefaultBubbleWidth = 416; +inline constexpr int kSpacing = 24; -constexpr int kFooterDefaultVerticalPadding = 20; -constexpr int kFooterNoExtensionVerticalPadding = 16; +inline constexpr int kFooterDefaultVerticalPadding = 20; +inline constexpr int kFooterNoExtensionVerticalPadding = 16; -constexpr int kExpandButtonInsideBorderInsetsVertical = 6; -constexpr int kExpandButtonInsideBorderInsetsHorizontal = 16; -constexpr int kExpandButtonBetweenChildSpacing = 8; -constexpr int kExpandButtonCaretIconSize = 20; +inline constexpr int kExpandButtonInsideBorderInsetsVertical = 6; +inline constexpr int kExpandButtonInsideBorderInsetsHorizontal = 16; +inline constexpr int kExpandButtonBetweenChildSpacing = 8; +inline constexpr int kExpandButtonCaretIconSize = 20; -constexpr size_t kTextPreviewMaximumLines = 3; -constexpr size_t kImagePreviewMaxIcons = 4; +inline constexpr size_t kTextPreviewMaximumLines = 3; +inline constexpr size_t kImagePreviewMaxIcons = 4; // TODO(crbug.com/40173943) |kImagePreviewHalfIconSize| value should actually be // 19. When refactoring HoldingSpaceImage, once the DCHECK_GT(20) is removed, // this should be set to 19. At that point |kImagePreviewFullIconSize| can be // be removed and set to |::sharesheet::kIconSize|. -constexpr size_t kImagePreviewHalfIconSize = 21; -constexpr size_t kImagePreviewFullIconSize = 44; +inline constexpr size_t kImagePreviewHalfIconSize = 21; +inline constexpr size_t kImagePreviewFullIconSize = 44; constexpr gfx::Size kImagePreviewFullSize(kImagePreviewFullIconSize, kImagePreviewFullIconSize); constexpr gfx::Size kImagePreviewHalfSize(kImagePreviewFullIconSize, kImagePreviewHalfIconSize); constexpr gfx::Size kImagePreviewQuarterSize(kImagePreviewHalfIconSize, kImagePreviewHalfIconSize); -constexpr int kImagePreviewFileEnumerationLineHeight = 10; -constexpr int kImagePreviewBetweenChildSpacing = 2; -constexpr int kImagePreviewIconCornerRadius = 2; -constexpr int kImagePreviewPlaceholderIconContentSize = 20; +inline constexpr int kImagePreviewFileEnumerationLineHeight = 10; +inline constexpr int kImagePreviewBetweenChildSpacing = 2; +inline constexpr int kImagePreviewIconCornerRadius = 2; +inline constexpr int kImagePreviewPlaceholderIconContentSize = 20; constexpr SkAlpha kImagePreviewBackgroundAlphaComponent = 0x32; -constexpr int kHeaderViewBetweenChildSpacing = 12; -constexpr int kHeaderViewNarrowInsideBorderInsets = 14; +inline constexpr int kHeaderViewBetweenChildSpacing = 12; +inline constexpr int kHeaderViewNarrowInsideBorderInsets = 14; -constexpr int kTitleTextLineHeight = 24; -constexpr int kSubtitleTextLineHeight = 22; -constexpr int kPrimaryTextLineHeight = 20; +inline constexpr int kTitleTextLineHeight = 24; +inline constexpr int kSubtitleTextLineHeight = 22; +inline constexpr int kPrimaryTextLineHeight = 20; } // namespace sharesheet } // namespace ash
diff --git a/chrome/browser/ui/autofill/payments/payments_ui_constants.h b/chrome/browser/ui/autofill/payments/payments_ui_constants.h index afb428de..f07b9ec0 100644 --- a/chrome/browser/ui/autofill/payments/payments_ui_constants.h +++ b/chrome/browser/ui/autofill/payments/payments_ui_constants.h
@@ -10,7 +10,7 @@ namespace autofill { -constexpr int kMigrationDialogMainContainerChildSpacing = 24; +inline constexpr int kMigrationDialogMainContainerChildSpacing = 24; constexpr auto kMigrationDialogInsets = gfx::Insets::TLBR(0, 24, 48, 24); // The time span an Autofill card bubble should be visible even if the document
diff --git a/chrome/browser/ui/autofill/payments/payments_view_factory.h b/chrome/browser/ui/autofill/payments/payments_view_factory.h index b861518..19a032c 100644 --- a/chrome/browser/ui/autofill/payments/payments_view_factory.h +++ b/chrome/browser/ui/autofill/payments/payments_view_factory.h
@@ -89,7 +89,7 @@ // "Save and Fill" suggestion in the credit card dropdown menu. It presents // a centered modal dialog where the user can conveniently save a new // credit card and simultaneously fill it into the form with a single click. -base::WeakPtr<SaveAndFillDialogView> CreateAndShowSaveAndFillDialog( +std::unique_ptr<SaveAndFillDialogView> CreateAndShowSaveAndFillDialog( base::WeakPtr<SaveAndFillDialogController> controller, content::WebContents* web_contents); #endif // !BUILDFLAG(IS_ANDROID)
diff --git a/chrome/browser/ui/browser.cc b/chrome/browser/ui/browser.cc index 2f0de74..68389dd 100644 --- a/chrome/browser/ui/browser.cc +++ b/chrome/browser/ui/browser.cc
@@ -200,6 +200,9 @@ #include "components/sessions/core/session_types.h" #include "components/sessions/core/tab_restore_service.h" #include "components/startup_metric_utils/browser/startup_metric_utils.h" +#include "components/tabs/public/split_tab_data.h" +#include "components/tabs/public/split_tab_id.h" +#include "components/tabs/public/split_tab_visual_data.h" #include "components/tabs/public/tab_interface.h" #include "components/user_manager/user_manager.h" #include "components/web_modal/web_contents_modal_dialog_manager.h" @@ -487,7 +490,11 @@ // TODO(crbug.com/382494946): Similar bespoke checks are used throughout the // codebase. This should be factored out as a common util and other callsites // converted to use this. -bool IsNTP(content::WebContents* web_contents) { +bool IsShowingNTP(content::WebContents* web_contents) { + if (SadTab::ShouldShow(web_contents->GetCrashedStatus())) { + return false; + } + // Use the committed entry (or the visible entry, if the committed entry is // the initial NavigationEntry) so the bookmarks bar disappears at the same // time the page does. @@ -1824,6 +1831,20 @@ instant_controller_.reset(); } +void Browser::OnSplitTabCreated( + std::vector<std::pair<tabs::TabInterface*, int>> tabs, + split_tabs::SplitTabId split_id, + SplitTabAddReason reason, + split_tabs::SplitTabVisualData visual_data) { + UpdateBookmarkBarState(BOOKMARK_BAR_STATE_CHANGE_SPLIT_TAB_CHANGE); +} +void Browser::OnSplitTabRemoved( + std::vector<std::pair<tabs::TabInterface*, int>> tabs, + split_tabs::SplitTabId split_id, + SplitTabRemoveReason reason) { + UpdateBookmarkBarState(BOOKMARK_BAR_STATE_CHANGE_SPLIT_TAB_CHANGE); +} + void Browser::SetTopControlsShownRatio(content::WebContents* web_contents, float ratio) { window_->SetTopControlsShownRatio(web_contents, ratio); @@ -3828,15 +3849,6 @@ return true; } - content::WebContents* web_contents = tab_strip_model_->GetActiveWebContents(); - if (!web_contents) { - return false; - } - - if (SadTab::ShouldShow(web_contents->GetCrashedStatus())) { - return false; - } - if (!browser_defaults::bookmarks_enabled) { return false; } @@ -3847,9 +3859,14 @@ return false; } + const tabs::TabInterface* active_tab = tab_strip_model_->GetActiveTab(); + if (!active_tab || !active_tab->GetContents()) { + return false; + } + bookmarks::BookmarkModel* bookmark_model = BookmarkModelFactory::GetForBrowserContext( - web_contents->GetBrowserContext()); + active_tab->GetContents()->GetBrowserContext()); const bool has_bookmarks = bookmark_model && bookmark_model->HasBookmarks(); tab_groups::TabGroupSyncService* tab_group_service = @@ -3857,9 +3874,23 @@ const bool has_saved_tab_groups = tab_group_service && !tab_group_service->GetAllGroups().empty(); - // The bookmark bar is only shown on the NTP if the user - // has added something to it. - return IsNTP(web_contents) && (has_bookmarks || has_saved_tab_groups); + // The bookmark bar is only shown if the user has added something to it. + if (!has_bookmarks && !has_saved_tab_groups) { + return false; + } + + // The bookmark bar is only shown on the NTP. If the active tab is part of a + // split, check if any tabs in the split are the NTP. + std::optional<split_tabs::SplitTabId> split_id = active_tab->GetSplit(); + if (split_id.has_value()) { + std::vector<tabs::TabInterface*> split_tabs = + tab_strip_model_->GetSplitData(split_id.value())->ListTabs(); + return std::any_of( + split_tabs.begin(), split_tabs.end(), + [](const auto& tab) { return IsShowingNTP(tab->GetContents()); }); + } + + return IsShowingNTP(active_tab->GetContents()); } bool Browser::IsBrowserClosing() const {
diff --git a/chrome/browser/ui/browser.h b/chrome/browser/ui/browser.h index c28ef87..70de622 100644 --- a/chrome/browser/ui/browser.h +++ b/chrome/browser/ui/browser.h
@@ -753,6 +753,13 @@ tabs::TabInterface* tab, int index) override; void TabStripEmpty() override; + void OnSplitTabCreated(std::vector<std::pair<tabs::TabInterface*, int>> tabs, + split_tabs::SplitTabId split_id, + SplitTabAddReason reason, + split_tabs::SplitTabVisualData visual_data) override; + void OnSplitTabRemoved(std::vector<std::pair<tabs::TabInterface*, int>> tabs, + split_tabs::SplitTabId split_id, + SplitTabRemoveReason reason) override; // Overridden from content::WebContentsDelegate: void ActivateContents(content::WebContents* contents) override; @@ -980,6 +987,9 @@ // Change is the result of a force show reason BOOKMARK_BAR_STATE_CHANGE_FORCE_SHOW, + + // Change is the result of a split tab being created or removed. + BOOKMARK_BAR_STATE_CHANGE_SPLIT_TAB_CHANGE, }; // Tracks whether a tabstrip call to action UI is showing.
diff --git a/chrome/browser/ui/browser_commands_browsertest.cc b/chrome/browser/ui/browser_commands_browsertest.cc index fb6d99f..904d155 100644 --- a/chrome/browser/ui/browser_commands_browsertest.cc +++ b/chrome/browser/ui/browser_commands_browsertest.cc
@@ -52,7 +52,6 @@ { features::kTabOrganization, features::kTabstripDeclutter, - toast_features::kToastFramework, toast_features::kReadingListToast, toast_features::kLinkCopiedToast, },
diff --git a/chrome/browser/ui/browser_window/browser_window_features.cc b/chrome/browser/ui/browser_window/browser_window_features.cc index 587bdd0..42006a0 100644 --- a/chrome/browser/ui/browser_window/browser_window_features.cc +++ b/chrome/browser/ui/browser_window/browser_window_features.cc
@@ -234,8 +234,7 @@ } } - if ((browser->is_type_normal() || browser->is_type_app()) && - base::FeatureList::IsEnabled(toast_features::kToastFramework)) { + if (browser->is_type_normal() || browser->is_type_app()) { toast_service_ = std::make_unique<ToastService>(browser); } }
diff --git a/chrome/browser/ui/bubble_anchor_util.h b/chrome/browser/ui/bubble_anchor_util.h index 292d1ce..6f0e5826 100644 --- a/chrome/browser/ui/bubble_anchor_util.h +++ b/chrome/browser/ui/bubble_anchor_util.h
@@ -27,7 +27,7 @@ // Offset from the window edge to show bubbles when there is no location bar. // E.g., when in fullscreen or in a Hosted App window. Don't center, since that // could obscure a fullscreen bubble. -constexpr int kNoToolbarLeftOffset = 40; +inline constexpr int kNoToolbarLeftOffset = 40; // Returns the Rect appropriate for anchoring a bubble to |browser|'s Page Info // icon, or an appropriate fallback when that is not visible. This is used only
diff --git a/chrome/browser/ui/commerce/product_specifications_page_action_controller_unittest.cc b/chrome/browser/ui/commerce/product_specifications_page_action_controller_unittest.cc index d205f83..02acdbb 100644 --- a/chrome/browser/ui/commerce/product_specifications_page_action_controller_unittest.cc +++ b/chrome/browser/ui/commerce/product_specifications_page_action_controller_unittest.cc
@@ -476,7 +476,6 @@ std::vector<base::test::FeatureRef> enabled_features = { commerce::kProductSpecifications}; if (GetParam()) { - enabled_features.push_back(toast_features::kToastFramework); enabled_features.push_back(commerce::kCompareConfirmationToast); } test_features.InitWithFeatures(enabled_features, /*disabled_features*/ {});
diff --git a/chrome/browser/ui/omnibox/omnibox_theme.h b/chrome/browser/ui/omnibox/omnibox_theme.h index 51bfe76..6803fc9 100644 --- a/chrome/browser/ui/omnibox/omnibox_theme.h +++ b/chrome/browser/ui/omnibox/omnibox_theme.h
@@ -10,8 +10,8 @@ enum class OmniboxPartState { NORMAL, HOVERED, SELECTED, IPH }; -constexpr float kOmniboxOpacityHovered = 0.10f; -constexpr float kOmniboxOpacitySelected = 0.16f; +inline constexpr float kOmniboxOpacityHovered = 0.10f; +inline constexpr float kOmniboxOpacitySelected = 0.16f; inline ui::ColorId GetOmniboxBackgroundColorId(OmniboxPartState state) { // TODO(crbug.com/333762301): Update the background color for the IPH
diff --git a/chrome/browser/ui/passwords/manage_passwords_ui_controller.h b/chrome/browser/ui/passwords/manage_passwords_ui_controller.h index dd3edb1..803f0e2 100644 --- a/chrome/browser/ui/passwords/manage_passwords_ui_controller.h +++ b/chrome/browser/ui/passwords/manage_passwords_ui_controller.h
@@ -45,7 +45,8 @@ } // namespace password_manager namespace { -constexpr int kMaxNumberOfTimesBiometricAuthForFillingPromoWillBeShown = 3; +inline constexpr int kMaxNumberOfTimesBiometricAuthForFillingPromoWillBeShown = + 3; } class AccountChooserPrompt;
diff --git a/chrome/browser/ui/passwords/ui_utils.h b/chrome/browser/ui/passwords/ui_utils.h index bc328fe0..668434d 100644 --- a/chrome/browser/ui/passwords/ui_utils.h +++ b/chrome/browser/ui/passwords/ui_utils.h
@@ -40,7 +40,7 @@ struct AccountInfo; // The desired width and height in pixels for an account avatar. -constexpr int kAvatarImageSize = 32; +inline constexpr int kAvatarImageSize = 32; // Crops and scales |image_skia| to the desired size for an account avatar. gfx::ImageSkia ScaleImageForAccountAvatar(gfx::ImageSkia image_skia);
diff --git a/chrome/browser/ui/safety_hub/abusive_notification_permissions_manager.h b/chrome/browser/ui/safety_hub/abusive_notification_permissions_manager.h index 557bc62..ac8a6caa 100644 --- a/chrome/browser/ui/safety_hub/abusive_notification_permissions_manager.h +++ b/chrome/browser/ui/safety_hub/abusive_notification_permissions_manager.h
@@ -17,7 +17,7 @@ // Maximum time in milliseconds to wait for the Safe Browsing service reputation // check. After this amount of time the outstanding check will be aborted, and // the resource will be treated as if it were safe. -const int kCheckUrlTimeoutMs = 5000; +inline constexpr int kCheckUrlTimeoutMs = 5000; } // namespace namespace safe_browsing {
diff --git a/chrome/browser/ui/safety_hub/menu_notification.h b/chrome/browser/ui/safety_hub/menu_notification.h index 992becf..971a871 100644 --- a/chrome/browser/ui/safety_hub/menu_notification.h +++ b/chrome/browser/ui/safety_hub/menu_notification.h
@@ -16,7 +16,7 @@ constexpr base::TimeDelta kSafetyHubMenuNotificationMinNotificationDuration = base::Days(3); -constexpr int kSafetyHubMenuNotificationMinImpressionCount = 5; +inline constexpr int kSafetyHubMenuNotificationMinImpressionCount = 5; // Class that represents the notifications of Safety Hub that are shown in the // Chrome menu.
diff --git a/chrome/browser/ui/send_tab_to_self/BUILD.gn b/chrome/browser/ui/send_tab_to_self/BUILD.gn new file mode 100644 index 0000000..41739ead --- /dev/null +++ b/chrome/browser/ui/send_tab_to_self/BUILD.gn
@@ -0,0 +1,58 @@ +# Copyright 2025 The Chromium Authors +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +import("//build/config/ui.gni") + +assert(is_win || is_mac || is_linux || is_chromeos) +assert(toolkit_views) + +source_set("send_tab_to_self") { + sources = [ + "send_tab_to_self_bubble.h", + "send_tab_to_self_toolbar_icon_controller.h", + ] + + public_deps = [ + "//base", + "//chrome/browser/send_tab_to_self", + "//chrome/browser/ui:browser_list", + "//components/send_tab_to_self", + ] +} + +source_set("impl") { + sources = [ "send_tab_to_self_toolbar_icon_controller.cc" ] + deps = [ + ":send_tab_to_self", + "//chrome/browser:primitives", + "//chrome/browser/profiles:profile", + "//chrome/browser/sync", + "//chrome/browser/ui:browser_list", + "//chrome/browser/ui/actions:actions_headers", + "//chrome/browser/ui/browser_window", + "//chrome/browser/ui/views/toolbar", + "//components/send_tab_to_self", + ] + public_deps = [ "//chrome/browser:browser_public_dependencies" ] +} + +if (!is_android) { + source_set("browser_tests") { + testonly = true + defines = [ "HAS_OUT_OF_PROC_TEST_RUNNER" ] + sources = [ "send_tab_to_self_toolbar_icon_controller_browsertest.cc" ] + + deps = [ + ":send_tab_to_self", + "//chrome/browser/ui/browser_window", + "//chrome/browser/web_applications", + "//chrome/browser/web_applications:web_applications_test_support", + "//chrome/test:test_support", + "//chrome/test:test_support_ui", + "//components/send_tab_to_self", + "//content/test:test_support", + "//ui/base:ozone_buildflags", + ] + } +}
diff --git a/chrome/browser/ui/tabs/BUILD.gn b/chrome/browser/ui/tabs/BUILD.gn index 912c65f..c69a8bc 100644 --- a/chrome/browser/ui/tabs/BUILD.gn +++ b/chrome/browser/ui/tabs/BUILD.gn
@@ -236,6 +236,7 @@ "//chrome/browser/ui:browser_list", "//chrome/browser/ui:ui_features", "//chrome/browser/ui/commerce:commerce", + "//chrome/browser/ui/send_tab_to_self", "//chrome/browser/web_applications", "//components/commerce/core:utils", "//components/content_settings/core/browser",
diff --git a/chrome/browser/ui/tabs/saved_tab_groups/collaboration_messaging_observer_browsertest.cc b/chrome/browser/ui/tabs/saved_tab_groups/collaboration_messaging_observer_browsertest.cc index 1a8384f..0a744f3 100644 --- a/chrome/browser/ui/tabs/saved_tab_groups/collaboration_messaging_observer_browsertest.cc +++ b/chrome/browser/ui/tabs/saved_tab_groups/collaboration_messaging_observer_browsertest.cc
@@ -129,11 +129,8 @@ public: CollaborationMessagingObserverBrowserTest() { features_.InitWithFeatures( - { - tab_groups::kTabGroupSyncServiceDesktopMigration, - data_sharing::features::kDataSharingFeature, - toast_features::kToastFramework, - }, + {tab_groups::kTabGroupSyncServiceDesktopMigration, + data_sharing::features::kDataSharingFeature}, {}); } ~CollaborationMessagingObserverBrowserTest() override = default;
diff --git a/chrome/browser/ui/tabs/saved_tab_groups/instant_message_queue_processor.cc b/chrome/browser/ui/tabs/saved_tab_groups/instant_message_queue_processor.cc index 655c5dc..94e574b6 100644 --- a/chrome/browser/ui/tabs/saved_tab_groups/instant_message_queue_processor.cc +++ b/chrome/browser/ui/tabs/saved_tab_groups/instant_message_queue_processor.cc
@@ -49,10 +49,6 @@ void InstantMessageQueueProcessor::Enqueue(InstantMessage message, SuccessCallback success_callback) { - if (!base::FeatureList::IsEnabled(toast_features::kToastFramework)) { - return; - } - if (message.level != collaboration::messaging::InstantNotificationLevel::BROWSER) { // Only handle browser notifications. @@ -206,9 +202,8 @@ // to show the next message. // TODO(crbug.com/390814333): Determine the correct heuristic for // time-between-messages. - return base::Seconds(1) + - std::max(toast_features::kToastTimeout.Get(), - toast_features::kToastWithoutActionTimeout.Get()); + return base::Seconds(1) + std::max(ToastController::kToastDefaultTimeout, + ToastController::kToastWithActionTimeout); } void InstantMessageQueueProcessor::ProcessQueueAfterMessageShown() {
diff --git a/chrome/browser/ui/tabs/saved_tab_groups/instant_message_queue_processor_unittest.cc b/chrome/browser/ui/tabs/saved_tab_groups/instant_message_queue_processor_unittest.cc index 2d9cb38..8855261 100644 --- a/chrome/browser/ui/tabs/saved_tab_groups/instant_message_queue_processor_unittest.cc +++ b/chrome/browser/ui/tabs/saved_tab_groups/instant_message_queue_processor_unittest.cc
@@ -93,7 +93,6 @@ class InstantMessageQueueProcessorTest : public testing::Test { protected: void SetUp() override { - feature_list_.InitAndEnableFeature(toast_features::kToastFramework); processor_ = std::make_unique<TestInstantMessageQueueProcessor>(); } @@ -107,7 +106,6 @@ base::test::TaskEnvironment task_environment_{ base::test::TaskEnvironment::TimeSource::MOCK_TIME}; std::unique_ptr<TestInstantMessageQueueProcessor> processor_; - base::test::ScopedFeatureList feature_list_; }; TEST_F(InstantMessageQueueProcessorTest, IgnoresUnsupportedEvents) {
diff --git a/chrome/browser/ui/toasts/toast_controller.cc b/chrome/browser/ui/toasts/toast_controller.cc index f29acde..a788f12 100644 --- a/chrome/browser/ui/toasts/toast_controller.cc +++ b/chrome/browser/ui/toasts/toast_controller.cc
@@ -70,9 +70,6 @@ } bool ToastController::CanShowToast(ToastId toast_id) const { - if (!base::FeatureList::IsEnabled(toast_features::kToastFramework)) { - return false; - } if (base::FeatureList::IsEnabled(toast_features::kToastRefinements) && static_cast<toasts::ToastAlertLevel>( g_browser_process->local_state()->GetInteger( @@ -235,8 +232,7 @@ current_toast_spec->action_button_string_id().has_value() || current_toast_spec->has_menu(); base::TimeDelta timeout = - is_actionable ? toast_features::kToastTimeout.Get() - : toast_features::kToastWithoutActionTimeout.Get(); + is_actionable ? kToastWithActionTimeout : kToastDefaultTimeout; toast_close_timer_.Start( FROM_HERE, timeout,
diff --git a/chrome/browser/ui/toasts/toast_controller.h b/chrome/browser/ui/toasts/toast_controller.h index 3f3ea75..8f8e2d6 100644 --- a/chrome/browser/ui/toasts/toast_controller.h +++ b/chrome/browser/ui/toasts/toast_controller.h
@@ -98,11 +98,13 @@ void WebContentsDestroyed() override; views::Widget* GetToastWidgetForTesting() { return toast_widget_; } - toasts::ToastView* GetToastViewForTesting() { return toast_view_; } base::OneShotTimer* GetToastCloseTimerForTesting(); + static constexpr base::TimeDelta kToastDefaultTimeout = base::Seconds(4); + static constexpr base::TimeDelta kToastWithActionTimeout = base::Seconds(8); + private: void OnActiveTabChanged(BrowserWindowInterface* browser_interface); void QueueToast(ToastParams params);
diff --git a/chrome/browser/ui/toasts/toast_controller_interactive_ui_test.cc b/chrome/browser/ui/toasts/toast_controller_interactive_ui_test.cc index cd79b237..d57f6cc 100644 --- a/chrome/browser/ui/toasts/toast_controller_interactive_ui_test.cc +++ b/chrome/browser/ui/toasts/toast_controller_interactive_ui_test.cc
@@ -118,9 +118,8 @@ public: void SetUp() override { feature_list_.InitWithFeatures( - {toast_features::kToastFramework, toast_features::kToastRefinements, - toast_features::kLinkCopiedToast, toast_features::kImageCopiedToast, - toast_features::kReadingListToast, + {toast_features::kToastRefinements, toast_features::kLinkCopiedToast, + toast_features::kImageCopiedToast, toast_features::kReadingListToast, toast_features::kPinnedTabToastOnClose, plus_addresses::features::kPlusAddressesEnabled, plus_addresses::features::kPlusAddressFullFormFill},
diff --git a/chrome/browser/ui/toasts/toast_controller_unittest.cc b/chrome/browser/ui/toasts/toast_controller_unittest.cc index 971463a..e9ae953 100644 --- a/chrome/browser/ui/toasts/toast_controller_unittest.cc +++ b/chrome/browser/ui/toasts/toast_controller_unittest.cc
@@ -44,9 +44,6 @@ class ToastControllerUnitTest : public testing::Test { public: void SetUp() override { - feature_list_.InitAndEnableFeatureWithParameters( - toast_features::kToastFramework, - {{toast_features::kToastWithoutActionTimeout.name, "8s"}}); toast_registry_ = std::make_unique<ToastRegistry>(); } @@ -145,8 +142,7 @@ EXPECT_TRUE(controller->IsShowingToast()); // The toast should stop showing after reaching toast timeout time. - task_environment().FastForwardBy( - toast_features::kToastWithoutActionTimeout.Get()); + task_environment().FastForwardBy(ToastController::kToastDefaultTimeout); EXPECT_FALSE(controller->IsShowingToast()); } @@ -164,7 +160,7 @@ EXPECT_TRUE(controller->IsShowingToast()); // The toast should stop showing after reaching toast timeout time. - task_environment().FastForwardBy(toast_features::kToastTimeout.Get()); + task_environment().FastForwardBy(ToastController::kToastDefaultTimeout); EXPECT_FALSE(controller->IsShowingToast()); } @@ -188,7 +184,7 @@ // The toast should still be showing because we didn't reach the time out time // yet. - task_environment().FastForwardBy(toast_features::kToastTimeout.Get() / 2); + task_environment().FastForwardBy(ToastController::kToastDefaultTimeout / 2); EXPECT_TRUE(controller->IsShowingToast()); // Show a different toast before the link copied toast times out. @@ -199,6 +195,6 @@ // The image copied toast should still be showing even though the link copied // toast should have timed out by now. - task_environment().FastForwardBy(toast_features::kToastTimeout.Get() / 2); + task_environment().FastForwardBy(ToastController::kToastDefaultTimeout / 2); EXPECT_TRUE(controller->IsShowingToast()); }
diff --git a/chrome/browser/ui/toasts/toast_features.cc b/chrome/browser/ui/toasts/toast_features.cc index da35feb..61b4d27 100644 --- a/chrome/browser/ui/toasts/toast_features.cc +++ b/chrome/browser/ui/toasts/toast_features.cc
@@ -10,27 +10,12 @@ namespace toast_features { -// Enables the new toast framework that allows features to trigger toasts. When -// this feature is disabled, no toasts will show. -BASE_FEATURE(kToastFramework, - "ToastFramework", - base::FEATURE_ENABLED_BY_DEFAULT); - // Enables refinements of the toast framework that allow for controlling the // visibility of non-actionable toasts. BASE_FEATURE(kToastRefinements, "ToastRefinements", base::FEATURE_DISABLED_BY_DEFAULT); -const base::FeatureParam<bool> kToastDemoMode{&kToastFramework, - "toast_demo_mode", false}; - -const base::FeatureParam<base::TimeDelta> kToastTimeout{ - &kToastFramework, "toast_timeout", base::Seconds(8)}; - -const base::FeatureParam<base::TimeDelta> kToastWithoutActionTimeout{ - &kToastFramework, "toast_without_action_timeout", base::Seconds(4)}; - // Enables the link copied confirmation toast. BASE_FEATURE(kLinkCopiedToast, "LinkCopiedToast", @@ -73,7 +58,7 @@ // static bool IsEnabled(const base::Feature& feature) { - return kToastDemoMode.Get() || base::FeatureList::IsEnabled(feature); + return base::FeatureList::IsEnabled(feature); } } // namespace toast_features
diff --git a/chrome/browser/ui/toasts/toast_features.h b/chrome/browser/ui/toasts/toast_features.h index 99824d99..5e003c7 100644 --- a/chrome/browser/ui/toasts/toast_features.h +++ b/chrome/browser/ui/toasts/toast_features.h
@@ -11,23 +11,12 @@ namespace toast_features { -// Base feature -BASE_DECLARE_FEATURE(kToastFramework); - BASE_DECLARE_FEATURE(kToastRefinements); // Enables all toast features queried through `toast_features::IsEnabled` which // is used for demo mode. extern const base::FeatureParam<bool> kToastDemoMode; -// The amount of time a toast should show before automatically -// closing. -extern const base::FeatureParam<base::TimeDelta> kToastTimeout; - -// The amount of time a toast without an action should show before -// automatically closing. -extern const base::FeatureParam<base::TimeDelta> kToastWithoutActionTimeout; - // Individual toasts BASE_DECLARE_FEATURE(kLinkCopiedToast); BASE_DECLARE_FEATURE(kImageCopiedToast);
diff --git a/chrome/browser/ui/toasts/toast_service_browsertest.cc b/chrome/browser/ui/toasts/toast_service_browsertest.cc index 4efa08c0..3022be32 100644 --- a/chrome/browser/ui/toasts/toast_service_browsertest.cc +++ b/chrome/browser/ui/toasts/toast_service_browsertest.cc
@@ -40,8 +40,7 @@ public: void SetUp() override { feature_list_.InitWithFeatures( - {toast_features::kToastFramework, commerce::kCompareConfirmationToast, - commerce::kProductSpecifications, + {commerce::kCompareConfirmationToast, commerce::kProductSpecifications, plus_addresses::features::kPlusAddressesEnabled, plus_addresses::features::kPlusAddressFullFormFill, safe_browsing::kEsbAsASyncedSetting,
diff --git a/chrome/browser/ui/views/autofill/payments/save_and_fill_dialog_views.cc b/chrome/browser/ui/views/autofill/payments/save_and_fill_dialog.cc similarity index 74% rename from chrome/browser/ui/views/autofill/payments/save_and_fill_dialog_views.cc rename to chrome/browser/ui/views/autofill/payments/save_and_fill_dialog.cc index cfe7880..fa68ad89 100644 --- a/chrome/browser/ui/views/autofill/payments/save_and_fill_dialog_views.cc +++ b/chrome/browser/ui/views/autofill/payments/save_and_fill_dialog.cc
@@ -2,16 +2,11 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "chrome/browser/ui/views/autofill/payments/save_and_fill_dialog_views.h" +#include "chrome/browser/ui/views/autofill/payments/save_and_fill_dialog.h" -#include "base/memory/weak_ptr.h" -#include "chrome/browser/ui/autofill/payments/payments_view_factory.h" #include "chrome/browser/ui/views/autofill/payments/payments_view_util.h" #include "chrome/browser/ui/views/chrome_layout_provider.h" #include "components/autofill/core/browser/ui/payments/save_and_fill_dialog_controller.h" -#include "components/constrained_window/constrained_window_views.h" -#include "components/strings/grit/components_strings.h" -#include "ui/base/l10n/l10n_util.h" #include "ui/base/mojom/dialog_button.mojom.h" #include "ui/views/accessibility/view_accessibility.h" #include "ui/views/bubble/bubble_frame_view.h" @@ -19,9 +14,14 @@ namespace autofill { -SaveAndFillDialogViews::SaveAndFillDialogViews( +SaveAndFillDialog::SaveAndFillDialog( base::WeakPtr<SaveAndFillDialogController> controller) : controller_(controller) { + // Set the ownership of the delegate, not the View. The View is owned by the + // Widget as a child view. + // TODO(crbug.com/338254375): Remove the following line once this is the + // default state for widgets. + SetOwnershipOfNewWidget(views::Widget::InitParams::CLIENT_OWNS_WIDGET); SetModalType(ui::mojom::ModalType::kChild); set_fixed_width(views::LayoutProvider::Get()->GetDistanceMetric( views::DISTANCE_MODAL_DIALOG_PREFERRED_WIDTH)); @@ -33,21 +33,9 @@ InitViews(); } -SaveAndFillDialogViews::~SaveAndFillDialogViews() = default; +SaveAndFillDialog::~SaveAndFillDialog() = default; -base::WeakPtr<SaveAndFillDialogView> SaveAndFillDialogViews::GetWeakPtr() { - return weak_ptr_factory_.GetWeakPtr(); -} - -base::WeakPtr<SaveAndFillDialogView> CreateAndShowSaveAndFillDialog( - base::WeakPtr<SaveAndFillDialogController> controller, - content::WebContents* web_contents) { - SaveAndFillDialogViews* dialog_view = new SaveAndFillDialogViews(controller); - constrained_window::ShowWebModalDialogViews(dialog_view, web_contents); - return dialog_view->GetWeakPtr(); -} - -void SaveAndFillDialogViews::AddedToWidget() { +void SaveAndFillDialog::AddedToWidget() { if (controller_->IsUploadSaveAndFill()) { GetBubbleFrameView()->SetTitleView( std::make_unique<TitleWithIconAfterLabelView>( @@ -61,11 +49,11 @@ } } -std::u16string SaveAndFillDialogViews::GetWindowTitle() const { +std::u16string SaveAndFillDialog::GetWindowTitle() const { return controller_ ? controller_->GetWindowTitle() : std::u16string(); } -void SaveAndFillDialogViews::InitViews() { +void SaveAndFillDialog::InitViews() { auto* layout = SetLayoutManager(std::make_unique<views::BoxLayout>( views::BoxLayout::Orientation::kVertical, gfx::Insets(), ChromeLayoutProvider::Get()->GetDistanceMetric(
diff --git a/chrome/browser/ui/views/autofill/payments/save_and_fill_dialog_views.h b/chrome/browser/ui/views/autofill/payments/save_and_fill_dialog.h similarity index 60% rename from chrome/browser/ui/views/autofill/payments/save_and_fill_dialog_views.h rename to chrome/browser/ui/views/autofill/payments/save_and_fill_dialog.h index 630ab9b..d6d3fc8 100644 --- a/chrome/browser/ui/views/autofill/payments/save_and_fill_dialog_views.h +++ b/chrome/browser/ui/views/autofill/payments/save_and_fill_dialog.h
@@ -2,8 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifndef CHROME_BROWSER_UI_VIEWS_AUTOFILL_PAYMENTS_SAVE_AND_FILL_DIALOG_VIEWS_H_ -#define CHROME_BROWSER_UI_VIEWS_AUTOFILL_PAYMENTS_SAVE_AND_FILL_DIALOG_VIEWS_H_ +#ifndef CHROME_BROWSER_UI_VIEWS_AUTOFILL_PAYMENTS_SAVE_AND_FILL_DIALOG_H_ +#define CHROME_BROWSER_UI_VIEWS_AUTOFILL_PAYMENTS_SAVE_AND_FILL_DIALOG_H_ #include "components/autofill/core/browser/ui/payments/save_and_fill_dialog_view.h" #include "ui/views/window/dialog_delegate.h" @@ -12,17 +12,15 @@ class SaveAndFillDialogController; -class SaveAndFillDialogViews : public SaveAndFillDialogView, - public views::DialogDelegateView { +// The dialog delegate view implementation for the Save and Fill dialog view. +// This is owned by the view hierarchy. +class SaveAndFillDialog : public views::DialogDelegateView { public: - explicit SaveAndFillDialogViews( + explicit SaveAndFillDialog( base::WeakPtr<SaveAndFillDialogController> controller); - SaveAndFillDialogViews(const SaveAndFillDialogViews&) = delete; - SaveAndFillDialogViews& operator=(const SaveAndFillDialogViews&) = delete; - ~SaveAndFillDialogViews() override; - - // SaveAndFillDialogView: - base::WeakPtr<SaveAndFillDialogView> GetWeakPtr() override; + SaveAndFillDialog(const SaveAndFillDialog&) = delete; + SaveAndFillDialog& operator=(const SaveAndFillDialog&) = delete; + ~SaveAndFillDialog() override; // DialogDelegateView: void AddedToWidget() override; @@ -33,10 +31,8 @@ void InitViews(); base::WeakPtr<SaveAndFillDialogController> controller_; - - base::WeakPtrFactory<SaveAndFillDialogViews> weak_ptr_factory_{this}; }; } // namespace autofill -#endif // CHROME_BROWSER_UI_VIEWS_AUTOFILL_PAYMENTS_SAVE_AND_FILL_DIALOG_VIEWS_H_ +#endif // CHROME_BROWSER_UI_VIEWS_AUTOFILL_PAYMENTS_SAVE_AND_FILL_DIALOG_H_
diff --git a/chrome/browser/ui/views/autofill/payments/save_and_fill_view_desktop.cc b/chrome/browser/ui/views/autofill/payments/save_and_fill_view_desktop.cc new file mode 100644 index 0000000..7576961 --- /dev/null +++ b/chrome/browser/ui/views/autofill/payments/save_and_fill_view_desktop.cc
@@ -0,0 +1,40 @@ +// Copyright 2025 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chrome/browser/ui/views/autofill/payments/save_and_fill_view_desktop.h" + +#include "base/memory/weak_ptr.h" +#include "chrome/browser/ui/autofill/payments/payments_view_factory.h" +#include "chrome/browser/ui/tabs/public/tab_dialog_manager.h" +#include "chrome/browser/ui/tabs/public/tab_features.h" +#include "chrome/browser/ui/views/autofill/payments/save_and_fill_dialog.h" +#include "components/autofill/core/browser/ui/payments/save_and_fill_dialog_controller.h" +#include "components/tabs/public/tab_interface.h" +#include "content/public/browser/web_contents.h" + +using tabs::TabInterface; + +namespace autofill { + +std::unique_ptr<SaveAndFillDialogView> CreateAndShowSaveAndFillDialog( + base::WeakPtr<SaveAndFillDialogController> controller, + content::WebContents* web_contents) { + return std::make_unique<SaveAndFillViewDesktop>(controller, web_contents); +} + +SaveAndFillViewDesktop::SaveAndFillViewDesktop( + base::WeakPtr<SaveAndFillDialogController> controller, + content::WebContents* web_contents) { + auto dialog_view = std::make_unique<SaveAndFillDialog>(controller); + TabInterface* tab_interface = TabInterface::GetFromContents(web_contents); + CHECK(tab_interface); + dialog_widget_ = + tab_interface->GetTabFeatures() + ->tab_dialog_manager() + ->CreateShowDialogAndBlockTabInteraction(dialog_view.release()); +} + +SaveAndFillViewDesktop::~SaveAndFillViewDesktop() = default; + +} // namespace autofill
diff --git a/chrome/browser/ui/views/autofill/payments/save_and_fill_view_desktop.h b/chrome/browser/ui/views/autofill/payments/save_and_fill_view_desktop.h new file mode 100644 index 0000000..97b5edb --- /dev/null +++ b/chrome/browser/ui/views/autofill/payments/save_and_fill_view_desktop.h
@@ -0,0 +1,36 @@ +// Copyright 2025 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CHROME_BROWSER_UI_VIEWS_AUTOFILL_PAYMENTS_SAVE_AND_FILL_VIEW_DESKTOP_H_ +#define CHROME_BROWSER_UI_VIEWS_AUTOFILL_PAYMENTS_SAVE_AND_FILL_VIEW_DESKTOP_H_ + +#include "components/autofill/core/browser/ui/payments/save_and_fill_dialog_view.h" +#include "ui/views/widget/widget.h" + +namespace content { +class WebContents; +} // namespace content + +namespace autofill { + +class SaveAndFillDialogController; + +// This class is the desktop implementation of the Save and Fill view +// container (or the "View" in MVC) and owns the widget for +// SaveAndFillDialogView. +class SaveAndFillViewDesktop : public SaveAndFillDialogView { + public: + SaveAndFillViewDesktop(base::WeakPtr<SaveAndFillDialogController> controller, + content::WebContents* web_contents); + SaveAndFillViewDesktop(const SaveAndFillViewDesktop&) = delete; + SaveAndFillViewDesktop& operator=(const SaveAndFillViewDesktop&) = delete; + ~SaveAndFillViewDesktop() override; + + private: + std::unique_ptr<views::Widget> dialog_widget_; +}; + +} // namespace autofill + +#endif // CHROME_BROWSER_UI_VIEWS_AUTOFILL_PAYMENTS_SAVE_AND_FILL_VIEW_DESKTOP_H_
diff --git a/chrome/browser/ui/views/bookmarks/bookmark_button_util.h b/chrome/browser/ui/views/bookmarks/bookmark_button_util.h index ba35f7c..0fbcabd 100644 --- a/chrome/browser/ui/views/bookmarks/bookmark_button_util.h +++ b/chrome/browser/ui/views/bookmarks/bookmark_button_util.h
@@ -14,7 +14,7 @@ namespace bookmark_button_util { // Max width of the buttons in the bookmark bar. -constexpr int kMaxButtonWidth = 150; +inline constexpr int kMaxButtonWidth = 150; std::unique_ptr<views::LabelButtonBorder> CreateBookmarkButtonBorder();
diff --git a/chrome/browser/ui/views/commerce/product_specifications_icon_view_integration_test.cc b/chrome/browser/ui/views/commerce/product_specifications_icon_view_integration_test.cc index e7cbbf0f..ec5b349 100644 --- a/chrome/browser/ui/views/commerce/product_specifications_icon_view_integration_test.cc +++ b/chrome/browser/ui/views/commerce/product_specifications_icon_view_integration_test.cc
@@ -56,7 +56,6 @@ std::vector<base::test::FeatureRef> enabled_features = { commerce::kProductSpecifications}; if (GetParam()) { - enabled_features.push_back(toast_features::kToastFramework); enabled_features.push_back(commerce::kCompareConfirmationToast); } test_features_.InitWithFeatures(enabled_features, /*disabled_features*/ {});
diff --git a/chrome/browser/ui/views/frame/browser_view_layout_unittest.cc b/chrome/browser/ui/views/frame/browser_view_layout_unittest.cc index ccf2bba1..8e404d4 100644 --- a/chrome/browser/ui/views/frame/browser_view_layout_unittest.cc +++ b/chrome/browser/ui/views/frame/browser_view_layout_unittest.cc
@@ -26,6 +26,8 @@ // Save space for the separator. constexpr int kToolbarHeight = 30 - views::Separator::kThickness; +constexpr int kNewTabFooterHeight = 56; +constexpr int kBaseWidth = 800; class MockBrowserViewLayoutDelegate : public BrowserViewLayoutDelegate { public: @@ -161,7 +163,8 @@ infobar_container_(nullptr), contents_container_(nullptr), contents_web_view_(nullptr), - devtools_web_view_(nullptr) {} + devtools_web_view_(nullptr), + new_tab_footer_web_view_(nullptr) {} BrowserViewLayoutTest(const BrowserViewLayoutTest&) = delete; BrowserViewLayoutTest& operator=(const BrowserViewLayoutTest&) = delete; @@ -182,23 +185,23 @@ void SetUp() override { ChromeViewsTestBase::SetUp(); - browser_view_ = CreateFixedSizeView(gfx::Size(800, 600)); + browser_view_ = CreateFixedSizeView(gfx::Size(kBaseWidth, 600)); immersive_mode_controller_ = std::make_unique<MockImmersiveModeController>(); - top_container_ = - browser_view_->AddChildView(CreateFixedSizeView(gfx::Size(800, 60))); + top_container_ = browser_view_->AddChildView( + CreateFixedSizeView(gfx::Size(kBaseWidth, 60))); auto tab_strip = std::make_unique<TabStrip>( std::make_unique<FakeBaseTabStripController>()); tab_strip_ = tab_strip.get(); TabStripRegionView* tab_strip_region_view = top_container_->AddChildView( std::make_unique<TabStripRegionView>(std::move(tab_strip))); - webui_tab_strip_ = - top_container_->AddChildView(CreateFixedSizeView(gfx::Size(800, 200))); + webui_tab_strip_ = top_container_->AddChildView( + CreateFixedSizeView(gfx::Size(kBaseWidth, 200))); webui_tab_strip_->SetVisible(false); toolbar_ = top_container_->AddChildView( - CreateFixedSizeView(gfx::Size(800, kToolbarHeight))); + CreateFixedSizeView(gfx::Size(kBaseWidth, kToolbarHeight))); separator_ = top_container_->AddChildView(std::make_unique<views::Separator>()); @@ -208,25 +211,29 @@ side_panel_rounded_corner_ = browser_view_->AddChildView(CreateFixedSizeView(gfx::Size(16, 16))); - contents_container_ = - browser_view_->AddChildView(CreateFixedSizeView(gfx::Size(800, 600))); + contents_container_ = browser_view_->AddChildView( + CreateFixedSizeView(gfx::Size(kBaseWidth, 600))); devtools_web_view_ = contents_container_->AddChildView( - CreateFixedSizeView(gfx::Size(800, 600))); + CreateFixedSizeView(gfx::Size(kBaseWidth, 600))); devtools_web_view_->SetVisible(false); devtools_scrim_view_ = contents_container_->AddChildView( - CreateFixedSizeView(gfx::Size(800, 600))); + CreateFixedSizeView(gfx::Size(kBaseWidth, 600))); devtools_scrim_view_->SetVisible(false); + new_tab_footer_web_view_ = contents_container_->AddChildView( + CreateFixedSizeView(gfx::Size(kBaseWidth, kNewTabFooterHeight))); + new_tab_footer_web_view_->SetVisible(false); contents_web_view_ = contents_container_->AddChildView( - CreateFixedSizeView(gfx::Size(800, 600))); + CreateFixedSizeView(gfx::Size(kBaseWidth, 600))); contents_scrim_view_ = contents_container_->AddChildView( - CreateFixedSizeView(gfx::Size(800, 600))); + CreateFixedSizeView(gfx::Size(kBaseWidth, 600))); lens_overlay_view_ = contents_container_->AddChildView( - CreateFixedSizeView(gfx::Size(800, 600))); + CreateFixedSizeView(gfx::Size(kBaseWidth, 600))); contents_container_->SetLayoutManager( std::make_unique<ContentsLayoutManager>( devtools_web_view_, devtools_scrim_view_, contents_web_view_, lens_overlay_view_, contents_scrim_view_, - /*contents_border_view=*/nullptr, /*watermark_view=*/nullptr)); + /*contents_border_view=*/nullptr, /*watermark_view=*/nullptr, + new_tab_footer_web_view_)); auto delegate = std::make_unique<MockBrowserViewLayoutDelegate>(); delegate_ = delegate.get(); @@ -282,6 +289,7 @@ raw_ptr<views::View> devtools_scrim_view_; raw_ptr<views::View> contents_scrim_view_; raw_ptr<views::View> lens_overlay_view_; + raw_ptr<views::View> new_tab_footer_web_view_; std::unique_ptr<MockImmersiveModeController> immersive_mode_controller_; }; @@ -302,10 +310,10 @@ // Top views are zero-height. EXPECT_EQ(gfx::Rect(0, 0, 0, 0), tab_strip()->bounds()); - EXPECT_EQ(gfx::Rect(0, 0, 800, 0), toolbar()->bounds()); + EXPECT_EQ(gfx::Rect(0, 0, kBaseWidth, 0), toolbar()->bounds()); EXPECT_EQ(gfx::Rect(0, 0, 0, 0), infobar_container()->bounds()); // Contents split fills the window. - EXPECT_EQ(gfx::Rect(0, 0, 800, 600), contents_container()->bounds()); + EXPECT_EQ(gfx::Rect(0, 0, kBaseWidth, 600), contents_container()->bounds()); // Turn on the toolbar, like in a pop-up window. delegate()->set_toolbar_visible(true); @@ -313,11 +321,12 @@ // Now the toolbar has bounds and other views shift down. EXPECT_EQ(gfx::Rect(0, 0, 0, 0), tab_strip()->bounds()); - EXPECT_EQ(gfx::Rect(0, 0, 800, kToolbarHeight), toolbar()->bounds()); - EXPECT_EQ(gfx::Rect(0, kToolbarHeight, 800, views::Separator::kThickness), - separator()->bounds()); + EXPECT_EQ(gfx::Rect(0, 0, kBaseWidth, kToolbarHeight), toolbar()->bounds()); + EXPECT_EQ( + gfx::Rect(0, kToolbarHeight, kBaseWidth, views::Separator::kThickness), + separator()->bounds()); EXPECT_EQ(gfx::Rect(0, 30, 0, 0), infobar_container()->bounds()); - EXPECT_EQ(gfx::Rect(0, 30, 800, 570), contents_container()->bounds()); + EXPECT_EQ(gfx::Rect(0, 30, kBaseWidth, 570), contents_container()->bounds()); // Disable the contents separator. delegate()->set_content_separator_enabled(false); @@ -325,10 +334,10 @@ // Now the separator is not visible and the content grows vertically. EXPECT_EQ(gfx::Rect(0, 0, 0, 0), tab_strip()->bounds()); - EXPECT_EQ(gfx::Rect(0, 0, 800, kToolbarHeight), toolbar()->bounds()); + EXPECT_EQ(gfx::Rect(0, 0, kBaseWidth, kToolbarHeight), toolbar()->bounds()); EXPECT_FALSE(separator()->GetVisible()); EXPECT_EQ(gfx::Rect(0, 29, 0, 0), infobar_container()->bounds()); - EXPECT_EQ(gfx::Rect(0, 29, 800, 571), contents_container()->bounds()); + EXPECT_EQ(gfx::Rect(0, 29, kBaseWidth, 571), contents_container()->bounds()); // TODO(jamescook): Tab strip and bookmark bar. } @@ -336,7 +345,7 @@ TEST_F(BrowserViewLayoutTest, LayoutDownloadShelf) { constexpr int kHeight = 50; std::unique_ptr<views::View> download_shelf = - CreateFixedSizeView(gfx::Size(800, kHeight)); + CreateFixedSizeView(gfx::Size(kBaseWidth, kHeight)); layout()->set_download_shelf(download_shelf.get()); // If the download shelf isn't visible, it doesn't move the bottom edge. @@ -361,30 +370,33 @@ delegate()->set_top_controls_slide_enabled(true); delegate()->set_top_controls_shown_ratio(1.f); InvalidateAndRunScheduledLayoutOnBrowserView(); - EXPECT_EQ(gfx::Rect(0, 0, 800, 30), top_container()->bounds()); - EXPECT_EQ(gfx::Rect(0, 0, 800, kToolbarHeight), toolbar()->bounds()); - EXPECT_EQ(gfx::Rect(0, kToolbarHeight, 800, views::Separator::kThickness), - separator()->bounds()); - EXPECT_EQ(gfx::Rect(0, 30, 800, 570), contents_container()->bounds()); + EXPECT_EQ(gfx::Rect(0, 0, kBaseWidth, 30), top_container()->bounds()); + EXPECT_EQ(gfx::Rect(0, 0, kBaseWidth, kToolbarHeight), toolbar()->bounds()); + EXPECT_EQ( + gfx::Rect(0, kToolbarHeight, kBaseWidth, views::Separator::kThickness), + separator()->bounds()); + EXPECT_EQ(gfx::Rect(0, 30, kBaseWidth, 570), contents_container()->bounds()); // Top controls are half shown, half hidden. delegate()->set_top_controls_shown_ratio(0.5f); InvalidateAndRunScheduledLayoutOnBrowserView(); - EXPECT_EQ(gfx::Rect(0, 0, 800, 30), top_container()->bounds()); - EXPECT_EQ(gfx::Rect(0, 0, 800, kToolbarHeight), toolbar()->bounds()); - EXPECT_EQ(gfx::Rect(0, kToolbarHeight, 800, views::Separator::kThickness), - separator()->bounds()); - EXPECT_EQ(gfx::Rect(0, 30, 800, 570), contents_container()->bounds()); + EXPECT_EQ(gfx::Rect(0, 0, kBaseWidth, 30), top_container()->bounds()); + EXPECT_EQ(gfx::Rect(0, 0, kBaseWidth, kToolbarHeight), toolbar()->bounds()); + EXPECT_EQ( + gfx::Rect(0, kToolbarHeight, kBaseWidth, views::Separator::kThickness), + separator()->bounds()); + EXPECT_EQ(gfx::Rect(0, 30, kBaseWidth, 570), contents_container()->bounds()); // Top controls are fully hidden. the contents are expanded in height by an // amount equal to the top controls height. delegate()->set_top_controls_shown_ratio(0.f); InvalidateAndRunScheduledLayoutOnBrowserView(); - EXPECT_EQ(gfx::Rect(0, -30, 800, 30), top_container()->bounds()); - EXPECT_EQ(gfx::Rect(0, 0, 800, kToolbarHeight), toolbar()->bounds()); - EXPECT_EQ(gfx::Rect(0, kToolbarHeight, 800, views::Separator::kThickness), - separator()->bounds()); - EXPECT_EQ(gfx::Rect(0, 0, 800, 600), contents_container()->bounds()); + EXPECT_EQ(gfx::Rect(0, -30, kBaseWidth, 30), top_container()->bounds()); + EXPECT_EQ(gfx::Rect(0, 0, kBaseWidth, kToolbarHeight), toolbar()->bounds()); + EXPECT_EQ( + gfx::Rect(0, kToolbarHeight, kBaseWidth, views::Separator::kThickness), + separator()->bounds()); + EXPECT_EQ(gfx::Rect(0, 0, kBaseWidth, 600), contents_container()->bounds()); } TEST_F(BrowserViewLayoutTest, WebUITabStripPushesDownContents) {
diff --git a/chrome/browser/ui/views/frame/contents_layout_manager.cc b/chrome/browser/ui/views/frame/contents_layout_manager.cc index c55fd05..09ce008 100644 --- a/chrome/browser/ui/views/frame/contents_layout_manager.cc +++ b/chrome/browser/ui/views/frame/contents_layout_manager.cc
@@ -66,6 +66,18 @@ devtools_scrim_view_.get(), devtools_scrim_view_->GetVisible(), host_view()->GetMirroredRect(new_devtools_bounds), views::SizeBounds(container_size)); + + // New Tab Footer view is displayed at the bottom of the contents view. + if (new_tab_footer_view_ && new_tab_footer_view_->GetVisible()) { + new_contents_bounds.set_height(new_contents_bounds.height() - + kNewTabFooterHeight); + + layouts.child_layouts.emplace_back( + new_tab_footer_view_.get(), new_tab_footer_view_->GetVisible(), + gfx::Rect(0, new_contents_bounds.height(), width, kNewTabFooterHeight), + views::SizeBounds(container_size)); + } + const auto& contents_rect = host_view()->GetMirroredRect(new_contents_bounds); views::SizeBounds optional_size_bound = views::SizeBounds(container_size); layouts.child_layouts.emplace_back(contents_view_.get(), @@ -100,14 +112,6 @@ gfx::Rect(0, 0, width, height), views::SizeBounds(container_size)); } - // New Tab Footer view is displayed at the bottom of the contents view. - if (new_tab_footer_view_) { - layouts.child_layouts.emplace_back( - new_tab_footer_view_.get(), new_tab_footer_view_->GetVisible(), - gfx::Rect(0, height - kNewTabFooterHeight, width, kNewTabFooterHeight), - views::SizeBounds(container_size)); - } - layouts.host_size = gfx::Size(width, height); return layouts; }
diff --git a/chrome/browser/ui/views/media_router/cast_dialog_helper.h b/chrome/browser/ui/views/media_router/cast_dialog_helper.h index 3b0fd4e..073dd41 100644 --- a/chrome/browser/ui/views/media_router/cast_dialog_helper.h +++ b/chrome/browser/ui/views/media_router/cast_dialog_helper.h
@@ -12,7 +12,7 @@ namespace media_router { // Icon sizes in DIP. -constexpr int kPrimaryIconSize = 20; +inline constexpr int kPrimaryIconSize = 20; constexpr auto kPrimaryIconBorder = gfx::Insets(6); // Creates a view containing a throbber. The throbber has a border around it so
diff --git a/chrome/browser/ui/views/overlay/constants.h b/chrome/browser/ui/views/overlay/constants.h index 3255a30..b31d634 100644 --- a/chrome/browser/ui/views/overlay/constants.h +++ b/chrome/browser/ui/views/overlay/constants.h
@@ -5,10 +5,10 @@ #ifndef CHROME_BROWSER_UI_VIEWS_OVERLAY_CONSTANTS_H_ #define CHROME_BROWSER_UI_VIEWS_OVERLAY_CONSTANTS_H_ -constexpr int kPipWindowIconPadding = 8; +inline constexpr int kPipWindowIconPadding = 8; -constexpr int kPipWindowIconPadding2024 = 4; +inline constexpr int kPipWindowIconPadding2024 = 4; -constexpr int kCenterButtonSize = 48; +inline constexpr int kCenterButtonSize = 48; #endif // CHROME_BROWSER_UI_VIEWS_OVERLAY_CONSTANTS_H_
diff --git a/chrome/browser/ui/views/payments/payment_request_views_util.h b/chrome/browser/ui/views/payments/payment_request_views_util.h index c1b4bd748..b0bfba65 100644 --- a/chrome/browser/ui/views/payments/payment_request_views_util.h +++ b/chrome/browser/ui/views/payments/payment_request_views_util.h
@@ -30,25 +30,25 @@ class PaymentsProfileComparator; enum class PaymentShippingType; -constexpr int kPaymentRequestRowHorizontalInsets = 16; -constexpr int kPaymentRequestRowVerticalInsets = 8; +inline constexpr int kPaymentRequestRowHorizontalInsets = 16; +inline constexpr int kPaymentRequestRowVerticalInsets = 8; // Extra inset relative to the header when a right edge should line up with the // close button's X rather than its invisible right edge. -constexpr int kPaymentRequestRowExtraRightInset = 8; -constexpr int kPaymentRequestButtonSpacing = 10; +inline constexpr int kPaymentRequestRowExtraRightInset = 8; +inline constexpr int kPaymentRequestButtonSpacing = 10; // Dimensions of the dialog itself. -constexpr int kDialogMinWidth = 512; -constexpr int kDialogHeight = 450; +inline constexpr int kDialogMinWidth = 512; +inline constexpr int kDialogHeight = 450; // Preferred dimensions of the payment handler dialog in pixels. -constexpr int kPreferredPaymentHandlerDialogWidth = 608; -constexpr int kPreferredPaymentHandlerDialogHeight = 600; +inline constexpr int kPreferredPaymentHandlerDialogWidth = 608; +inline constexpr int kPreferredPaymentHandlerDialogHeight = 600; // Fixed width of the amount sections in the payment sheet and the order summary // sheet, in pixels. -constexpr int kAmountSectionWidth = 96; +inline constexpr int kAmountSectionWidth = 96; // Returns an instrument image view for the given |icon_bitmap| or // |icon_resource_id| and wanted |opacity|. Includes a rounded rect border.
diff --git a/chrome/browser/ui/views/promos/ios_promo_constants.h b/chrome/browser/ui/views/promos/ios_promo_constants.h index b574f2d..1355554 100644 --- a/chrome/browser/ui/views/promos/ios_promo_constants.h +++ b/chrome/browser/ui/views/promos/ios_promo_constants.h
@@ -21,7 +21,7 @@ // Size of the QR code image view including the quiet zone margin added by the // QR code generator. -const int kQrCodeImageSize = 90; +inline constexpr int kQrCodeImageSize = 90; struct IOSPromoTypeConfigs { int bubble_title_id = -1;
diff --git a/chrome/browser/ui/views/tabs/tab_style_views.cc b/chrome/browser/ui/views/tabs/tab_style_views.cc index 9015c37b..7179ba64 100644 --- a/chrome/browser/ui/views/tabs/tab_style_views.cc +++ b/chrome/browser/ui/views/tabs/tab_style_views.cc
@@ -367,8 +367,12 @@ const float stroke_adjustment = stroke_thickness * scale; if (path_type == TabStyle::PathType::kFill || path_type == TabStyle::PathType::kBorder) { - tab_left += 0.5f * stroke_adjustment; - tab_right -= 0.5f * stroke_adjustment; + if (!IsRightSplitTab(tab())) { + tab_left += 0.5f * stroke_adjustment; + } + if (!IsLeftSplitTab(tab())) { + tab_right -= 0.5f * stroke_adjustment; + } tab_top += 0.5f * stroke_adjustment; content_corner_radius -= 0.5f * stroke_adjustment; tab_bottom -= 0.5f * stroke_adjustment; @@ -385,17 +389,15 @@ scale; } - if (tab()->split().has_value()) { - if (IsLeftSplitTab(tab())) { - top_right_corner_radius = 0; - // Assign half of the tab overlap to each of the split tabs. - tab_right = tab_right + extension - tab_style()->GetTabOverlap() / 2; - extension_corner_radius = 0; - } else if (IsRightSplitTab(tab())) { - top_left_corner_radius = 0; - tab_left = tab_left - extension + tab_style()->GetTabOverlap() / 2; - left_extension_corner_radius = 0; - } + if (IsLeftSplitTab(tab())) { + top_right_corner_radius = 0; + // Assign half of the tab overlap to each of the split tabs. + tab_right = tab_right + extension - tab_style()->GetTabOverlap() / 2; + extension_corner_radius = 0; + } else if (IsRightSplitTab(tab())) { + top_left_corner_radius = 0; + tab_left = tab_left - extension + tab_style()->GetTabOverlap() / 2; + left_extension_corner_radius = 0; } // Avoid mallocs at every new path verb by preallocating an
diff --git a/chrome/browser/ui/views/toolbar/toolbar_ink_drop_util.h b/chrome/browser/ui/views/toolbar/toolbar_ink_drop_util.h index ec34cb0..742f7f4 100644 --- a/chrome/browser/ui/views/toolbar/toolbar_ink_drop_util.h +++ b/chrome/browser/ui/views/toolbar/toolbar_ink_drop_util.h
@@ -16,7 +16,7 @@ class View; } // namespace views -constexpr float kToolbarInkDropVisibleOpacity = 0.06f; +inline constexpr float kToolbarInkDropVisibleOpacity = 0.06f; // Creates insets for a host view so that when insetting from the host view // the resulting mask or inkdrop has the desired inkdrop size.
diff --git a/chrome/browser/ui/views/user_education/tutorial_interactive_uitest.cc b/chrome/browser/ui/views/user_education/tutorial_interactive_uitest.cc index 66c13ff..5e6a934 100644 --- a/chrome/browser/ui/views/user_education/tutorial_interactive_uitest.cc +++ b/chrome/browser/ui/views/user_education/tutorial_interactive_uitest.cc
@@ -179,7 +179,8 @@ auto StartTutorial(ui::ElementIdentifier page_id) { DEFINE_LOCAL_CUSTOM_ELEMENT_EVENT_TYPE(kHelpBubbleShownEvent); StateChange help_bubble_shown; - help_bubble_shown.where = {"ntp-app", "help-bubble"}; + help_bubble_shown.where = {"ntp-app", "ntp-customize-buttons", + "help-bubble"}; help_bubble_shown.type = StateChange::Type::kExists; help_bubble_shown.event = kHelpBubbleShownEvent; @@ -198,7 +199,8 @@ DEFINE_LOCAL_CUSTOM_ELEMENT_EVENT_TYPE(kHelpBubbleHiddenEvent); StateChange help_bubble_hidden; help_bubble_hidden.type = StateChange::Type::kDoesNotExist; - help_bubble_hidden.where = {"ntp-app", "help-bubble"}; + help_bubble_hidden.where = {"ntp-app", "ntp-customize-buttons", + "help-bubble"}; help_bubble_hidden.event = kHelpBubbleHiddenEvent; auto steps = Steps(Do([this]() {
diff --git a/chrome/browser/ui/views/web_apps/frame_toolbar/web_app_frame_toolbar_utils.h b/chrome/browser/ui/views/web_apps/frame_toolbar/web_app_frame_toolbar_utils.h index ea0fc53..f3fe49e9 100644 --- a/chrome/browser/ui/views/web_apps/frame_toolbar/web_app_frame_toolbar_utils.h +++ b/chrome/browser/ui/views/web_apps/frame_toolbar/web_app_frame_toolbar_utils.h
@@ -11,7 +11,7 @@ class ToolbarButtonProvider; #if BUILDFLAG(IS_MAC) -constexpr int kWebAppMenuMargin = 7; +inline constexpr int kWebAppMenuMargin = 7; #endif // Makes adjustments to |toolbar_button| for display in a web app frame.
diff --git a/chrome/browser/ui/views/webauthn/authenticator_common_views.h b/chrome/browser/ui/views/webauthn/authenticator_common_views.h index e650f81c..8c07e0f 100644 --- a/chrome/browser/ui/views/webauthn/authenticator_common_views.h +++ b/chrome/browser/ui/views/webauthn/authenticator_common_views.h
@@ -27,6 +27,6 @@ // +---------------------+ std::unique_ptr<views::View> CreateGpmIconWithLabel(); -constexpr int kWebAuthnGpmDialogSpacingBetweenTitleAndDescription = 4; +inline constexpr int kWebAuthnGpmDialogSpacingBetweenTitleAndDescription = 4; #endif // CHROME_BROWSER_UI_VIEWS_WEBAUTHN_AUTHENTICATOR_COMMON_VIEWS_H_
diff --git a/chrome/browser/ui/webui/ash/app_install/app_install_dialog.h b/chrome/browser/ui/webui/ash/app_install/app_install_dialog.h index 6ff7c9e9..78e71d4 100644 --- a/chrome/browser/ui/webui/ash/app_install/app_install_dialog.h +++ b/chrome/browser/ui/webui/ash/app_install/app_install_dialog.h
@@ -23,7 +23,7 @@ namespace ash::app_install { -const int kIconSize = 32; +inline constexpr int kIconSize = 32; // Defines the web dialog used for installing an app. class AppInstallDialog : public SystemWebDialogDelegate {
diff --git a/chrome/browser/ui/webui/ash/audio/audio_ui.cc b/chrome/browser/ui/webui/ash/audio/audio_ui.cc index 5fb38407..d0d01e7 100644 --- a/chrome/browser/ui/webui/ash/audio/audio_ui.cc +++ b/chrome/browser/ui/webui/ash/audio/audio_ui.cc
@@ -19,10 +19,6 @@ namespace ash { -bool AudioUIConfig::IsWebUIEnabled(content::BrowserContext* browser_context) { - return base::FeatureList::IsEnabled(features::kAudioUrl); -} - AudioUI::AudioUI(content::WebUI* web_ui) : ui::MojoWebUIController(web_ui) { // Set up the chrome://audio source. content::WebUIDataSource* html_source =
diff --git a/chrome/browser/ui/webui/ash/audio/audio_ui.h b/chrome/browser/ui/webui/ash/audio/audio_ui.h index 1d6a837..911790d 100644 --- a/chrome/browser/ui/webui/ash/audio/audio_ui.h +++ b/chrome/browser/ui/webui/ash/audio/audio_ui.h
@@ -27,8 +27,6 @@ AudioUIConfig() : DefaultWebUIConfig(content::kChromeUIScheme, chrome::kChromeUIAudioHost) {} - - bool IsWebUIEnabled(content::BrowserContext* browser_context) override; }; // The WebUI Controller for chrome://audio
diff --git a/chrome/browser/ui/webui/ash/settings/pages/storage/device_storage_handler.h b/chrome/browser/ui/webui/ash/settings/pages/storage/device_storage_handler.h index b2e53dd..b7090704 100644 --- a/chrome/browser/ui/webui/ash/settings/pages/storage/device_storage_handler.h +++ b/chrome/browser/ui/webui/ash/settings/pages/storage/device_storage_handler.h
@@ -38,10 +38,10 @@ }; // Threshold to show a message indicating space is critically low (512 MB). -const int64_t kSpaceCriticallyLowBytes = 512 * 1024 * 1024; +inline constexpr int64_t kSpaceCriticallyLowBytes = 512 * 1024 * 1024; // Threshold to show a message indicating space is low (1 GB). -const int64_t kSpaceLowBytes = 1 * 1024 * 1024 * 1024; +inline constexpr int64_t kSpaceLowBytes = 1 * 1024 * 1024 * 1024; class StorageHandler : public ::settings::SettingsPageUIHandler, public arc::ArcSessionManagerObserver,
diff --git a/chrome/browser/ui/webui/bookmarks/bookmarks_ui.cc b/chrome/browser/ui/webui/bookmarks/bookmarks_ui.cc index 609936a6..469efd3 100644 --- a/chrome/browser/ui/webui/bookmarks/bookmarks_ui.cc +++ b/chrome/browser/ui/webui/bookmarks/bookmarks_ui.cc
@@ -9,6 +9,7 @@ #include <string> #include <utility> +#include "base/feature_list.h" #include "chrome/browser/profiles/profile.h" #include "chrome/browser/ui/ui_features.h" #include "chrome/browser/ui/webui/bookmarks/bookmarks_message_handler.h" @@ -57,6 +58,8 @@ source->AddString("undoDescription", l10n_util::GetStringFUTF16( IDS_UNDO_DESCRIPTION, undo_accelerator.GetShortcutText())); + source->AddBoolean("splitViewEnabled", + base::FeatureList::IsEnabled(features::kSideBySide)); // Localized strings (alphabetical order). static constexpr webui::LocalizedString kStrings[] = { @@ -111,6 +114,7 @@ {"menuOpenNewTabGroup", IDS_BOOKMARK_MANAGER_MENU_OPEN_IN_NEW_TAB_GROUP}, {"menuOpenNewWindow", IDS_BOOKMARK_MANAGER_MENU_OPEN_IN_NEW_WINDOW}, {"menuOpenIncognito", IDS_BOOKMARK_MANAGER_MENU_OPEN_INCOGNITO}, + {"menuOpenSplitView", IDS_BOOKMARK_MANAGER_MENU_OPEN_IN_SPLIT_VIEW}, {"menuRename", IDS_BOOKMARK_MANAGER_MENU_RENAME}, {"menuShowInFolder", IDS_BOOKMARK_MANAGER_MENU_SHOW_IN_FOLDER}, {"menuSort", IDS_BOOKMARK_MANAGER_MENU_SORT},
diff --git a/chrome/browser/ui/webui/new_tab_page/new_tab_page_ui.cc b/chrome/browser/ui/webui/new_tab_page/new_tab_page_ui.cc index 3bd9304..88ecce4 100644 --- a/chrome/browser/ui/webui/new_tab_page/new_tab_page_ui.cc +++ b/chrome/browser/ui/webui/new_tab_page/new_tab_page_ui.cc
@@ -99,6 +99,11 @@ #include "url/origin.h" #include "url/url_util.h" +#if !BUILDFLAG(OPTIMIZE_WEBUI) +#include "chrome/grit/new_tab_shared_resources.h" +#include "chrome/grit/new_tab_shared_resources_map.h" +#endif + #if !defined(OFFICIAL_BUILD) #include "chrome/browser/ui/webui/new_tab_page/foo/foo_handler.h" #endif @@ -208,6 +213,8 @@ "mostRelevantTabResumptionModuleFallbackToHost", base::FeatureList::IsEnabled( ntp_features::kNtpMostRelevantTabResumptionModuleFallbackToHost)); + source->AddBoolean("footerEnabled", + base::FeatureList::IsEnabled(ntp_features::kNtpFooter)); static constexpr webui::LocalizedString kStrings[] = { {"doneButton", IDS_DONE}, @@ -474,6 +481,10 @@ webui::SetupWebUIDataSource(source, kNewTabPageResources, IDR_NEW_TAB_PAGE_NEW_TAB_PAGE_HTML); +#if !BUILDFLAG(OPTIMIZE_WEBUI) + source->AddResourcePaths(kNewTabSharedResources); +#endif + // Allow embedding of iframes for the doodle and // chrome-untrusted://new-tab-page for other external content and resources. // NOTE: Use caution when overriding content security policies as that cean
diff --git a/chrome/browser/ui/webui/settings/settings_clear_browsing_data_handler_unittest.cc b/chrome/browser/ui/webui/settings/settings_clear_browsing_data_handler_unittest.cc index 64c45e63..1e9c78d5 100644 --- a/chrome/browser/ui/webui/settings/settings_clear_browsing_data_handler_unittest.cc +++ b/chrome/browser/ui/webui/settings/settings_clear_browsing_data_handler_unittest.cc
@@ -124,9 +124,7 @@ }; void ClearBrowsingDataHandlerUnitTest::SetUp() { - feature_list_.InitWithFeatures({toast_features::kToastFramework, - toast_features::kClearBrowsingDataToast}, - {}); + feature_list_.InitWithFeatures({toast_features::kClearBrowsingDataToast}, {}); TestingProfile::Builder builder; profile_ = builder.Build();
diff --git a/chrome/browser/ui/webui/side_panel/customize_chrome/wallpaper_search/wallpaper_search_interactive_uitest.cc b/chrome/browser/ui/webui/side_panel/customize_chrome/wallpaper_search/wallpaper_search_interactive_uitest.cc index d223e33..07e7df52 100644 --- a/chrome/browser/ui/webui/side_panel/customize_chrome/wallpaper_search/wallpaper_search_interactive_uitest.cc +++ b/chrome/browser/ui/webui/side_panel/customize_chrome/wallpaper_search/wallpaper_search_interactive_uitest.cc
@@ -124,7 +124,7 @@ IN_PROC_BROWSER_TEST_F(WallpaperSearchInteractiveTest, NTPWallpaperSearchButtonVisibilityDependsOnSettings) { - const DeepQuery kWallpaperSearchButton = {"ntp-app", + const DeepQuery kWallpaperSearchButton = {"ntp-app", "ntp-customize-buttons", "#wallpaperSearchButton"}; RunTestSequence( @@ -258,7 +258,8 @@ InteractiveTestApi::MultiStep OpenCustomizeChromeAt( const ui::ElementIdentifier& contents_id) { - const DeepQuery kCustomizeChromeButton = {"ntp-app", "#customizeButton"}; + const DeepQuery kCustomizeChromeButton = { + "ntp-app", "ntp-customize-buttons", "#customizeButton"}; return Steps(ClickElement(kNewTabPageElementId, kCustomizeChromeButton), WaitForShow(kCustomizeChromeSidePanelWebViewElementId), InstrumentNonTabWebView( @@ -267,8 +268,8 @@ InteractiveTestApi::MultiStep OpenWallpaperSearchAt( const ui::ElementIdentifier& contents_id) { - const DeepQuery kWallpaperSearchButton = {"ntp-app", - "#wallpaperSearchButton"}; + const DeepQuery kWallpaperSearchButton = { + "ntp-app", "ntp-customize-buttons", "#wallpaperSearchButton"}; return Steps(ClickElement(kNewTabPageElementId, kWallpaperSearchButton), WaitForShow(kCustomizeChromeSidePanelWebViewElementId), InstrumentNonTabWebView( @@ -340,8 +341,9 @@ CustomizeButtonsWorkTogether) { DEFINE_LOCAL_ELEMENT_IDENTIFIER_VALUE(kReopenedCustomizeChromeElementId); - const DeepQuery kCustomizeChromeButton = {"ntp-app", "#customizeButton"}; - const DeepQuery kWallpaperSearchButton = {"ntp-app", + const DeepQuery kCustomizeChromeButton = {"ntp-app", "ntp-customize-buttons", + "#customizeButton"}; + const DeepQuery kWallpaperSearchButton = {"ntp-app", "ntp-customize-buttons", "#wallpaperSearchButton"}; RunTestSequence( @@ -362,8 +364,9 @@ IN_PROC_BROWSER_TEST_F(WallpaperSearchOptimizationGuideInteractiveTest, NTPButtonAnimatesUnderThreshold) { - const DeepQuery kCustomizeChromeButton = {"ntp-app", "#customizeButton"}; - const DeepQuery kWallpaperSearchButton = {"ntp-app", + const DeepQuery kCustomizeChromeButton = {"ntp-app", "ntp-customize-buttons", + "#customizeButton"}; + const DeepQuery kWallpaperSearchButton = {"ntp-app", "ntp-customize-buttons", "#wallpaperSearchButton"}; RunTestSequence( @@ -391,8 +394,9 @@ IN_PROC_BROWSER_TEST_F(WallpaperSearchOptimizationGuideInteractiveTest, NTPButtonDoesNotAnimateAboveThreshold) { - const DeepQuery kCustomizeChromeButton = {"ntp-app", "#customizeButton"}; - const DeepQuery kWallpaperSearchButton = {"ntp-app", + const DeepQuery kCustomizeChromeButton = {"ntp-app", "ntp-customize-buttons", + "#customizeButton"}; + const DeepQuery kWallpaperSearchButton = {"ntp-app", "ntp-customize-buttons", "#wallpaperSearchButton"}; RunTestSequence( @@ -440,7 +444,8 @@ "#submitButton"}; const DeepQuery kWallpaperSearchResult = {"customize-chrome-app", "#wallpaperSearchPage", ".result"}; - const DeepQuery kCustomizeChromeButton = {"ntp-app", "#customizeButton"}; + const DeepQuery kCustomizeChromeButton = {"ntp-app", "ntp-customize-buttons", + "#customizeButton"}; const DeepQuery kSetClassicChromeButton = { "customize-chrome-app", "#appearanceElement", "#setClassicChromeButton"}; const DeepQuery kHistoryCard = {"customize-chrome-app", @@ -630,8 +635,9 @@ IN_PROC_BROWSER_TEST_F(NTPWallpaperSearchButtonAnimationTest, AnimatesUnconditionally) { - const DeepQuery kCustomizeChromeButton = {"ntp-app", "#customizeButton"}; - const DeepQuery kWallpaperSearchButton = {"ntp-app", + const DeepQuery kCustomizeChromeButton = {"ntp-app", "ntp-customize-buttons", + "#customizeButton"}; + const DeepQuery kWallpaperSearchButton = {"ntp-app", "ntp-customize-buttons", "#wallpaperSearchButton"}; RunTestSequence(
diff --git a/chrome/browser/webid/BUILD.gn b/chrome/browser/webid/BUILD.gn index 0ec176e..6991549 100644 --- a/chrome/browser/webid/BUILD.gn +++ b/chrome/browser/webid/BUILD.gn
@@ -6,11 +6,22 @@ import("//third_party/jni_zero/jni_zero.gni") android_library("java") { - sources = [ "android/java/src/org/chromium/chrome/browser/webid/DigitalIdentityProvider.java" ] + sources = [ + "android/java/src/org/chromium/chrome/browser/webid/DigitalCredentialsCreationDelegateImplementation.java", + "android/java/src/org/chromium/chrome/browser/webid/DigitalCredentialsPresentationDelegate.java", + "android/java/src/org/chromium/chrome/browser/webid/DigitalIdentityProvider.java", + "android/java/src/org/chromium/chrome/browser/webid/IdentityCredentialsDelegate.java", + ] deps = [ + "$google_play_services_package:google_play_services_identity_credentials_java", + "$google_play_services_package:google_play_services_tasks_java", "//base:base_java", + "//base:service_loader_java", "//content/public/android:content_java", + "//third_party/androidx:androidx_annotation_annotation_experimental_java", "//third_party/androidx:androidx_annotation_annotation_java", + "//third_party/androidx:androidx_core_core_java", + "//third_party/androidx:androidx_credentials_credentials_java", "//third_party/jni_zero:jni_zero_java", "//ui/android:ui_no_recycler_view_java", "//url:url_java", @@ -25,12 +36,17 @@ robolectric_library("junit") { testonly = true - sources = [ "android/java/src/org/chromium/chrome/browser/webid/DigitalIdentityProviderUnitTest.java" ] + sources = [ + "android/junit/src/org/chromium/chrome/browser/webid/DigitalCredentialsPresentationDelegateTest.java", + "android/junit/src/org/chromium/chrome/browser/webid/DigitalIdentityProviderUnitTest.java", + ] deps = [ ":java", + "$google_play_services_package:google_play_services_identity_credentials_java", "//base:base_junit_test_support", "//content/public/android:content_java", "//third_party/junit:junit", + "//third_party/mockito:mockito_java", ] }
diff --git a/chrome/browser/webid/android/java/src/org/chromium/chrome/browser/webid/DigitalCredentialsCreationDelegateImplementation.java b/chrome/browser/webid/android/java/src/org/chromium/chrome/browser/webid/DigitalCredentialsCreationDelegateImplementation.java new file mode 100644 index 0000000..8fef243 --- /dev/null +++ b/chrome/browser/webid/android/java/src/org/chromium/chrome/browser/webid/DigitalCredentialsCreationDelegateImplementation.java
@@ -0,0 +1,147 @@ +// 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. + +package org.chromium.chrome.browser.webid; + +import static androidx.core.app.ActivityCompat.startIntentSenderForResult; + +import android.app.Activity; +import android.content.Intent; +import android.content.IntentSender.SendIntentException; +import android.os.Build; +import android.os.Bundle; +import android.os.Handler; +import android.os.Looper; +import android.os.ResultReceiver; + +import androidx.annotation.OptIn; +import androidx.credentials.DigitalCredential; +import androidx.credentials.exceptions.CreateCredentialException; +import androidx.credentials.exceptions.CreateCredentialUnknownException; +import androidx.credentials.provider.PendingIntentHandler; + +import com.google.android.gms.identitycredentials.CreateCredentialRequest; +import com.google.android.gms.identitycredentials.IdentityCredentialClient; +import com.google.android.gms.identitycredentials.IdentityCredentialManager; + +import org.chromium.base.Log; +import org.chromium.base.Promise; +import org.chromium.build.annotations.NullMarked; + +@NullMarked +public class DigitalCredentialsCreationDelegateImplementation { + private static final String TAG = "DCCreateDelegateImpl"; + + // Arbitrary request code that is used when invoking the GMSCore API. + private static final int REQUEST_CODE_DIGITAL_CREDENTIALS_CREATION = 777; + + private static final String BUNDLE_KEY_PROVIDER_DATA = "androidx.identitycredentials.BUNDLE_KEY_PROVIDER_DATA"; + + @OptIn(markerClass = androidx.credentials.ExperimentalDigitalCredentialApi.class) + public Promise<String> create(Activity window, String origin, String request) { + final IdentityCredentialClient client = + IdentityCredentialManager.Companion.getClient(window); + + final Promise<String> result = new Promise<String>(); + + ResultReceiver resultReceiver = + new ResultReceiver(new Handler(Looper.getMainLooper())) { + @Override + protected void onReceiveResult(int code, Bundle data) { + if (!result.isPending()) { + // Promises don't support duplicate calls to fulfill/reject. If the + // promise has been fulfilled/rejected already (e.g. due to erroneous + // exception during the API call), return immediately. + return; + } + Log.d(TAG, "Received a response"); + Intent providerData = + Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU + ? data.getParcelable( + BUNDLE_KEY_PROVIDER_DATA, Intent.class) + : data.getParcelable(BUNDLE_KEY_PROVIDER_DATA); + + if (providerData == null) { + Log.d(TAG, "Response doesn't contain providerData"); + result.reject( + new CreateCredentialUnknownException( + "Response doesn't contain providerData")); + return; + } + + var response = + PendingIntentHandler.retrieveCreateCredentialResponse( + DigitalCredential.TYPE_DIGITAL_CREDENTIAL, providerData); + if (response == null) { + CreateCredentialException exception = + PendingIntentHandler.retrieveCreateCredentialException( + providerData); + result.reject( + exception == null + ? new CreateCredentialUnknownException("empty response") + : exception); + return; + } + String responseJson = + response.getData() + .getString("androidx.credentials.BUNDLE_KEY_RESPONSE_JSON"); + if (responseJson == null) { + result.reject( + new CreateCredentialUnknownException( + "Response doesn't contain responseJson")); + return; + } + Log.d(TAG, "Response JSON: " + responseJson); + result.fulfill(responseJson); + } + }; + + Bundle requestBundle = new Bundle(); + requestBundle.putString("androidx.credentials.BUNDLE_KEY_REQUEST_JSON", request); + CreateCredentialRequest createRequest = + new CreateCredentialRequest( + DigitalCredential.TYPE_DIGITAL_CREDENTIAL, + /* credentialData= */ requestBundle, + /* candidateQueryData= */ new Bundle(), + origin, + request, + resultReceiver); + + client.createCredential(createRequest) + .addOnSuccessListener( + response -> { + if (response.getPendingIntent() == null) { + Log.d(TAG, "Response doesn't contain pendingIntent"); + result.reject( + new CreateCredentialUnknownException( + "Response doesn't contain pendingIntent")); + return; + } + try { + Log.d(TAG, "Sending an intent for sender"); + Log.d(TAG, request); + startIntentSenderForResult( + /* activity= */ window, + /* intent= */ response.getPendingIntent().getIntentSender(), + /* requestCode= */ REQUEST_CODE_DIGITAL_CREDENTIALS_CREATION, + /* fillInIntent= */ null, + /* flagsMask= */ 0, + /* flagsValues= */ 0, + /* extraFlags= */ 0, + /* options= */ null); + } catch (SendIntentException e) { + Log.e(TAG, "Sending an intent for sender failed"); + if (result.isPending()) { + result.reject(e); + } + } + }) + .addOnFailureListener( + e -> { + result.reject(new CreateCredentialUnknownException("unknown error")); + }); + + return result; + } +}
diff --git a/content/public/android/java/src/org/chromium/content/browser/webid/DigitalCredentialsPresentationDelegate.java b/chrome/browser/webid/android/java/src/org/chromium/chrome/browser/webid/DigitalCredentialsPresentationDelegate.java similarity index 98% rename from content/public/android/java/src/org/chromium/content/browser/webid/DigitalCredentialsPresentationDelegate.java rename to chrome/browser/webid/android/java/src/org/chromium/chrome/browser/webid/DigitalCredentialsPresentationDelegate.java index 5b89755..719243c 100644 --- a/content/public/android/java/src/org/chromium/content/browser/webid/DigitalCredentialsPresentationDelegate.java +++ b/chrome/browser/webid/android/java/src/org/chromium/chrome/browser/webid/DigitalCredentialsPresentationDelegate.java
@@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -package org.chromium.content.browser.webid; +package org.chromium.chrome.browser.webid; import static androidx.core.app.ActivityCompat.startIntentSenderForResult; @@ -34,7 +34,7 @@ import org.chromium.base.Promise; import org.chromium.build.annotations.NullMarked; import org.chromium.build.annotations.Nullable; -import org.chromium.content.browser.webid.IdentityCredentialsDelegate.DigitalCredential; +import org.chromium.chrome.browser.webid.IdentityCredentialsDelegate.DigitalCredential; import java.util.Arrays; import java.util.Objects; @@ -235,4 +235,4 @@ // without a protocol. return new DigitalCredential(null, credentialJson); } -} \ No newline at end of file +}
diff --git a/chrome/browser/webid/android/java/src/org/chromium/chrome/browser/webid/DigitalIdentityProvider.java b/chrome/browser/webid/android/java/src/org/chromium/chrome/browser/webid/DigitalIdentityProvider.java index 8987610..8052cfe 100644 --- a/chrome/browser/webid/android/java/src/org/chromium/chrome/browser/webid/DigitalIdentityProvider.java +++ b/chrome/browser/webid/android/java/src/org/chromium/chrome/browser/webid/DigitalIdentityProvider.java
@@ -19,7 +19,6 @@ import org.chromium.base.ResettersForTesting; import org.chromium.build.annotations.NullMarked; import org.chromium.build.annotations.Nullable; -import org.chromium.content.browser.webid.IdentityCredentialsDelegate; import org.chromium.content_public.browser.webid.DigitalIdentityRequestStatusForMetrics; import org.chromium.ui.base.WindowAndroid;
diff --git a/content/public/android/java/src/org/chromium/content/browser/webid/IdentityCredentialsDelegate.java b/chrome/browser/webid/android/java/src/org/chromium/chrome/browser/webid/IdentityCredentialsDelegate.java similarity index 78% rename from content/public/android/java/src/org/chromium/content/browser/webid/IdentityCredentialsDelegate.java rename to chrome/browser/webid/android/java/src/org/chromium/chrome/browser/webid/IdentityCredentialsDelegate.java index 22d8b3b..7d5161b 100644 --- a/content/public/android/java/src/org/chromium/content/browser/webid/IdentityCredentialsDelegate.java +++ b/chrome/browser/webid/android/java/src/org/chromium/chrome/browser/webid/IdentityCredentialsDelegate.java
@@ -2,12 +2,11 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -package org.chromium.content.browser.webid; +package org.chromium.chrome.browser.webid; import android.app.Activity; import org.chromium.base.Promise; -import org.chromium.base.ServiceLoaderUtil; import org.chromium.build.annotations.NullMarked; import org.chromium.build.annotations.Nullable; @@ -42,11 +41,8 @@ } public Promise<String> create(Activity window, String origin, String request) { - DigitalCredentialsCreationDelegate delegateImpl = - ServiceLoaderUtil.maybeCreate(DigitalCredentialsCreationDelegate.class); - if (delegateImpl != null) { - return delegateImpl.create(window, origin, request); - } - return Promise.rejected(); + DigitalCredentialsCreationDelegateImplementation creationDelegate = + new DigitalCredentialsCreationDelegateImplementation(); + return creationDelegate.create(window, origin, request); } }
diff --git a/chrome/android/junit/src/org/chromium/content/browser/webid/DigitalCredentialsPresentationDelegateTest.java b/chrome/browser/webid/android/junit/src/org/chromium/chrome/browser/webid/DigitalCredentialsPresentationDelegateTest.java similarity index 90% rename from chrome/android/junit/src/org/chromium/content/browser/webid/DigitalCredentialsPresentationDelegateTest.java rename to chrome/browser/webid/android/junit/src/org/chromium/chrome/browser/webid/DigitalCredentialsPresentationDelegateTest.java index c387be0..f99e8ec 100644 --- a/chrome/android/junit/src/org/chromium/content/browser/webid/DigitalCredentialsPresentationDelegateTest.java +++ b/chrome/browser/webid/android/junit/src/org/chromium/chrome/browser/webid/DigitalCredentialsPresentationDelegateTest.java
@@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -package org.chromium.content.browser.webid; +package org.chromium.chrome.browser.webid; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; @@ -11,11 +11,11 @@ import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; -import static org.chromium.content.browser.webid.DigitalCredentialsPresentationDelegate.BUNDLE_KEY_IDENTITY_TOKEN; -import static org.chromium.content.browser.webid.DigitalCredentialsPresentationDelegate.BUNDLE_KEY_PROVIDER_DATA; -import static org.chromium.content.browser.webid.DigitalCredentialsPresentationDelegate.BUNDLE_KEY_REQUEST_JSON; -import static org.chromium.content.browser.webid.DigitalCredentialsPresentationDelegate.EXTRA_CREDENTIAL_DATA; -import static org.chromium.content.browser.webid.DigitalCredentialsPresentationDelegate.EXTRA_GET_CREDENTIAL_RESPONSE; +import static org.chromium.chrome.browser.webid.DigitalCredentialsPresentationDelegate.BUNDLE_KEY_IDENTITY_TOKEN; +import static org.chromium.chrome.browser.webid.DigitalCredentialsPresentationDelegate.BUNDLE_KEY_PROVIDER_DATA; +import static org.chromium.chrome.browser.webid.DigitalCredentialsPresentationDelegate.BUNDLE_KEY_REQUEST_JSON; +import static org.chromium.chrome.browser.webid.DigitalCredentialsPresentationDelegate.EXTRA_CREDENTIAL_DATA; +import static org.chromium.chrome.browser.webid.DigitalCredentialsPresentationDelegate.EXTRA_GET_CREDENTIAL_RESPONSE; import android.app.Activity; import android.content.Intent; @@ -32,7 +32,7 @@ import org.robolectric.annotation.Config; import org.chromium.base.test.BaseRobolectricTestRunner; -import org.chromium.content.browser.webid.IdentityCredentialsDelegate.DigitalCredential; +import org.chromium.chrome.browser.webid.IdentityCredentialsDelegate.DigitalCredential; /** Unit tests for {@link DigitalCredentialsPresentationDelegate}. */ @RunWith(BaseRobolectricTestRunner.class) @@ -179,8 +179,7 @@ NullPointerException.class, () -> { DigitalCredentialsPresentationDelegate - .extractDigitalCredentialFromResponseBundle( - Activity.RESULT_OK, bundle); + .extractDigitalCredentialFromResponseBundle(Activity.RESULT_OK, bundle); }); } @@ -195,8 +194,7 @@ JSONException.class, () -> { DigitalCredentialsPresentationDelegate - .extractDigitalCredentialFromResponseBundle( - Activity.RESULT_OK, bundle); + .extractDigitalCredentialFromResponseBundle(Activity.RESULT_OK, bundle); }); } -} \ No newline at end of file +}
diff --git a/chrome/browser/webid/android/java/src/org/chromium/chrome/browser/webid/DigitalIdentityProviderUnitTest.java b/chrome/browser/webid/android/junit/src/org/chromium/chrome/browser/webid/DigitalIdentityProviderUnitTest.java similarity index 100% rename from chrome/browser/webid/android/java/src/org/chromium/chrome/browser/webid/DigitalIdentityProviderUnitTest.java rename to chrome/browser/webid/android/junit/src/org/chromium/chrome/browser/webid/DigitalIdentityProviderUnitTest.java
diff --git a/chrome/build/android-arm32.pgo.txt b/chrome/build/android-arm32.pgo.txt index 037a857..e50eb21 100644 --- a/chrome/build/android-arm32.pgo.txt +++ b/chrome/build/android-arm32.pgo.txt
@@ -1 +1 @@ -chrome-android32-main-1746705526-ef2069b32a925234573a52e10fb9fdc3b624a6c5-d85876276c358c68ff50521bcaa24e5d3998dc14.profdata +chrome-android32-main-1746727053-c006a3a875b245a51c092aeb67b377805c30708e-8a7e3eb58b861bad419563c7eab04a42cd628814.profdata
diff --git a/chrome/build/android-arm64.pgo.txt b/chrome/build/android-arm64.pgo.txt index 49a8f65..9b87470 100644 --- a/chrome/build/android-arm64.pgo.txt +++ b/chrome/build/android-arm64.pgo.txt
@@ -1 +1 @@ -chrome-android64-main-1746709455-fdc70e1ca6442cf37ee196241b9a00d31f5ed8b6-07e60562914a38f99dd9467bbb724bb1855dcd6b.profdata +chrome-android64-main-1746726018-ef891a08c418460a6c677b527a250b14a21e205a-25859c3ed77222679ee9535931365b4e8531cdeb.profdata
diff --git a/chrome/build/mac-arm.pgo.txt b/chrome/build/mac-arm.pgo.txt index 6b92a6b9..40d6783 100644 --- a/chrome/build/mac-arm.pgo.txt +++ b/chrome/build/mac-arm.pgo.txt
@@ -1 +1 @@ -chrome-mac-arm-main-1746712766-4458ed48f730363c2b714603f1f0408bce8ff88e-d7d2bdf36c4042a97f70f25e75e9033607a3047d.profdata +chrome-mac-arm-main-1746727053-d8e673618cc0fda4407fdb86ef3cc8b00db58b8c-8a7e3eb58b861bad419563c7eab04a42cd628814.profdata
diff --git a/chrome/build/win32.pgo.txt b/chrome/build/win32.pgo.txt index ddd1c3f..cb3d110 100644 --- a/chrome/build/win32.pgo.txt +++ b/chrome/build/win32.pgo.txt
@@ -1 +1 @@ -chrome-win32-main-1746694515-002ca32b80ee1aaaf587fc5f94b4b43b3cf9b44b-06dbc4719305742aa3f7c09eec1b2bc5f2612f5e.profdata +chrome-win32-main-1746705526-40334b70257e603470ff093884495a1ac7a52dbc-d85876276c358c68ff50521bcaa24e5d3998dc14.profdata
diff --git a/chrome/build/win64.pgo.txt b/chrome/build/win64.pgo.txt index c16638bf..dd84077 100644 --- a/chrome/build/win64.pgo.txt +++ b/chrome/build/win64.pgo.txt
@@ -1 +1 @@ -chrome-win64-main-1746673110-f73d31180ac7a2ce88e391f0d4e50971e3626f3f-199eafc03bc1e54dabcedf16898b07f11f2fc77d.profdata +chrome-win64-main-1746694515-0ede87d31ffde351912da9e2bd0803f2b55eb044-06dbc4719305742aa3f7c09eec1b2bc5f2612f5e.profdata
diff --git a/chrome/chrome_paks.gni b/chrome/chrome_paks.gni index e96d83c..87e95823 100644 --- a/chrome/chrome_paks.gni +++ b/chrome/chrome_paks.gni
@@ -197,6 +197,7 @@ "$root_gen_dir/chrome/new_tab_page_resources.pak", "$root_gen_dir/chrome/new_tab_page_third_party_resources.pak", "$root_gen_dir/chrome/new_tab_page_untrusted_resources.pak", + "$root_gen_dir/chrome/new_tab_shared_resources.pak", "$root_gen_dir/chrome/omnibox_popup_resources.pak", "$root_gen_dir/chrome/password_manager_resources.pak", "$root_gen_dir/chrome/privacy_sandbox_resources.pak",
diff --git a/chrome/common/chrome_features.cc b/chrome/common/chrome_features.cc index 644991d..8dde0f01 100644 --- a/chrome/common/chrome_features.cc +++ b/chrome/common/chrome_features.cc
@@ -415,17 +415,17 @@ BASE_FEATURE_PARAM(std::string, kGlicLauncherToggleLearnMoreURL, &kGlicLearnMoreURLConfig, - "glic-launcher-toggle-learn-more-url", + "glic-shortcuts-launcher-toggle-learn-more-url", ""); BASE_FEATURE_PARAM(std::string, kGlicLocationToggleLearnMoreURL, &kGlicLearnMoreURLConfig, - "glic-location-toggle-learn-more-url", + "glic-shortcuts-location-toggle-learn-more-url", ""); BASE_FEATURE_PARAM(std::string, kGlicTabAccessToggleLearnMoreURL, &kGlicLearnMoreURLConfig, - "glic-tab-access-toggle-learn-more-url", + "glic-shortcuts-tab-access-toggle-learn-more-url", ""); BASE_FEATURE_PARAM(std::string, kGlicSettingsPageLearnMoreURL,
diff --git a/chrome/common/extensions/api/bookmark_manager_private.json b/chrome/common/extensions/api/bookmark_manager_private.json index 812f3729..df1ec62 100644 --- a/chrome/common/extensions/api/bookmark_manager_private.json +++ b/chrome/common/extensions/api/bookmark_manager_private.json
@@ -43,6 +43,15 @@ "items": {"$ref": "BookmarkNodeDataElement"} } } + }, + { + "id": "OpenInNewTabParams", + "type": "object", + "description": "Parameters for the Open In New Tab method", + "properties": { + "active": {"type": "boolean", "description": "Whether this tab should be active."}, + "split": {"type": "boolean", "description": "Whether this tab should enter into a split view alongside the active tab."} + } } ], "functions": [ @@ -271,9 +280,10 @@ "type": "string" }, { - "name": "active", - "description": "Whether this tab should be active.", - "type": "boolean" + "name": "params", + "optional": true, + "$ref": "OpenInNewTabParams", + "description": "Parameters for the new tab the given bookmark is opened into." } ] },
diff --git a/chrome/renderer/accessibility/read_anything/read_anything_app_controller.cc b/chrome/renderer/accessibility/read_anything/read_anything_app_controller.cc index d1af751..4a1062ff 100644 --- a/chrome/renderer/accessibility/read_anything/read_anything_app_controller.cc +++ b/chrome/renderer/accessibility/read_anything/read_anything_app_controller.cc
@@ -615,7 +615,7 @@ // Cancel any running draw timers. post_user_entry_draw_timer_->Stop(); - model_.SetActiveTreeId(tree_id); + model_.SetRootTreeId(tree_id); model_.SetUkmSourceIdForTree(tree_id, ukm_source_id); model_.set_is_pdf(is_pdf); @@ -759,6 +759,12 @@ model_.ComputeDisplayNodeIdsForDistilledTree(); } + // If there's no distillable content on the active tree, allow child tree + // content to be distilled. This is needed to distill content on pages with + // a single root node containing an iframe that contains a tree with all + // the page's content. + model_.AllowChildTreeForActiveTree(model_.content_node_ids().empty()); + // Draw the selection in the side panel (if one exists in the main panel). if (!PostProcessSelection()) { // If a draw did not occur, make sure to draw. This will happen if there is
diff --git a/chrome/renderer/accessibility/read_anything/read_anything_app_model.cc b/chrome/renderer/accessibility/read_anything/read_anything_app_model.cc index 2fdea11..bc48b3b 100644 --- a/chrome/renderer/accessibility/read_anything/read_anything_app_model.cc +++ b/chrome/renderer/accessibility/read_anything/read_anything_app_model.cc
@@ -2,7 +2,6 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. - #include "chrome/renderer/accessibility/read_anything/read_anything_app_model.h" #include <cstddef> @@ -554,6 +553,22 @@ } } + if (may_use_child_for_active_tree_) { + // If this is the original root tree id, set it back to the active tree + // in case there has been a delay in receiving valid accessibility tree + // updates. + if (root_tree_id_ == tree_id) { + SetRootTreeId(root_tree_id_); + } else if (active_tree_id_ != ui::AXTreeIDUnknown() && + active_tree_id_ != tree_id && + child_tree_ids_.find(tree_id) != child_tree_ids_.end()) { + // If read aloud is searching for a child tree to distill and this tree id + // matches one of the possible child ids, set the active tree to this tree + // so that it can be distilled. + SetActiveTreeId(tree_id); + } + } + // If a tree update on the active tree is received while distillation is in // progress, cache updates that are received but do not yet unserialize them. // Drawing must be done on the same tree that was sent to the distiller, @@ -730,6 +745,17 @@ event); } +void ReadAnythingAppModel::SetRootTreeId(ui::AXTreeID root_tree_id) { + root_tree_id_ = root_tree_id; + SetActiveTreeId(root_tree_id); + + // Whenever reading mode receives a signal of a new active tree id, clear + // previous attempts to search for a valid child tree on the active tree in + // case the new active tree is distillable. + may_use_child_for_active_tree_ = false; + child_tree_ids_.clear(); +} + void ReadAnythingAppModel::SetActiveTreeId(ui::AXTreeID active_tree_id) { // Unserialize any updates on the previous active tree; // Otherwise, this can cause tree inconsistency issues if reading mode later @@ -1092,3 +1118,24 @@ return selection_node_ids_.empty() ? &display_node_ids() : &selection_node_ids_; } + +void ReadAnythingAppModel::AllowChildTreeForActiveTree(bool use_child_tree) { + may_use_child_for_active_tree_ = use_child_tree; + + if (!may_use_child_for_active_tree_) { + child_tree_ids_.clear(); + } + + ui::AXSerializableTree* active_tree = GetTreeFromId(active_tree_id_); + if (!active_tree) { + return; + } + std::set<ui::AXTreeID> child_ids = active_tree->GetAllChildTreeIds(); + if (!child_ids.size()) { + return; + } + + // Store all the possible child tree ids that could be used as the active + // tree if they have distillable content. + child_tree_ids_.insert(child_ids.begin(), child_ids.end()); +}
diff --git a/chrome/renderer/accessibility/read_anything/read_anything_app_model.h b/chrome/renderer/accessibility/read_anything/read_anything_app_model.h index 8e7f301d..43d6764 100644 --- a/chrome/renderer/accessibility/read_anything/read_anything_app_model.h +++ b/chrome/renderer/accessibility/read_anything/read_anything_app_model.h
@@ -232,6 +232,7 @@ const ui::AXTreeID& active_tree_id() const { return active_tree_id_; } void SetActiveTreeId(ui::AXTreeID active_tree_id); + void SetRootTreeId(ui::AXTreeID root_tree_id); ukm::SourceId GetUkmSourceId() const; void SetUkmSourceIdForTree(const ui::AXTreeID& tree, @@ -322,6 +323,15 @@ void AddObserver(ModelObserver* observer); void RemoveObserver(ModelObserver* observer); + // TODO: crbug.com/416483312 - Longer term, reading mode should support + // distilling from multiple trees, if they have important content. + // Currently, reading mode only distills from a child tree if the root tree + // has no distillable content. + + // Signal if reading mode should allow use of child trees for the active tree + // if the web content's root AXTree has no distillable content. + void AllowChildTreeForActiveTree(bool use_child_tree); + private: struct SelectionEndpoint { enum class Source { @@ -377,6 +387,11 @@ // child). ui::AXTreeID active_tree_id_ = ui::AXTreeIDUnknown(); + // The AXTreeID of the root tree of the web contents. This will be the same + // as active_tree_id_ unless root_tree_id_ has no distillable content but has + // a child tree with distillable content. + ui::AXTreeID root_tree_id_ = ui::AXTreeIDUnknown(); + // For determining whether the latest tree is a reload or new page. std::string previous_tree_url_; base::OnceCallback<void()> set_url_information_callback_; @@ -474,6 +489,15 @@ std::map<ui::AXTreeID, ukm::SourceId> pending_ukm_sources_; + // Possible child tree ids that could be used to distill content if the + // root tree has no distillable content. This will only be used if + // may_use_child_for_active_tree_ is true. + std::set<ui::AXTreeID> child_tree_ids_; + + // If reading mode should attempt to use child trees to distill content. This + // should only be true if the root tree has no distillable content. + bool may_use_child_for_active_tree_ = false; + // List of observers of model state changes. base::ObserverList<ModelObserver, /*check_empty=*/true> observers_;
diff --git a/chrome/renderer/accessibility/read_anything/read_anything_node_utils.cc b/chrome/renderer/accessibility/read_anything/read_anything_node_utils.cc index c43e2de8..396de915 100644 --- a/chrome/renderer/accessibility/read_anything/read_anything_node_utils.cc +++ b/chrome/renderer/accessibility/read_anything/read_anything_node_utils.cc
@@ -14,12 +14,12 @@ namespace a11y { -bool IsSuperscript(ui::AXNode* ax_node) { +bool IsSuperscript(const ui::AXNode* ax_node) { return ax_node->data().GetTextPosition() == ax::mojom::TextPosition::kSuperscript; } -bool IsTextForReadAnything(ui::AXNode* node, bool is_pdf, bool is_docs) { +bool IsTextForReadAnything(const ui::AXNode* node, bool is_pdf, bool is_docs) { if (!node) { return false; } @@ -70,7 +70,7 @@ return (ui::IsControl(role) && !ui::IsTextField(role)) || ui::IsSelect(role); } -std::string GetHtmlTag(ui::AXNode* ax_node, bool is_pdf, bool is_docs) { +std::string GetHtmlTag(const ui::AXNode* ax_node, bool is_pdf, bool is_docs) { std::string html_tag = ax_node->GetStringAttribute(ax::mojom::StringAttribute::kHtmlTag); @@ -107,7 +107,8 @@ return html_tag; } -std::string GetHtmlTagForPDF(ui::AXNode* ax_node, const std::string& html_tag) { +std::string GetHtmlTagForPDF(const ui::AXNode* ax_node, + const std::string& html_tag) { ax::mojom::Role role = ax_node->GetRole(); // Some nodes in PDFs don't have an HTML tag so use role instead. @@ -138,7 +139,7 @@ } } -std::string GetHeadingHtmlTagForPDF(ui::AXNode* ax_node, +std::string GetHeadingHtmlTagForPDF(const ui::AXNode* ax_node, const std::string& html_tag) { // Sometimes whole paragraphs can be formatted as a heading. If the text is // longer than 2 lines, assume it was meant to be a paragragh. @@ -172,19 +173,21 @@ return html_tag; } -std::string GetAltText(ui::AXNode* ax_node) { +std::string GetAltText(const ui::AXNode* ax_node) { std::string alt_text = ax_node->GetStringAttribute(ax::mojom::StringAttribute::kName); return alt_text; } -std::string GetImageDataUrl(ui::AXNode* ax_node) { +std::string GetImageDataUrl(const ui::AXNode* ax_node) { std::string url = ax_node->GetStringAttribute(ax::mojom::StringAttribute::kImageDataUrl); return url; } -std::u16string GetTextContent(ui::AXNode* ax_node, bool is_docs, bool is_pdf) { +std::u16string GetTextContent(const ui::AXNode* ax_node, + bool is_docs, + bool is_pdf) { // For Google Docs, because the content is rendered in canvas, we distill // text from the "Annotated Canvas" // (https://sites.google.com/corp/google.com/docs-canvas-migration/home) @@ -240,7 +243,7 @@ return ax_node->GetTextContentUTF16(); } -std::u16string GetNameAttributeText(ui::AXNode* ax_node) { +std::u16string GetNameAttributeText(const ui::AXNode* ax_node) { DCHECK(ax_node); std::u16string node_text; if (ax_node->HasStringAttribute(ax::mojom::StringAttribute::kName)) {
diff --git a/chrome/renderer/accessibility/read_anything/read_anything_node_utils.h b/chrome/renderer/accessibility/read_anything/read_anything_node_utils.h index c3cff9af..16ecb4a 100644 --- a/chrome/renderer/accessibility/read_anything/read_anything_node_utils.h +++ b/chrome/renderer/accessibility/read_anything/read_anything_node_utils.h
@@ -12,36 +12,41 @@ namespace a11y { // Returns whether the given node represents a superscript. -bool IsSuperscript(ui::AXNode* ax_node); +bool IsSuperscript(const ui::AXNode* ax_node); // Returns whether the given node is a text node displayed by read anything. -bool IsTextForReadAnything(ui::AXNode* ax_node, bool is_pdf, bool is_docs); +bool IsTextForReadAnything(const ui::AXNode* ax_node, + bool is_pdf, + bool is_docs); // Returns whether the given node should be ignored by reading mode. bool IsIgnored(const ui::AXNode* const ax_node, bool is_pdf); // Returns the html tag for the given node. -std::string GetHtmlTag(ui::AXNode* ax_node, bool is_pdf, bool is_docs); +std::string GetHtmlTag(const ui::AXNode* ax_node, bool is_pdf, bool is_docs); // Returns the html tag for the given node which is inside a pdf. -std::string GetHtmlTagForPDF(ui::AXNode* ax_node, const std::string& html_tag); +std::string GetHtmlTagForPDF(const ui::AXNode* ax_node, + const std::string& html_tag); // Returns the heading html tag for the given node which is inside a pdf. -std::string GetHeadingHtmlTagForPDF(ui::AXNode* ax_node, +std::string GetHeadingHtmlTagForPDF(const ui::AXNode* ax_node, const std::string& html_tag); // Returns the alt text for the given node. -std::string GetAltText(ui::AXNode* ax_node); +std::string GetAltText(const ui::AXNode* ax_node); // Returns the image data url for the given node. -std::string GetImageDataUrl(ui::AXNode* ax_node); +std::string GetImageDataUrl(const ui::AXNode* ax_node); // Returns the text content for the given node. This needs to be a wrapper // instead of getting text from the node directly because the text content // is different if in Google Docs or pdfs. -std::u16string GetTextContent(ui::AXNode* ax_node, bool is_docs, bool is_pdf); +std::u16string GetTextContent(const ui::AXNode* ax_node, + bool is_docs, + bool is_pdf); -std::u16string GetNameAttributeText(ui::AXNode* ax_node); +std::u16string GetNameAttributeText(const ui::AXNode* ax_node); } // namespace a11y #endif // CHROME_RENDERER_ACCESSIBILITY_READ_ANYTHING_READ_ANYTHING_NODE_UTILS_H_
diff --git a/chrome/renderer/accessibility/read_anything/read_anything_node_utils_unittest.cc b/chrome/renderer/accessibility/read_anything/read_anything_node_utils_unittest.cc index 4057903..2595f7d 100644 --- a/chrome/renderer/accessibility/read_anything/read_anything_node_utils_unittest.cc +++ b/chrome/renderer/accessibility/read_anything/read_anything_node_utils_unittest.cc
@@ -24,6 +24,43 @@ EXPECT_FALSE(a11y::IsTextForReadAnything(nullptr, false, false)); } +TEST_F(ReadAnythingNodeUtilsTest, + IsTextForReadAnything_ReturnsTrueOnListMarker) { + static constexpr ui::AXNodeID kId = 2; + ui::AXNodeData data = test::TextNode(kId, u"Just regular text."); + data.role = ax::mojom::Role::kListMarker; + ui::AXTree tree; + ui::AXNode node(&tree, nullptr, kId, 0); + node.SetData(std::move(data)); + EXPECT_TRUE(a11y::IsTextForReadAnything(&node, false, false)); +} + +TEST_F(ReadAnythingNodeUtilsTest, + IsTextForReadAnything_ReturnsFalseWithHtmlTag) { + static constexpr ui::AXNodeID kId = 2; + ui::AXNodeData data = test::TextNode(kId, u"Text in HTML"); + // Explicitly set the html tag to an empty string. + data.AddStringAttribute(ax::mojom::StringAttribute::kHtmlTag, "p"); + + ui::AXTree tree; + ui::AXNode node(&tree, nullptr, kId, 0); + node.SetData(std::move(data)); + EXPECT_FALSE(a11y::IsTextForReadAnything(&node, false, false)); +} + +TEST_F(ReadAnythingNodeUtilsTest, + IsTextForReadAnything_ReturnsTrueWithEmptyHtmlTag) { + static constexpr ui::AXNodeID kId = 2; + ui::AXNodeData data = test::TextNode(kId, u"Sentence"); + // Explicitly set the html tag to an empty string. + data.AddStringAttribute(ax::mojom::StringAttribute::kHtmlTag, ""); + + ui::AXTree tree; + ui::AXNode node(&tree, nullptr, kId, 0); + node.SetData(std::move(data)); + EXPECT_TRUE(a11y::IsTextForReadAnything(&node, false, false)); +} + TEST_F(ReadAnythingNodeUtilsTest, GetTextContent_PDF_FiltersReturnCharacters) { const std::u16string sentence = u"Hello, this is\n a sentence \r with line breaks."; @@ -74,3 +111,17 @@ EXPECT_NE(text.find('\n'), std::string::npos); EXPECT_NE(text.find('\r'), std::string::npos); } + +TEST_F(ReadAnythingNodeUtilsTest, IsSuperscript) { + const std::u16string sentence = + u"This is a superscript: <sup>superscript</sup>"; + ui::AXNodeData data = test::TextNode(2, sentence); + ui::AXTree tree; + ui::AXNode node(&tree, nullptr, 2, 0); + node.SetData(std::move(data)); + EXPECT_FALSE(a11y::IsSuperscript(&node)); + + data.SetTextPosition(ax::mojom::TextPosition::kSuperscript); + node.SetData(std::move(data)); + EXPECT_TRUE(a11y::IsSuperscript(&node)); +}
diff --git a/chrome/test/BUILD.gn b/chrome/test/BUILD.gn index 422673a..09daa5a 100644 --- a/chrome/test/BUILD.gn +++ b/chrome/test/BUILD.gn
@@ -1345,7 +1345,6 @@ "../browser/metrics/server_urls_browsertest.cc", "../browser/metrics/startup_metrics_browsertest.cc", "../browser/metrics/tab_stats/tab_stats_tracker_browsertest.cc", - "../browser/metrics/usage_scenario/tab_usage_scenario_tracker_browsertest.cc", "../browser/net/cert_verifier_service_browsertest.cc", "../browser/net/cert_verify_proc_browsertest.cc", "../browser/policy/policy_prefs_browsertest.cc", @@ -2300,6 +2299,7 @@ "//chrome/browser/ui/page_info", "//chrome/browser/ui/permission_bubble:browser_tests", "//chrome/browser/ui/prefs:browser_tests", + "//chrome/browser/ui/send_tab_to_self:browser_tests", # TODO(413315837): Remove this dependency when # c/b/ui/views/privacy_sandbox/privacy_sandbox_dialog_view_browsertest.cc gets modularized. @@ -3125,6 +3125,7 @@ "../browser/metrics/process_memory_metrics_emitter_browsertest.cc", "../browser/metrics/ukm_background_recorder_browsertest.cc", "../browser/metrics/ukm_browsertest.cc", + "../browser/metrics/usage_scenario/tab_usage_scenario_tracker_browsertest.cc", "../browser/metrics/variations/field_trials_on_off_browsertest.cc", "../browser/metrics/variations/force_field_trials_browsertest.cc", "../browser/metrics/variations/limited_entropy_randomization_browsertest.cc", @@ -3401,7 +3402,6 @@ "../browser/ui/search/new_tab_page_navigation_throttle_browsertest.cc", "../browser/ui/search/third_party_ntp_browsertest.cc", "../browser/ui/search_engines/search_engine_tab_helper_browsertest.cc", - "../browser/ui/send_tab_to_self/send_tab_to_self_toolbar_icon_controller_browsertest.cc", "../browser/ui/startup/startup_browser_creator_browsertest.cc", "../browser/ui/sync/one_click_signin_links_delegate_impl_browsertest.cc", "../browser/ui/tab_modal_confirm_dialog_browsertest.cc", @@ -6127,10 +6127,6 @@ "../browser/metrics/tab_footprint_aggregator_unittest.cc", "../browser/metrics/tab_stats/tab_stats_data_store_unittest.cc", "../browser/metrics/tab_stats/tab_stats_tracker_unittest.cc", - "../browser/metrics/usage_scenario/system_event_provider_unittest.cc", - "../browser/metrics/usage_scenario/tab_usage_scenario_tracker_unittest.cc", - "../browser/metrics/usage_scenario/usage_scenario_data_store_unittest.cc", - "../browser/metrics/usage_scenario/usage_scenario_unittest.cc", "../browser/metrics/variations/chrome_variations_service_client_unittest.cc", "../browser/navigation_predictor/navigation_predictor_unittest.cc", "../browser/net/cert_verifier_policy_unittest.cc", @@ -7739,6 +7735,10 @@ "../browser/metrics/power/power_metrics_unittest.cc", "../browser/metrics/power/process_monitor_unittest.cc", "../browser/metrics/usage_scenario/chrome_responsiveness_calculator_delegate_unittest.cc", + "../browser/metrics/usage_scenario/system_event_provider_unittest.cc", + "../browser/metrics/usage_scenario/tab_usage_scenario_tracker_unittest.cc", + "../browser/metrics/usage_scenario/usage_scenario_data_store_unittest.cc", + "../browser/metrics/usage_scenario/usage_scenario_unittest.cc", "../browser/new_tab_page/one_google_bar/one_google_bar_loader_impl_unittest.cc", "../browser/new_tab_page/one_google_bar/one_google_bar_service_unittest.cc", "../browser/new_tab_page/promos/promo_service_unittest.cc", @@ -8083,6 +8083,9 @@ "//components/data_sharing:test_support", "//components/passage_embeddings", "//components/passage_embeddings:test_support", + + # TODO(416215722): Remove this dep when send_tab_to_self_client_service_unittest.cc gets modularized. + "//chrome/browser/send_tab_to_self", ] if (is_chrome_branded) {
diff --git a/chrome/test/android/BUILD.gn b/chrome/test/android/BUILD.gn index 5701773a..c6f34ce 100644 --- a/chrome/test/android/BUILD.gn +++ b/chrome/test/android/BUILD.gn
@@ -216,6 +216,8 @@ "javatests/src/org/chromium/chrome/test/transit/edge_to_edge/EdgeToEdgeBottomChinFacility.java", "javatests/src/org/chromium/chrome/test/transit/edge_to_edge/EdgeToEdgeConditions.java", "javatests/src/org/chromium/chrome/test/transit/edge_to_edge/ViewportFitCoverPageStation.java", + "javatests/src/org/chromium/chrome/test/transit/hub/ArchiveMessageCardFacility.java", + "javatests/src/org/chromium/chrome/test/transit/hub/ArchivedTabsDialogStation.java", "javatests/src/org/chromium/chrome/test/transit/hub/CardAtPositionCondition.java", "javatests/src/org/chromium/chrome/test/transit/hub/HubBaseStation.java", "javatests/src/org/chromium/chrome/test/transit/hub/HubStationUtils.java", @@ -284,6 +286,7 @@ "//base:base_java", "//base:base_java_test_support", "//base:base_java_test_support_uncommon", + "//base:base_shared_preferences_java", "//base/test:public_transit_java", "//build/android:build_java", "//chrome/android:chrome_app_java_resources", @@ -297,6 +300,7 @@ "//chrome/browser/hub:factory_java", "//chrome/browser/hub:java", "//chrome/browser/hub/internal:java", + "//chrome/browser/preferences:java", "//chrome/browser/profiles/android:java", "//chrome/browser/settings:java", "//chrome/browser/settings:test_support_java",
diff --git a/chrome/test/android/DEPS b/chrome/test/android/DEPS index 28f09f8..4d10ee0c 100644 --- a/chrome/test/android/DEPS +++ b/chrome/test/android/DEPS
@@ -12,6 +12,7 @@ # This test code needs to depend on sync related classes and test tools. "+components/sync/android/java/src/org/chromium/components/sync", "+components/sync/test", + "+components/tab_groups/android/java", "+components/translate/content/android/java", "+components/webapk/android", ]
diff --git a/chrome/test/android/javatests/src/org/chromium/chrome/test/transit/AppMenuFacility.java b/chrome/test/android/javatests/src/org/chromium/chrome/test/transit/AppMenuFacility.java index 39c611b..9a6d5d1 100644 --- a/chrome/test/android/javatests/src/org/chromium/chrome/test/transit/AppMenuFacility.java +++ b/chrome/test/android/javatests/src/org/chromium/chrome/test/transit/AppMenuFacility.java
@@ -4,12 +4,8 @@ package org.chromium.chrome.test.transit; -import static androidx.test.espresso.Espresso.onView; -import static androidx.test.espresso.matcher.ViewMatchers.isDescendantOfA; import static androidx.test.espresso.matcher.ViewMatchers.withId; -import static androidx.test.espresso.matcher.ViewMatchers.withText; -import static org.hamcrest.CoreMatchers.allOf; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; @@ -18,7 +14,6 @@ import android.view.View; import android.widget.ListView; -import androidx.annotation.CallSuper; import androidx.annotation.IdRes; import androidx.test.espresso.action.GeneralClickAction; import androidx.test.espresso.action.Press; @@ -28,12 +23,12 @@ import org.hamcrest.Matcher; import org.hamcrest.TypeSafeMatcher; -import org.chromium.base.test.transit.Elements; import org.chromium.base.test.transit.Facility; import org.chromium.base.test.transit.ScrollableFacility; import org.chromium.base.test.transit.Station; import org.chromium.base.test.transit.Transition; import org.chromium.base.test.transit.ViewElement; +import org.chromium.base.test.transit.ViewSpec; import org.chromium.chrome.R; import org.chromium.chrome.browser.settings.MainSettings; import org.chromium.chrome.browser.ui.appmenu.AppMenuCoordinator; @@ -60,9 +55,13 @@ public ViewElement<ListView> menuListElement; + public AppMenuFacility() { + menuListElement = declareView(viewSpec(ListView.class, withId(R.id.app_menu_list))); + } + /** Create a new app menu item stub which throws UnsupportedOperationException if selected. */ protected Item<Void> declareStubMenuItem(ItemsBuilder items, @IdRes int id) { - return items.declareStubItem(itemViewMatcher(id), itemDataMatcher(id)); + return items.declareStubItem(itemViewSpec(withId(id)), itemDataMatcher(id)); } /** Create a new app menu item which runs |selectHandler| when selected. */ @@ -70,7 +69,7 @@ ItemsBuilder items, @IdRes int id, Function<ItemOnScreenFacility<SelectReturnT>, SelectReturnT> selectHandler) { - return items.declareItem(itemViewMatcher(id), itemDataMatcher(id), selectHandler); + return items.declareItem(itemViewSpec(withId(id)), itemDataMatcher(id), selectHandler); } /** Create a new app menu item which transitions to a |DestinationStationT| when selected. */ @@ -80,7 +79,7 @@ @IdRes int id, Callable<DestinationStationT> destinationStationFactory) { return items.declareItemToStation( - itemViewMatcher(id), itemDataMatcher(id), destinationStationFactory); + itemViewSpec(withId(id)), itemDataMatcher(id), destinationStationFactory); } /** Create a new app menu item which enters a |EnteredFacilityT| when selected. */ @@ -89,17 +88,17 @@ @IdRes int id, Callable<EnteredFacilityT> destinationFacilityFactory) { return items.declareItemToFacility( - itemViewMatcher(id), itemDataMatcher(id), destinationFacilityFactory); + itemViewSpec(withId(id)), itemDataMatcher(id), destinationFacilityFactory); } /** Create a new disabled app menu item. */ protected Item<Void> declareDisabledMenuItem(ItemsBuilder items, @IdRes int id) { - return items.declareDisabledItem(itemViewMatcher(id), itemDataMatcher(id)); + return items.declareDisabledItem(itemViewSpec(withId(id)), itemDataMatcher(id)); } /** Create a new app menu item expected to be absent. */ protected Item<Void> declareAbsentMenuItem(ItemsBuilder items, @IdRes int id) { - return items.declareAbsentItem(itemViewMatcher(id), itemDataMatcher(id)); + return items.declareAbsentItem(itemViewSpec(withId(id)), itemDataMatcher(id)); } /** @@ -120,11 +119,10 @@ ItemsBuilder items, @IdRes int id, Function<ItemOnScreenFacility<SelectReturnT>, SelectReturnT> selectHandler) { - return items.declarePossibleItem(itemViewMatcher(id), itemDataMatcher(id), selectHandler); + return items.declarePossibleItem( + itemViewSpec(withId(id)), itemDataMatcher(id), selectHandler); } - public static final Matcher<View> MENU_LIST_MATCHER = withId(R.id.app_menu_list); - public static final @IdRes int NEW_TAB_ID = R.id.new_tab_menu_id; public static final @IdRes int NEW_INCOGNITO_TAB_ID = R.id.new_incognito_tab_menu_id; public static final @IdRes int NEW_WINDOW_ID = R.id.new_window_menu_id; @@ -142,21 +140,6 @@ public static final @IdRes int SETTINGS_ID = R.id.preferences_id; public static final @IdRes int HELP_AND_FEEDBACK_ID = R.id.help_id; - @CallSuper - @Override - public void declareElements(Elements.Builder elements) { - menuListElement = elements.declareView(viewSpec(ListView.class, MENU_LIST_MATCHER)); - - super.declareElements(elements); - } - - @Override - public int getMinimumOnScreenItemCount() { - // Expect at least the first two menu items, it's enough to establish the transition is - // done. - return 2; - } - /** Default behavior for "Open new tab". */ protected RegularNewTabPageStation createNewTabPageStation() { return RegularNewTabPageStation.newBuilder() @@ -188,12 +171,8 @@ return new SettingsStation<>(MainSettings.class); } - protected static Matcher<View> itemViewMatcher(@IdRes int id) { - return allOf(withId(id), isDescendantOfA(MENU_LIST_MATCHER)); - } - - protected static Matcher<View> itemViewMatcher(String text) { - return allOf(withText(text), isDescendantOfA(MENU_LIST_MATCHER)); + protected ViewSpec<View> itemViewSpec(Matcher<View> matcher) { + return menuListElement.descendant(matcher); } protected static Matcher<ListItem> itemDataMatcher(@IdRes int id) { @@ -230,7 +209,7 @@ }, Press.FINGER); mHostStation.exitFacilitySync( - this, () -> onView(MENU_LIST_MATCHER).perform(clickBetweenViewAndLeftEdge)); + this, menuListElement.getPerformTrigger(clickBetweenViewAndLeftEdge)); } /** Close the menu programmatically. */
diff --git a/chrome/test/android/javatests/src/org/chromium/chrome/test/transit/context_menu/ContextMenuFacility.java b/chrome/test/android/javatests/src/org/chromium/chrome/test/transit/context_menu/ContextMenuFacility.java index e47e850f..dd0abcc 100644 --- a/chrome/test/android/javatests/src/org/chromium/chrome/test/transit/context_menu/ContextMenuFacility.java +++ b/chrome/test/android/javatests/src/org/chromium/chrome/test/transit/context_menu/ContextMenuFacility.java
@@ -4,26 +4,20 @@ package org.chromium.chrome.test.transit.context_menu; -import static androidx.test.espresso.matcher.ViewMatchers.isDescendantOfA; import static androidx.test.espresso.matcher.ViewMatchers.withId; -import static androidx.test.espresso.matcher.ViewMatchers.withText; - -import static org.hamcrest.CoreMatchers.allOf; import static org.chromium.base.test.transit.ViewSpec.viewSpec; import android.view.View; import androidx.annotation.CallSuper; -import androidx.annotation.IdRes; -import androidx.annotation.StringRes; import org.hamcrest.Description; import org.hamcrest.Matcher; import org.hamcrest.TypeSafeMatcher; -import org.chromium.base.test.transit.Elements; import org.chromium.base.test.transit.ScrollableFacility; +import org.chromium.base.test.transit.ViewElement; import org.chromium.base.test.transit.ViewSpec; import org.chromium.chrome.R; import org.chromium.chrome.browser.contextmenu.ContextMenuCoordinator.ListItemType; @@ -32,39 +26,24 @@ /** Station represents a opened context menu on a webpage. */ public class ContextMenuFacility extends ScrollableFacility<WebPageStation> { + public ViewElement<View> menuListElement; - public static final Matcher<View> CONTEXT_MENU_LIST_MATCHER = - withId(R.id.context_menu_list_view); - public static final ViewSpec MENU_LIST = viewSpec(CONTEXT_MENU_LIST_MATCHER); + public ContextMenuFacility() { + menuListElement = declareView(viewSpec(withId(R.id.context_menu_list_view))); + } @CallSuper @Override protected void declareItems(ScrollableFacility<WebPageStation>.ItemsBuilder items) { // Context menu always has a header. items.declareItem( - itemViewMatcher(R.id.title_and_url), withMenuItemType(ListItemType.HEADER), null); + itemViewSpec(withId(R.id.title_and_url)), + withMenuItemType(ListItemType.HEADER), + null); } - @CallSuper - @Override - public void declareElements(Elements.Builder elements) { - elements.declareView(MENU_LIST); - super.declareElements(elements); - } - - @Override - protected int getMinimumOnScreenItemCount() { - // Expect at least the first two menu items, it's enough to establish the transition is - // done. - return 2; - } - - protected static Matcher<View> itemViewMatcher(@IdRes int id) { - return allOf(withId(id), isDescendantOfA(CONTEXT_MENU_LIST_MATCHER)); - } - - protected static Matcher<View> itemViewMatcherWithText(@StringRes int stringRes) { - return allOf(withText(stringRes), isDescendantOfA(CONTEXT_MENU_LIST_MATCHER)); + protected ViewSpec<View> itemViewSpec(Matcher<View> matcher) { + return menuListElement.descendant(matcher); } protected static Matcher<MVCListAdapter.ListItem> withMenuItemType(@ListItemType int type) {
diff --git a/chrome/test/android/javatests/src/org/chromium/chrome/test/transit/context_menu/LinkContextMenuFacility.java b/chrome/test/android/javatests/src/org/chromium/chrome/test/transit/context_menu/LinkContextMenuFacility.java index a1bc5b9..52b8112 100644 --- a/chrome/test/android/javatests/src/org/chromium/chrome/test/transit/context_menu/LinkContextMenuFacility.java +++ b/chrome/test/android/javatests/src/org/chromium/chrome/test/transit/context_menu/LinkContextMenuFacility.java
@@ -4,7 +4,7 @@ package org.chromium.chrome.test.transit.context_menu; -import androidx.annotation.StringRes; +import static androidx.test.espresso.matcher.ViewMatchers.withText; import org.chromium.base.test.transit.Condition; import org.chromium.base.test.transit.Transition; @@ -20,10 +20,6 @@ * Facility represents a context menu triggered for a text link. This has to be used for a webpage. */ public class LinkContextMenuFacility extends ContextMenuFacility { - private static final @StringRes int MENU_OPEN_IN_NEW_TAB = R.string.contextmenu_open_in_new_tab; - private static final @StringRes int MENU_OPEN_IN_NEW_TAB_IN_GROUP = - R.string.contextmenu_open_in_new_tab_group; - private Item<Void> mOpenTabInNewTab; private Item<TabGroupUiFacility<WebPageStation>> mOpenTabInNewTabInGroup; @@ -33,13 +29,13 @@ mOpenTabInNewTab = items.declareItem( - itemViewMatcherWithText(MENU_OPEN_IN_NEW_TAB), + itemViewSpec(withText(R.string.contextmenu_open_in_new_tab)), null, this::createTabInBackground); mOpenTabInNewTabInGroup = items.declareItem( - itemViewMatcherWithText(MENU_OPEN_IN_NEW_TAB_IN_GROUP), + itemViewSpec(withText(R.string.contextmenu_open_in_new_tab_group)), null, this::createTabInBackgroundInGroup); }
diff --git a/chrome/test/android/javatests/src/org/chromium/chrome/test/transit/hub/ArchiveMessageCardFacility.java b/chrome/test/android/javatests/src/org/chromium/chrome/test/transit/hub/ArchiveMessageCardFacility.java new file mode 100644 index 0000000..58362bc3 --- /dev/null +++ b/chrome/test/android/javatests/src/org/chromium/chrome/test/transit/hub/ArchiveMessageCardFacility.java
@@ -0,0 +1,29 @@ +// Copyright 2025 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +package org.chromium.chrome.test.transit.hub; + +import static androidx.test.espresso.matcher.ViewMatchers.withText; + +import static org.hamcrest.Matchers.containsString; + +import org.chromium.base.test.transit.Facility; +import org.chromium.base.test.transit.ViewElement; + +/** Facility to represent the declutter message card shown in the regular tab switcher. */ +public class ArchiveMessageCardFacility extends Facility<TabSwitcherStation> { + private final ViewElement mTextElement; + + public ArchiveMessageCardFacility(TabSwitcherStation tabSwitcherStation) { + mTextElement = + declareView( + tabSwitcherStation.recyclerViewElement.descendant( + withText(containsString("Inactive tab")))); + } + + public ArchivedTabsDialogStation openArchivedTabsDialog() { + return mHostStation.travelToSync( + new ArchivedTabsDialogStation(), mTextElement.getClickTrigger()); + } +}
diff --git a/chrome/test/android/javatests/src/org/chromium/chrome/test/transit/hub/ArchivedTabsDialogStation.java b/chrome/test/android/javatests/src/org/chromium/chrome/test/transit/hub/ArchivedTabsDialogStation.java new file mode 100644 index 0000000..945d6b6 --- /dev/null +++ b/chrome/test/android/javatests/src/org/chromium/chrome/test/transit/hub/ArchivedTabsDialogStation.java
@@ -0,0 +1,83 @@ +// 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.test.transit.hub; + +import static androidx.test.espresso.matcher.ViewMatchers.withContentDescription; +import static androidx.test.espresso.matcher.ViewMatchers.withId; +import static androidx.test.espresso.matcher.ViewMatchers.withText; + +import static org.chromium.base.ThreadUtils.runOnUiThreadBlocking; +import static org.chromium.base.test.transit.ViewSpec.viewSpec; + +import android.view.View; + +import androidx.recyclerview.widget.RecyclerView; + +import org.chromium.base.test.transit.Station; +import org.chromium.base.test.transit.Transition.Trigger; +import org.chromium.base.test.transit.ViewElement; +import org.chromium.chrome.browser.ChromeTabbedActivity; +import org.chromium.chrome.browser.preferences.ChromeSharedPreferences; +import org.chromium.chrome.browser.tab.TabArchiveSettings; +import org.chromium.chrome.browser.tasks.tab_management.ArchivedTabsDialogCoordinator; +import org.chromium.chrome.browser.tasks.tab_management.TabArchiveSettingsFragment; +import org.chromium.chrome.test.R; +import org.chromium.chrome.test.transit.settings.SettingsStation; + +/** The station for the archived tabs dialog. */ +public class ArchivedTabsDialogStation extends Station<ChromeTabbedActivity> { + private final TabArchiveSettings mTabArchiveSettings; + + public ViewElement<View> dialogElement; + public ViewElement<RecyclerView> recyclerViewElement; + public ViewElement<View> closeButtonElement; + public ViewElement<View> iphElement; + + public ArchivedTabsDialogStation() { + super(ChromeTabbedActivity.class); + mTabArchiveSettings = + runOnUiThreadBlocking( + () -> new TabArchiveSettings(ChromeSharedPreferences.getInstance())); + dialogElement = declareView(viewSpec(withId(R.id.archived_tabs_dialog))); + recyclerViewElement = + declareView( + dialogElement + .getViewSpec() + .descendant( + RecyclerView.class, withId(R.id.tab_list_recycler_view))); + closeButtonElement = + declareView(viewSpec(withContentDescription("Hide inactive tabs dialog"))); + if (mTabArchiveSettings.shouldShowDialogIph()) { + declareElementFactory( + mActivityElement, + delayedElements -> { + iphElement = + delayedElements.declareView( + recyclerViewElement.descendant( + withText(getIphDescription()))); + }); + } + } + + public SettingsStation<TabArchiveSettingsFragment> openSettings(Trigger settingsTrigger) { + assert mTabArchiveSettings.shouldShowDialogIph(); + return travelToSync( + new SettingsStation<>(TabArchiveSettingsFragment.class), settingsTrigger); + } + + public RegularTabSwitcherStation closeDialog() { + return travelToSync( + RegularTabSwitcherStation.from(getActivity().getTabModelSelector()), + closeButtonElement.getClickTrigger()); + } + + // Private functions. + + private String getIphDescription() { + return String.valueOf( + ArchivedTabsDialogCoordinator.getIphDescription( + getActivity(), mTabArchiveSettings)); + } +}
diff --git a/chrome/test/android/javatests/src/org/chromium/chrome/test/transit/hub/HubBaseStation.java b/chrome/test/android/javatests/src/org/chromium/chrome/test/transit/hub/HubBaseStation.java index 91fc281..412e80ff 100644 --- a/chrome/test/android/javatests/src/org/chromium/chrome/test/transit/hub/HubBaseStation.java +++ b/chrome/test/android/javatests/src/org/chromium/chrome/test/transit/hub/HubBaseStation.java
@@ -4,33 +4,26 @@ package org.chromium.chrome.test.transit.hub; -import static androidx.test.espresso.Espresso.onView; -import static androidx.test.espresso.action.ViewActions.click; -import static androidx.test.espresso.assertion.ViewAssertions.matches; -import static androidx.test.espresso.matcher.ViewMatchers.isDescendantOfA; -import static androidx.test.espresso.matcher.ViewMatchers.isDisplayed; import static androidx.test.espresso.matcher.ViewMatchers.withContentDescription; import static androidx.test.espresso.matcher.ViewMatchers.withId; -import static org.hamcrest.CoreMatchers.allOf; import static org.hamcrest.CoreMatchers.containsString; +import static org.chromium.base.test.transit.ViewElement.unscopedOption; import static org.chromium.base.test.transit.ViewSpec.viewSpec; import android.view.View; import androidx.annotation.Nullable; import androidx.test.espresso.Espresso; -import androidx.test.espresso.NoMatchingViewException; import com.google.android.material.tabs.TabLayout; import org.chromium.base.test.transit.Element; +import org.chromium.base.test.transit.Facility; import org.chromium.base.test.transit.Station; import org.chromium.base.test.transit.Transition; -import org.chromium.base.test.transit.TravelException; import org.chromium.base.test.transit.ViewElement; -import org.chromium.base.test.transit.ViewSpec; import org.chromium.chrome.browser.ChromeTabbedActivity; import org.chromium.chrome.browser.hub.HubToolbarView; import org.chromium.chrome.browser.hub.PaneId; @@ -43,12 +36,6 @@ /** The base station for Hub, with several panes and a toolbar. */ public abstract class HubBaseStation extends Station<ChromeTabbedActivity> { - public static final ViewSpec<HubToolbarView> HUB_TOOLBAR = - viewSpec(HubToolbarView.class, withId(R.id.hub_toolbar)); - public static final ViewSpec<View> HUB_PANE_HOST = viewSpec(withId(R.id.hub_pane_host)); - public static final ViewSpec<TabLayout> HUB_PANE_SWITCHER = - HUB_TOOLBAR.descendant(TabLayout.class, withId(R.id.pane_switcher)); - public final Element<TabModelSelector> tabModelSelectorElement; public final ViewElement<HubToolbarView> toolbarElement; public final ViewElement<View> paneHostElement; @@ -68,14 +55,15 @@ tabModelSelectorElement = declareEnterConditionAsElement(new TabModelSelectorCondition(mActivityElement)); - toolbarElement = declareView(HUB_TOOLBAR); - paneHostElement = declareView(HUB_PANE_HOST); + toolbarElement = declareView(viewSpec(HubToolbarView.class, withId(R.id.hub_toolbar))); + paneHostElement = declareView(viewSpec(withId(R.id.hub_pane_host))); menuButtonElement = hasMenuButton ? declareView(toolbarElement.descendant(withId(R.id.menu_button))) : null; - paneSwitcherElement = declareView(HUB_PANE_SWITCHER); + paneSwitcherElement = + declareView(toolbarElement.descendant(TabLayout.class, withId(R.id.pane_switcher))); // TODO(crbug.com/386819654): Add a member of type ViewElement representing tab group pane // The non-regular toggle tab button contentDescription is a substring found in the string: @@ -126,20 +114,12 @@ HubStationUtils.createHubStation( paneId, mRegularTabsExist, mIncognitoTabsExist)); - try { - onView(HUB_PANE_SWITCHER.getViewMatcher()).check(matches(isDisplayed())); - } catch (NoMatchingViewException e) { - throw TravelException.newTravelException( - "Hub pane switcher is not visible to switch to " + paneId); - } - String contentDescription = HubStationUtils.getContentDescriptionSubstringForIdPaneSelection(paneId); - return travelToSync( - destinationStation, - () -> { - clickPaneSwitcherForPaneWithContentDescription(contentDescription); - }); + SwitchPaneButtonFacility button = + enterFacilitySync( + new SwitchPaneButtonFacility(contentDescription), /* trigger= */ null); + return travelToSync(destinationStation, button.buttonElement.getClickTrigger()); } /** Convenience method to select the Regular Tab Switcher pane. */ @@ -152,13 +132,17 @@ return selectPane(PaneId.INCOGNITO_TAB_SWITCHER, IncognitoTabSwitcherStation.class); } - private void clickPaneSwitcherForPaneWithContentDescription(String contentDescription) { - // TODO(crbug.com/40287437): Content description seems reasonable for now, this might get - // harder once we use a recycler view with text based buttons. - onView( - allOf( - isDescendantOfA(HUB_PANE_SWITCHER.getViewMatcher()), - withContentDescription(containsString(contentDescription)))) - .perform(click()); + public class SwitchPaneButtonFacility extends Facility<HubBaseStation> { + public final ViewElement<View> buttonElement; + + public SwitchPaneButtonFacility(String contentDescription) { + // TODO(crbug.com/40287437): Content description seems reasonable for now, this might + // get harder once we use a recycler view with text based buttons. + buttonElement = + declareView( + paneSwitcherElement.descendant( + withContentDescription(containsString(contentDescription))), + unscopedOption()); + } } }
diff --git a/chrome/test/android/javatests/src/org/chromium/chrome/test/transit/hub/RegularTabSwitcherStation.java b/chrome/test/android/javatests/src/org/chromium/chrome/test/transit/hub/RegularTabSwitcherStation.java index d2839d81..be111d97 100644 --- a/chrome/test/android/javatests/src/org/chromium/chrome/test/transit/hub/RegularTabSwitcherStation.java +++ b/chrome/test/android/javatests/src/org/chromium/chrome/test/transit/hub/RegularTabSwitcherStation.java
@@ -63,4 +63,9 @@ return travelToSync(page, newTabButtonElement.getClickTrigger()); } + + public ArchiveMessageCardFacility expectArchiveMessageCard() { + return enterFacilitySync( + new ArchiveMessageCardFacility(/* tabSwitcherStation= */ this), null); + } }
diff --git a/chrome/test/android/javatests/src/org/chromium/chrome/test/transit/hub/TabGroupPaneStation.java b/chrome/test/android/javatests/src/org/chromium/chrome/test/transit/hub/TabGroupPaneStation.java index ea95b86..b139d58 100644 --- a/chrome/test/android/javatests/src/org/chromium/chrome/test/transit/hub/TabGroupPaneStation.java +++ b/chrome/test/android/javatests/src/org/chromium/chrome/test/transit/hub/TabGroupPaneStation.java
@@ -8,21 +8,22 @@ import androidx.recyclerview.widget.RecyclerView; -import org.chromium.base.test.transit.Elements; import org.chromium.base.test.transit.ViewElement; -import org.chromium.base.test.transit.ViewSpec; import org.chromium.chrome.browser.hub.PaneId; import org.chromium.chrome.test.R; /** The tab groups pane station. */ public class TabGroupPaneStation extends HubBaseStation { - public static final ViewSpec<RecyclerView> TAB_GROUP_LIST = - HUB_PANE_HOST.descendant(RecyclerView.class, withId(R.id.tab_group_list_recycler_view)); - public ViewElement<RecyclerView> recyclerViewElement; public TabGroupPaneStation(boolean regularTabsExist, boolean incognitoTabsExist) { super(regularTabsExist, incognitoTabsExist, /* hasMenuButton= */ false); + + // TODO(crbug.com/413652567): Handle empty state case. + recyclerViewElement = + declareView( + paneHostElement.descendant( + RecyclerView.class, withId(R.id.tab_group_list_recycler_view))); } @Override @@ -30,12 +31,5 @@ return PaneId.TAB_GROUPS; } - @Override - public void declareElements(Elements.Builder elements) { - super.declareElements(elements); - // TODO(crbug.com/413652567): Handle empty state case. - recyclerViewElement = elements.declareView(TAB_GROUP_LIST); - } - // TODO(crbug.com/413652567): Implement actions. }
diff --git a/chrome/test/android/javatests/src/org/chromium/chrome/test/transit/hub/TabListEditorAppMenu.java b/chrome/test/android/javatests/src/org/chromium/chrome/test/transit/hub/TabListEditorAppMenu.java index d792aa7..58f072c 100644 --- a/chrome/test/android/javatests/src/org/chromium/chrome/test/transit/hub/TabListEditorAppMenu.java +++ b/chrome/test/android/javatests/src/org/chromium/chrome/test/transit/hub/TabListEditorAppMenu.java
@@ -4,6 +4,8 @@ package org.chromium.chrome.test.transit.hub; +import static androidx.test.espresso.matcher.ViewMatchers.withText; + import android.util.Pair; import org.chromium.base.test.transit.Condition; @@ -45,30 +47,30 @@ mCloseMenuItem = items.declareItem( - itemViewMatcher("Close " + tabOrTabs), + itemViewSpec(withText("Close " + tabOrTabs)), itemDataMatcher(R.id.tab_list_editor_close_menu_item), this::doCloseTabs); if (mListEditor.isAnyGroupSelected()) { mGroupWithoutDialogMenuItem = items.declareItem( - itemViewMatcher("Group " + tabOrTabs), + itemViewSpec(withText("Group " + tabOrTabs)), itemDataMatcher(R.id.tab_list_editor_group_menu_item), this::doGroupTabsWithoutDialog); } else { mGroupWithDialogMenuItem = items.declareItem( - itemViewMatcher("Group " + tabOrTabs), + itemViewSpec(withText("Group " + tabOrTabs)), itemDataMatcher(R.id.tab_list_editor_group_menu_item), this::doGroupTabs); } items.declareStubItem( - itemViewMatcher("Bookmark " + tabOrTabs), + itemViewSpec(withText("Bookmark " + tabOrTabs)), itemDataMatcher(R.id.tab_list_editor_bookmark_menu_item)); items.declareStubItem( - itemViewMatcher("Share " + tabOrTabs), + itemViewSpec(withText("Share " + tabOrTabs)), itemDataMatcher(R.id.tab_list_editor_share_menu_item)); }
diff --git a/chrome/test/android/javatests/src/org/chromium/chrome/test/transit/hub/TabSwitcherGroupCardAppMenuFacility.java b/chrome/test/android/javatests/src/org/chromium/chrome/test/transit/hub/TabSwitcherGroupCardAppMenuFacility.java index 10f2059..2bcf366 100644 --- a/chrome/test/android/javatests/src/org/chromium/chrome/test/transit/hub/TabSwitcherGroupCardAppMenuFacility.java +++ b/chrome/test/android/javatests/src/org/chromium/chrome/test/transit/hub/TabSwitcherGroupCardAppMenuFacility.java
@@ -4,34 +4,29 @@ package org.chromium.chrome.test.transit.hub; -import static androidx.test.espresso.matcher.ViewMatchers.isDescendantOfA; import static androidx.test.espresso.matcher.ViewMatchers.withId; import static androidx.test.espresso.matcher.ViewMatchers.withText; -import static org.hamcrest.CoreMatchers.allOf; +import static org.chromium.base.test.transit.ViewSpec.viewSpec; import android.view.View; -import org.hamcrest.Matcher; - import org.chromium.base.test.transit.ScrollableFacility; +import org.chromium.base.test.transit.ViewElement; import org.chromium.chrome.test.R; import org.chromium.chrome.test.transit.tabmodel.TabGroupUtil; /** The app menu shown when pressing ("...") in the Hub on a tab group card. */ public class TabSwitcherGroupCardAppMenuFacility extends ScrollableFacility<TabSwitcherStation> { - public static final Matcher<View> CLOSE_TAB_GROUP_MENU_ITEM_MATCHER = - allOf( - withText(R.string.close_tab_group_menu_item), - isDescendantOfA(withId(R.id.tab_group_action_menu_list))); - private final boolean mIsIncognito; private final String mTitle; + public final ViewElement<View> menuListElement; private Item<UndoSnackbarFacility> mCloseRegularTabGroup; public TabSwitcherGroupCardAppMenuFacility(boolean isIncognito, String title) { mIsIncognito = isIncognito; mTitle = title; + menuListElement = declareView(viewSpec(withId(R.id.tab_group_action_menu_list))); } @Override @@ -39,17 +34,13 @@ if (!mIsIncognito) { mCloseRegularTabGroup = items.declareItem( - CLOSE_TAB_GROUP_MENU_ITEM_MATCHER, null, this::doCloseRegularTabGroup); + menuListElement.descendant( + withText(R.string.close_tab_group_menu_item)), + /* offScreenDataMatcher= */ null, + this::doCloseRegularTabGroup); } } - @Override - public int getMinimumOnScreenItemCount() { - // Expect at least the first two menu items, it's enough to establish the transition is - // done. - return 2; - } - /** Select "Close" from the tab group overflow menu to close (hide) the tab group. */ public UndoSnackbarFacility closeRegularTabGroup() { return mCloseRegularTabGroup.scrollToAndSelect();
diff --git a/chrome/test/android/javatests/src/org/chromium/chrome/test/transit/hub/TabSwitcherGroupCardFacility.java b/chrome/test/android/javatests/src/org/chromium/chrome/test/transit/hub/TabSwitcherGroupCardFacility.java index d278e25d..9060f10 100644 --- a/chrome/test/android/javatests/src/org/chromium/chrome/test/transit/hub/TabSwitcherGroupCardFacility.java +++ b/chrome/test/android/javatests/src/org/chromium/chrome/test/transit/hub/TabSwitcherGroupCardFacility.java
@@ -4,14 +4,29 @@ package org.chromium.chrome.test.transit.hub; +import static androidx.test.espresso.matcher.ViewMatchers.withId; +import static androidx.test.espresso.matcher.ViewMatchers.withParent; + +import static org.chromium.base.test.transit.Condition.whetherEquals; +import static org.chromium.base.test.transit.SimpleConditions.uiThreadCondition; +import static org.chromium.base.test.transit.ViewSpec.viewSpec; + +import android.content.res.ColorStateList; +import android.graphics.drawable.GradientDrawable; import android.view.View; +import android.widget.FrameLayout; import androidx.annotation.Nullable; import org.chromium.base.test.transit.Elements; +import org.chromium.base.test.transit.Facility; import org.chromium.base.test.transit.ViewElement; +import org.chromium.base.test.transit.ViewSpec; +import org.chromium.chrome.test.R; import org.chromium.chrome.test.transit.tabmodel.TabGroupExistsCondition; import org.chromium.chrome.test.transit.tabmodel.TabGroupUtil; +import org.chromium.components.tab_groups.TabGroupColorId; +import org.chromium.components.tab_groups.TabGroupColorPickerUtils; import java.util.ArrayList; import java.util.Collections; @@ -79,4 +94,39 @@ new TabSwitcherGroupCardAppMenuFacility(isIncognito, mTitle), menuButtonElement.getClickTrigger()); } + + public TabGroupCardColorFacility expectColor(@TabGroupColorId int color) { + return mHostStation.enterFacilitySync( + new TabGroupCardColorFacility(color), /* trigger= */ null); + } + + public class TabGroupCardColorFacility extends Facility<TabSwitcherStation> { + public final ViewElement<FrameLayout> colorViewElement; + + public TabGroupCardColorFacility(@TabGroupColorId int color) { + ColorStateList expectedColor = + ColorStateList.valueOf( + TabGroupColorPickerUtils.getTabGroupColorPickerItemColor( + TabSwitcherGroupCardFacility.this.mHostStation.getActivity(), + color, + false)); + + ViewSpec<View> colorContainerSpec = + cardViewElement.descendant(withId(R.id.tab_group_color_view_container)); + colorViewElement = + declareView( + viewSpec( + FrameLayout.class, + withParent(colorContainerSpec.getViewMatcher()))); + declareEnterCondition( + uiThreadCondition( + "Tab group color should be " + expectedColor, + colorViewElement, + colorView -> { + return whetherEquals( + expectedColor, + ((GradientDrawable) colorView.getBackground()).getColor()); + })); + } + } }
diff --git a/chrome/test/android/javatests/src/org/chromium/chrome/test/transit/hub/TabSwitcherStation.java b/chrome/test/android/javatests/src/org/chromium/chrome/test/transit/hub/TabSwitcherStation.java index 5e676a51..5edc3ee 100644 --- a/chrome/test/android/javatests/src/org/chromium/chrome/test/transit/hub/TabSwitcherStation.java +++ b/chrome/test/android/javatests/src/org/chromium/chrome/test/transit/hub/TabSwitcherStation.java
@@ -12,6 +12,7 @@ import static org.hamcrest.CoreMatchers.allOf; import static org.hamcrest.CoreMatchers.instanceOf; +import static org.hamcrest.CoreMatchers.is; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; @@ -43,13 +44,6 @@ /** The base station for Hub tab switcher stations. */ public abstract class TabSwitcherStation extends HubBaseStation { - public static final ViewSpec<RecyclerView> TAB_LIST_RECYCLER_VIEW = - HUB_PANE_HOST.descendant(RecyclerView.class, withId(R.id.tab_list_recycler_view)); - public static final ViewSpec<View> TAB_GROUP_COLOR_ICON_VIEW = - viewSpec( - allOf( - withId(R.id.tab_group_color_view_container), - withParent(withId(R.id.card_view)))); public static final Matcher<View> TAB_CLOSE_BUTTON = allOf( withId(R.id.action_button), @@ -96,7 +90,10 @@ } }); } - recyclerViewElement = declareView(TAB_LIST_RECYCLER_VIEW); + recyclerViewElement = + declareView( + paneHostElement.descendant( + RecyclerView.class, withId(R.id.tab_list_recycler_view))); } public boolean isIncognito() { @@ -135,7 +132,7 @@ destination, () -> { ViewActionOnDescendant.performOnRecyclerViewNthItemDescendant( - TAB_LIST_RECYCLER_VIEW.getViewMatcher(), index, TAB_THUMBNAIL, click()); + is(recyclerViewElement.get()), index, TAB_THUMBNAIL, click()); }); } @@ -182,10 +179,7 @@ Transition.conditionOption(tabCountDecremented), () -> { ViewActionOnDescendant.performOnRecyclerViewNthItemDescendant( - TAB_LIST_RECYCLER_VIEW.getViewMatcher(), - index, - TAB_CLOSE_BUTTON, - click()); + is(recyclerViewElement.get()), index, TAB_CLOSE_BUTTON, click()); }); }
diff --git a/chrome/test/android/javatests/src/org/chromium/chrome/test/transit/ntp/MvtsFacility.java b/chrome/test/android/javatests/src/org/chromium/chrome/test/transit/ntp/MvtsFacility.java index 6a7a20e..6ad2b85d 100644 --- a/chrome/test/android/javatests/src/org/chromium/chrome/test/transit/ntp/MvtsFacility.java +++ b/chrome/test/android/javatests/src/org/chromium/chrome/test/transit/ntp/MvtsFacility.java
@@ -7,19 +7,17 @@ import static androidx.test.espresso.matcher.ViewMatchers.withId; import static androidx.test.espresso.matcher.ViewMatchers.withParentIndex; -import static org.hamcrest.CoreMatchers.allOf; import static org.hamcrest.CoreMatchers.instanceOf; import static org.chromium.base.test.transit.ViewSpec.viewSpec; import android.view.View; -import org.hamcrest.Matcher; - import org.chromium.base.test.transit.Elements; import org.chromium.base.test.transit.MoreViewConditions.ViewHasChildrenCountCondition; import org.chromium.base.test.transit.ScrollableFacility; import org.chromium.base.test.transit.ViewElement; +import org.chromium.base.test.transit.ViewSpec; import org.chromium.chrome.R; import org.chromium.chrome.browser.suggestions.SiteSuggestion; import org.chromium.chrome.browser.suggestions.tile.SuggestionsTileView; @@ -72,12 +70,12 @@ while (mNonTileIndices.contains(parentIndex)) { ++parentIndex; } - Matcher<View> tileMatcher = - allOf(instanceOf(SuggestionsTileView.class), withParentIndex(parentIndex)); + ViewSpec<View> tileSpec = + viewSpec(instanceOf(SuggestionsTileView.class), withParentIndex(parentIndex)); SiteSuggestion siteSuggestion = mSiteSuggestions.get(i); Item<WebPageStation> item = items.declareItemToStation( - tileMatcher, + tileSpec, /* offScreenDataMatcher= */ null, () -> WebPageStation.newBuilder()
diff --git a/chrome/test/data/extensions/api_test/printing/cancel_job.js b/chrome/test/data/extensions/api_test/printing/cancel_job.js index 58db3518..ba742542 100644 --- a/chrome/test/data/extensions/api_test/printing/cancel_job.js +++ b/chrome/test/data/extensions/api_test/printing/cancel_job.js
@@ -24,7 +24,7 @@ }); const url = 'http://localhost:' + config.testServer.port + '/pdf/test.pdf'; - submitJob('id', 'test job', url, response => { + submitJob('id', 'test job', url, minimal_ticket, response => { chrome.test.assertNe(undefined, response); chrome.test.assertNe(undefined, response.status); chrome.test.assertEq(chrome.printing.SubmitJobStatus.OK, response.status);
diff --git a/chrome/test/data/extensions/api_test/printing/get_print_job_status.js b/chrome/test/data/extensions/api_test/printing/get_print_job_status.js index bf14d5d..7388a63f 100644 --- a/chrome/test/data/extensions/api_test/printing/get_print_job_status.js +++ b/chrome/test/data/extensions/api_test/printing/get_print_job_status.js
@@ -8,7 +8,7 @@ }); const url = 'http://localhost:' + config.testServer.port + '/pdf/test.pdf'; - submitJob('id', 'test job', url, response => { + submitJob('id', 'test job', url, minimal_ticket, response => { chrome.test.assertNe(undefined, response); chrome.test.assertNe(undefined, response.status); chrome.test.assertEq(chrome.printing.SubmitJobStatus.OK, response.status);
diff --git a/chrome/test/data/extensions/api_test/printing/get_printer_info_margin_and_scale.html b/chrome/test/data/extensions/api_test/printing/get_printer_info_margin_and_scale.html new file mode 100644 index 0000000..32959756 --- /dev/null +++ b/chrome/test/data/extensions/api_test/printing/get_printer_info_margin_and_scale.html
@@ -0,0 +1,6 @@ +<!-- +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. +--> +<script src="get_printer_info_margin_and_scale.js"></script>
diff --git a/chrome/test/data/extensions/api_test/printing/get_printer_info_margin_and_scale.js b/chrome/test/data/extensions/api_test/printing/get_printer_info_margin_and_scale.js new file mode 100644 index 0000000..8076e1f --- /dev/null +++ b/chrome/test/data/extensions/api_test/printing/get_printer_info_margin_and_scale.js
@@ -0,0 +1,46 @@ +// 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. + +chrome.test.runTests([() => { + chrome.printing.getPrinterInfo('id', response => { + chrome.test.assertNe(undefined, response); + chrome.test.assertNe(undefined, response.capabilities); + chrome.test.assertNe(undefined, response.capabilities.printer); + + let fit_to_page = response.capabilities.printer.fit_to_page; + chrome.test.assertNe(undefined, fit_to_page); + chrome.test.assertEq(3, fit_to_page.option.length); + chrome.test.assertNe(undefined, fit_to_page.option[0]); + chrome.test.assertEq(undefined, fit_to_page.option[0].is_default); + chrome.test.assertEq('FIT', fit_to_page.option[0].type); + + chrome.test.assertNe(undefined, fit_to_page.option[1]); + chrome.test.assertEq(undefined, fit_to_page.option[1].is_default); + chrome.test.assertEq('AUTO', fit_to_page.option[1].type); + + chrome.test.assertNe(undefined, fit_to_page.option[2]); + chrome.test.assertNe(undefined, fit_to_page.option[2].is_default); + chrome.test.assertEq('AUTO', fit_to_page.option[2].type); + chrome.test.assertEq(true, fit_to_page.option[2].is_default); + + let margin = response.capabilities.printer.margins; + chrome.test.assertNe(undefined, margin); + chrome.test.assertEq(2, margin.option.length); + chrome.test.assertNe(undefined, margin.option[0]); + chrome.test.assertNe(undefined, margin.option[0].is_default); + chrome.test.assertEq(5008, margin.option[0].bottom_microns); + chrome.test.assertEq(3050, margin.option[0].left_microns); + chrome.test.assertEq(3002, margin.option[0].right_microns); + chrome.test.assertEq(1003, margin.option[0].top_microns); + + chrome.test.assertNe(undefined, margin.option[1]); + chrome.test.assertEq(undefined, margin.option[1].is_default); + chrome.test.assertEq(0, margin.option[1].bottom_microns); + chrome.test.assertEq(0, margin.option[1].left_microns); + chrome.test.assertEq(0, margin.option[1].right_microns); + chrome.test.assertEq(0, margin.option[1].top_microns); + + chrome.test.succeed(); + }); +}]);
diff --git a/chrome/test/data/extensions/api_test/printing/printing_util.js b/chrome/test/data/extensions/api_test/printing/printing_util.js index 72b8c4c..3abb8016 100644 --- a/chrome/test/data/extensions/api_test/printing/printing_util.js +++ b/chrome/test/data/extensions/api_test/printing/printing_util.js
@@ -3,7 +3,7 @@ // found in the LICENSE file. // Minimal required ticket. -const ticket = { +const minimal_ticket = { version: '1.0', print: { color: {type: 'STANDARD_COLOR'}, @@ -20,7 +20,32 @@ } }; -function formatPrintJobRequest(printerId, title, arrayBuffer) { +// Ticket with margins and scale. +const ticket_with_margins_and_scale = { + version: '1.0', + print: { + color: {type: 'STANDARD_COLOR'}, + duplex: {type: 'NO_DUPLEX'}, + page_orientation: {type: 'LANDSCAPE'}, + copies: {copies: 1}, + dpi: {horizontal_dpi: 300, vertical_dpi: 400}, + media_size: { + width_microns: 210000, + height_microns: 297000, + vendor_id: 'iso_a4_210x297mm' + }, + collate: {collate: false}, + fit_to_page: {type: 'FIT'}, + margins: { + top_microns: 1003, + right_microns: 3002, + bottom_microns: 5008, + left_microns: 3050 + } + } +}; + +function formatPrintJobRequest(printerId, title, arrayBuffer, ticket) { return { job: { printerId: printerId, @@ -33,18 +58,18 @@ }; } -function submitJob(printerId, title, url, callback) { - +function submitJob(printerId, title, url, ticket, callback) { fetch(url).then(response => response.arrayBuffer()).then(arrayBuffer => { - const submitJobRequest = formatPrintJobRequest( - printerId, title, arrayBuffer); + const submitJobRequest = + formatPrintJobRequest(printerId, title, arrayBuffer, ticket); chrome.printing.submitJob(submitJobRequest, callback); }); } -async function submitJobPromise(printerId, title, url) { +async function submitJobPromise(printerId, title, url, ticket) { let response = await fetch(url); let arrayBuffer = await response.arrayBuffer(); - const submitJobRequest = formatPrintJobRequest(printerId, title, arrayBuffer); + const submitJobRequest = + formatPrintJobRequest(printerId, title, arrayBuffer, ticket); return chrome.printing.submitJob(submitJobRequest); }
diff --git a/chrome/test/data/extensions/api_test/printing/submit_job.js b/chrome/test/data/extensions/api_test/printing/submit_job.js index c7263ae..70fe516d 100644 --- a/chrome/test/data/extensions/api_test/printing/submit_job.js +++ b/chrome/test/data/extensions/api_test/printing/submit_job.js
@@ -4,7 +4,7 @@ chrome.test.getConfig(function(config) { const url = 'http://localhost:' + config.testServer.port + '/pdf/test.pdf'; - submitJob('id', 'test job', url, response => { + submitJob('id', 'test job', url, minimal_ticket, response => { chrome.test.assertNe(undefined, response); chrome.test.assertNe(undefined, response.status); chrome.test.assertEq(chrome.printing.SubmitJobStatus.OK, response.status);
diff --git a/chrome/test/data/extensions/api_test/printing/submit_job_margins_and_scale.html b/chrome/test/data/extensions/api_test/printing/submit_job_margins_and_scale.html new file mode 100644 index 0000000..7172cde0 --- /dev/null +++ b/chrome/test/data/extensions/api_test/printing/submit_job_margins_and_scale.html
@@ -0,0 +1,7 @@ +<!-- +Copyright 2025 The Chromium Authors +Use of this source code is governed by a BSD-style license that can be +found in the LICENSE file. +--> +<script src="printing_util.js"></script> +<script src="submit_job_margins_and_scale.js"></script>
diff --git a/chrome/test/data/extensions/api_test/printing/submit_job_margins_and_scale.js b/chrome/test/data/extensions/api_test/printing/submit_job_margins_and_scale.js new file mode 100644 index 0000000..031e89f --- /dev/null +++ b/chrome/test/data/extensions/api_test/printing/submit_job_margins_and_scale.js
@@ -0,0 +1,15 @@ +// 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. + +chrome.test.getConfig(function(config) { + const url = 'http://localhost:' + config.testServer.port + '/pdf/test.pdf'; + submitJob('id', 'test job', url, ticket_with_margins_and_scale, response => { + chrome.test.assertNe(undefined, response); + chrome.test.assertNe(undefined, response.status); + chrome.test.assertEq(chrome.printing.SubmitJobStatus.OK, response.status); + chrome.test.assertNe(undefined, response.jobId); + + chrome.test.notifyPass(); + }); +});
diff --git a/chrome/test/data/extensions/api_test/printing/submit_job_promise.js b/chrome/test/data/extensions/api_test/printing/submit_job_promise.js index 3537bad..ba73b6c 100644 --- a/chrome/test/data/extensions/api_test/printing/submit_job_promise.js +++ b/chrome/test/data/extensions/api_test/printing/submit_job_promise.js
@@ -4,7 +4,7 @@ chrome.test.getConfig(async function(config) { const url = `http://localhost:${config.testServer.port}/pdf/test.pdf`; - let response = await submitJobPromise('id', 'test job', url); + let response = await submitJobPromise('id', 'test job', url, minimal_ticket); chrome.test.assertTrue(!!response); chrome.test.assertEq(chrome.printing.SubmitJobStatus.OK, response.status); chrome.test.assertTrue(!!response.jobId);
diff --git a/chrome/test/data/extensions/api_test/printing/submit_job_promise_margins_and_scale.html b/chrome/test/data/extensions/api_test/printing/submit_job_promise_margins_and_scale.html new file mode 100644 index 0000000..a3200cb --- /dev/null +++ b/chrome/test/data/extensions/api_test/printing/submit_job_promise_margins_and_scale.html
@@ -0,0 +1,7 @@ +<!-- +Copyright 2025 The Chromium Authors +Use of this source code is governed by a BSD-style license that can be +found in the LICENSE file. +--> +<script src="printing_util.js"></script> +<script src="submit_job_promise_margins_and_scale.js"></script>
diff --git a/chrome/test/data/extensions/api_test/printing/submit_job_promise_margins_and_scale.js b/chrome/test/data/extensions/api_test/printing/submit_job_promise_margins_and_scale.js new file mode 100644 index 0000000..2bfd429d --- /dev/null +++ b/chrome/test/data/extensions/api_test/printing/submit_job_promise_margins_and_scale.js
@@ -0,0 +1,14 @@ +// Copyright 2025 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +chrome.test.getConfig(async function(config) { + const url = `http://localhost:${config.testServer.port}/pdf/test.pdf`; + let response = await submitJobPromise( + 'id', 'test job', url, ticket_with_margins_and_scale); + chrome.test.assertTrue(!!response); + chrome.test.assertEq(chrome.printing.SubmitJobStatus.OK, response.status); + chrome.test.assertTrue(!!response.jobId); + + chrome.test.notifyPass(); +});
diff --git a/chrome/test/data/permissions/aiv3_ret_0.tflite b/chrome/test/data/permissions/aiv3_ret_0.tflite new file mode 100644 index 0000000..10e2358 --- /dev/null +++ b/chrome/test/data/permissions/aiv3_ret_0.tflite Binary files differ
diff --git a/chrome/test/data/permissions/aiv3_ret_1.tflite b/chrome/test/data/permissions/aiv3_ret_1.tflite new file mode 100644 index 0000000..b4e154a --- /dev/null +++ b/chrome/test/data/permissions/aiv3_ret_1.tflite Binary files differ
diff --git a/chrome/test/data/webui/bookmarks/command_manager_test.ts b/chrome/test/data/webui/bookmarks/command_manager_test.ts index 8dcffb1..2c91ddc8 100644 --- a/chrome/test/data/webui/bookmarks/command_manager_test.ts +++ b/chrome/test/data/webui/bookmarks/command_manager_test.ts
@@ -4,6 +4,7 @@ import type {BookmarksFolderNodeElement, BookmarksItemElement, BookmarksListElement, SelectFolderAction, SelectItemsAction} from 'chrome://bookmarks/bookmarks.js'; import {BookmarkManagerApiProxyImpl, BookmarksApiProxyImpl, BookmarksCommandManagerElement, Command, createBookmark, DialogFocusManager, getDisplayedList, MenuSource, selectFolder, setDebouncerForTesting} from 'chrome://bookmarks/bookmarks.js'; +import {loadTimeData} from 'chrome://resources/js/load_time_data.js'; import {isMac} from 'chrome://resources/js/platform.js'; import {assertDeepEquals, assertEquals, assertFalse, assertTrue} from 'chrome://webui-test/chai_assert.js'; import {pressAndReleaseKeyOn} from 'chrome://webui-test/keyboard_mock_interactions.js'; @@ -23,6 +24,10 @@ let bookmarkManagerProxy: TestBookmarkManagerApiProxy; setup(function() { + loadTimeData.overrideValues({ + splitViewEnabled: true, + }); + const bulkChildren = []; for (let i = 1; i <= 20; i++) { const id = '3' + i; @@ -309,6 +314,21 @@ assertEquals(20, ids.length); }); + test('"Open in Split View" passes correct args', async function() { + const items = new Set(['141']); + assertTrue(commandManager.canExecute(Command.OPEN_SPLIT_VIEW, items)); + + commandManager.handle(Command.OPEN_SPLIT_VIEW, items); + await microtasksFinished(); + + const [id, {active, split}] = + await bookmarkManagerProxy.whenCalled('openInNewTab'); + + assertEquals('141', id); + assertFalse(active); + assertTrue(split); + }); + test( 'cannot execute "Open in New Tab" on folders with no items', async () => { const items = new Set(['2']); @@ -338,6 +358,10 @@ assertTrue(commandItem[Command.OPEN_INCOGNITO].disabled); assertFalse(commandItem[Command.OPEN_INCOGNITO].hidden); + assertTrue(!!commandItem[Command.OPEN_SPLIT_VIEW]); + assertTrue(commandItem[Command.OPEN_SPLIT_VIEW].disabled); + assertFalse(commandItem[Command.OPEN_SPLIT_VIEW].hidden); + assertTrue(!!commandItem[Command.OPEN_NEW_GROUP]); assertTrue(commandItem[Command.OPEN_NEW_GROUP].disabled); assertFalse(commandItem[Command.OPEN_NEW_GROUP].hidden); @@ -545,10 +569,12 @@ test('double click opens items in foreground tab', async function() { simulateDoubleClick(items[1]!); - const [id, active] = await bookmarkManagerProxy.whenCalled('openInNewTab'); + const [id, {active, split}] = + await bookmarkManagerProxy.whenCalled('openInNewTab'); assertEquals('12', id); assertTrue(active); + assertFalse(split); }); test('shift-double click opens full selection', function() { @@ -599,10 +625,12 @@ // Only the middle-clicked item is opened. simulateMiddleClick(item2); - const [id, active] = await bookmarkManagerProxy.whenCalled('openInNewTab'); + const [id, {active, split}] = + await bookmarkManagerProxy.whenCalled('openInNewTab'); assertEquals('13', id); assertFalse(active); + assertFalse(split); }); test('middle-click does not open folders', function() { @@ -620,10 +648,12 @@ assertTrue(!!item); simulateMiddleClick(item, {shiftKey: true}); - const [id, active] = await bookmarkManagerProxy.whenCalled('openInNewTab'); + const [id, {active, split}] = + await bookmarkManagerProxy.whenCalled('openInNewTab'); assertEquals('12', id); assertTrue(active); + assertFalse(split); }); test(
diff --git a/chrome/test/data/webui/bookmarks/test_bookmark_manager_api_proxy.ts b/chrome/test/data/webui/bookmarks/test_bookmark_manager_api_proxy.ts index be142f0..40f438b 100644 --- a/chrome/test/data/webui/bookmarks/test_bookmark_manager_api_proxy.ts +++ b/chrome/test/data/webui/bookmarks/test_bookmark_manager_api_proxy.ts
@@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -import type {BookmarkManagerApiProxy} from 'chrome://bookmarks/bookmarks.js'; +import type {BookmarkManagerApiProxy, OpenInNewTabParams} from 'chrome://bookmarks/bookmarks.js'; import {FakeChromeEvent} from 'chrome://webui-test/fake_chrome_event.js'; import {TestBrowserProxy} from 'chrome://webui-test/test_browser_proxy.js'; @@ -55,8 +55,8 @@ this.methodCalled('openInNewWindow', [idList, incognito]); } - openInNewTab(id: string, active: boolean) { - this.methodCalled('openInNewTab', [id, active]); + openInNewTab(id: string, params?: OpenInNewTabParams) { + this.methodCalled('openInNewTab', [id, params]); } openInNewTabGroup(idList: string[]) {
diff --git a/chrome/test/data/webui/glic/api_test.ts b/chrome/test/data/webui/glic/api_test.ts index 1e0015e..78d6148cc 100644 --- a/chrome/test/data/webui/glic/api_test.ts +++ b/chrome/test/data/webui/glic/api_test.ts
@@ -661,12 +661,30 @@ async testScrollToFindsText() { assertTrue(!!this.host.scrollTo); + assertTrue(!!this.host.setTabContextPermissionState); + await this.host.setTabContextPermissionState(true); await this.host.scrollTo( {selector: {exactText: {text: 'Test Page'}}, highlight: true}); } + async testScrollToFindsTextNoTabContextPermission() { + assertTrue(!!this.host.scrollTo); + try { + await this.host.scrollTo( + {selector: {exactText: {text: 'Abracadabra'}}, highlight: true}); + } catch (e) { + assertEquals( + ScrollToErrorReason.TAB_CONTEXT_PERMISSION_DISABLED, + (e as ScrollToError).reason); + return; + } + assertTrue(false, 'scrollTo should have thrown an error'); + } + async testScrollToNoMatchFound() { assertTrue(!!this.host.scrollTo); + assertTrue(!!this.host.setTabContextPermissionState); + await this.host.setTabContextPermissionState(true); try { await this.host.scrollTo( {selector: {exactText: {text: 'Abracadabra'}}, highlight: true});
diff --git a/chrome/test/data/webui/new_tab_page/app_test.ts b/chrome/test/data/webui/new_tab_page/app_test.ts index 3f6386f..453bb30 100644 --- a/chrome/test/data/webui/new_tab_page/app_test.ts +++ b/chrome/test/data/webui/new_tab_page/app_test.ts
@@ -4,10 +4,11 @@ import type {Module} from 'chrome://new-tab-page/lazy_load.js'; import {counterfactualLoad, ModuleDescriptor, ModuleRegistry} from 'chrome://new-tab-page/lazy_load.js'; -import type {AppElement} from 'chrome://new-tab-page/new_tab_page.js'; import {$$, BackgroundManager, BrowserCommandProxy, CUSTOMIZE_CHROME_BUTTON_ELEMENT_ID, CustomizeDialogPage, NewTabPageProxy, NtpCustomizeChromeEntryPoint, NtpElement, VoiceAction, WindowProxy} from 'chrome://new-tab-page/new_tab_page.js'; +import type {AppElement, CustomizeButtonsElement} from 'chrome://new-tab-page/new_tab_page.js'; import type {PageRemote} from 'chrome://new-tab-page/new_tab_page.mojom-webui.js'; import {CustomizeChromeSection, NtpBackgroundImageSource, PageCallbackRouter, PageHandlerRemote} from 'chrome://new-tab-page/new_tab_page.mojom-webui.js'; +import type {CrButtonElement} from 'chrome://resources/cr_elements/cr_button/cr_button.js'; import type {CrToastElement} from 'chrome://resources/cr_elements/cr_toast/cr_toast.js'; import {Command, CommandHandlerRemote} from 'chrome://resources/js/browser_command.mojom-webui.js'; import {loadTimeData} from 'chrome://resources/js/load_time_data.js'; @@ -23,6 +24,7 @@ suite('NewTabPageAppTest', () => { let app: AppElement; + let customizeButtons: CustomizeButtonsElement; let windowProxy: TestMock<WindowProxy>; let handler: TestMock<PageHandlerRemote>; let callbackRouterRemote: PageRemote; @@ -34,7 +36,7 @@ const url: URL = new URL(location.href); const backgroundImageLoadTime: number = 123; - setup(() => { + setup(async () => { document.body.innerHTML = window.trustedTypes!.emptyHTML; windowProxy = installMock(WindowProxy); @@ -74,9 +76,19 @@ app = document.createElement('ntp-app'); document.body.appendChild(app); - return microtasksFinished(); + await microtasksFinished(); + + customizeButtons = app.$.customizeButtons; }); + function getCustomizeButton(): CrButtonElement { + return $$(customizeButtons, '#customizeButton')!; + } + + function getWallpaperSearchButton(): CrButtonElement { + return $$(customizeButtons, '#wallpaperSearchButton')!; + } + suite('Misc', () => { test('logs height', () => { // Assert. @@ -674,7 +686,6 @@ ['cr-most-visited', NtpElement.MOST_VISITED], ['ntp-middle-slot-promo', NtpElement.MIDDLE_SLOT_PROMO], ['#modules', NtpElement.MODULE], - ['#customizeButton', NtpElement.CUSTOMIZE_BUTTON], ] as Array<[string, NtpElement]>) .forEach(([selector, element]) => { test(`clicking '${selector}' records click`, () => { @@ -687,6 +698,16 @@ }); }); + test(`clicking #customizeButton records click`, () => { + // Act. + getCustomizeButton().click(); + + // Assert. + assertEquals(1, metrics.count('NewTabPage.Click')); + assertEquals( + 1, metrics.count('NewTabPage.Click', NtpElement.CUSTOMIZE_BUTTON)); + }); + test('clicking OGB records click', () => { // Act. window.dispatchEvent(new MessageEvent('message', { @@ -803,7 +824,7 @@ test('clicking customize button opens side panel', () => { // Act. - $$<HTMLElement>(app, '#customizeButton')!.click(); + getCustomizeButton().click(); // Assert. assertDeepEquals( @@ -827,7 +848,7 @@ 'NewTabPage.CustomizeChromeOpened', NtpCustomizeChromeEntryPoint.CUSTOMIZE_BUTTON)); await callbackRouterRemote.$.flushForTesting(); - $$<HTMLElement>(app, '#customizeButton')!.click(); + getCustomizeButton().click(); // Assert. assertDeepEquals( @@ -845,16 +866,10 @@ test('clicking customize button is accessible', async () => { callbackRouterRemote.setCustomizeChromeSidePanelVisibility(true); await callbackRouterRemote.$.flushForTesting(); - assertEquals( - 'true', - $$<HTMLElement>( - app, '#customizeButton')!.getAttribute('aria-pressed')); + assertEquals('true', getCustomizeButton().getAttribute('aria-pressed')); callbackRouterRemote.setCustomizeChromeSidePanelVisibility(false); await callbackRouterRemote.$.flushForTesting(); - assertEquals( - 'false', - $$<HTMLElement>( - app, '#customizeButton')!.getAttribute('aria-pressed')); + assertEquals('false', getCustomizeButton().getAttribute('aria-pressed')); }); suite('modules', () => { @@ -960,8 +975,8 @@ }; test('wallpaper search button is not shown if it is disabled', () => { - assertTrue(!!app.shadowRoot.querySelector('#customizeButton')); - assertFalse(!!app.shadowRoot.querySelector('#wallpaperSearchButton')); + assertTrue(!!getCustomizeButton()); + assertFalse(!!getWallpaperSearchButton()); }); test( @@ -969,13 +984,12 @@ async () => { // Customize chrome button is expanded and its icon has a // non-white color. - assertNotEquals( - 32, $$<HTMLElement>(app, '#customizeButton')!.offsetWidth); + assertNotEquals(32, getCustomizeButton().offsetWidth); assertNotStyle( - $$(app, '#customizeButton .customize-text')!, 'display', - 'none'); + getCustomizeButton().querySelector('.customize-text')!, + 'display', 'none'); assertNotStyle( - $$(app, '#customizeButton .customize-icon')!, + getCustomizeButton().querySelector('.customize-icon')!, 'background-color', 'rgb(255, 255, 255)'); const theme = createTheme({isDark: true}); @@ -984,36 +998,34 @@ await callbackRouterRemote.$.flushForTesting(); // Customize chrome button is collapsed and its icon is white. - assertEquals( - 32, $$<HTMLElement>(app, '#customizeButton')!.offsetWidth); + assertEquals(32, getCustomizeButton().offsetWidth); assertStyle( - $$(app, '#customizeButton .customize-icon')!, + getCustomizeButton().querySelector('.customize-icon')!, 'background-color', 'rgb(255, 255, 255)'); assertStyle( - $$(app, '#customizeButton .customize-text')!, 'display', - 'none'); + getCustomizeButton().querySelector('.customize-text')!, + 'display', 'none'); }); }); function assertButtonAnimated() { + assertNotStyle(getWallpaperSearchButton(), 'animation-name', 'none'); assertNotStyle( - $$(app, '#wallpaperSearchButton')!, 'animation-name', 'none'); - assertNotStyle( - $$(app, '#wallpaperSearchButton .customize-icon')!, 'animation-name', - 'none'); + getWallpaperSearchButton().querySelector('.customize-icon')!, + 'animation-name', 'none'); assertStyle( - $$(app, '#wallpaperSearchButton .customize-text')!, 'animation-name', - 'none'); + getWallpaperSearchButton().querySelector('.customize-text')!, + 'animation-name', 'none'); } function assertButtonNotAnimated() { - assertStyle($$(app, '#wallpaperSearchButton')!, 'animation-name', 'none'); + assertStyle(getWallpaperSearchButton(), 'animation-name', 'none'); assertStyle( - $$(app, '#wallpaperSearchButton .customize-icon')!, 'animation-name', - 'none'); + getWallpaperSearchButton().querySelector('.customize-icon')!, + 'animation-name', 'none'); assertStyle( - $$(app, '#wallpaperSearchButton .customize-text')!, 'animation-name', - 'none'); + getWallpaperSearchButton().querySelector('.customize-text')!, + 'animation-name', 'none'); } suite('ButtonEnabled', () => { @@ -1031,30 +1043,32 @@ }; test('wallpaper search button shows if it is enabled', () => { - assertTrue(!!app.shadowRoot.querySelector('#customizeButton')); - assertTrue(!!app.shadowRoot.querySelector('#wallpaperSearchButton')); + assertTrue(!!getCustomizeButton()); + assertTrue(!!getWallpaperSearchButton()); }); test('button has animation', () => { assertButtonAnimated(); }); - ([ - ['#customizeButton', NtpElement.CUSTOMIZE_BUTTON], - ['#wallpaperSearchButton', NtpElement.WALLPAPER_SEARCH_BUTTON], - ] as Array<[string, NtpElement]>) - .forEach(([selector, element]) => { - test(`clicking #wallpaperSearchButton records click`, () => { - $$<HTMLElement>(app, selector)!.click(); + test(`clicking #customizeButton records click`, () => { + getCustomizeButton().click(); + assertEquals(1, metrics.count('NewTabPage.Click')); + assertEquals( + 1, metrics.count('NewTabPage.Click', NtpElement.CUSTOMIZE_BUTTON)); + }); - assertEquals(1, metrics.count('NewTabPage.Click')); - assertEquals(1, metrics.count('NewTabPage.Click', element)); - }); - }); + test(`clicking #wallpaperSearchButton records click`, () => { + getWallpaperSearchButton().click(); + assertEquals(1, metrics.count('NewTabPage.Click')); + assertEquals( + 1, + metrics.count( + 'NewTabPage.Click', NtpElement.WALLPAPER_SEARCH_BUTTON)); + }); test('clicking wallpaper search button opens side panel', () => { - $$<HTMLElement>(app, '#wallpaperSearchButton')!.click(); - + getWallpaperSearchButton().click(); assertDeepEquals( [true, CustomizeChromeSection.kWallpaperSearch], handler.getArgs('setCustomizeChromeSidePanelVisible')[0]); @@ -1082,16 +1096,14 @@ // Clicking the wallpaper search button should navigate the side // panel to the wallpaper search page. - $$<HTMLElement>(app, '#wallpaperSearchButton')!.click(); - + getWallpaperSearchButton().click(); assertDeepEquals( [true, CustomizeChromeSection.kWallpaperSearch], handler.getArgs('setCustomizeChromeSidePanelVisible')[0]); // Clicking the wallpaper search button, when the wallpaper search // page is opened, should close the side panel. - $$<HTMLElement>(app, '#wallpaperSearchButton')!.click(); - + getWallpaperSearchButton().click(); assertDeepEquals( [false, CustomizeChromeSection.kUnspecified], handler.getArgs('setCustomizeChromeSidePanelVisible')[1]); @@ -1104,83 +1116,60 @@ // Only customize chrome button should be labeled as pressed. assertEquals( - 'false', - $$<HTMLElement>( - app, '#wallpaperSearchButton')!.getAttribute('aria-pressed')); - assertEquals( - 'true', - $$<HTMLElement>( - app, '#customizeButton')!.getAttribute('aria-pressed')); - + 'false', getWallpaperSearchButton().getAttribute('aria-pressed')); + assertEquals('true', getCustomizeButton().getAttribute('aria-pressed')); // Open wallpaper search page. - $$<HTMLElement>(app, '#wallpaperSearchButton')!.click(); + getWallpaperSearchButton().click(); await microtasksFinished(); // Both buttons should be labeled as pressed. assertEquals( - 'true', - $$<HTMLElement>( - app, '#wallpaperSearchButton')!.getAttribute('aria-pressed')); - assertEquals( - 'true', - $$<HTMLElement>( - app, '#customizeButton')!.getAttribute('aria-pressed')); - + 'true', getWallpaperSearchButton().getAttribute('aria-pressed')); + assertEquals('true', getCustomizeButton().getAttribute('aria-pressed')); // Close the side panel. callbackRouterRemote.setCustomizeChromeSidePanelVisibility(false); await callbackRouterRemote.$.flushForTesting(); // Both buttons should not be labeled as pressed. assertEquals( - 'false', - $$<HTMLElement>( - app, '#wallpaperSearchButton')!.getAttribute('aria-pressed')); + 'false', getWallpaperSearchButton().getAttribute('aria-pressed')); assertEquals( - 'false', - $$<HTMLElement>( - app, '#customizeButton')!.getAttribute('aria-pressed')); + 'false', getCustomizeButton().getAttribute('aria-pressed')); }); test( 'clicking wallpaper search button collapses/expands it', async () => { - assertNotEquals( - 32, - $$<HTMLElement>(app, '#wallpaperSearchButton')!.offsetWidth); + assertNotEquals(32, getWallpaperSearchButton().offsetWidth); assertNotStyle( - $$(app, '#wallpaperSearchButton .customize-text')!, 'display', - 'none'); - - $$<HTMLElement>(app, '#wallpaperSearchButton')!.click(); + getWallpaperSearchButton().querySelector('.customize-text')!, + 'display', 'none'); + getWallpaperSearchButton().click(); await microtasksFinished(); - assertEquals( - 32, - $$<HTMLElement>(app, '#wallpaperSearchButton')!.offsetWidth); + assertEquals(32, getWallpaperSearchButton().offsetWidth); assertStyle( - $$(app, '#wallpaperSearchButton .customize-text')!, 'display', - 'none'); + getWallpaperSearchButton().querySelector('.customize-text')!, + 'display', 'none'); }); test('button hides in accordance with callback router', async () => { // Both buttons shown. - assertNotStyle($$(app, '#customizeButton')!, 'display', 'none'); - assertNotStyle($$(app, '#wallpaperSearchButton')!, 'display', 'none'); - + assertNotStyle(getCustomizeButton(), 'display', 'none'); + assertNotStyle(getWallpaperSearchButton(), 'display', 'none'); callbackRouterRemote.setWallpaperSearchButtonVisibility(false); await callbackRouterRemote.$.flushForTesting(); await microtasksFinished(); // Wallpaper search button hides. - assertNotStyle($$(app, '#customizeButton')!, 'display', 'none'); - assertEquals(null, $$(app, '#wallpaperSearchButton')); - + assertNotStyle(getCustomizeButton(), 'display', 'none'); + assertEquals(null, getWallpaperSearchButton()); callbackRouterRemote.setWallpaperSearchButtonVisibility(true); await callbackRouterRemote.$.flushForTesting(); await microtasksFinished(); // Wallpaper search button remains hidden. - assertNotStyle($$(app, '#customizeButton')!, 'display', 'none'); - assertEquals(null, $$(app, '#wallpaperSearchButton')); + assertNotStyle(getCustomizeButton(), 'display', 'none'); + assertEquals(null, getWallpaperSearchButton()); }); }); @@ -1207,15 +1196,13 @@ }); test('hide condition 0 shows button unconditonally', async () => { - assertTrue(!!app.shadowRoot.querySelector('#customizeButton')); - assertTrue(!!app.shadowRoot.querySelector('#wallpaperSearchButton')); - + assertTrue(!!getCustomizeButton()); + assertTrue(!!getWallpaperSearchButton()); const theme = createTheme({isBaseline: false}); theme.backgroundImage = createBackgroundImage('https://foo.com'); await callbackRouterRemote.$.flushForTesting(); - - assertTrue(!!app.shadowRoot.querySelector('#customizeButton')); - assertTrue(!!app.shadowRoot.querySelector('#wallpaperSearchButton')); + assertTrue(!!getCustomizeButton()); + assertTrue(!!getWallpaperSearchButton()); }); test( @@ -1223,24 +1210,20 @@ async () => { // Both buttons' icons should have a non-white color. assertNotStyle( - $$<HTMLElement>(app, '#wallpaperSearchButton .customize-icon')!, + getWallpaperSearchButton().querySelector('.customize-icon')!, 'background-color', 'rgb(255, 255, 255)'); assertNotStyle( - $$<HTMLElement>(app, '#customizeButton .customize-icon')!, + getCustomizeButton().querySelector('.customize-icon')!, 'background-color', 'rgb(255, 255, 255)'); // Only customize chrome button should be collapsed. assertNotStyle( - $$(app, '#wallpaperSearchButton .customize-text')!, 'display', - 'none'); + getWallpaperSearchButton().querySelector('.customize-text')!, + 'display', 'none'); assertStyle( - $$(app, '#customizeButton .customize-text')!, 'display', - 'none'); - assertNotEquals( - 32, - $$<HTMLElement>(app, '#wallpaperSearchButton')!.offsetWidth); - assertEquals( - 32, $$<HTMLElement>(app, '#customizeButton')!.offsetWidth); - + getCustomizeButton().querySelector('.customize-text')!, + 'display', 'none'); + assertNotEquals(32, getWallpaperSearchButton().offsetWidth); + assertEquals(32, getCustomizeButton().offsetWidth); // Create and set theme. const theme = createTheme({isDark: true}); theme.backgroundImage = createBackgroundImage('https://foo.com'); @@ -1249,23 +1232,20 @@ // Both buttons' icons should be white. assertStyle( - $$(app, '#wallpaperSearchButton .customize-icon')!, + getWallpaperSearchButton().querySelector('.customize-icon')!, 'background-color', 'rgb(255, 255, 255)'); assertStyle( - $$(app, '#customizeButton .customize-icon')!, + getCustomizeButton().querySelector('.customize-icon')!, 'background-color', 'rgb(255, 255, 255)'); // Only customize chrome button should be collapsed. assertNotStyle( - $$(app, '#wallpaperSearchButton .customize-text')!, 'display', - 'none'); + getWallpaperSearchButton().querySelector('.customize-text')!, + 'display', 'none'); assertStyle( - $$(app, '#customizeButton .customize-text')!, 'display', - 'none'); - assertNotEquals( - 32, - $$<HTMLElement>(app, '#wallpaperSearchButton')!.offsetWidth); - assertEquals( - 32, $$<HTMLElement>(app, '#customizeButton')!.offsetWidth); + getCustomizeButton().querySelector('.customize-text')!, + 'display', 'none'); + assertNotEquals(32, getWallpaperSearchButton().offsetWidth); + assertEquals(32, getCustomizeButton().offsetWidth); }); [NtpBackgroundImageSource.kWallpaperSearch, @@ -1303,19 +1283,16 @@ loadTimeData.overrideValues({ wallpaperSearchButtonHideCondition: /*BACKGROUND_IMAGE_SET*/ 1, }); - - assertTrue(!!app.shadowRoot.querySelector('#customizeButton')); - assertTrue(!!app.shadowRoot.querySelector('#wallpaperSearchButton')); - + assertTrue(!!getCustomizeButton()); + assertTrue(!!getWallpaperSearchButton()); // Set theme with a background image and baseline color. const theme = createTheme({isBaseline: true}); theme.backgroundImage = createBackgroundImage('https://img.png'); callbackRouterRemote.setTheme(theme); await backgroundManager.whenCalled('setShowBackgroundImage'); await microtasksFinished(); - - assertTrue(!!app.shadowRoot.querySelector('#customizeButton')); - assertFalse(!!app.shadowRoot.querySelector('#wallpaperSearchButton')); + assertTrue(!!getCustomizeButton()); + assertFalse(!!getWallpaperSearchButton()); }); test( @@ -1325,40 +1302,29 @@ loadTimeData.overrideValues({ wallpaperSearchButtonHideCondition: /*THEME_SET*/ 2, }); - - assertTrue(!!app.shadowRoot.querySelector('#customizeButton')); - assertTrue( - !!app.shadowRoot.querySelector('#wallpaperSearchButton')); - + assertTrue(!!getCustomizeButton()); + assertTrue(!!getWallpaperSearchButton()); // Set theme with a non-baseline color that has no background image. callbackRouterRemote.setTheme(createTheme({isBaseline: false})); await callbackRouterRemote.$.flushForTesting(); await microtasksFinished(); - - assertTrue(!!app.shadowRoot.querySelector('#customizeButton')); - assertFalse( - !!app.shadowRoot.querySelector('#wallpaperSearchButton')); - + assertTrue(!!getCustomizeButton()); + assertFalse(!!getWallpaperSearchButton()); // Resurface button by setting a theme with a baseline color (and no // background image). callbackRouterRemote.setTheme(createTheme({isBaseline: true})); await callbackRouterRemote.$.flushForTesting(); await microtasksFinished(); - - assertTrue(!!app.shadowRoot.querySelector('#customizeButton')); - assertTrue( - !!app.shadowRoot.querySelector('#wallpaperSearchButton')); - + assertTrue(!!getCustomizeButton()); + assertTrue(!!getWallpaperSearchButton()); // Set theme with a background image and baseline color. const theme = createTheme({isBaseline: true}); theme.backgroundImage = createBackgroundImage('https://img.png'); callbackRouterRemote.setTheme(theme); await backgroundManager.whenCalled('setShowBackgroundImage'); await microtasksFinished(); - - assertTrue(!!app.shadowRoot.querySelector('#customizeButton')); - assertFalse( - !!app.shadowRoot.querySelector('#wallpaperSearchButton')); + assertTrue(!!getCustomizeButton()); + assertFalse(!!getWallpaperSearchButton()); }); }); });
diff --git a/chrome/test/data/webui/print_preview/destination_select_test.ts b/chrome/test/data/webui/print_preview/destination_select_test.ts index c441e2e..2b4746e 100644 --- a/chrome/test/data/webui/print_preview/destination_select_test.ts +++ b/chrome/test/data/webui/print_preview/destination_select_test.ts
@@ -2,17 +2,23 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +import 'chrome://print/print_preview.js'; + import type {PrintPreviewDestinationSelectElement} from 'chrome://print/print_preview.js'; import {Destination, DestinationOrigin, getSelectDropdownBackground, IconsetMap} from 'chrome://print/print_preview.js'; import {assertEquals, assertTrue} from 'chrome://webui-test/chai_assert.js'; -import {flushTasks, waitAfterNextRender} from 'chrome://webui-test/polymer_test_util.js'; +import {microtasksFinished} from 'chrome://webui-test/test_util.js'; import {selectOption} from './print_preview_test_utils.js'; suite('DestinationSelectTest', function() { let destinationSelect: PrintPreviewDestinationSelectElement; - let recentDestinationList: Destination[] = []; + const recentDestinationList: Destination[] = [ + new Destination('ID1', DestinationOrigin.LOCAL, 'One'), + new Destination( + 'ID4', DestinationOrigin.LOCAL, 'Four', {isEnterprisePrinter: true}), + ]; setup(function() { document.body.innerHTML = window.trustedTypes!.emptyHTML; @@ -21,23 +27,11 @@ destinationSelect.disabled = false; destinationSelect.loaded = false; destinationSelect.noDestinations = false; - populateRecentDestinationList(); destinationSelect.recentDestinationList = recentDestinationList; document.body.appendChild(destinationSelect); - return waitAfterNextRender(destinationSelect); }); - // Create three different destinations and use them to populate - // |recentDestinationList|. - function populateRecentDestinationList() { - recentDestinationList = [ - new Destination('ID1', DestinationOrigin.LOCAL, 'One'), - new Destination( - 'ID4', DestinationOrigin.LOCAL, 'Four', {isEnterprisePrinter: true}), - ]; - } - function compareIcon(selectEl: HTMLSelectElement, expectedIcon: string) { const icon = selectEl.style.getPropertyValue('background-image').replace(/ /gi, ''); @@ -47,40 +41,39 @@ assertEquals(expected, icon); } - test('change icon', function() { - populateRecentDestinationList(); + test('change icon', async function() { destinationSelect.recentDestinationList = recentDestinationList; const destination = recentDestinationList[0]!; destinationSelect.destination = destination; destinationSelect.updateDestination(); destinationSelect.loaded = true; + await microtasksFinished(); const selectEl = - destinationSelect.shadowRoot!.querySelector<HTMLSelectElement>( + destinationSelect.shadowRoot.querySelector<HTMLSelectElement>( '.md-select')!; compareIcon(selectEl, 'print'); // Select a destination with the enterprise printer icon. - return selectOption(destinationSelect, `ID4/local/`).then(() => { - const enterpriseIcon = 'business'; + await selectOption(destinationSelect, `ID4/local/`); + const enterpriseIcon = 'business'; + compareIcon(selectEl, enterpriseIcon); - compareIcon(selectEl, enterpriseIcon); - - // Update destination. - destinationSelect.destination = recentDestinationList[1]!; - compareIcon(selectEl, enterpriseIcon); - }); + // Update destination. + destinationSelect.destination = recentDestinationList[1]!; + await microtasksFinished(); + compareIcon(selectEl, enterpriseIcon); }); test('ShowsSelectedDestination', async function() { - const select = destinationSelect.shadowRoot!.querySelector('select'); + const select = destinationSelect.shadowRoot.querySelector('select'); assertTrue(!!select); const destination = recentDestinationList[0]!; destinationSelect.loaded = true; destinationSelect.destination = destination; destinationSelect.updateDestination(); - await flushTasks(); + await microtasksFinished(); assertEquals(destination.key, select.value); assertEquals(destination.key, destinationSelect.selectedValue); @@ -90,7 +83,7 @@ destinationSelect.recentDestinationList = [newDestination]; destinationSelect.destination = newDestination; destinationSelect.updateDestination(); - await flushTasks(); + await microtasksFinished(); assertEquals(newDestination.key, select.value); assertEquals(newDestination.key, destinationSelect.selectedValue);
diff --git a/chrome/test/data/webui/print_preview/print_preview_sidebar_test.ts b/chrome/test/data/webui/print_preview/print_preview_sidebar_test.ts index 361090d..341afac 100644 --- a/chrome/test/data/webui/print_preview/print_preview_sidebar_test.ts +++ b/chrome/test/data/webui/print_preview/print_preview_sidebar_test.ts
@@ -29,7 +29,6 @@ document.body.appendChild(model); sidebar = document.createElement('print-preview-sidebar'); - sidebar.settings = model.settings; sidebar.setSetting('duplex', false); sidebar.pageCount = 1; document.body.appendChild(sidebar);
diff --git a/chrome/test/data/webui/settings/settings_browsertest.cc b/chrome/test/data/webui/settings/settings_browsertest.cc index 122859de..58bb010 100644 --- a/chrome/test/data/webui/settings/settings_browsertest.cc +++ b/chrome/test/data/webui/settings/settings_browsertest.cc
@@ -411,7 +411,8 @@ scoped_feature_list_.InitWithFeaturesAndParameters( {{features::kGlicLearnMoreURLConfig, { - {"glic-launcher-toggle-learn-more-url", "https://google.com/"}, + {"glic-shortcuts-launcher-toggle-learn-more-url", + "https://google.com/"}, }}}, /*disabled_features=*/{}); } @@ -432,7 +433,8 @@ scoped_feature_list_.InitWithFeaturesAndParameters( {{features::kGlicLearnMoreURLConfig, { - {"glic-location-toggle-learn-more-url", "https://google.com/"}, + {"glic-shortcuts-location-toggle-learn-more-url", + "https://google.com/"}, }}}, /*disabled_features=*/{}); } @@ -454,7 +456,8 @@ scoped_feature_list_.InitWithFeaturesAndParameters( {{features::kGlicLearnMoreURLConfig, { - {"glic-tab-access-toggle-learn-more-url", "https://google.com/"}, + {"glic-shortcuts-tab-access-toggle-learn-more-url", + "https://google.com/"}, }}}, /*disabled_features=*/{}); }
diff --git a/chrome/test/data/webui/side_panel/read_anything/image_test.ts b/chrome/test/data/webui/side_panel/read_anything/image_test.ts index 655de6747..157c986 100644 --- a/chrome/test/data/webui/side_panel/read_anything/image_test.ts +++ b/chrome/test/data/webui/side_panel/read_anything/image_test.ts
@@ -44,8 +44,8 @@ chrome.readingMode.onConnected = () => {}; speech = new TestSpeechBrowserProxy(); SpeechBrowserProxyImpl.setInstance(speech); - SpeechController.setInstance(new SpeechController()); VoicePackController.setInstance(new VoicePackController()); + SpeechController.setInstance(new SpeechController()); // Override chrome.readingMode.requestImageData to avoid the cross-process // hop.
diff --git a/chrome/test/data/webui/side_panel/read_anything/phrase_highlighting_test.ts b/chrome/test/data/webui/side_panel/read_anything/phrase_highlighting_test.ts index 0014774..c9e294f 100644 --- a/chrome/test/data/webui/side_panel/read_anything/phrase_highlighting_test.ts +++ b/chrome/test/data/webui/side_panel/read_anything/phrase_highlighting_test.ts
@@ -65,11 +65,11 @@ // the rest of the Read Anything feature, which we are not testing here. chrome.readingMode.onConnected = () => {}; - SpeechController.setInstance(new SpeechController()); VoicePackController.setInstance(new VoicePackController()); wordBoundaries = new WordBoundaries(); WordBoundaries.setInstance(wordBoundaries); ReadAloudHighlighter.setInstance(new ReadAloudHighlighter()); + SpeechController.setInstance(new SpeechController()); metrics = mockMetrics(); app = await createApp();
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 47bd14c5..10f96396 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
@@ -1,7 +1,7 @@ // Copyright 2025 The Chromium Authors // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -import {BrowserProxy, PauseActionSource, SpeechBrowserProxyImpl, SpeechController, SpeechEngineState} from 'chrome-untrusted://read-anything-side-panel.top-chrome/read_anything.js'; +import {BrowserProxy, PauseActionSource, SpeechBrowserProxyImpl, SpeechController, SpeechEngineState, VoicePackController} from 'chrome-untrusted://read-anything-side-panel.top-chrome/read_anything.js'; import {assertEquals, assertFalse, assertNotEquals, assertTrue} from 'chrome-untrusted://webui-test/chai_assert.js'; import {createSpeechSynthesisVoice, mockMetrics} from './common.js'; @@ -56,6 +56,10 @@ }, }; + const voicePackController = new VoicePackController(); + voicePackController.setCurrentVoice( + createSpeechSynthesisVoice({lang: 'en', name: 'Google Alpaca'})); + VoicePackController.setInstance(voicePackController); speechController = new SpeechController(); speechController.addListener(speechListener); });
diff --git a/chrome/test/data/webui/side_panel/read_anything/update_content_selection_with_highlights_test.ts b/chrome/test/data/webui/side_panel/read_anything/update_content_selection_with_highlights_test.ts index e2b39c92..3b3b1c7 100644 --- a/chrome/test/data/webui/side_panel/read_anything/update_content_selection_with_highlights_test.ts +++ b/chrome/test/data/webui/side_panel/read_anything/update_content_selection_with_highlights_test.ts
@@ -3,9 +3,8 @@ // found in the LICENSE file. import 'chrome-untrusted://read-anything-side-panel.top-chrome/read_anything.js'; -import {BrowserProxy} from 'chrome-untrusted://read-anything-side-panel.top-chrome/read_anything.js'; import type {AppElement} from 'chrome-untrusted://read-anything-side-panel.top-chrome/read_anything.js'; -import {currentReadHighlightClass, previousReadHighlightClass} from 'chrome-untrusted://read-anything-side-panel.top-chrome/read_anything.js'; +import {BrowserProxy, currentReadHighlightClass, previousReadHighlightClass, SpeechController} from 'chrome-untrusted://read-anything-side-panel.top-chrome/read_anything.js'; import {assertEquals} from 'chrome-untrusted://webui-test/chai_assert.js'; import {microtasksFinished} from 'chrome-untrusted://webui-test/test_util.js'; @@ -28,6 +27,7 @@ suite('UpdateContentSelectionWithHighlights', () => { let app: AppElement; let fakeTree: FakeTree; + let speechController: SpeechController; const textNodeIds = [3, 5, 7, 9]; const texts = [ @@ -43,6 +43,7 @@ BrowserProxy.setInstance(new TestColorUpdaterBrowserProxy()); const readingMode = new FakeReadingMode(); chrome.readingMode = readingMode as unknown as typeof chrome.readingMode; + speechController = new SpeechController(); // Don't use await createApp() when using a FakeTree, as it seems to cause // flakiness. @@ -70,13 +71,13 @@ let i = 0; while (textNodeIds[i]! !== id) { fakeTree.highlightNode(textNodeIds[i]!); - app.highlightCurrentGranularity([textNodeIds[i]!]); + speechController.highlightCurrentGranularity([textNodeIds[i]!]); i++; } // highlight given node fakeTree.highlightNode(id); - app.highlightCurrentGranularity([id]); + speechController.highlightCurrentGranularity([id]); return microtasksFinished(); } @@ -86,7 +87,7 @@ let i = 0; while (fromId !== textNodeIds[i]!) { fakeTree.highlightNode(textNodeIds[i]!); - app.highlightCurrentGranularity([textNodeIds[i]!]); + speechController.highlightCurrentGranularity([textNodeIds[i]!]); i++; } @@ -96,7 +97,7 @@ if (toId !== fromId) { nodeIds.push(toId); } - app.highlightCurrentGranularity(nodeIds); + speechController.highlightCurrentGranularity(nodeIds); return microtasksFinished(); }
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 c20466c..5ea223a 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
@@ -65,12 +65,12 @@ chrome.readingMode.onConnected = () => {}; speech = new TestSpeechBrowserProxy(); SpeechBrowserProxyImpl.setInstance(speech); - speechController = new SpeechController(); - SpeechController.setInstance(speechController); VoicePackController.setInstance(new VoicePackController()); wordBoundaries = new WordBoundaries(); WordBoundaries.setInstance(wordBoundaries); ReadAloudHighlighter.setInstance(new ReadAloudHighlighter()); + speechController = new SpeechController(); + SpeechController.setInstance(speechController); app = await createApp(); chrome.readingMode.setContentForTesting(axTree, [2, 4]);
diff --git a/chrome/test/data/webui/tab_search/split_new_tab_page_test.ts b/chrome/test/data/webui/tab_search/split_new_tab_page_test.ts index 9143df3..9e23115 100644 --- a/chrome/test/data/webui/tab_search/split_new_tab_page_test.ts +++ b/chrome/test/data/webui/tab_search/split_new_tab_page_test.ts
@@ -77,7 +77,7 @@ ]; } - function splitNewTabPageSetup() { + function splitNewTabPageSetup(windowData?: any) { loadTimeData.overrideValues({ splitViewEnabled: true, }); @@ -86,7 +86,7 @@ testApiProxy = new TestTabSearchApiProxy(); testApiProxy.setProfileData( - createProfileData({windows: createWindowData()})); + createProfileData({windows: (windowData || createWindowData())})); testApiProxy.setIsSplit(true); TabSearchApiProxyImpl.setInstance(testApiProxy); @@ -139,6 +139,28 @@ assertEquals('New Title', updatedTabSearchItems[0]!.data.tab.title); }); + test('Updates on tab visibility updated', async () => { + const windowData = createWindowData(); + const tab = windowData[0]!.tabs[1] as Tab; + tab.visible = false; + await splitNewTabPageSetup(windowData); + const initialTabSearchItems = + splitNewTabPage.shadowRoot.querySelectorAll('tab-search-item'); + assertEquals(4, initialTabSearchItems.length); + + tab.visible = true; + const tabUpdateInfo = { + inActiveWindow: true, + tab: tab, + }; + testApiProxy.getCallbackRouterRemote().tabUpdated(tabUpdateInfo); + await microtasksFinished(); + + const updatedTabSearchItems = + splitNewTabPage.shadowRoot.querySelectorAll('tab-search-item'); + assertEquals(3, updatedTabSearchItems.length); + }); + test('Updates on tabs changed', async () => { await splitNewTabPageSetup(); assertEquals(
diff --git a/chrome/test/enterprise/e2e/webstore/__init__.py b/chrome/test/enterprise/e2e/webstore/__init__.py new file mode 100644 index 0000000..7ff902c --- /dev/null +++ b/chrome/test/enterprise/e2e/webstore/__init__.py
@@ -0,0 +1,5 @@ +# 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. + +from .enterprise_cws.managed_browser_enterprise_cws import * \ No newline at end of file
diff --git a/chrome/test/enterprise/e2e/webstore/enterprise_cws/__init__.py b/chrome/test/enterprise/e2e/webstore/enterprise_cws/__init__.py new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/chrome/test/enterprise/e2e/webstore/enterprise_cws/__init__.py
diff --git a/chrome/test/enterprise/e2e/webstore/enterprise_cws/enterprise_cws_webdriver.py b/chrome/test/enterprise/e2e/webstore/enterprise_cws/enterprise_cws_webdriver.py new file mode 100644 index 0000000..7138ccee4 --- /dev/null +++ b/chrome/test/enterprise/e2e/webstore/enterprise_cws/enterprise_cws_webdriver.py
@@ -0,0 +1,42 @@ +# Copyright 2025 The Chromium Authors +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +import time + +from absl import app # Added import +from selenium.webdriver.common.by import By + +from test_util import create_chrome_webdriver + +# Constants +CHROME_POLICY_URL = 'chrome://policy' +CWS_URL = 'https://chromewebstore.google.com/' + + +def main(argv): + + driver = None + try: + driver = create_chrome_webdriver() + + # 1. Ensure it's getting CWS related policies + driver.get(CHROME_POLICY_URL) + driver.find_element(By.ID, 'reload-policies').click() + time.sleep(5) + + # 2. Then visit the CWS site + print(f"Navigating to Chrome Web Store: {CWS_URL}") + driver.get(CWS_URL) + + # 3. Verify if it's a customized version by checking the webpage + # TODO(crbug.com/416031859): Add logic to verify CWS customization + except Exception as e: + print(f"An error occurred: {e}") + finally: + if driver: + driver.quit() + + +if __name__ == '__main__': + app.run(main)
diff --git a/chrome/test/enterprise/e2e/webstore/enterprise_cws/managed_browser_enterprise_cws.py b/chrome/test/enterprise/e2e/webstore/enterprise_cws/managed_browser_enterprise_cws.py new file mode 100644 index 0000000..76cd6fac --- /dev/null +++ b/chrome/test/enterprise/e2e/webstore/enterprise_cws/managed_browser_enterprise_cws.py
@@ -0,0 +1,41 @@ +# Copyright 2025 The Chromium Authors +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +import os + +from chrome_ent_test.infra.core import before_all +from chrome_ent_test.infra.core import category +from chrome_ent_test.infra.core import environment +from chrome_ent_test.infra.core import test +from infra import ChromeEnterpriseTestCase + + +@category('chrome_only') +@environment(file="../policy_test.asset.textpb") +class ManagedBrowserEnterpriseWebStore(ChromeEnterpriseTestCase): + + @before_all + def setup(self): + self.InstallChrome(self.win_config['client']) + self.InstallWebDriver(self.win_config['client']) + + @test + def test_enterprise_web_store(self): + # Domain enrollment + path = "gs://%s/secrets/CWStoken" % self.gsbucket + cmd = r'gsutil cat ' + path + token = self.RunCommand(self.win_config['dc'], cmd).rstrip().decode() + + self.SetPolicy(self.win_config['dc'], r'CloudManagementEnrollmentToken', + token, 'String') + self.RunCommand(self.win_config['client'], 'gpupdate /force') + + local_dir = os.path.dirname(os.path.abspath(__file__)) + + output = self.RunWebDriverTest( + self.win_config['client'], + os.path.join(local_dir, '../enterprise_cws_webdriver.py')) + + # Verify Enterprise signals + print(output)
diff --git a/chrome/test/enterprise/e2e/webstore/policy_test.asset.textpb b/chrome/test/enterprise/e2e/webstore/policy_test.asset.textpb new file mode 100644 index 0000000..15db692 --- /dev/null +++ b/chrome/test/enterprise/e2e/webstore/policy_test.asset.textpb
@@ -0,0 +1,29 @@ +# The test configuration used by most policy tests. +# It consists of one domain controller and one client. +network { + name: 'primary' +} + +# An ActiveDirectory domain. +ad_domain { + name: 'test1.com' + netbios_name: 'example' + + domain_controller { + windows_machine: 'win2022-dc' + } +} + +# the domain controller. +windows_machine { + name: 'win2022-dc' + machine_type: 'win2022' + network_interface { network: 'primary' } +} + +windows_machine { + name: 'client2022' + machine_type: 'win2022' + network_interface { network: 'primary' } + container { ad_domain: 'test1.com' } +} \ No newline at end of file
diff --git a/chrome/test/fuzzing/renderer_fuzzing/BUILD.gn b/chrome/test/fuzzing/renderer_fuzzing/BUILD.gn index 17ada5f9..a370004 100644 --- a/chrome/test/fuzzing/renderer_fuzzing/BUILD.gn +++ b/chrome/test/fuzzing/renderer_fuzzing/BUILD.gn
@@ -121,9 +121,6 @@ "//third_party/blink/public/common:storage_key_proto_converter", ] - deps += _mojolpm_deps - - proto_deps = [ ":renderer_in_process_mojolpm_fuzzer_generator" ] - proto_deps += _mojolpm_deps + proto_deps = _mojolpm_deps } }
diff --git a/chromeos/CHROMEOS_LKGM b/chromeos/CHROMEOS_LKGM index 1fe9cf60..1dca82b 100644 --- a/chromeos/CHROMEOS_LKGM +++ b/chromeos/CHROMEOS_LKGM
@@ -1 +1 @@ -16277.0.0-1068716 \ No newline at end of file +16277.0.0-1068727 \ No newline at end of file
diff --git a/chromeos/ash/components/boca/on_task/on_task_session_manager.cc b/chromeos/ash/components/boca/on_task/on_task_session_manager.cc index 1cd3a82..2aa33fa 100644 --- a/chromeos/ash/components/boca/on_task/on_task_session_manager.cc +++ b/chromeos/ash/components/boca/on_task/on_task_session_manager.cc
@@ -97,7 +97,9 @@ // Unlock SWA window before closing it to ensure we restore things like // global accelerators, etc. LockOrUnlockWindow(/*lock_window=*/false); - system_web_app_manager_->CloseSystemWebAppWindow(window_id); + if (!features::IsBocaKeepSWAOpenOnSessionEndedEnabled()) { + system_web_app_manager_->CloseSystemWebAppWindow(window_id); + } } active_session_id_ = std::nullopt; provider_url_set_.clear();
diff --git a/chromeos/ash/components/boca/on_task/on_task_session_manager_unittest.cc b/chromeos/ash/components/boca/on_task/on_task_session_manager_unittest.cc index 771dd6e1..b9344625 100644 --- a/chromeos/ash/components/boca/on_task/on_task_session_manager_unittest.cc +++ b/chromeos/ash/components/boca/on_task/on_task_session_manager_unittest.cc
@@ -16,6 +16,7 @@ #include "base/functional/callback.h" #include "base/sequence_checker.h" #include "base/task/current_thread.h" +#include "base/test/scoped_feature_list.h" #include "base/test/task_environment.h" #include "chromeos/ash/components/boca/on_task/activity/active_tab_tracker.h" #include "chromeos/ash/components/boca/on_task/notification_constants.h" @@ -274,6 +275,10 @@ } TEST_F(OnTaskSessionManagerTest, ShouldCloseBocaSWAOnSessionEnd) { + base::test::ScopedFeatureList scoped_feature_list; + scoped_feature_list.InitWithFeatures( + /*enabled_features=*/{}, + /*disabled_features=*/{ash::features::kBocaKeepSWAOpenOnSessionEnded}); const SessionID kWindowId = SessionID::NewUnique(); Sequence s; EXPECT_CALL(*system_web_app_manager_ptr_, GetActiveSystemWebAppWindowID()) @@ -294,6 +299,27 @@ EXPECT_FALSE(*should_lock_window()); } +TEST_F(OnTaskSessionManagerTest, ShouldKeepBocaSWAOpenOnSessionEnd) { + const SessionID kWindowId = SessionID::NewUnique(); + Sequence s; + EXPECT_CALL(*system_web_app_manager_ptr_, GetActiveSystemWebAppWindowID()) + .WillRepeatedly(Return(kWindowId)); + EXPECT_CALL(*system_web_app_manager_ptr_, + SetPinStateForSystemWebAppWindow(false, kWindowId)) + .Times(1) + .InSequence(s); + EXPECT_CALL(*system_web_app_manager_ptr_, CloseSystemWebAppWindow(kWindowId)) + .Times(0) + .InSequence(s); + session_manager_->OnSessionEnded("test_session_id"); + + // Verify session end notification was shown and window lock state was reset. + task_environment_.FastForwardBy(kOnTaskNotificationCountdownInterval); + EXPECT_TRUE(fake_notifications_delegate_ptr_->WasNotificationShown( + kOnTaskSessionEndNotificationId)); + EXPECT_FALSE(*should_lock_window()); +} + TEST_F(OnTaskSessionManagerTest, ShouldReEnableExtensionsOnSessionEnd) { const SessionID kWindowId = SessionID::NewUnique(); EXPECT_CALL(*system_web_app_manager_ptr_, GetActiveSystemWebAppWindowID())
diff --git a/clank b/clank index 7e5d0fb..1881fd2 160000 --- a/clank +++ b/clank
@@ -1 +1 @@ -Subproject commit 7e5d0fb6ef7e00fd425fcf363a4f4123ee271ae2 +Subproject commit 1881fd29e10d5d32e8b8f6ed892c860e43dded76
diff --git a/components/BUILD.gn b/components/BUILD.gn index 0bb5e83..3c5bb6b 100644 --- a/components/BUILD.gn +++ b/components/BUILD.gn
@@ -577,7 +577,7 @@ "//components/signin/public/identity_manager/objc:unit_tests", "//components/translate/ios/browser:unit_tests", "//components/ukm/ios:unit_tests", - "//components/webauthn/ios:js_tests", + "//components/webauthn/ios:unit_tests", ] if (use_blink) { deps += [ ":components_tests_distiller_bundle_data" ]
diff --git a/components/android_autofill/browser/java/src/org/chromium/components/autofill/AutofillProvider.java b/components/android_autofill/browser/java/src/org/chromium/components/autofill/AutofillProvider.java index 84617ca..b11438a 100644 --- a/components/android_autofill/browser/java/src/org/chromium/components/autofill/AutofillProvider.java +++ b/components/android_autofill/browser/java/src/org/chromium/components/autofill/AutofillProvider.java
@@ -115,6 +115,7 @@ } private void initializeFrameworkWrapper(Context context) { + if (mAutofillManager != null) return; mAutofillManager = new AutofillManagerWrapper(context); maybeInitializeUmaRecorder(context); maybeInitializeInputObserver(); @@ -311,7 +312,7 @@ } transformFormFieldToContainViewCoordinates(formData); - maybeInitializeUmaRecorder(mContext); + initializeFrameworkWrapper(mContext); mAutofillUMA.onSessionStarted(getAutofillManagerWrapper().isDisabled()); mRequest = new AutofillRequest(
diff --git a/components/attribution_reporting/aggregatable_utils.cc b/components/attribution_reporting/aggregatable_utils.cc index d396fac..ac036e5 100644 --- a/components/attribution_reporting/aggregatable_utils.cc +++ b/components/attribution_reporting/aggregatable_utils.cc
@@ -53,7 +53,7 @@ std::optional<base::Time> attributed_source_time, GenerateNullAggregatableReportFunc generate_func) { // See spec - // https://wicg.github.io/attribution-reporting-api/#generate-null-reports. + // https://wicg.github.io/attribution-reporting-api/#generate-null-attribution-reports. mojom::SourceRegistrationTimeConfig source_registration_time_config = config.source_registration_time_config();
diff --git a/components/autofill/core/browser/suggestions/valuables/valuable_suggestion_generator_unittest.cc b/components/autofill/core/browser/suggestions/valuables/valuable_suggestion_generator_unittest.cc index e6da56b..49329df 100644 --- a/components/autofill/core/browser/suggestions/valuables/valuable_suggestion_generator_unittest.cc +++ b/components/autofill/core/browser/suggestions/valuables/valuable_suggestion_generator_unittest.cc
@@ -80,48 +80,79 @@ test_api(valuables_data_manager()).SetLoyaltyCards(loyalty_cards); - const Matcher<Suggestion> lc1_suggestion_matcher = - EqualsSuggestion(SuggestionType::kLoyaltyCardEntry, u"987654321987654321", - /*is_main_text_primary=*/true, Suggestion::Icon::kNoIcon, - {{Suggestion::Text(u"CVS Pharmacy")}}, - Suggestion::Guid("loyalty_card_id_1")); - const Matcher<Suggestion> lc2_suggestion_matcher = - EqualsSuggestion(SuggestionType::kLoyaltyCardEntry, u"998766823", - /*is_main_text_primary=*/true, Suggestion::Icon::kNoIcon, - {{Suggestion::Text(u"Walgreens")}}, - Suggestion::Guid("loyalty_card_id_3")); - const Matcher<Suggestion> lc3_suggestion_matcher = - EqualsSuggestion(SuggestionType::kLoyaltyCardEntry, u"37262999281", - /*is_main_text_primary=*/true, Suggestion::Icon::kNoIcon, - {{Suggestion::Text(u"Ticket Maester")}}, - Suggestion::Guid("loyalty_card_id_2")); + // No matching domains. + EXPECT_THAT(GetLoyaltyCardSuggestions( + valuables_data_manager(), + GURL("https://not-existing-domain.example/test")), + testing::ElementsAre( + EqualsSuggestion( + SuggestionType::kLoyaltyCardEntry, u"987654321987654321", + /*is_main_text_primary=*/true, Suggestion::Icon::kNoIcon, + {{Suggestion::Text(u"CVS Pharmacy")}}, + Suggestion::Guid("loyalty_card_id_1")), + EqualsSuggestion( + SuggestionType::kLoyaltyCardEntry, u"37262999281", + /*is_main_text_primary=*/true, Suggestion::Icon::kNoIcon, + {{Suggestion::Text(u"Ticket Maester")}}, + Suggestion::Guid("loyalty_card_id_2")), + EqualsSuggestion( + SuggestionType::kLoyaltyCardEntry, u"998766823", + /*is_main_text_primary=*/true, Suggestion::Icon::kNoIcon, + {{Suggestion::Text(u"Walgreens")}}, + Suggestion::Guid("loyalty_card_id_3")), + EqualsSuggestion(SuggestionType::kSeparator), + EqualsSuggestion(SuggestionType::kManageLoyaltyCard, + u"Manage loyalty cards...", + Suggestion::Icon::kSettings))); - const Matcher<Suggestion> separatorMatcher = - EqualsSuggestion(SuggestionType::kSeparator); - const Matcher<Suggestion> manageLoyaltyCardsMatcher = - EqualsSuggestion(SuggestionType::kManageLoyaltyCard, - u"Manage loyalty cards...", Suggestion::Icon::kSettings); - - EXPECT_THAT( - GetLoyaltyCardSuggestions( - valuables_data_manager(), - GURL("https://not-existing-domain.example/test")), - testing::ElementsAre(lc1_suggestion_matcher, lc3_suggestion_matcher, - lc2_suggestion_matcher, separatorMatcher, - manageLoyaltyCardsMatcher)); - EXPECT_THAT( - GetLoyaltyCardSuggestions(valuables_data_manager(), - GURL("https://domain2.example/test")), - testing::ElementsAre(lc3_suggestion_matcher, lc2_suggestion_matcher, - separatorMatcher, lc1_suggestion_matcher, - separatorMatcher, manageLoyaltyCardsMatcher)); - + // A couple of matching domains. + EXPECT_THAT(GetLoyaltyCardSuggestions(valuables_data_manager(), + GURL("https://domain2.example/test")), + testing::ElementsAre( + EqualsSuggestion( + SuggestionType::kLoyaltyCardEntry, u"37262999281", + /*is_main_text_primary=*/true, Suggestion::Icon::kNoIcon, + {{Suggestion::Text(u"Ticket Maester")}}, + Suggestion::Guid("loyalty_card_id_2")), + EqualsSuggestion( + SuggestionType::kLoyaltyCardEntry, u"998766823", + /*is_main_text_primary=*/true, Suggestion::Icon::kNoIcon, + {{Suggestion::Text(u"Walgreens")}}, + Suggestion::Guid("loyalty_card_id_3")), + EqualsSuggestion(SuggestionType::kSeparator), + EqualsSuggestion( + SuggestionType::kLoyaltyCardEntry, u"987654321987654321", + /*is_main_text_primary=*/true, Suggestion::Icon::kNoIcon, + {{Suggestion::Text(u"CVS Pharmacy")}}, + Suggestion::Guid("loyalty_card_id_1")), + EqualsSuggestion(SuggestionType::kSeparator), + EqualsSuggestion(SuggestionType::kManageLoyaltyCard, + u"Manage loyalty cards...", + Suggestion::Icon::kSettings))); + // All matching domains. EXPECT_THAT( GetLoyaltyCardSuggestions(valuables_data_manager(), GURL("https://common-domain.example/test")), - testing::ElementsAre(lc1_suggestion_matcher, lc3_suggestion_matcher, - lc2_suggestion_matcher, separatorMatcher, - manageLoyaltyCardsMatcher)); + testing::ElementsAre( + EqualsSuggestion( + SuggestionType::kLoyaltyCardEntry, u"987654321987654321", + /*is_main_text_primary=*/true, Suggestion::Icon::kNoIcon, + {{Suggestion::Text(u"CVS Pharmacy")}}, + Suggestion::Guid("loyalty_card_id_1")), + EqualsSuggestion(SuggestionType::kLoyaltyCardEntry, u"37262999281", + /*is_main_text_primary=*/true, + Suggestion::Icon::kNoIcon, + {{Suggestion::Text(u"Ticket Maester")}}, + Suggestion::Guid("loyalty_card_id_2")), + EqualsSuggestion(SuggestionType::kLoyaltyCardEntry, u"998766823", + /*is_main_text_primary=*/true, + Suggestion::Icon::kNoIcon, + {{Suggestion::Text(u"Walgreens")}}, + Suggestion::Guid("loyalty_card_id_3")), + EqualsSuggestion(SuggestionType::kSeparator), + EqualsSuggestion(SuggestionType::kManageLoyaltyCard, + u"Manage loyalty cards...", + Suggestion::Icon::kSettings))); } TEST_F(ValuableSuggestionGeneratorTest,
diff --git a/components/autofill/core/browser/ui/payments/save_and_fill_dialog_controller_impl.cc b/components/autofill/core/browser/ui/payments/save_and_fill_dialog_controller_impl.cc index 86c9ad7..ab3f7d6 100644 --- a/components/autofill/core/browser/ui/payments/save_and_fill_dialog_controller_impl.cc +++ b/components/autofill/core/browser/ui/payments/save_and_fill_dialog_controller_impl.cc
@@ -15,7 +15,7 @@ SaveAndFillDialogControllerImpl::~SaveAndFillDialogControllerImpl() = default; void SaveAndFillDialogControllerImpl::ShowDialog( - base::OnceCallback<base::WeakPtr<SaveAndFillDialogView>()> + base::OnceCallback<std::unique_ptr<SaveAndFillDialogView>()> create_and_show_view_callback) { dialog_view_ = std::move(create_and_show_view_callback).Run(); CHECK(dialog_view_);
diff --git a/components/autofill/core/browser/ui/payments/save_and_fill_dialog_controller_impl.h b/components/autofill/core/browser/ui/payments/save_and_fill_dialog_controller_impl.h index db8b6f7..74126e4 100644 --- a/components/autofill/core/browser/ui/payments/save_and_fill_dialog_controller_impl.h +++ b/components/autofill/core/browser/ui/payments/save_and_fill_dialog_controller_impl.h
@@ -24,7 +24,7 @@ const SaveAndFillDialogControllerImpl&) = delete; ~SaveAndFillDialogControllerImpl() override; - void ShowDialog(base::OnceCallback<base::WeakPtr<SaveAndFillDialogView>()> + void ShowDialog(base::OnceCallback<std::unique_ptr<SaveAndFillDialogView>()> create_and_show_view_callback); #if !BUILDFLAG(IS_ANDROID) && !BUILDFLAG(IS_IOS) @@ -39,7 +39,7 @@ base::WeakPtr<SaveAndFillDialogController> GetWeakPtr() override; private: - base::WeakPtr<SaveAndFillDialogView> dialog_view_; + std::unique_ptr<SaveAndFillDialogView> dialog_view_; // Determines whether the local or upload save version of the UI should be // shown.
diff --git a/components/autofill/core/browser/ui/payments/save_and_fill_dialog_view.h b/components/autofill/core/browser/ui/payments/save_and_fill_dialog_view.h index a992e5b..4dc98de 100644 --- a/components/autofill/core/browser/ui/payments/save_and_fill_dialog_view.h +++ b/components/autofill/core/browser/ui/payments/save_and_fill_dialog_view.h
@@ -5,16 +5,12 @@ #ifndef COMPONENTS_AUTOFILL_CORE_BROWSER_UI_PAYMENTS_SAVE_AND_FILL_DIALOG_VIEW_H_ #define COMPONENTS_AUTOFILL_CORE_BROWSER_UI_PAYMENTS_SAVE_AND_FILL_DIALOG_VIEW_H_ -#include "base/memory/weak_ptr.h" - namespace autofill { // Interface that exposes the view to SaveAndFillControllerImpl. class SaveAndFillDialogView { public: virtual ~SaveAndFillDialogView() = default; - - virtual base::WeakPtr<SaveAndFillDialogView> GetWeakPtr() = 0; }; } // namespace autofill
diff --git a/components/commerce/core/commerce_feature_list.cc b/components/commerce/core/commerce_feature_list.cc index c3050f7..6c62ec1 100644 --- a/components/commerce/core/commerce_feature_list.cc +++ b/components/commerce/core/commerce_feature_list.cc
@@ -55,6 +55,7 @@ {"ca", {"en", "en-ca", "en-gb", "en-us"}}, {"in", {"en", "en-gb", "en-in", "en-us"}}, {"jp", {"ja", "ja-jp"}}}; + map[&kDiscountAutofillRegionLaunched] = {{"us", {"en-us"}}}; return map; }()); @@ -180,6 +181,14 @@ const base::FeatureParam<bool> kPriceInsightsUseCache{ &commerce::kPriceInsights, kPriceInsightsUseCacheParam, true}; +// Discount Autofill at Checkout +BASE_FEATURE(kDiscountAutofill, + "DiscountAutofill", + base::FEATURE_DISABLED_BY_DEFAULT); +BASE_FEATURE(kDiscountAutofillRegionLaunched, + "DiscountAutofillRegionLaunched", + base::FEATURE_DISABLED_BY_DEFAULT); + // Promotion in Magic Stack for Price Tracking users from other platforms. BASE_FEATURE(kPriceTrackingPromo, "PriceTrackingPromo",
diff --git a/components/commerce/core/commerce_feature_list.h b/components/commerce/core/commerce_feature_list.h index 3473334..d97ad48 100644 --- a/components/commerce/core/commerce_feature_list.h +++ b/components/commerce/core/commerce_feature_list.h
@@ -64,6 +64,9 @@ BASE_DECLARE_FEATURE(kSubscriptionsApi); BASE_DECLARE_FEATURE(kSubscriptionsApiRegionLaunched); BASE_DECLARE_FEATURE(kTrackByDefaultOnMobile); +// Feature flag for showing discounts on checkout autofill. +BASE_DECLARE_FEATURE(kDiscountAutofill); +BASE_DECLARE_FEATURE(kDiscountAutofillRegionLaunched); #if BUILDFLAG(IS_IOS) BASE_DECLARE_FEATURE(kPriceInsightsIos);
diff --git a/components/commerce/core/feature_utils.cc b/components/commerce/core/feature_utils.cc index f14a3ed..37e22f5d 100644 --- a/components/commerce/core/feature_utils.cc +++ b/components/commerce/core/feature_utils.cc
@@ -192,4 +192,11 @@ account_checker->GetCountry(), account_checker->GetLocale()); } +bool IsDiscountAutofillEnabled(AccountChecker* account_checker) { + return account_checker && account_checker->IsSignedIn() && + account_checker->IsAnonymizedUrlDataCollectionEnabled() && + commerce::IsRegionLockedFeatureEnabled( + kDiscountAutofill, kDiscountAutofillRegionLaunched, + account_checker->GetCountry(), account_checker->GetLocale()); +} } // namespace commerce
diff --git a/components/commerce/core/feature_utils.h b/components/commerce/core/feature_utils.h index ff500be..5cc6b6fd 100644 --- a/components/commerce/core/feature_utils.h +++ b/components/commerce/core/feature_utils.h
@@ -93,6 +93,13 @@ // user-facing feature. bool IsShoppingPageTypesApiEnabled(AccountChecker* account_checker); +// This is a feature check for showing discounts at the checkout page, which +// will return true if the user has the feature flag enabled, is signed-in and +// synced, has MSBB enabled, and (if applicable) is in an eligible country and +// locale. The value returned by this method can change at runtime, so it +// should not be used when deciding whether to create critical, +// feature-related infrastructure. +bool IsDiscountAutofillEnabled(AccountChecker* account_checker); } // namespace commerce #endif // COMPONENTS_COMMERCE_CORE_FEATURE_UTILS_H_
diff --git a/components/commerce/core/flag_descriptions.cc b/components/commerce/core/flag_descriptions.cc index f85c363..2c8dc47 100644 --- a/components/commerce/core/flag_descriptions.cc +++ b/components/commerce/core/flag_descriptions.cc
@@ -73,4 +73,8 @@ "Enable the product version logging for price tracking subscription " "service"; +const char kDiscountAutofillName[] = "Discount Autofill"; +const char kDiscountAutofillDescription[] = + "Enable discount autofill experiment."; + } // namespace commerce::flag_descriptions
diff --git a/components/commerce/core/flag_descriptions.h b/components/commerce/core/flag_descriptions.h index a6ba848..16675f0c 100644 --- a/components/commerce/core/flag_descriptions.h +++ b/components/commerce/core/flag_descriptions.h
@@ -53,6 +53,9 @@ extern const char kPriceTrackingSubscriptionServiceProductVersionName[]; extern const char kPriceTrackingSubscriptionServiceProductVersionDescription[]; +extern const char kDiscountAutofillName[]; +extern const char kDiscountAutofillDescription[]; + } // namespace commerce::flag_descriptions #endif // COMPONENTS_COMMERCE_CORE_FLAG_DESCRIPTIONS_H_
diff --git a/components/input/DEPS b/components/input/DEPS index 86db081..b9be29b 100644 --- a/components/input/DEPS +++ b/components/input/DEPS
@@ -1,6 +1,7 @@ include_rules = [ "+cc/input", "+cc/trees", + '+components/crash/core/common/crash_key.h', "+components/viz/common", "+mojo/public/cpp", "+services/tracing/public/cpp",
diff --git a/components/input/render_input_router.cc b/components/input/render_input_router.cc index f6a1024..a9abe36 100644 --- a/components/input/render_input_router.cc +++ b/components/input/render_input_router.cc
@@ -14,6 +14,7 @@ #include "base/memory/ptr_util.h" #include "base/tracing/protos/chrome_track_event.pbzero.h" #include "cc/input/browser_controls_offset_tag_modifications.h" +#include "components/crash/core/common/crash_key.h" #include "components/input/input_constants.h" #include "components/input/input_router_config_helper.h" #include "components/input/input_router_impl.h" @@ -121,7 +122,11 @@ } // namespace RenderInputRouter::~RenderInputRouter() { - TRACE_EVENT_INSTANT("input", "RenderInputRouter::~RenderInputRouter"); + TRACE_EVENT("input", "RenderInputRouter::~RenderInputRouter"); + // TODO(413442819): Remove these crash keys after the UAF bug has been fixed. + static crash_reporter::CrashKeyString<1024> trace_key("crbug413442819"); + crash_reporter::SetCrashKeyStringToStackTrace(&trace_key, + base::debug::StackTrace()); } RenderInputRouter::RenderInputRouter(
diff --git a/components/optimization_guide/core/optimization_guide_features.cc b/components/optimization_guide/core/optimization_guide_features.cc index 8b6cb44..255f39cf 100644 --- a/components/optimization_guide/core/optimization_guide_features.cc +++ b/components/optimization_guide/core/optimization_guide_features.cc
@@ -203,12 +203,12 @@ const base::FeatureParam<std::string> kPerformanceClassListForOnDeviceModel{ &kOnDeviceModelPerformanceParams, - "compatible_on_device_performance_classes", "5,6"}; + "compatible_on_device_performance_classes", "3,4,5,6"}; const base::FeatureParam<std::string> kLowTierPerformanceClassListForOnDeviceModel{ &kOnDeviceModelPerformanceParams, - "compatible_low_tier_on_device_performance_classes", ""}; + "compatible_low_tier_on_device_performance_classes", "3,4"}; BASE_FEATURE(kOptimizationGuideIconView, "OptimizationGuideIconView",
diff --git a/components/optimization_guide/internal b/components/optimization_guide/internal index 448e864..6032e32 160000 --- a/components/optimization_guide/internal +++ b/components/optimization_guide/internal
@@ -1 +1 @@ -Subproject commit 448e864469af5010a1dbb83b15d0cbfa1dcb5f55 +Subproject commit 6032e32fa8b82048023e47709f1e5a22dd277791
diff --git a/components/permissions/prediction_service/permissions_aiv3_handler.cc b/components/permissions/prediction_service/permissions_aiv3_handler.cc index 076b57da..e6929fb 100644 --- a/components/permissions/prediction_service/permissions_aiv3_handler.cc +++ b/components/permissions/prediction_service/permissions_aiv3_handler.cc
@@ -10,7 +10,6 @@ #include "components/optimization_guide/core/optimization_guide_model_provider.h" #include "components/permissions/features.h" #include "components/permissions/prediction_service/permissions_aiv3_encoder.h" -#include "components/permissions/prediction_service/prediction_signature_model_executor.h" #include "components/version_info/version_info.h" namespace permissions { @@ -46,8 +45,6 @@ {base::MayBlock(), base::TaskPriority::USER_VISIBLE}), std::make_unique<PermissionsAiv3Encoder>(request_type)) {} -PermissionsAiv3Handler::~PermissionsAiv3Handler() = default; - void PermissionsAiv3Handler::OnModelUpdated( optimization_guide::proto::OptimizationTarget optimization_target, base::optional_ref<const optimization_guide::ModelInfo> model_info) {
diff --git a/components/permissions/prediction_service/permissions_aiv3_handler.h b/components/permissions/prediction_service/permissions_aiv3_handler.h index 1135e158..6899983 100644 --- a/components/permissions/prediction_service/permissions_aiv3_handler.h +++ b/components/permissions/prediction_service/permissions_aiv3_handler.h
@@ -5,9 +5,6 @@ #ifndef COMPONENTS_PERMISSIONS_PREDICTION_SERVICE_PERMISSIONS_AIV3_HANDLER_H_ #define COMPONENTS_PERMISSIONS_PREDICTION_SERVICE_PERMISSIONS_AIV3_HANDLER_H_ -#include <array> -#include <vector> - #include "base/task/sequenced_task_runner.h" #include "components/optimization_guide/core/model_handler.h" #include "components/optimization_guide/proto/models.pb.h" @@ -32,7 +29,6 @@ scoped_refptr<base::SequencedTaskRunner> model_executor_task_runner, std::unique_ptr<PermissionsAiv3Encoder> model_executor); - ~PermissionsAiv3Handler() override; PermissionsAiv3Handler(const PermissionsAiv3Handler&) = delete; PermissionsAiv3Handler& operator=(const PermissionsAiv3Handler&) = delete; @@ -41,7 +37,7 @@ base::optional_ref<const optimization_guide::ModelInfo> model_info) override; - void ExecuteModel( + virtual void ExecuteModel( ExecutionCallback callback, std::unique_ptr<PermissionsAiv3Encoder::ModelInput> snapshot); };
diff --git a/components/permissions/prediction_service/permissions_aiv3_handler_unittest.cc b/components/permissions/prediction_service/permissions_aiv3_handler_unittest.cc index 9e4cb0f..33ad85c 100644 --- a/components/permissions/prediction_service/permissions_aiv3_handler_unittest.cc +++ b/components/permissions/prediction_service/permissions_aiv3_handler_unittest.cc
@@ -122,7 +122,7 @@ geolocation_model_handler_.reset(); notification_model_handler_.reset(); model_provider_.reset(); - RunUntilIdle(); + task_environment_.RunUntilIdle(); } void PushModelFileToModelExecutor(OptimizationTarget opt_target, @@ -139,8 +139,6 @@ task_environment_.RunUntilIdle(); } - void RunUntilIdle() { task_environment_.RunUntilIdle(); } - PermissionsAiv3Handler* model_handler(OptimizationTarget target) { return target == kOptTargetGeolocation ? geolocation_model_handler_.get() : notification_model_handler_.get();
diff --git a/components/privacy_sandbox/android/java/src/org/chromium/components/privacy_sandbox/FingerprintingProtectionSettingsFragment.java b/components/privacy_sandbox/android/java/src/org/chromium/components/privacy_sandbox/FingerprintingProtectionSettingsFragment.java index fc43746f..c50e5a6 100644 --- a/components/privacy_sandbox/android/java/src/org/chromium/components/privacy_sandbox/FingerprintingProtectionSettingsFragment.java +++ b/components/privacy_sandbox/android/java/src/org/chromium/components/privacy_sandbox/FingerprintingProtectionSettingsFragment.java
@@ -6,6 +6,8 @@ import android.os.Bundle; +import androidx.preference.Preference; + import org.chromium.base.metrics.RecordHistogram; import org.chromium.base.supplier.ObservableSupplier; import org.chromium.base.supplier.ObservableSupplierImpl; @@ -14,6 +16,7 @@ import org.chromium.build.annotations.Nullable; import org.chromium.components.browser_ui.settings.ChromeSwitchPreference; import org.chromium.components.browser_ui.settings.SettingsUtils; +import org.chromium.components.browser_ui.site_settings.ForwardingManagedPreferenceDelegate; /** * PreferenceFragment for managing fingerprinting protection settings. @@ -44,6 +47,16 @@ ChromeSwitchPreference fpProtectionSwitch = findPreference(PREF_FP_PROTECTION_SWITCH); fpProtectionSwitch.setChecked(mDelegate.isFingerprintingProtectionEnabled()); + fpProtectionSwitch.setManagedPreferenceDelegate( + new ForwardingManagedPreferenceDelegate( + mDelegate + .getSiteSettingsDelegate(getContext()) + .getManagedPreferenceDelegate()) { + @Override + public boolean isPreferenceControlledByPolicy(Preference preference) { + return mDelegate.isFingerprintingProtectionManaged(); + } + }); fpProtectionSwitch.setOnPreferenceChangeListener( (preference, newValue) -> { mDelegate.setFingerprintingProtection((boolean) newValue);
diff --git a/components/privacy_sandbox/android/java/src/org/chromium/components/privacy_sandbox/IncognitoTrackingProtectionsFragment.java b/components/privacy_sandbox/android/java/src/org/chromium/components/privacy_sandbox/IncognitoTrackingProtectionsFragment.java index 9642316..c4d8da03 100644 --- a/components/privacy_sandbox/android/java/src/org/chromium/components/privacy_sandbox/IncognitoTrackingProtectionsFragment.java +++ b/components/privacy_sandbox/android/java/src/org/chromium/components/privacy_sandbox/IncognitoTrackingProtectionsFragment.java
@@ -88,7 +88,10 @@ Preference ipProtectionPref = findPreference(PREF_IP_PROTECTION); if (ipProtectionPref != null) { ipProtectionPref.setSummary( - mDelegate.isIpProtectionEnabled() ? IPP_ON_SUBLABEL : IPP_OFF_SUBLABEL); + mDelegate.isIpProtectionEnabled() + && !mDelegate.isIpProtectionDisabledForEnterprise() + ? IPP_ON_SUBLABEL + : IPP_OFF_SUBLABEL); } Preference fpProtectionPref = findPreference(PREF_FP_PROTECTION);
diff --git a/components/privacy_sandbox/android/java/src/org/chromium/components/privacy_sandbox/IpProtectionSettingsFragment.java b/components/privacy_sandbox/android/java/src/org/chromium/components/privacy_sandbox/IpProtectionSettingsFragment.java index bd3467f3..6fa79abf 100644 --- a/components/privacy_sandbox/android/java/src/org/chromium/components/privacy_sandbox/IpProtectionSettingsFragment.java +++ b/components/privacy_sandbox/android/java/src/org/chromium/components/privacy_sandbox/IpProtectionSettingsFragment.java
@@ -7,6 +7,7 @@ import android.os.Bundle; import androidx.annotation.VisibleForTesting; +import androidx.preference.Preference; import org.chromium.base.metrics.RecordHistogram; import org.chromium.base.supplier.ObservableSupplier; @@ -16,6 +17,7 @@ import org.chromium.build.annotations.Nullable; import org.chromium.components.browser_ui.settings.ChromeSwitchPreference; import org.chromium.components.browser_ui.settings.SettingsUtils; +import org.chromium.components.browser_ui.site_settings.ForwardingManagedPreferenceDelegate; /** Fragment to manage settings for ip protection. */ @NullMarked @@ -38,7 +40,32 @@ getString(R.string.incognito_tracking_protections_ip_protection_toggle_label)); ChromeSwitchPreference ipProtectionSwitch = findPreference(PREF_IP_PROTECTION_SWITCH); - ipProtectionSwitch.setChecked(mDelegate.isIpProtectionEnabled()); + if (mDelegate.isIpProtectionDisabledForEnterprise()) { + ipProtectionSwitch.setEnabled(false); + ipProtectionSwitch.setChecked(false); + ipProtectionSwitch.setManagedPreferenceDelegate( + new ForwardingManagedPreferenceDelegate( + mDelegate + .getSiteSettingsDelegate(getContext()) + .getManagedPreferenceDelegate()) { + @Override + public boolean isPreferenceControlledByPolicy(Preference preference) { + return true; + } + }); + } else { + ipProtectionSwitch.setChecked(mDelegate.isIpProtectionEnabled()); + ipProtectionSwitch.setManagedPreferenceDelegate( + new ForwardingManagedPreferenceDelegate( + mDelegate + .getSiteSettingsDelegate(getContext()) + .getManagedPreferenceDelegate()) { + @Override + public boolean isPreferenceControlledByPolicy(Preference preference) { + return mDelegate.isIpProtectionManaged(); + } + }); + } ipProtectionSwitch.setOnPreferenceChangeListener( (preference, newValue) -> { mDelegate.setIpProtection((boolean) newValue);
diff --git a/components/privacy_sandbox/android/java/src/org/chromium/components/privacy_sandbox/TrackingProtectionDelegate.java b/components/privacy_sandbox/android/java/src/org/chromium/components/privacy_sandbox/TrackingProtectionDelegate.java index 54d1d9e..de46898 100644 --- a/components/privacy_sandbox/android/java/src/org/chromium/components/privacy_sandbox/TrackingProtectionDelegate.java +++ b/components/privacy_sandbox/android/java/src/org/chromium/components/privacy_sandbox/TrackingProtectionDelegate.java
@@ -43,6 +43,21 @@ void setIpProtection(boolean enabled); /** + * @return whether IP protection is disabled for users on enterprise devices. + */ + boolean isIpProtectionDisabledForEnterprise(); + + /** + * @return whether IP protection is managed. + */ + boolean isIpProtectionManaged(); + + /** + * @return whether fingerprinting protection is managed. + */ + boolean isFingerprintingProtectionManaged(); + + /** * @return whether the fingerprinting protection UX is enabled. */ boolean isFingerprintingProtectionUxEnabled();
diff --git a/components/privacy_sandbox/android/javatests/src/org/chromium/components/privacy_sandbox/FingerprintingProtectionSettingsFragmentTest.java b/components/privacy_sandbox/android/javatests/src/org/chromium/components/privacy_sandbox/FingerprintingProtectionSettingsFragmentTest.java index d0a518b12..9d78cd5 100644 --- a/components/privacy_sandbox/android/javatests/src/org/chromium/components/privacy_sandbox/FingerprintingProtectionSettingsFragmentTest.java +++ b/components/privacy_sandbox/android/javatests/src/org/chromium/components/privacy_sandbox/FingerprintingProtectionSettingsFragmentTest.java
@@ -11,9 +11,12 @@ import static androidx.test.espresso.matcher.ViewMatchers.isDisplayed; import static androidx.test.espresso.matcher.ViewMatchers.withText; +import static org.hamcrest.CoreMatchers.containsString; import static org.hamcrest.Matchers.allOf; +import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyBoolean; import static org.mockito.Mockito.doNothing; +import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @@ -35,6 +38,8 @@ import org.chromium.base.test.util.Batch; import org.chromium.base.test.util.HistogramWatcher; import org.chromium.components.browser_ui.settings.BlankUiTestActivitySettingsTestRule; +import org.chromium.components.browser_ui.settings.ManagedPreferenceTestDelegates; +import org.chromium.components.browser_ui.site_settings.SiteSettingsDelegate; /** Tests for {@link FingerprintingProtectionSettingsFragment}. */ @RunWith(BaseJUnit4ClassRunner.class) @@ -66,6 +71,10 @@ @Before public void setUp() { MockitoAnnotations.openMocks(this); + SiteSettingsDelegate mockDelegate = mock(SiteSettingsDelegate.class); + when(mDelegate.getSiteSettingsDelegate(any())).thenReturn(mockDelegate); + when(mockDelegate.getManagedPreferenceDelegate()) + .thenReturn(ManagedPreferenceTestDelegates.UNMANAGED_DELEGATE); } private void launchTrackingProtectionSettings() { @@ -127,4 +136,23 @@ verify(mDelegate).setFingerprintingProtection(false); fingerprintingProtectionHistogramWatcher.assertExpected(); } + + @Test + @SmallTest + public void fpProtectionToggleIsManagedWhenFpProtectionIsManaged() { + when(mDelegate.isFingerprintingProtectionEnabled()).thenReturn(true); + when(mDelegate.isFingerprintingProtectionManaged()).thenReturn(true); + + launchTrackingProtectionSettings(); + + String fppSublabel = mFragment.getContext().getString(PREF_TOGGLE_SUBLABEL); + String enterpriseSublabel = + mFragment.getContext().getString(R.string.managed_by_your_organization); + onView( + allOf( + withText(PREF_TOGGLE_LABEL), + hasSibling(withText(containsString(fppSublabel))), + isDisplayed())); + onView(withText(containsString(enterpriseSublabel))).check(matches(isDisplayed())); + } }
diff --git a/components/privacy_sandbox/android/javatests/src/org/chromium/components/privacy_sandbox/IncognitoTrackingProtectionsFragmentTest.java b/components/privacy_sandbox/android/javatests/src/org/chromium/components/privacy_sandbox/IncognitoTrackingProtectionsFragmentTest.java index c49d860..0185257 100644 --- a/components/privacy_sandbox/android/javatests/src/org/chromium/components/privacy_sandbox/IncognitoTrackingProtectionsFragmentTest.java +++ b/components/privacy_sandbox/android/javatests/src/org/chromium/components/privacy_sandbox/IncognitoTrackingProtectionsFragmentTest.java
@@ -169,4 +169,20 @@ hasSibling(withText(IP_PROTECTION_TOGGLE_SUBLABEL_ON)))) .check(matches(isDisplayed())); } + + @Test + @SmallTest + public void showIpProtectionOffSublabelWhenIpProtectionDisabledForEnterprise() { + when(mDelegate.isIpProtectionUxEnabled()).thenReturn(true); + when(mDelegate.isIpProtectionDisabledForEnterprise()).thenReturn(true); + + launchIncognitoTrackingProtectionsSettings(); + + checkTitleDescriptionAndBlock3pcsToggleAreShown(); + onView( + allOf( + withText(IP_PROTECTION_TOGGLE_LABEL), + hasSibling(withText(IP_PROTECTION_TOGGLE_SUBLABEL_OFF)))) + .check(matches(isDisplayed())); + } }
diff --git a/components/privacy_sandbox/android/javatests/src/org/chromium/components/privacy_sandbox/IpProtectionSettingsFragmentTest.java b/components/privacy_sandbox/android/javatests/src/org/chromium/components/privacy_sandbox/IpProtectionSettingsFragmentTest.java index 5040ab9..bf755b9 100644 --- a/components/privacy_sandbox/android/javatests/src/org/chromium/components/privacy_sandbox/IpProtectionSettingsFragmentTest.java +++ b/components/privacy_sandbox/android/javatests/src/org/chromium/components/privacy_sandbox/IpProtectionSettingsFragmentTest.java
@@ -11,9 +11,12 @@ import static androidx.test.espresso.matcher.ViewMatchers.isDisplayed; import static androidx.test.espresso.matcher.ViewMatchers.withText; +import static org.hamcrest.CoreMatchers.containsString; import static org.hamcrest.Matchers.allOf; +import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyBoolean; import static org.mockito.Mockito.doNothing; +import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @@ -35,6 +38,8 @@ import org.chromium.base.test.util.Batch; import org.chromium.base.test.util.HistogramWatcher; import org.chromium.components.browser_ui.settings.BlankUiTestActivitySettingsTestRule; +import org.chromium.components.browser_ui.settings.ManagedPreferenceTestDelegates; +import org.chromium.components.browser_ui.site_settings.SiteSettingsDelegate; /** Tests for {@link IpProtectionSettingsFragment}. */ @RunWith(BaseJUnit4ClassRunner.class) @@ -62,6 +67,10 @@ @Before public void setUp() { MockitoAnnotations.openMocks(this); + SiteSettingsDelegate mockDelegate = mock(SiteSettingsDelegate.class); + when(mDelegate.getSiteSettingsDelegate(any())).thenReturn(mockDelegate); + when(mockDelegate.getManagedPreferenceDelegate()) + .thenReturn(ManagedPreferenceTestDelegates.UNMANAGED_DELEGATE); } private void launchTrackingProtectionSettings() { @@ -146,4 +155,46 @@ verify(mDelegate).setIpProtection(false); ipProtectionHistogramWatcher.assertExpected(); } + + @Test + @SmallTest + public void ipProtectionToggleIsManagedWhenIpProtectionIsDisabledForEnterprise() { + when(mDelegate.isIpProtectionEnabled()).thenReturn(true); + when(mDelegate.isIpProtectionDisabledForEnterprise()).thenReturn(true); + SiteSettingsDelegate mockDelegate = mock(SiteSettingsDelegate.class); + when(mDelegate.getSiteSettingsDelegate(any())).thenReturn(mockDelegate); + when(mockDelegate.getManagedPreferenceDelegate()) + .thenReturn(ManagedPreferenceTestDelegates.UNMANAGED_DELEGATE); + + launchTrackingProtectionSettings(); + + String ippSublabel = mFragment.getContext().getString(PREF_TOGGLE_SUBLABEL); + String enterpriseSublabel = + mFragment.getContext().getString(R.string.managed_by_your_organization); + onView( + allOf( + withText(PREF_TOGGLE_LABEL), + hasSibling(withText(containsString(ippSublabel))), + isDisplayed())); + onView(withText(containsString(enterpriseSublabel))).check(matches(isDisplayed())); + } + + @Test + @SmallTest + public void ipProtectionToggleIsManagedWhenIpProtectionIsManaged() { + when(mDelegate.isIpProtectionEnabled()).thenReturn(true); + when(mDelegate.isIpProtectionManaged()).thenReturn(true); + + launchTrackingProtectionSettings(); + + String ippSublabel = mFragment.getContext().getString(PREF_TOGGLE_SUBLABEL); + String enterpriseSublabel = + mFragment.getContext().getString(R.string.managed_by_your_organization); + onView( + allOf( + withText(PREF_TOGGLE_LABEL), + hasSibling(withText(containsString(ippSublabel))), + isDisplayed())); + onView(withText(containsString(enterpriseSublabel))).check(matches(isDisplayed())); + } }
diff --git a/components/sync/base/hash_util.cc b/components/sync/base/hash_util.cc index 96c9cdf..d8aab23 100644 --- a/components/sync/base/hash_util.cc +++ b/components/sync/base/hash_util.cc
@@ -4,7 +4,7 @@ #include "components/sync/base/hash_util.h" -#include "base/notreached.h" +#include "base/logging.h" #include "base/strings/strcat.h" #include "base/strings/string_number_conversions.h" #include "components/sync/base/data_type.h" @@ -41,7 +41,11 @@ case sync_pb::AutofillWalletSpecifics::MASKED_IBAN: return std::string(); case sync_pb::AutofillWalletSpecifics::UNKNOWN: - NOTREACHED(); + DVLOG(1) << "New or unknown Autofill Wallet Specifics type is sent from " + "the sync server side while not handled by Chrome. This is " + "expected when the new type is not yet supported on current " + "chrome version."; + return std::string(); } return std::string(); }
diff --git a/components/trusted_vault/recovery_key_store_certificate.cc b/components/trusted_vault/recovery_key_store_certificate.cc index 4d272954..45bfe7f 100644 --- a/components/trusted_vault/recovery_key_store_certificate.cc +++ b/components/trusted_vault/recovery_key_store_certificate.cc
@@ -5,15 +5,145 @@ #include "components/trusted_vault/recovery_key_store_certificate.h" #include <string> +#include <string_view> #include <vector> +#include "base/base64.h" +#include "base/containers/span.h" +#include "base/memory/scoped_refptr.h" #include "base/notimplemented.h" +#include "crypto/signature_verifier.h" +#include "net/cert/time_conversions.h" +#include "net/cert/x509_certificate.h" +#include "net/cert/x509_util.h" +#include "third_party/boringssl/src/pki/cert_issuer_source_static.h" +#include "third_party/boringssl/src/pki/parse_certificate.h" +#include "third_party/boringssl/src/pki/parsed_certificate.h" +#include "third_party/boringssl/src/pki/path_builder.h" +#include "third_party/boringssl/src/pki/simple_path_builder_delegate.h" +#include "third_party/boringssl/src/pki/trust_store_in_memory.h" #include "third_party/libxml/chromium/xml_reader.h" namespace trusted_vault { namespace { +// This is the root of trust for the backend certificate list verification. This +// is generated by the trusted execution platforms team +// (trusted-execution-platforms@google.com) and hardcoded in the server code +// that uses this cert in the google3 codebase. As a root CA cert, it should +// very rarely be changed. +constexpr uint8_t kRecoverableKeyStoreServiceRootCaCert[] = { + 0x30, 0x82, 0x05, 0x0f, 0x30, 0x82, 0x02, 0xf7, 0xa0, 0x03, 0x02, 0x01, + 0x02, 0x02, 0x10, 0x6c, 0xd7, 0x6e, 0x79, 0x4d, 0xa8, 0xd2, 0xf3, 0x3d, + 0x80, 0x6a, 0xb8, 0x37, 0xa6, 0xe1, 0x8f, 0x30, 0x0d, 0x06, 0x09, 0x2a, + 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00, 0x30, 0x31, + 0x31, 0x2f, 0x30, 0x2d, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x26, 0x47, + 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x20, 0x43, 0x6c, 0x6f, 0x75, 0x64, 0x20, + 0x4b, 0x65, 0x79, 0x20, 0x56, 0x61, 0x75, 0x6c, 0x74, 0x20, 0x53, 0x65, + 0x72, 0x76, 0x69, 0x63, 0x65, 0x20, 0x52, 0x6f, 0x6f, 0x74, 0x20, 0x43, + 0x41, 0x30, 0x1e, 0x17, 0x0d, 0x31, 0x38, 0x30, 0x35, 0x30, 0x37, 0x31, + 0x38, 0x32, 0x34, 0x30, 0x32, 0x5a, 0x17, 0x0d, 0x33, 0x38, 0x30, 0x35, + 0x30, 0x38, 0x31, 0x39, 0x32, 0x34, 0x30, 0x32, 0x5a, 0x30, 0x31, 0x31, + 0x2f, 0x30, 0x2d, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x26, 0x47, 0x6f, + 0x6f, 0x67, 0x6c, 0x65, 0x20, 0x43, 0x6c, 0x6f, 0x75, 0x64, 0x20, 0x4b, + 0x65, 0x79, 0x20, 0x56, 0x61, 0x75, 0x6c, 0x74, 0x20, 0x53, 0x65, 0x72, + 0x76, 0x69, 0x63, 0x65, 0x20, 0x52, 0x6f, 0x6f, 0x74, 0x20, 0x43, 0x41, + 0x30, 0x82, 0x02, 0x22, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, + 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x03, 0x82, 0x02, 0x0f, 0x00, + 0x30, 0x82, 0x02, 0x0a, 0x02, 0x82, 0x02, 0x01, 0x00, 0xad, 0x48, 0x33, + 0xbb, 0xee, 0x28, 0xf7, 0x29, 0x76, 0xd9, 0xea, 0xa5, 0xd4, 0x18, 0x86, + 0x06, 0xad, 0xe0, 0x59, 0x7a, 0x28, 0x87, 0x6a, 0xa5, 0xdc, 0x9f, 0xaf, + 0x56, 0xec, 0xdf, 0xfd, 0x38, 0x63, 0xcd, 0xd2, 0x20, 0xd3, 0x19, 0x24, + 0x93, 0x0f, 0xcd, 0x00, 0x5c, 0x58, 0x16, 0x2e, 0x3d, 0x12, 0x8d, 0x5f, + 0x6b, 0xf8, 0x5f, 0xf3, 0x00, 0x88, 0xa0, 0x0a, 0x82, 0x12, 0xcd, 0x65, + 0x0f, 0xab, 0x44, 0xdd, 0xc0, 0x83, 0xdd, 0x3d, 0xfe, 0x11, 0x03, 0xea, + 0xba, 0x1e, 0x82, 0x07, 0x62, 0xa6, 0x64, 0x32, 0x7a, 0x98, 0xf9, 0xd7, + 0xbd, 0x55, 0x25, 0x52, 0xe1, 0x6b, 0xd0, 0xed, 0x8c, 0xc1, 0x99, 0x30, + 0xca, 0x5a, 0x81, 0x11, 0x3c, 0xca, 0xd3, 0x1e, 0x4d, 0x08, 0x78, 0x0b, + 0xfe, 0x9e, 0x3b, 0xbe, 0x48, 0xe1, 0x1f, 0x1e, 0x8b, 0xa9, 0x56, 0xa8, + 0x6e, 0x28, 0x0a, 0x94, 0x7c, 0x6c, 0xce, 0xf5, 0x62, 0xe1, 0xf9, 0x2d, + 0xfe, 0xaa, 0xbb, 0x29, 0x6d, 0xd8, 0x4d, 0x5c, 0x61, 0xc1, 0xd2, 0xc6, + 0x11, 0xa6, 0xfe, 0x3a, 0xa4, 0x9f, 0xc0, 0xcc, 0x5d, 0x04, 0xb8, 0x4c, + 0x7c, 0x4d, 0x0a, 0xd1, 0xdb, 0xc5, 0xb7, 0xc6, 0xec, 0xf3, 0x22, 0x40, + 0x17, 0x4e, 0x03, 0x26, 0xc3, 0x1b, 0x44, 0x28, 0x45, 0x14, 0x1a, 0x53, + 0xd7, 0xb6, 0x74, 0xbb, 0x9d, 0xe2, 0x20, 0x00, 0x2e, 0xe6, 0xa5, 0x50, + 0x84, 0xaa, 0xd0, 0x5e, 0x22, 0x00, 0xc2, 0x06, 0xe8, 0x66, 0xa7, 0x7e, + 0x26, 0x41, 0xcb, 0x5d, 0x4d, 0x5f, 0x25, 0xe5, 0x53, 0xe5, 0x62, 0x4e, + 0x26, 0x0a, 0x09, 0x15, 0x61, 0xe4, 0x75, 0x69, 0x07, 0xb5, 0xae, 0x26, + 0x49, 0x89, 0x52, 0xef, 0x62, 0x75, 0x43, 0xdd, 0xbb, 0x24, 0x3d, 0x49, + 0x74, 0x44, 0xf5, 0x90, 0x5b, 0x47, 0xf8, 0x40, 0xed, 0x60, 0x0b, 0x71, + 0xef, 0x1e, 0xc5, 0xf7, 0x10, 0x00, 0x6d, 0xbd, 0xad, 0x30, 0x84, 0xf0, + 0xb3, 0xfc, 0x30, 0x77, 0x6a, 0xc0, 0xcd, 0x94, 0xd7, 0xfe, 0x4c, 0x51, + 0x6c, 0x39, 0x57, 0x54, 0xb4, 0xe8, 0x53, 0x4e, 0x4b, 0x15, 0x83, 0xeb, + 0xf9, 0xd1, 0x55, 0xd7, 0x0b, 0xd7, 0x9a, 0x2d, 0x23, 0x96, 0x42, 0x31, + 0x9e, 0x17, 0x5a, 0x54, 0x1a, 0x96, 0x0b, 0xdd, 0xe9, 0xe7, 0x6f, 0x14, + 0x89, 0x47, 0x0b, 0xa6, 0x26, 0xfe, 0x1d, 0x5c, 0xcc, 0x58, 0x67, 0x58, + 0x23, 0x71, 0xb5, 0x34, 0xe6, 0xbf, 0x95, 0x3a, 0x74, 0x73, 0xc2, 0xdc, + 0x6c, 0x98, 0xda, 0xa6, 0x28, 0x95, 0x9d, 0xe4, 0x50, 0x27, 0x77, 0x08, + 0xa8, 0x33, 0xce, 0x48, 0x49, 0xc4, 0xab, 0x8d, 0x21, 0xc9, 0x97, 0x75, + 0x8f, 0x1d, 0xc9, 0x9c, 0xec, 0x49, 0x33, 0x01, 0xec, 0xf2, 0xfe, 0x2c, + 0xf4, 0x62, 0x25, 0x6f, 0x70, 0x5c, 0x3a, 0x60, 0xef, 0x03, 0xf3, 0x2e, + 0xd3, 0xdc, 0x44, 0x30, 0xac, 0x29, 0x1c, 0x19, 0xb8, 0x4c, 0x50, 0xca, + 0x5d, 0xe1, 0x87, 0x39, 0x68, 0x5a, 0xed, 0xc7, 0x16, 0x10, 0x40, 0x9b, + 0xc8, 0xee, 0x67, 0x72, 0xee, 0x97, 0xb8, 0xdd, 0xa2, 0xcb, 0x3f, 0x52, + 0xf9, 0x3b, 0x8c, 0xca, 0x36, 0x41, 0x13, 0x9e, 0x76, 0xa7, 0xa3, 0xee, + 0xe6, 0x01, 0x02, 0xdb, 0x19, 0x3e, 0xa9, 0xa6, 0xf4, 0x34, 0x60, 0xd3, + 0x1d, 0xd2, 0xca, 0x2d, 0xbc, 0x96, 0x9f, 0x72, 0x31, 0x76, 0x60, 0x47, + 0xc9, 0x3a, 0xfb, 0x88, 0xf0, 0xaa, 0x9a, 0x9c, 0x87, 0x9e, 0x09, 0x02, + 0xfe, 0x96, 0xc6, 0x7e, 0xf1, 0xae, 0xb1, 0xce, 0x41, 0xa4, 0x1b, 0xa0, + 0xb0, 0x1b, 0x65, 0xcf, 0xae, 0xe6, 0xe1, 0x15, 0x8b, 0x27, 0xbd, 0xb2, + 0x01, 0xe5, 0x4f, 0x3b, 0xf9, 0x72, 0xff, 0xcc, 0x38, 0xa2, 0xb3, 0x6c, + 0x19, 0x68, 0xe7, 0xde, 0xcd, 0x02, 0x03, 0x01, 0x00, 0x01, 0xa3, 0x23, + 0x30, 0x21, 0x30, 0x0e, 0x06, 0x03, 0x55, 0x1d, 0x0f, 0x01, 0x01, 0xff, + 0x04, 0x04, 0x03, 0x02, 0x01, 0x86, 0x30, 0x0f, 0x06, 0x03, 0x55, 0x1d, + 0x13, 0x01, 0x01, 0xff, 0x04, 0x05, 0x30, 0x03, 0x01, 0x01, 0xff, 0x30, + 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b, + 0x05, 0x00, 0x03, 0x82, 0x02, 0x01, 0x00, 0x68, 0x96, 0x96, 0xef, 0xb8, + 0xab, 0x2c, 0x60, 0x3d, 0xbe, 0x91, 0xb0, 0x0d, 0x26, 0xc1, 0x1e, 0xd5, + 0xdb, 0x55, 0x09, 0xf6, 0xb1, 0x29, 0x62, 0x14, 0x8d, 0xf8, 0x04, 0x69, + 0x4d, 0x61, 0xa2, 0x23, 0x6d, 0x91, 0x08, 0x94, 0x86, 0x2b, 0x46, 0x6b, + 0x3c, 0x67, 0x85, 0x43, 0x31, 0x36, 0x06, 0x99, 0xd5, 0x45, 0xd9, 0x8b, + 0x4d, 0x5e, 0x63, 0x7f, 0xd2, 0x54, 0x4a, 0xd8, 0xc7, 0xa0, 0xfb, 0xcd, + 0x60, 0x59, 0xf0, 0x2d, 0x32, 0x74, 0x13, 0x67, 0xbd, 0x3a, 0x58, 0xfc, + 0x32, 0xb1, 0xeb, 0x02, 0xe7, 0x00, 0x50, 0x52, 0xe1, 0x97, 0x8c, 0x28, + 0x5b, 0x3d, 0x24, 0x53, 0xf5, 0xf4, 0x10, 0x1d, 0x63, 0xe9, 0x8d, 0x92, + 0x5e, 0xb0, 0x1a, 0x64, 0xf8, 0x8c, 0x32, 0x9a, 0xbc, 0x04, 0xa8, 0xf7, + 0x98, 0x7d, 0x4c, 0x6d, 0x5e, 0x2c, 0xb5, 0x9d, 0x24, 0x88, 0x75, 0x29, + 0x66, 0xee, 0x8f, 0x12, 0x6a, 0xe8, 0x8d, 0x81, 0xbb, 0x14, 0xa2, 0x8d, + 0xdf, 0x49, 0xda, 0xdb, 0x15, 0xdf, 0x88, 0xe7, 0xe5, 0xd8, 0x9b, 0xac, + 0x56, 0xcc, 0x87, 0x0e, 0xa9, 0x94, 0x9f, 0xf1, 0xe3, 0x4e, 0x1e, 0xb3, + 0x01, 0x22, 0xaa, 0x13, 0xa8, 0x71, 0x13, 0xe1, 0x49, 0x46, 0x5d, 0x95, + 0x43, 0x87, 0x5c, 0xaf, 0x84, 0xaf, 0x88, 0x7b, 0x5d, 0x6e, 0x29, 0x8c, + 0xcb, 0x62, 0xe0, 0x84, 0xb9, 0xd9, 0xe0, 0x51, 0x38, 0x5e, 0x12, 0x60, + 0x46, 0x7c, 0x3d, 0x4d, 0xe4, 0x55, 0xe4, 0x2f, 0x52, 0x9f, 0xba, 0x66, + 0x9c, 0x00, 0x39, 0x5f, 0x40, 0xbc, 0x65, 0x78, 0x57, 0xbd, 0xfb, 0xeb, + 0x77, 0x1e, 0x1f, 0x2f, 0x75, 0x0a, 0x96, 0x4d, 0x4b, 0xaa, 0x56, 0xf9, + 0xb2, 0x4c, 0x68, 0x2b, 0x54, 0x7d, 0xc3, 0x0a, 0xfe, 0x68, 0xd2, 0x0e, + 0xf9, 0xcb, 0x91, 0xf0, 0x90, 0x39, 0x0e, 0x61, 0x03, 0x58, 0x5c, 0x39, + 0x06, 0xe7, 0x61, 0xfe, 0xe5, 0x96, 0x66, 0x50, 0x53, 0xc9, 0x55, 0xb6, + 0xc9, 0xb6, 0xd3, 0x07, 0xab, 0x64, 0x98, 0xe7, 0x3c, 0x73, 0x4f, 0x17, + 0x3a, 0x0c, 0x41, 0x68, 0xaa, 0xec, 0x5c, 0x02, 0xe6, 0x25, 0xc9, 0x6e, + 0x69, 0xb8, 0xe6, 0x37, 0x58, 0xe9, 0xf3, 0x56, 0x9e, 0xd5, 0xea, 0x5e, + 0x37, 0x7d, 0x63, 0x27, 0x83, 0x79, 0x20, 0x4e, 0x5a, 0xab, 0xb5, 0xdb, + 0x4c, 0x59, 0x8a, 0x93, 0x0a, 0x60, 0x4d, 0x3b, 0xc9, 0x9a, 0xa0, 0x91, + 0xd9, 0x7c, 0xd6, 0xba, 0x39, 0x6f, 0x75, 0xb7, 0x63, 0x7e, 0x20, 0x01, + 0x96, 0x25, 0xac, 0x9c, 0xbc, 0xcb, 0x40, 0x23, 0xfc, 0x79, 0x9c, 0x1c, + 0x33, 0x82, 0xc0, 0xe5, 0x42, 0xb7, 0x7f, 0x59, 0x2d, 0x1e, 0x89, 0x6b, + 0xd4, 0xe2, 0x3d, 0x7a, 0xd4, 0x8e, 0x91, 0xe6, 0xc4, 0xda, 0x9c, 0xbc, + 0x2c, 0xf6, 0x01, 0x25, 0x69, 0x57, 0xdf, 0xab, 0x51, 0x01, 0xa8, 0x19, + 0x85, 0x4f, 0xcd, 0xa3, 0x23, 0xad, 0xbe, 0x14, 0x73, 0xab, 0xda, 0xdb, + 0xde, 0xb6, 0xdf, 0xf7, 0xa0, 0x0f, 0x10, 0x72, 0x94, 0x16, 0xc5, 0xc6, + 0xf6, 0x70, 0x44, 0x77, 0x60, 0x54, 0x29, 0x98, 0x21, 0x2c, 0xa3, 0xdf, + 0xf8, 0x93, 0x42, 0x53, 0xfd, 0xa9, 0x38, 0x6c, 0x74, 0x8d, 0xd3, 0x1c, + 0x7f, 0xe2, 0xbe, 0x6b, 0x50, 0xde, 0x1b, 0x46, 0x33, 0x19, 0x83, 0x11, + 0x90, 0x5b, 0xe9, 0x37, 0x78, 0x6f, 0x1d, 0x8a, 0xc9, 0x74, 0x1c, 0x8b, + 0xde, 0x44, 0x8f, 0xdd, 0x4b, 0x43, 0xd0, 0x7d, 0xa0, 0xf0, 0xfb, 0x3b, + 0x81, 0x12, 0xc4, 0x2c, 0x6d, 0x87, 0x38, 0x3c, 0x5b, 0x4a, 0x7f, 0x0f, + 0x57, 0x75, 0xed, 0xb9, 0x2b, 0xf1, 0x55, 0xa9, 0xc0, 0xea, 0xec, 0x9a, + 0x69, 0x2d, 0xc0, 0x67, 0xa4, 0xb1, 0x79, 0x52, 0xef, 0x9b, 0x0e, 0x74, + 0xed, 0xa6, 0x08}; + // Returns a list of certificates from a certificate tag, traversing its entire // contents. Ignores anything that is not a <cert> tag directly below the // certificate tag. @@ -43,6 +173,35 @@ return cert_list; } +// Given a base64-encoded x509 certificate, returns the bssl representation (or +// nullptr if decoding failed). +std::shared_ptr<const bssl::ParsedCertificate> CertificateFromBase64( + std::string_view b64) { + std::optional<std::vector<uint8_t>> certificate_data = + base::Base64Decode(b64); + if (!certificate_data) { + return nullptr; + } + return bssl::ParsedCertificate::Create( + net::x509_util::CreateCryptoBuffer(*certificate_data), + net::x509_util::DefaultParseCertificateOptions(), /*errors=*/nullptr); +} + +// Returns a fresh trusted cert store for the cloud root. +std::unique_ptr<bssl::TrustStoreInMemory> ConstructTrustedCertStore() { + // This code will run at most once a day. So there's no point keeping the + // trust store around after use. + auto trust_store = std::make_unique<bssl::TrustStoreInMemory>(); + std::shared_ptr<const bssl::ParsedCertificate> root_cert = + bssl::ParsedCertificate::Create( + net::x509_util::CreateCryptoBuffer( + kRecoverableKeyStoreServiceRootCaCert), + net::x509_util::DefaultParseCertificateOptions(), /*errors=*/nullptr); + CHECK(root_cert); + trust_store->AddTrustAnchor(root_cert); + return trust_store; +} + } // namespace namespace internal { @@ -116,13 +275,114 @@ std::move(intermediates), std::move(certificate), std::move(signature)); } +std::shared_ptr<const bssl::ParsedCertificate> VerifySignatureChain( + std::string_view certificate_b64, + base::span<std::string> intermediates_b64, + base::Time current_time) { + std::shared_ptr<const bssl::ParsedCertificate> certificate = + CertificateFromBase64(certificate_b64); + if (!certificate) { + return nullptr; + } + + bssl::CertIssuerSourceStatic intermediates; + for (const auto& intermediate_b64 : intermediates_b64) { + std::shared_ptr<const bssl::ParsedCertificate> intermediate = + CertificateFromBase64(intermediate_b64); + if (!intermediate) { + // Ignore invalid base64. We may still be able to build a valid path. + continue; + } + intermediates.AddCert(std::move(intermediate)); + } + + std::unique_ptr<bssl::TrustStoreInMemory> trusted_cert_store = + ConstructTrustedCertStore(); + bssl::SimplePathBuilderDelegate builder_delegate( + /*min_rsa_modulus_length_bits=*/2048, + bssl::SimplePathBuilderDelegate::DigestPolicy::kStrong); + bssl::der::GeneralizedTime verification_time; + CHECK(net::EncodeTimeAsGeneralizedTime(current_time, &verification_time)); + bssl::CertPathBuilder path_builder(certificate, trusted_cert_store.get(), + &builder_delegate, verification_time, + bssl::KeyPurpose::ANY_EKU, + bssl::InitialExplicitPolicy::kFalse, + {bssl::der::Input(bssl::kAnyPolicyOid)}, + bssl::InitialPolicyMappingInhibit::kFalse, + bssl::InitialAnyPolicyInhibit::kFalse); + path_builder.AddCertIssuerSource(&intermediates); + bssl::CertPathBuilder::Result result = path_builder.Run(); + if (!result.HasValidPath()) { + return nullptr; + } + if (!certificate->has_key_usage() || + !certificate->key_usage().AssertsBit( + bssl::KEY_USAGE_BIT_DIGITAL_SIGNATURE)) { + return nullptr; + } + return certificate; +} + +bool VerifySignature(std::shared_ptr<const bssl::ParsedCertificate> certificate, + std::string_view cert_xml, + std::string_view signature_b64) { + std::optional<std::vector<uint8_t>> signature = + base::Base64Decode(signature_b64); + if (!signature) { + return false; + } + size_t size_bits; + net::X509Certificate::PublicKeyType type; + net::X509Certificate::GetPublicKeyInfo(certificate->cert_buffer(), &size_bits, + &type); + crypto::SignatureVerifier::SignatureAlgorithm algo; + switch (type) { + case net::X509Certificate::PublicKeyType::kPublicKeyTypeECDSA: + algo = crypto::SignatureVerifier::SignatureAlgorithm::ECDSA_SHA256; + break; + case net::X509Certificate::PublicKeyType::kPublicKeyTypeRSA: + algo = crypto::SignatureVerifier::SignatureAlgorithm::RSA_PKCS1_SHA256; + break; + case net::X509Certificate::PublicKeyType::kPublicKeyTypeUnknown: + return false; + } + crypto::SignatureVerifier signature_verifier; + if (!net::x509_util::SignatureVerifierInitWithCertificate( + &signature_verifier, algo, *signature, certificate->cert_buffer())) { + return false; + } + signature_verifier.VerifyUpdate(base::as_byte_span(cert_xml)); + return signature_verifier.VerifyFinal(); +} + } // namespace internal // static std::optional<RecoveryKeyStoreCertificate> RecoveryKeyStoreCertificate::Parse( std::string_view cert_xml, - std::string_view sig_xml) { - NOTIMPLEMENTED(); + std::string_view sig_xml, + base::Time current_time) { + std::optional<internal::ParsedRecoveryKeyStoreSigXML> parsed_sig_xml = + internal::ParseRecoveryKeyStoreSigXML(sig_xml); + if (!parsed_sig_xml) { + return std::nullopt; + } + std::shared_ptr<const bssl::ParsedCertificate> signing_certificate = + internal::VerifySignatureChain(std::move(parsed_sig_xml->certificate), + parsed_sig_xml->intermediates, + current_time); + if (!signing_certificate) { + return std::nullopt; + } + if (!internal::VerifySignature(std::move(signing_certificate), cert_xml, + parsed_sig_xml->signature)) { + return std::nullopt; + } + std::optional<std::vector<std::string>> endpoints = + internal::ParseRecoveryKeyStoreCertXML(cert_xml); + if (!endpoints) { + return std::nullopt; + } return RecoveryKeyStoreCertificate(); }
diff --git a/components/trusted_vault/recovery_key_store_certificate.h b/components/trusted_vault/recovery_key_store_certificate.h index 3b84bbb..bba99ef6 100644 --- a/components/trusted_vault/recovery_key_store_certificate.h +++ b/components/trusted_vault/recovery_key_store_certificate.h
@@ -5,11 +5,19 @@ #ifndef COMPONENTS_TRUSTED_VAULT_RECOVERY_KEY_STORE_CERTIFICATE_H_ #define COMPONENTS_TRUSTED_VAULT_RECOVERY_KEY_STORE_CERTIFICATE_H_ +#include <memory> #include <optional> #include <string> #include <string_view> #include <vector> +#include "base/containers/span.h" +#include "base/time/time.h" + +namespace bssl { +class ParsedCertificate; +} // namespace bssl + namespace trusted_vault { namespace internal { @@ -46,6 +54,20 @@ std::optional<ParsedRecoveryKeyStoreSigXML> ParseRecoveryKeyStoreSigXML( std::string_view sig_xml); +// Given a base64-encoded x509 certificate, and a list of intermediates, +// attempts to build a chain to the root certificate. +// Returns the leaf certificate if successful, nullptr otherwise. +std::shared_ptr<const bssl::ParsedCertificate> VerifySignatureChain( + std::string_view certificate_b64, + base::span<std::string> intermediates_b64, + base::Time current_time); + +// Verifies a signature by `certificate` over `cert_xml`. Returns true if the +// signature matches, false otherwise. +bool VerifySignature(std::shared_ptr<const bssl::ParsedCertificate> certificate, + std::string_view cert_xml, + std::string_view signature_b64); + } // namespace internal // Represents a certificate chain from the recovery key store. @@ -56,7 +78,8 @@ // source, like https://gstatic.com. static std::optional<RecoveryKeyStoreCertificate> Parse( std::string_view cert_xml, - std::string_view sig_xml); + std::string_view sig_xml, + base::Time current_time); private: RecoveryKeyStoreCertificate();
diff --git a/components/trusted_vault/recovery_key_store_certificate_unittest.cc b/components/trusted_vault/recovery_key_store_certificate_unittest.cc index 859895a..9a710dc 100644 --- a/components/trusted_vault/recovery_key_store_certificate_unittest.cc +++ b/components/trusted_vault/recovery_key_store_certificate_unittest.cc
@@ -7,6 +7,8 @@ #include <string> #include <vector> +#include "base/containers/span.h" +#include "base/time/time.h" #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" @@ -35,7 +37,8 @@ <cert>MIIDOzCCASOgAwIBAgIRALohAkmP2SJK75Xsk8FsngUwDQYJKoZIhvcNAQELBQAwOTE3MDUGA1UEAxMuR29vZ2xlIENsb3VkIEtleSBWYXVsdCBTZXJ2aWNlIEludGVybWVkaWF0ZSBDQTAeFw0yMzA5MDUyMTUwNThaFw0yNTA0MDkwMDAwMDBaMDIxMDAuBgNVBAMTJ0dvb2dsZSBDbG91ZCBLZXkgVmF1bHQgU2VydmljZSBFbmRwb2ludDBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABOHSWq/RFpU1VnCCCmPcTDeJT3t3+27+BjFOdsC8/hcnbFUKwHt6Tt0uiHV3LP/aO0/DHYC8Kdb/KAMC+ai+aJ2jEDAOMAwGA1UdEwEB/wQCMAAwDQYJKoZIhvcNAQELBQADggIBALz6PK44f46capH7isFvHMdTosG3DIV4QP70zLtGtGBM+57RKU0UYLtgdtKfCCwQVIgru9PfMdNdbxKojI96cfB/QxsH5H/96iUET+EnvvQ63NMSnLtOp7H4UceBujpXeSLN0yRNr59JS+mLtyL5+5KjHgtOM7tpxJ3eP1tx8NnE30TE0BoeTQyoKu0wfHVsc5+Fs3EWJUpgV+Z0/KJFoy3M2Z0DHZxfn6fg+/xYxn8ttkMhlZXhJMjNqtcGmlwLYktmsG5LlsQNimXwGl9olVviEZwcHGUzHw8QWszoKzn+TgTgv76m2eZ5MwJeN1JnaLb+1gQtgKRpnG8TFxWGC/TIHUqLow/GruH2TSlLPr6l6ed+QjG01sAN5cdI7OR84D8W1F0vb8fVOr7kjf7N3qLDNQXDCRUUKHlRVanIt6h+kT1ctlM51+QmRhDsAkzY/3lFrXDySnQk18vlzTyA+QgqmvfNkPhgCp/fpgtWJFaPL9bJWaMaW/soXRUf26F6RMLK43EihdoVMtUAvmCIKUQyI88X6hJxEhWLyy/8Y45nAFk5CgXuzV2doOJTSITtJligTy1IuczH75bmp87c5ZPp51vUO4WYXuwffTCoQ8UYSYbNxxqKOfFkILnM1WoGAzCrVt5aKOyGPILzOsOS8X0EeQ9YF6Mvaf2iFljc2o30</cert> <cert>MIIDOzCCASOgAwIBAgIRALohAkmP2SJK75Xsk8FsngUwDQYJKoZIhvcNAQELBQAwOTE3MDUGA1UEAxMuR29vZ2xlIENsb3VkIEtleSBWYXVsdCBTZXJ2aWNlIEludGVybWVkaWF0ZSBDQTAeFw0yMzA5MDUyMTUwNThaFw0yNTA0MDkwMDAwMDBaMDIxMDAuBgNVBAMTJ0dvb2dsZSBDbG91ZCBLZXkgVmF1bHQgU2VydmljZSBFbmRwb2ludDBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABNeVqPpEctoVzN48WNefTpJEmRrrbpXoWRhHwH/AOYmQgXR6xX/AE1/qeen8fMj4Lnyb8KPveZjXvTlFq2mdBHGjEDAOMAwGA1UdEwEB/wQCMAAwDQYJKoZIhvcNAQELBQADggIBAEQIGwhKa7MDq+Wt5p7fvv1AXhX4HxpgkKv5xbuMWCcw6R8zTYQ4hF/XHegIEqjmwWFxEvD95Lu3oLz4gMEoZVywBt2QFb1wkWUjdeT9oy5YbrJiLm9evhMFWyjnu2h9OVqxCVvarVx35ZySThDr2n3CYntLSKyTSdVlzCsdcCOj1UFkqMe73gOUZFMkXETUoINlFYwX6NP5V1Moy8OjsSNa6/8zyYwivm3rQlj3GUEhSlX+0ib+IXYpcrDFF7/6+G8lWBAHmKGwGR6kpAQ7Zg7KEjY0gSYWOr86oJIMFzeXVjaqhwGXK2tO+JBTPZSf4zljke+QCDN1uZjscgpOOXcBvT3LqLDaz2TSen4EMXhD56lYrq/970a1ol7B26nNAjJr1Q2ZyH4kXgBnK/b7AjYzNhTx0k0o7zRdh4tMeNkxhHgpBQ7d8VM81lZJg95n5SuOvJkJlEsPus9nJ1QeKAAjLV+Hp4n+xEImnvwnPEeE9vo07KHeHsCaBFVVan+9VKMiFEnYO+JdA8DwVTwTHHRH2T2OcEF+oo6m9nZZgGZbcovftryoOetJRY8E2JG+j5ScVWwnh5QcWhP1oOqsZdFWbKmJyxbN0qhKRWB1l6xZipMTj4RYzrZtwXNWdJIudC1Lkr6GgMn2UybLPc4xDH5FLWDtLN7griLweFrniuAQ</cert> </endpoints> -</certificate>)"; +</certificate> +)"; static constexpr std::string_view kSigXml = R"(<?xml version="1.0" encoding="UTF-8"?> @@ -46,7 +49,11 @@ </intermediates> <certificate>MIIFGTCCAwGgAwIBAgIRAOUOMMnP/H98t0zAwO3YjxIwDQYJKoZIhvcNAQELBQAwOTE3MDUGA1UEAxMuR29vZ2xlIENsb3VkIEtleSBWYXVsdCBTZXJ2aWNlIEludGVybWVkaWF0ZSBDQTAeFw0yMzA5MDUyMTUxMDBaFw0yODA5MDYyMTUxMDBaMDUxMzAxBgNVBAMTKkdvb2dsZSBDbG91ZCBLZXkgVmF1bHQgU2VydmljZSBTaWduaW5nIEtleTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBANqoaDjGHUrdnO6raw9omQ+xnhSxqwTSY2dlC83an+F9JNlL/CHjvn+kyKP7rP57k4y9+9REqjvk+zaR6rQjzP6m2FbYf/kXsmS8ohtTXsmI9NTvobGCGZOYwFbB28yxoOiXA2A91cG+Rt/KmetMcGphFE0/9PGZg9JSmWiGLDJEvgG4ckz6fmL/orhbC/V1K3ArNZ2eJ8Sw29eMo62XpJqvmi+6BrFS3edcJNC1dUpC/ixP73G1J5XDVb60no4JolG1N7Utug/WlPr88eI7LdV05sMfRfX+ta4TrIK7yJ1urGuOVsIDBGFjsfgpRTlwiG829D9uGhRSAE8GzVCFiVF8AfQwlEtgahwg23QzWRaKYo6qeRMCw1hNURF31hQ5bgQeKcaS98x6MkzszBOT2aFiK0EWBzwsJLI3KadRYUMcKa3AFXSv7QLGkAU+Ivas/m3Mt0s7KQnIzjsYbOqiC895WsylxaQyMy5xvVKp0gYjmK2YtgfXo59hznqns1FzeR4fBsbKsh+NnWXzcJ8cEg8jbk0nxAz0reMj1IN25Wb1WDfUCiTy+9V6dfFLQFQ6KYDb/bbIRyPk4g176gWK9agVrHrhiQsDVstSN/cAgLBVUFi1oeLzZ0SwB4wCXuP8SmEVrGl3zxxv3szgUxwfm+elaZ0BrA5deSenJdhV1QQ3AgMBAAGjIDAeMA4GA1UdDwEB/wQEAwIHgDAMBgNVHRMBAf8EAjAAMA0GCSqGSIb3DQEBCwUAA4ICAQDuLSK5nov/grmYNc8CTnrKNZ1w8p5Wbi9QThzJXoSV1BuFklXNX4GlgjZ04eS5ns/lUCdqByx0K2ZGX24wzZX0sSUQ+74Fq5uDINm6ESPV46y6hXvqIotLYIrgpl7Z2Ej7D6JT5fPYzAncUQd8Z9LuNMMt/rG8IlfSN6yOuZnAxI8wKtCrp23QugtqYKHyfxCN/HzCMEs1XP7qhgolnmLoTqU9j2HlPPESmH4+St4w7QPVQWARQ2S0hdtT4dhjmkqeDBojBjkGn9fS+vsOKsH3CDTt3A0pFI66xQ9TwT5mHCIIkAxGzc/DzPtpTUz6XBhtWNyI59adbCHfOtWWNjpriYvTbOm1ZZL6DXsaFJIbYX0Cmh6unonuvZ2c1Pu6nnVxR1HamIdtDZjvgbyFRJ4wCWpMhAU9WVJSotz57OXf/CvbBI0gfhl/EmWtKsGiDryPjphILWrnO55V6G6HJgk6xpzcjZzSnWpf5UF9RGjUaZNwOtxma/57pM8o5vTCeaOrq/3dKUWO2JBgxkOG+/ZCOe0E0Q2CwCCWTtf4ReaUIbeYQTj4cfR4eaj6Z8euytwEM2UQCep+HXJdOxv6/eHRXPK21Alt0crWmhZ8J7hZyeZ/24a3in8hqg9X9wxZXPghXo4W3My3Tn+dP2m36RiBQOCHSoYWMRINZccj9284GQ==</certificate> <value>n6kI2dGZKz5CGbXnbz79m51QTDt+WszzNOvcqXsGm6g3ObmpjkghTU3wPmrJ0c5zUD1l4QQEmTKRBIACgK7Sp64JdC4IGP5y+z8HhXPslP3Dc5aySOk4b++m7AIbkAuw63SbPD8L2nQ20CMNiaVVBqZJ0uWUV04qN8IOll1L8NbeZLhjFUcx9riYBrzWOr9uis5IANkfPTFgFyPFjqFk9XrbVpPcNCRtz7Pew+L7OW5z7sh5rW8iZmjhhV/e4VDTgYBFq/Js5W4yalRI9uuEXLJqG1/US4L5cMnJoZOxPmz48an0ug/Pi8yV9cIq+xvER/XaeeUG53Fqy9cn2qG6ROwxH109toaLx3TZaLjdVh7wcJCLtOY6WngHksQbIyU1mDYzz7uWItCss2Nb0NbZ+QMn3k1GxDGIwlY/HXdt7OihPQWLRM2H/QRqlI9p8i1L+DaPrhyGrGHzYKN8z9qGZYx1AsQUWQCR0YeXvlxjtSvBEPtWkfEE0RrZPJtFh+bvrD55Id7XapnGKKXYMmYf9KbDJ3GMD1aT6xgMhlAhtltN5vNg08LSH5Ma4TXhmNpKny5JQqlAUTby1wIhgdElQSdU0jYpmle8N0wsuLoX+e3bHFKxWVkrwvXDC0v2wqH5mzm8FLhxXZDA2ApnGT+eOC1gjd8qTuouzm5GuMhjvig=</value> -</signature>)"; +</signature> +)"; + +static constexpr base::Time kValidCertificateDate = + base::Time::FromSecondsSinceUnixEpoch(1740614400); std::string_view GetCertificate() { return std::string_view(std::begin(kSigXml) + 3615, @@ -281,6 +288,124 @@ EXPECT_FALSE(internal::ParseRecoveryKeyStoreSigXML("")); } +TEST_F(RecoveryKeyStoreCertificateTest, Internals_VerifySignatureChainSuccess) { + std::vector<std::string> intermediates{std::string(GetIntermediate1()), + std::string(GetIntermediate2())}; + std::shared_ptr<const bssl::ParsedCertificate> certificate = + internal::VerifySignatureChain(GetCertificate(), intermediates, + kValidCertificateDate); + EXPECT_TRUE(certificate); +} + +TEST_F(RecoveryKeyStoreCertificateTest, + Internals_VerifySignatureChainCertificateNotB64) { + std::vector<std::string> intermediates{std::string(GetIntermediate1()), + std::string(GetIntermediate2())}; + std::shared_ptr<const bssl::ParsedCertificate> certificate = + internal::VerifySignatureChain("not base 64", intermediates, + kValidCertificateDate); + EXPECT_FALSE(certificate); +} + +TEST_F(RecoveryKeyStoreCertificateTest, + Internals_VerifySignatureChainSkipsNotB64Intermediates) { + std::vector<std::string> intermediates{"not base 64", + std::string(GetIntermediate1()), + std::string(GetIntermediate2())}; + std::shared_ptr<const bssl::ParsedCertificate> certificate = + internal::VerifySignatureChain(GetCertificate(), intermediates, + kValidCertificateDate); + EXPECT_TRUE(certificate); +} + +TEST_F(RecoveryKeyStoreCertificateTest, Internals_VerifySignatureChainExpired) { + std::vector<std::string> intermediates{std::string(GetIntermediate1()), + std::string(GetIntermediate2())}; + std::shared_ptr<const bssl::ParsedCertificate> certificate = + internal::VerifySignatureChain(GetCertificate(), intermediates, + kValidCertificateDate + base::Days(10000)); + EXPECT_FALSE(certificate); +} + +TEST_F(RecoveryKeyStoreCertificateTest, + Internals_VerifySignatureChainNotYetValid) { + std::vector<std::string> intermediates = {std::string(GetIntermediate1()), + std::string(GetIntermediate2())}; + std::shared_ptr<const bssl::ParsedCertificate> certificate = + internal::VerifySignatureChain(GetCertificate(), intermediates, + kValidCertificateDate - base::Days(10000)); + EXPECT_FALSE(certificate); +} + +TEST_F(RecoveryKeyStoreCertificateTest, Internals_VerifySignature) { + std::vector<std::string> intermediates = {std::string(GetIntermediate1()), + std::string(GetIntermediate2())}; + auto certificate = internal::VerifySignatureChain( + GetCertificate(), intermediates, kValidCertificateDate); + ASSERT_TRUE(certificate); + EXPECT_TRUE(internal::VerifySignature(std::move(certificate), kCertXml, + GetSignature())); +} + +TEST_F(RecoveryKeyStoreCertificateTest, Internals_VerifySignatureNotValid) { + std::vector<std::string> intermediates = {std::string(GetIntermediate1()), + std::string(GetIntermediate2())}; + auto certificate = internal::VerifySignatureChain( + GetCertificate(), intermediates, kValidCertificateDate); + ASSERT_TRUE(certificate); + std::string invalid_signature(GetSignature()); + invalid_signature[0] = 'X'; + EXPECT_FALSE(internal::VerifySignature(std::move(certificate), kCertXml, + invalid_signature)); +} + +TEST_F(RecoveryKeyStoreCertificateTest, Internals_VerifySignatureNotBase64) { + std::vector<std::string> intermediates = {std::string(GetIntermediate1()), + std::string(GetIntermediate2())}; + auto certificate = internal::VerifySignatureChain( + GetCertificate(), intermediates, kValidCertificateDate); + ASSERT_TRUE(certificate); + EXPECT_FALSE(internal::VerifySignature(std::move(certificate), kCertXml, + "not base 64")); +} + +TEST_F(RecoveryKeyStoreCertificateTest, ParseSuccess) { + EXPECT_TRUE(RecoveryKeyStoreCertificate::Parse(kCertXml, kSigXml, + kValidCertificateDate)); +} + +TEST_F(RecoveryKeyStoreCertificateTest, Parse_BadCertXml) { + EXPECT_FALSE(RecoveryKeyStoreCertificate::Parse("not cert xml", kSigXml, + kValidCertificateDate)); +} + +TEST_F(RecoveryKeyStoreCertificateTest, Parse_BadSigXml) { + EXPECT_FALSE(RecoveryKeyStoreCertificate::Parse(kCertXml, "not sig xml", + kValidCertificateDate)); +} + +TEST_F(RecoveryKeyStoreCertificateTest, Parse_BadChain) { + static constexpr std::string_view kBadSigXml = + R"(<?xml version="1.0" encoding="UTF-8"?> +<signature> + <intermediates> + <cert>intermediate</cert> + </intermediates> + <certificate>certificate</certificate> + <value>signature</value> +</signature>)"; + EXPECT_FALSE(RecoveryKeyStoreCertificate::Parse(kCertXml, kBadSigXml, + kValidCertificateDate)); +} + +TEST_F(RecoveryKeyStoreCertificateTest, Parse_BadSignature) { + std::string sig_xml_bad_signature(kSigXml); + size_t signature_index = sig_xml_bad_signature.find(GetSignature()); + sig_xml_bad_signature[signature_index] = 'X'; + EXPECT_FALSE(RecoveryKeyStoreCertificate::Parse( + kCertXml, sig_xml_bad_signature, kValidCertificateDate)); +} + } // namespace } // namespace trusted_vault
diff --git a/components/viz/common/quads/aggregated_render_pass.cc b/components/viz/common/quads/aggregated_render_pass.cc index cdf029e..773cafb 100644 --- a/components/viz/common/quads/aggregated_render_pass.cc +++ b/components/viz/common/quads/aggregated_render_pass.cc
@@ -222,7 +222,6 @@ value->SetInteger("content_color_usage", base::to_underlying(content_color_usage)); - value->SetBoolean("is_color_conversion_pass", is_color_conversion_pass); value->SetBoolean("is_from_surface_root_pass", is_from_surface_root_pass); #if BUILDFLAG(IS_WIN) value->SetBoolean("will_backing_be_read_by_viz", will_backing_be_read_by_viz);
diff --git a/components/viz/common/quads/aggregated_render_pass.h b/components/viz/common/quads/aggregated_render_pass.h index 5bbd68b..6dba835 100644 --- a/components/viz/common/quads/aggregated_render_pass.h +++ b/components/viz/common/quads/aggregated_render_pass.h
@@ -101,9 +101,6 @@ // The type of color content present in this RenderPass. gfx::ContentColorUsage content_color_usage = gfx::ContentColorUsage::kSRGB; - // Indicates current RenderPass is a color conversion pass. - bool is_color_conversion_pass = false; - // |true| if this render pass, prior to aggregation, was the root pass of a // surface's resolved frame. // TODO(crbug.com/324460866): Used for partially delegated compositing.
diff --git a/components/viz/service/display/display.cc b/components/viz/service/display/display.cc index 2e95e6a..aa08f6d 100644 --- a/components/viz/service/display/display.cc +++ b/components/viz/service/display/display.cc
@@ -94,6 +94,46 @@ namespace { +#if !BUILDFLAG(IS_APPLE) +DBG_FLAG_FBOOL("delegated.fd.usage", usage_every_frame) + +void RecordFDUsageUMA() { + static uint64_t sReportUsageFrameCounter = 0; + sReportUsageFrameCounter++; + constexpr uint32_t kReportEveryNFrames = 60 * 60 * 5; + if (((sReportUsageFrameCounter % kReportEveryNFrames) != 0) && + !usage_every_frame()) { + return; + } + + base::TimeDelta delta_time_taken; + int fd_max; + int active_fd_count; + int rlim_cur; + + if (!GatherFDStats(&delta_time_taken, &fd_max, &active_fd_count, &rlim_cur)) { + return; + } + + static constexpr base::TimeDelta kHistogramMinTime = base::Microseconds(5); + static constexpr base::TimeDelta kHistogramMaxTime = base::Milliseconds(10); + static constexpr int kHistogramTimeBuckets = 50; + int percentage_usage_int = (active_fd_count * 100) / fd_max; + UMA_HISTOGRAM_PERCENTAGE("Viz.FileDescriptorTracking.PercentageUsed", + percentage_usage_int); + UMA_HISTOGRAM_COUNTS_100000("Viz.FileDescriptorTracking.NumActive", + active_fd_count); + UMA_HISTOGRAM_COUNTS_100000("Viz.FileDescriptorTracking.NumSoftMax", + rlim_cur); + UMA_HISTOGRAM_CUSTOM_MICROSECONDS_TIMES( + "Viz.FileDescriptorTracking.TimeToCompute", delta_time_taken, + kHistogramMinTime, kHistogramMaxTime, kHistogramTimeBuckets); + + DBG_LOG("delegated.fd.usage", "FD usage: %d / %d - time us: %f", + active_fd_count, fd_max, delta_time_taken.InMicrosecondsF()); +} +#endif + #if !BUILDFLAG(IS_MAC) constexpr base::TimeDelta kAllowedDeltaFromFuture = base::Milliseconds(16); #endif @@ -818,6 +858,10 @@ bool Display::DrawAndSwap(const DrawAndSwapParams& params) { TRACE_EVENT0("viz", "Display::DrawAndSwap"); VIZ_HIT_PATH("DrawAndSwap"); +#if !BUILDFLAG(IS_APPLE) + RecordFDUsageUMA(); +#endif + if (debug_settings_->show_aggregated_damage != aggregator_->HasFrameAnnotator()) { if (debug_settings_->show_aggregated_damage) {
diff --git a/components/viz/service/display/overlay_dc_unittest.cc b/components/viz/service/display/overlay_dc_unittest.cc index 5a53488..4cffb16 100644 --- a/components/viz/service/display/overlay_dc_unittest.cc +++ b/components/viz/service/display/overlay_dc_unittest.cc
@@ -3280,22 +3280,6 @@ EXPECT_THAT(result.candidates(), testing::IsEmpty()); } -// Check that delegated compositing fails when there is a color conversion pass. -TEST_F(OverlayProcessorWinDelegatedCompositingTest, HdrNotSupported) { - AggregatedRenderPassList pass_list; - - pass_list.push_back(CreateRenderPass(AggregatedRenderPassId{2})); - - auto pass = CreateRenderPass(); - pass->is_color_conversion_pass = true; - pass_list.push_back(std::move(pass)); - - damage_rect_ = pass_list.back()->damage_rect; - - auto result = TryProcessForDelegatedOverlays(pass_list); - result.ExpectDelegationFailure(); -} - // Check that delegated compositing fails when the root is being captured. TEST_F(OverlayProcessorWinDelegatedCompositingTest, CaptureNotSupported) { AggregatedRenderPassList pass_list;
diff --git a/components/viz/service/display/overlay_processor_delegated.cc b/components/viz/service/display/overlay_processor_delegated.cc index b55f1db..350f7ca 100644 --- a/components/viz/service/display/overlay_processor_delegated.cc +++ b/components/viz/service/display/overlay_processor_delegated.cc
@@ -28,44 +28,6 @@ #include "ui/ozone/public/ozone_platform.h" namespace { -DBG_FLAG_FBOOL("delegated.fd.usage", usage_every_frame) - -void RecordFDUsageUMA() { - static uint64_t sReportUsageFrameCounter = 0; - sReportUsageFrameCounter++; - constexpr uint32_t kReportEveryNFrames = 60 * 60 * 5; - if (((sReportUsageFrameCounter % kReportEveryNFrames) != 0) && - !usage_every_frame()) { - return; - } - - base::TimeDelta delta_time_taken; - int fd_max; - int active_fd_count; - int rlim_cur; - - if (!viz::GatherFDStats(&delta_time_taken, &fd_max, &active_fd_count, - &rlim_cur)) - return; - - static constexpr base::TimeDelta kHistogramMinTime = base::Microseconds(5); - static constexpr base::TimeDelta kHistogramMaxTime = base::Milliseconds(10); - static constexpr int kHistogramTimeBuckets = 50; - int percentage_usage_int = (active_fd_count * 100) / fd_max; - UMA_HISTOGRAM_PERCENTAGE("Viz.FileDescriptorTracking.PercentageUsed", - percentage_usage_int); - UMA_HISTOGRAM_COUNTS_100000("Viz.FileDescriptorTracking.NumActive", - active_fd_count); - UMA_HISTOGRAM_COUNTS_100000("Viz.FileDescriptorTracking.NumSoftMax", - rlim_cur); - UMA_HISTOGRAM_CUSTOM_MICROSECONDS_TIMES( - "Viz.FileDescriptorTracking.TimeToCompute", delta_time_taken, - kHistogramMinTime, kHistogramMaxTime, kHistogramTimeBuckets); - - DBG_LOG("delegated.fd.usage", "FD usage: %d / %d - time us: %f", - active_fd_count, fd_max, delta_time_taken.InMicrosecondsF()); -} - // Block delegation if there has been a copy request in the last 3 frames. constexpr int kCopyRequestBlockFrames = 3; @@ -231,9 +193,6 @@ std::vector<gfx::Rect>* content_bounds) { DCHECK(candidates->empty()); bool success = false; -#if !BUILDFLAG(IS_APPLE) - RecordFDUsageUMA(); -#endif DebugLogBeforeDelegation(*damage_rect, surface_damage_rect_list);
diff --git a/components/viz/service/display/overlay_processor_win.cc b/components/viz/service/display/overlay_processor_win.cc index 20d59009..912c449 100644 --- a/components/viz/service/display/overlay_processor_win.cc +++ b/components/viz/service/display/overlay_processor_win.cc
@@ -332,10 +332,6 @@ CandidateList* candidates, gfx::Rect* root_damage_rect) { auto* root_render_pass = render_passes->back().get(); - if (render_passes->back()->is_color_conversion_pass) { - DCHECK_GT(render_passes->size(), 1u); - root_render_pass = (*render_passes)[render_passes->size() - 2].get(); - } DCLayerOverlayProcessor::RenderPassOverlayDataMap render_pass_overlay_data_map; @@ -506,12 +502,6 @@ return base::unexpected(DelegationStatus::kCompositedTooManyQuads); } - if (root_render_pass->is_color_conversion_pass) { - // We don't expect to handle a color conversion pass (e.g. for frames with - // HDR content) with delegated compositing. See: crbug.com/41497086 - return base::unexpected(DelegationStatus::kCompositedOther); - } - DelegatedCompositingResult result; result.candidates.reserve(root_render_pass->quad_list.size());
diff --git a/components/viz/service/display/surface_aggregator.cc b/components/viz/service/display/surface_aggregator.cc index 52a68aad..eef09f8f 100644 --- a/components/viz/service/display/surface_aggregator.cc +++ b/components/viz/service/display/surface_aggregator.cc
@@ -1233,7 +1233,6 @@ AddRenderPassHelper(readback_render_pass_id_, output_rect, root_render_pass->damage_rect, root_content_color_usage_, has_transparent_background, - /*pass_is_color_conversion_pass=*/false, /*quad_state_to_target_transform=*/gfx::Transform(), /*quad_state_contents_opaque=*/false, SkBlendMode::kSrcOver, root_render_pass->id); @@ -1266,8 +1265,7 @@ cc::MathUtil::MapEnclosedRectWith2dAxisAlignedTransform( root_surface_transform_, root_render_pass->damage_rect), root_render_pass->content_color_usage, - root_render_pass->has_transparent_background, - /*pass_is_color_conversion_pass=*/false, root_surface_transform_, + root_render_pass->has_transparent_background, root_surface_transform_, are_contents_opaque, SkBlendMode::kSrcOver, root_render_pass->id); } @@ -1277,7 +1275,6 @@ const gfx::Rect& render_pass_damage_rect, gfx::ContentColorUsage pass_color_usage, bool pass_has_transparent_background, - bool pass_is_color_conversion_pass, const gfx::Transform& quad_state_to_target_transform, bool quad_state_contents_opaque, SkBlendMode quad_state_blend_mode, @@ -1294,7 +1291,6 @@ /*cache_render_pass=*/false, /*has_damage_from_contributing_content=*/false, /*generate_mipmap=*/false); - render_pass->is_color_conversion_pass = pass_is_color_conversion_pass; auto* shared_quad_state = render_pass->CreateAndAppendSharedQuadState(); shared_quad_state->SetAll(
diff --git a/components/viz/service/display/surface_aggregator.h b/components/viz/service/display/surface_aggregator.h index 8717589d..2644b4a 100644 --- a/components/viz/service/display/surface_aggregator.h +++ b/components/viz/service/display/surface_aggregator.h
@@ -268,7 +268,6 @@ const gfx::Rect& render_pass_damage_rect, gfx::ContentColorUsage pass_color_usage, bool pass_has_transparent_background, - bool pass_is_color_conversion_pass, const gfx::Transform& quad_state_to_target_transform, bool quad_state_contents_opaque, SkBlendMode quad_state_blend_mode,
diff --git a/components/viz/service/frame_sinks/frame_sink_manager_impl.cc b/components/viz/service/frame_sinks/frame_sink_manager_impl.cc index b97e325..330092f 100644 --- a/components/viz/service/frame_sinks/frame_sink_manager_impl.cc +++ b/components/viz/service/frame_sinks/frame_sink_manager_impl.cc
@@ -183,6 +183,8 @@ void FrameSinkManagerImpl::InvalidateFrameSinkId( const FrameSinkId& frame_sink_id) { + TRACE_EVENT("viz", "FrameSinkManagerImpl::InvalidateFrameSinkId", + "frame_sink_id", frame_sink_id); DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); surface_manager_.InvalidateFrameSinkId(frame_sink_id); @@ -352,6 +354,9 @@ void FrameSinkManagerImpl::UnregisterFrameSinkHierarchy( const FrameSinkId& parent_frame_sink_id, const FrameSinkId& child_frame_sink_id) { + TRACE_EVENT("viz", "FrameSinkManagerImpl::UnregisterFrameSinkHierarchy", + "parent_frame_sink_id", parent_frame_sink_id, + "child_frame_sink_id", child_frame_sink_id); // Deliberately do not check validity of either parent or child FrameSinkId // here. They were valid during the registration, so were valid at some point // in time. This makes it possible to invalidate parent and child FrameSinkIds @@ -614,6 +619,9 @@ void FrameSinkManagerImpl::UnregisterCompositorFrameSinkSupport( const FrameSinkId& frame_sink_id) { + TRACE_EVENT("viz", + "FrameSinkManagerImpl::UnregisterCompositorFrameSinkSupport", + "frame_sink_id", frame_sink_id); DCHECK(base::Contains(support_map_, frame_sink_id)); for (auto& observer : observer_list_) @@ -674,10 +682,14 @@ mapping.source = source; auto iter = support_map_.find(frame_sink_id); if (iter != support_map_.end()) { - iter->second->SetBeginFrameSource(source); + // Updates the InputManager(or FlingScheduler) of BeginFrameSource changes + // before CompositorFrameSinkSupport since it is 1:1 with + // RenderInputRouter (for layer tree frame sinks associated CFSS) and + // updating it earlier may cause UAF bugs. if (GetInputManager()) { GetInputManager()->SetBeginFrameSource(frame_sink_id, source); } + iter->second->SetBeginFrameSource(source); } } @@ -700,10 +712,14 @@ mapping.source = nullptr; auto client_iter = support_map_.find(frame_sink_id); if (client_iter != support_map_.end()) { - client_iter->second->SetBeginFrameSource(nullptr); + // Updates the InputManager(or FlingScheduler) of BeginFrameSource changes + // before CompositorFrameSinkSupport since it is 1:1 with + // RenderInputRouter (for layer tree frame sinks associated CFSS) and + // updating it earlier may cause UAF bugs. if (GetInputManager()) { GetInputManager()->SetBeginFrameSource(frame_sink_id, nullptr); } + client_iter->second->SetBeginFrameSource(nullptr); } }
diff --git a/components/viz/service/frame_sinks/video_capture/frame_sink_video_capturer_impl_unittest.cc b/components/viz/service/frame_sinks/video_capture/frame_sink_video_capturer_impl_unittest.cc index eec192c..d46a6153 100644 --- a/components/viz/service/frame_sinks/video_capture/frame_sink_video_capturer_impl_unittest.cc +++ b/components/viz/service/frame_sinks/video_capture/frame_sink_video_capturer_impl_unittest.cc
@@ -46,7 +46,6 @@ #include "media/base/video_util.h" #include "media/capture/mojom/video_capture_buffer.mojom.h" #include "media/capture/mojom/video_capture_types.mojom.h" -#include "media/video/fake_gpu_memory_buffer.h" #include "media/video/renderable_gpu_memory_buffer_video_frame_pool.h" #include "mojo/public/cpp/bindings/pending_receiver.h" #include "mojo/public/cpp/bindings/pending_remote.h"
diff --git a/components/viz/service/input/input_manager.cc b/components/viz/service/input/input_manager.cc index 2bc6f1e..e3d792b 100644 --- a/components/viz/service/input/input_manager.cc +++ b/components/viz/service/input/input_manager.cc
@@ -651,6 +651,8 @@ void InputManager::SetBeginFrameSource(const FrameSinkId& frame_sink_id, BeginFrameSource* begin_frame_source) { + TRACE_EVENT("input", "InputManager::SetBeginFrameSource", "frame_sink_id", + frame_sink_id); // Return early if |frame_sink_id| is associated with non layer tree frame // sink. auto itr = rir_map_.find(frame_sink_id);
diff --git a/components/webauthn/ios/BUILD.gn b/components/webauthn/ios/BUILD.gn index 72f7316..87d38e73 100644 --- a/components/webauthn/ios/BUILD.gn +++ b/components/webauthn/ios/BUILD.gn
@@ -9,12 +9,16 @@ deps = [ ":passkey_controller_js", "//base", + "//components/webauthn/core/browser:passkey_model", + "//ios/web/public", "//ios/web/public/js_messaging", ] sources = [ "passkey_java_script_feature.h", "passkey_java_script_feature.mm", + "passkey_tab_helper.h", + "passkey_tab_helper.mm", ] } @@ -37,6 +41,20 @@ public_deps = [ "//base" ] } +source_set("unit_tests") { + testonly = true + sources = [ "passkey_tab_helper_unittest.mm" ] + deps = [ + ":ios", + ":js_tests", + "//base/test:test_support", + "//components/webauthn/core/browser:passkey_model", + "//components/webauthn/core/browser:test_support", + "//ios/web/public/test", + "//testing/gtest", + ] +} + source_set("js_tests") { testonly = true sources = [ "passkey_controller_javascript_test.mm" ]
diff --git a/components/webauthn/ios/passkey_controller_javascript_test.mm b/components/webauthn/ios/passkey_controller_javascript_test.mm index 82df2d0e..25ca8aee5 100644 --- a/components/webauthn/ios/passkey_controller_javascript_test.mm +++ b/components/webauthn/ios/passkey_controller_javascript_test.mm
@@ -67,6 +67,8 @@ // APIs still working with shim injected. It is infeasible in JS tests since // navigator.credentials APIs for public key credentials require some user // interaction with system UI. +// TODO(crbug.com/396929469): Similarly to previous TODO, if feasible, add tests +// for the get events logged on resolved promises. class PasskeyControllerJavaScriptTest : public web::JavascriptTest { protected: PasskeyControllerJavaScriptTest()
diff --git a/components/webauthn/ios/passkey_java_script_feature.mm b/components/webauthn/ios/passkey_java_script_feature.mm index fb25865b..e0deb21 100644 --- a/components/webauthn/ios/passkey_java_script_feature.mm +++ b/components/webauthn/ios/passkey_java_script_feature.mm
@@ -4,11 +4,9 @@ #import "components/webauthn/ios/passkey_java_script_feature.h" -#import <optional> - -#import "base/metrics/histogram_functions.h" #import "base/no_destructor.h" #import "base/values.h" +#import "components/webauthn/ios/passkey_tab_helper.h" #import "ios/web/public/js_messaging/java_script_feature_util.h" #import "ios/web/public/js_messaging/script_message.h" @@ -17,24 +15,6 @@ constexpr char kScriptName[] = "passkey_controller"; constexpr char kHandlerName[] = "PasskeyInteractionHandler"; -// These values are logged to UMA. Entries should not be renumbered and -// numeric values should never be reused. -// -// LINT.IfChange -enum class WebAuthenticationIOSContentAreaEvent { - kGetRequested, - kCreateRequested, - kMaxValue = kCreateRequested, -}; -// LINT.ThenChange(//tools/metrics/histograms/metadata/webauthn/enums.xml) - -// Logs metrics indicating that an event occurred, with the event type -// determined by the given string. -void LogEvent(WebAuthenticationIOSContentAreaEvent event) { - base::UmaHistogramEnumeration("WebAuthentication.IOS.ContentAreaEvent", - event); -} - } // namespace // static @@ -68,6 +48,13 @@ void PasskeyJavaScriptFeature::ScriptMessageReceived( web::WebState* web_state, const web::ScriptMessage& message) { + // This message is sent whenever a navigator.credentials get() or create() is + // called for a WebAuthn credential. + // Expected argument: + // event: (string) Describes a type of event. + // + // For some events there are more expected arguments described below. + base::Value* body = message.body(); if (!body || !body->is_dict()) { return; @@ -79,11 +66,27 @@ return; } - if (*event == "getRequested") { - LogEvent(WebAuthenticationIOSContentAreaEvent::kGetRequested); - } else if (*event == "createRequested") { - LogEvent(WebAuthenticationIOSContentAreaEvent::kCreateRequested); + PasskeyTabHelper* passkey_tab_helper = + PasskeyTabHelper::FromWebState(web_state); + CHECK(passkey_tab_helper); + + // For those events there are no more expected arguments. + if (*event == "getRequested" || *event == "createRequested") { + passkey_tab_helper->LogEventFromString(*event); + return; } - // TODO(crbug.com/369629469): Log other types of events. + // Expected arguments for "getResolved" event: + // credential_id: (string) base64url encoded identifer of the credential. + // rp_id: (string) The relying party's identifier. + if (*event == "getResolved") { + const std::string* credential_id = dict.FindString("credential_id"); + const std::string* rp_id = dict.FindString("rp_id"); + if (credential_id && !credential_id->empty() && rp_id && !rp_id->empty()) { + passkey_tab_helper->HandleGetResolvedEvent(*credential_id, *rp_id); + } + return; + } + + // TODO(crbug.com/369629469): Handle other types of events. }
diff --git a/components/webauthn/ios/passkey_tab_helper.h b/components/webauthn/ios/passkey_tab_helper.h new file mode 100644 index 0000000..816b47d --- /dev/null +++ b/components/webauthn/ios/passkey_tab_helper.h
@@ -0,0 +1,44 @@ +// 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_WEBAUTHN_IOS_PASSKEY_TAB_HELPER_H_ +#define COMPONENTS_WEBAUTHN_IOS_PASSKEY_TAB_HELPER_H_ + +#import "ios/web/public/web_state_user_data.h" + +namespace webauthn { +class PasskeyModel; +} // namespace webauthn + +// Handles script messages received from PasskeyJavaScriptFeature related to +// interactions with WebAuthn credentials and for now logs appropriate metrics. +class PasskeyTabHelper : public web::WebStateUserData<PasskeyTabHelper> { + public: + PasskeyTabHelper(const PasskeyTabHelper&) = delete; + PasskeyTabHelper& operator=(const PasskeyTabHelper&) = delete; + + ~PasskeyTabHelper() override; + + // Logs metric indicating that an event occurred, with the event type + // determined by the given string. + void LogEventFromString(const std::string& event); + + // Checks whether a navigator.credentials.get() call that returned a WebAuthn + // credential was resolved by Google Password Manager as the authenticator by + // checking its presence in `passkey_model_` and logs it. + void HandleGetResolvedEvent( + const std::string& credential_id_base64url_encoded, + const std::string& rp_id); + + private: + friend class web::WebStateUserData<PasskeyTabHelper>; + + explicit PasskeyTabHelper(web::WebState* web_state, + webauthn::PasskeyModel* passkey_model); + + // Provides access to stored WebAuthn credentials. + raw_ptr<webauthn::PasskeyModel> passkey_model_; +}; + +#endif // COMPONENTS_WEBAUTHN_IOS_PASSKEY_TAB_HELPER_H_
diff --git a/components/webauthn/ios/passkey_tab_helper.mm b/components/webauthn/ios/passkey_tab_helper.mm new file mode 100644 index 0000000..270b3303 --- /dev/null +++ b/components/webauthn/ios/passkey_tab_helper.mm
@@ -0,0 +1,73 @@ +// Copyright 2025 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#import "components/webauthn/ios/passkey_tab_helper.h" + +#import "base/base64.h" +#import "base/base64url.h" +#import "base/metrics/histogram_functions.h" +#import "base/notreached.h" +#import "components/webauthn/core/browser/passkey_model.h" +#import "ios/web/public/js_messaging/script_message.h" +#import "ios/web/public/web_state.h" + +namespace { + +// These values are logged to UMA. Entries should not be renumbered and +// numeric values should never be reused. +// +// LINT.IfChange +enum class WebAuthenticationIOSContentAreaEvent { + kGetRequested, + kCreateRequested, + kGetResolvedGpm, + kGetResolvedNonGpm, + kMaxValue = kGetResolvedNonGpm, +}; +// LINT.ThenChange(//tools/metrics/histograms/metadata/webauthn/enums.xml) + +// Logs a metric indicating that an `event` occurred. +void LogEvent(WebAuthenticationIOSContentAreaEvent event) { + base::UmaHistogramEnumeration("WebAuthentication.IOS.ContentAreaEvent", + event); +} + +} // namespace + +PasskeyTabHelper::~PasskeyTabHelper() = default; + +void PasskeyTabHelper::LogEventFromString(const std::string& event) { + if (event == "getRequested") { + LogEvent(WebAuthenticationIOSContentAreaEvent::kGetRequested); + } else if (event == "createRequested") { + LogEvent(WebAuthenticationIOSContentAreaEvent::kCreateRequested); + } else { + NOTREACHED(); + } +} + +void PasskeyTabHelper::HandleGetResolvedEvent( + const std::string& credential_id_base64url_encoded, + const std::string& rp_id) { + std::string credential_id; + if (!base::Base64UrlDecode(credential_id_base64url_encoded, + base::Base64UrlDecodePolicy::IGNORE_PADDING, + &credential_id)) { + return; + } + + if (passkey_model_->GetPasskeyByCredentialId(rp_id, credential_id) + .has_value()) { + LogEvent(WebAuthenticationIOSContentAreaEvent::kGetResolvedGpm); + } else { + LogEvent(WebAuthenticationIOSContentAreaEvent::kGetResolvedNonGpm); + } +} + +PasskeyTabHelper::PasskeyTabHelper(web::WebState* web_state, + webauthn::PasskeyModel* passkey_model) + : passkey_model_(passkey_model) { + CHECK(web_state); + CHECK(passkey_model_); +}
diff --git a/components/webauthn/ios/passkey_tab_helper_unittest.mm b/components/webauthn/ios/passkey_tab_helper_unittest.mm new file mode 100644 index 0000000..33621ac --- /dev/null +++ b/components/webauthn/ios/passkey_tab_helper_unittest.mm
@@ -0,0 +1,92 @@ +// Copyright 2025 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#import "components/webauthn/ios/passkey_tab_helper.h" + +#import "base/base64url.h" +#import "base/test/metrics/histogram_tester.h" +#import "components/webauthn/core/browser/passkey_model.h" +#import "components/webauthn/core/browser/test_passkey_model.h" +#import "ios/web/public/test/fakes/fake_web_state.h" +#import "ios/web/public/test/web_task_environment.h" +#import "testing/platform_test.h" + +namespace { + +constexpr char kCredentialId[] = "credential_id"; +constexpr char kRpId[] = "example.com"; + +constexpr char kWebAuthenticationIOSContentAreaEventHistogram[] = + "WebAuthentication.IOS.ContentAreaEvent"; + +} // namespace + +class PasskeyTabHelperTest : public PlatformTest { + public: + PasskeyTabHelperTest() { + PasskeyTabHelper::CreateForWebState(&fake_web_state_, passkey_model_.get()); + } + + protected: + PasskeyTabHelper* passkey_tab_helper() { + return PasskeyTabHelper::FromWebState(&fake_web_state_); + } + + web::WebTaskEnvironment task_environment_; + base::HistogramTester histogram_tester_; + std::unique_ptr<webauthn::PasskeyModel> passkey_model_ = + std::make_unique<webauthn::TestPasskeyModel>(); + web::FakeWebState fake_web_state_; +}; + +TEST_F(PasskeyTabHelperTest, LogsEventFromGetRequestedString) { + passkey_tab_helper()->LogEventFromString("getRequested"); + + constexpr int kGetRequestedBucket = 0; + histogram_tester_.ExpectUniqueSample( + kWebAuthenticationIOSContentAreaEventHistogram, kGetRequestedBucket, + /*count=*/1); +} + +TEST_F(PasskeyTabHelperTest, LogsEventFromCreateRequestedString) { + passkey_tab_helper()->LogEventFromString("createRequested"); + + constexpr int kCreateRequestedBucket = 1; + histogram_tester_.ExpectUniqueSample( + kWebAuthenticationIOSContentAreaEventHistogram, kCreateRequestedBucket, + /*count=*/1); +} + +TEST_F(PasskeyTabHelperTest, LogsGetResolvedEventGpmPasskey) { + sync_pb::WebauthnCredentialSpecifics passkey; + passkey.set_credential_id(kCredentialId); + passkey.set_rp_id(kRpId); + passkey_model_->AddNewPasskeyForTesting(std::move(passkey)); + + std::string credential_id_base64url_encoded; + base::Base64UrlEncode(kCredentialId, + base::Base64UrlEncodePolicy::INCLUDE_PADDING, + &credential_id_base64url_encoded); + passkey_tab_helper()->HandleGetResolvedEvent(credential_id_base64url_encoded, + kRpId); + + constexpr int kGetResolvedGpmBucket = 2; + histogram_tester_.ExpectUniqueSample( + kWebAuthenticationIOSContentAreaEventHistogram, kGetResolvedGpmBucket, + /*count=*/1); +} + +TEST_F(PasskeyTabHelperTest, LogsGetResolvedEventNonGpmPasskey) { + std::string credential_id_base64url_encoded; + base::Base64UrlEncode(kCredentialId, + base::Base64UrlEncodePolicy::INCLUDE_PADDING, + &credential_id_base64url_encoded); + passkey_tab_helper()->HandleGetResolvedEvent(credential_id_base64url_encoded, + kRpId); + + constexpr int kGetResolvedNonGpmBucket = 3; + histogram_tester_.ExpectUniqueSample( + kWebAuthenticationIOSContentAreaEventHistogram, kGetResolvedNonGpmBucket, + /*count=*/1); +}
diff --git a/components/webauthn/ios/resources/passkey_controller.ts b/components/webauthn/ios/resources/passkey_controller.ts index 7834183..70312da33 100644 --- a/components/webauthn/ios/resources/passkey_controller.ts +++ b/components/webauthn/ios/resources/passkey_controller.ts
@@ -22,14 +22,29 @@ * Chromium-specific implementation of CredentialsContainer. */ const credentialsContainer: CredentialsContainer = { - get: function(options?: CredentialRequestOptions|undefined): - Promise<Credential|null> { - // Only process WebAuthn requests. - if (options?.publicKey) { - sendWebKitMessage(HANDLER_NAME, {'event': 'getRequested'}); - } - return cachedNavigatorCredentials.get(options); - }, + get: function(options?: CredentialRequestOptions): Promise<Credential|null> { + // Only process WebAuthn requests. + if (!options?.publicKey) { + return cachedNavigatorCredentials.get(options); + } + + sendWebKitMessage(HANDLER_NAME, {'event': 'getRequested'}); + + return cachedNavigatorCredentials.get(options).then((credential) => { + if (credential && credential instanceof PublicKeyCredential) { + // rpId is an optional member of publicKey. Default value (caller's + // origin domain) should be used if it is not specified + // (https://w3c.github.io/webauthn/#dom-publickeycredentialrequestoptions-rpid). + const rpId = options!.publicKey!.rpId ?? document.location.host; + sendWebKitMessage(HANDLER_NAME, { + 'event': 'getResolved', + 'credential_id': credential.id, + 'rp_id': rpId, + }); + } + return credential; + }); + }, create: function(options?: CredentialCreationOptions|undefined): Promise<Credential|null> { // Only process WebAuthn requests.
diff --git a/components/webxr_strings.grdp b/components/webxr_strings.grdp index a4f2c030..6757ae78 100644 --- a/components/webxr_strings.grdp +++ b/components/webxr_strings.grdp
@@ -13,7 +13,7 @@ View augmented reality content </message> </if> - <if expr="enable_vr"> + <if expr="enable_cardboard"> <message name="IDS_CARDBOARD_PRODUCT_SAFETY" desc="Short text shown on the settings menu to let the user open the Cardboard product safety page.[CHAR_LIMIT=30]" formatter_data="android_java"> Product safety </message>
diff --git a/content/browser/attribution_reporting/attribution_data_host_manager_impl_unittest.cc b/content/browser/attribution_reporting/attribution_data_host_manager_impl_unittest.cc index 8146600..379ff312 100644 --- a/content/browser/attribution_reporting/attribution_data_host_manager_impl_unittest.cc +++ b/content/browser/attribution_reporting/attribution_data_host_manager_impl_unittest.cc
@@ -2,11 +2,6 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifdef UNSAFE_BUFFERS_BUILD -// TODO(crbug.com/390223051): Remove C-library calls to fix the errors. -#pragma allow_unsafe_libc_calls -#endif - #include "content/browser/attribution_reporting/attribution_data_host_manager_impl.h" #include <stddef.h>
diff --git a/content/browser/attribution_reporting/attribution_storage_sql.cc b/content/browser/attribution_reporting/attribution_storage_sql.cc index 3ae4de7..2c9d755d 100644 --- a/content/browser/attribution_reporting/attribution_storage_sql.cc +++ b/content/browser/attribution_reporting/attribution_storage_sql.cc
@@ -2214,9 +2214,6 @@ // // |source_id| uses AUTOINCREMENT to ensure that IDs aren't reused over // the lifetime of the DB. - // - // TODO(linnan): Read and update |num_aggregatable_debug_reports| when - // creating an aggregatable debug report for the source. static constexpr char kImpressionTableSql[] = "CREATE TABLE sources(" "source_id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,"
diff --git a/content/browser/devtools/devtools_http_handler.cc b/content/browser/devtools/devtools_http_handler.cc index a28237b..9bbf031 100644 --- a/content/browser/devtools/devtools_http_handler.cc +++ b/content/browser/devtools/devtools_http_handler.cc
@@ -63,7 +63,7 @@ #include "base/android/build_info.h" #endif -#if !BUILDFLAG(IS_ANDROID) && !BUILDFLAG(IS_FUCHSIA) && !BUILDFLAG(IS_IOS) +#if BUILDFLAG(ENABLE_DEVTOOLS_FRONTEND) extern const int kCcompressedProtocolJSON; #endif @@ -694,9 +694,7 @@ } void DevToolsHttpHandler::DecompressAndSendJsonProtocol(int connection_id) { -#if BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_FUCHSIA) || BUILDFLAG(IS_IOS) - NOTREACHED(); -#else +#if BUILDFLAG(ENABLE_DEVTOOLS_FRONTEND) scoped_refptr<base::RefCountedMemory> bytes = GetContentClient()->GetDataResourceBytes(kCcompressedProtocolJSON); CHECK(bytes) << "Could not load protocol"; @@ -709,7 +707,9 @@ FROM_HERE, base::BindOnce(&ServerWrapper::SendResponse, base::Unretained(server_wrapper_.get()), connection_id, response)); -#endif // BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_FUCHSIA) || BUILDFLAG(IS_IOS) +#else + NOTREACHED(); +#endif // BUILDFLAG(ENABLE_DEVTOOLS_FRONTEND) } void DevToolsHttpHandler::RespondToJsonList(int connection_id, @@ -741,12 +741,12 @@ void DevToolsHttpHandler::OnFrontendResourceRequest( int connection_id, const std::string& path) { -#if BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_IOS) || BUILDFLAG(IS_FUCHSIA) - Send404(connection_id); -#else +#if BUILDFLAG(ENABLE_DEVTOOLS_FRONTEND) Send200(connection_id, content::DevToolsFrontendHost::GetFrontendResource(path), GetMimeType(path)); +#else + Send404(connection_id); #endif }
diff --git a/content/browser/devtools/protocol/storage_handler.cc b/content/browser/devtools/protocol/storage_handler.cc index 7c0fc74..b88d721a2 100644 --- a/content/browser/devtools/protocol/storage_handler.cc +++ b/content/browser/devtools/protocol/storage_handler.cc
@@ -1460,7 +1460,7 @@ return frame_host_ ? frame_host_->GetGlobalId() : GlobalRenderFrameHostId(); } -bool StorageHandler::ShouldReceiveAllReports() const { +bool StorageHandler::ShouldReceiveAllSharedStorageReports() const { DCHECK_CURRENTLY_ON(BrowserThread::UI); return false; } @@ -1658,11 +1658,12 @@ std::move(protocol_params)); } -void StorageHandler::OnUrnUuidGenerated(const GURL& urn_uuid) {} -void StorageHandler::OnConfigPopulated( +void StorageHandler::OnSharedStorageSelectUrlUrnUuidGenerated( + const GURL& urn_uuid) {} +void StorageHandler::OnSharedStorageSelectUrlConfigPopulated( const std::optional<FencedFrameConfig>& config) {} -void StorageHandler::OnWorkletOperationExecutionFinished( +void StorageHandler::OnSharedStorageWorkletOperationExecutionFinished( base::Time finished_time, base::TimeDelta execution_time, SharedStorageRuntimeManager::SharedStorageObserverInterface::AccessMethod
diff --git a/content/browser/devtools/protocol/storage_handler.h b/content/browser/devtools/protocol/storage_handler.h index 5241f91..e70f76f 100644 --- a/content/browser/devtools/protocol/storage_handler.h +++ b/content/browser/devtools/protocol/storage_handler.h
@@ -222,9 +222,9 @@ bool is_debug_report, const SendResult&) override; - // SharedStorageObserverInterface + // content::SharedStorageRuntimeManager::SharedStorageObserverInterface GlobalRenderFrameHostId AssociatedFrameHostId() const override; - bool ShouldReceiveAllReports() const override; + bool ShouldReceiveAllSharedStorageReports() const override; void OnSharedStorageAccessed( base::Time access_time, blink::SharedStorageAccessScope scope, @@ -233,10 +233,10 @@ GlobalRenderFrameHostId main_frame_id, const std::string& owner_origin, const SharedStorageEventParams& params) override; - void OnUrnUuidGenerated(const GURL& urn_uuid) override; - void OnConfigPopulated( + void OnSharedStorageSelectUrlUrnUuidGenerated(const GURL& urn_uuid) override; + void OnSharedStorageSelectUrlConfigPopulated( const std::optional<FencedFrameConfig>& config) override; - void OnWorkletOperationExecutionFinished( + void OnSharedStorageWorkletOperationExecutionFinished( base::Time finished_time, base::TimeDelta execution_time, SharedStorageRuntimeManager::SharedStorageObserverInterface::AccessMethod
diff --git a/content/browser/renderer_host/render_process_host_impl.cc b/content/browser/renderer_host/render_process_host_impl.cc index 3b2c512..2dcb080 100644 --- a/content/browser/renderer_host/render_process_host_impl.cc +++ b/content/browser/renderer_host/render_process_host_impl.cc
@@ -3461,7 +3461,6 @@ switches::kDisableSkiaRuntimeOpts, switches::kDisableSpeechAPI, switches::kDisableThreadedCompositing, - switches::kDisableTouchDragDrop, switches::kDisableV8IdleTasks, switches::kDisableVideoCaptureUseGpuMemoryBuffer, switches::kDisableWebGLImageChromium, @@ -3481,7 +3480,6 @@ switches::kEnablePluginPlaceholderTesting, switches::kEnablePreciseMemoryInfo, switches::kEnableSkiaBenchmarking, - switches::kEnableTouchDragDrop, switches::kEnableUnsafeWebGPU, switches::kEnableViewport, switches::kEnableVtune,
diff --git a/content/browser/shared_storage/shared_storage_runtime_manager.cc b/content/browser/shared_storage/shared_storage_runtime_manager.cc index a55dd7c..7e33728 100644 --- a/content/browser/shared_storage/shared_storage_runtime_manager.cc +++ b/content/browser/shared_storage/shared_storage_runtime_manager.cc
@@ -22,7 +22,7 @@ // that global render frame host ID matches the main frame ID passed as a // parameter of the report (and hence the observer is attached to the relevant // main render frame host). - return observer.ShouldReceiveAllReports() || + return observer.ShouldReceiveAllSharedStorageReports() || (observer.AssociatedFrameHostId() && observer.AssociatedFrameHostId() == main_frame_id); } @@ -169,9 +169,9 @@ // "finish" time/report time as part of the DevTools notification. Note, // however, that there may be a discrepancy between `execution_time` and // `finished_time - start-time`. - observer.OnWorkletOperationExecutionFinished(now, execution_time, method, - operation_id, worklet_id, - main_frame_id, owner_origin); + observer.OnSharedStorageWorkletOperationExecutionFinished( + now, execution_time, method, operation_id, worklet_id, main_frame_id, + owner_origin); } } @@ -204,14 +204,14 @@ void SharedStorageRuntimeManager::NotifyUrnUuidGenerated(const GURL& urn_uuid) { for (SharedStorageObserverInterface& observer : observers_) { - observer.OnUrnUuidGenerated(urn_uuid); + observer.OnSharedStorageSelectUrlUrnUuidGenerated(urn_uuid); } } void SharedStorageRuntimeManager::NotifyConfigPopulated( const std::optional<FencedFrameConfig>& config) { for (SharedStorageObserverInterface& observer : observers_) { - observer.OnConfigPopulated(config); + observer.OnSharedStorageSelectUrlConfigPopulated(config); } }
diff --git a/content/browser/shared_storage/shared_storage_runtime_manager.h b/content/browser/shared_storage/shared_storage_runtime_manager.h index e01171f4..cfa9b37 100644 --- a/content/browser/shared_storage/shared_storage_runtime_manager.h +++ b/content/browser/shared_storage/shared_storage_runtime_manager.h
@@ -62,7 +62,7 @@ virtual GlobalRenderFrameHostId AssociatedFrameHostId() const = 0; - virtual bool ShouldReceiveAllReports() const = 0; + virtual bool ShouldReceiveAllSharedStorageReports() const = 0; virtual void OnSharedStorageAccessed( base::Time access_time, @@ -72,12 +72,13 @@ const std::string& owner_origin, const SharedStorageEventParams& params) = 0; - virtual void OnUrnUuidGenerated(const GURL& urn_uuid) = 0; + virtual void OnSharedStorageSelectUrlUrnUuidGenerated( + const GURL& urn_uuid) = 0; - virtual void OnConfigPopulated( + virtual void OnSharedStorageSelectUrlConfigPopulated( const std::optional<FencedFrameConfig>& config) = 0; - virtual void OnWorkletOperationExecutionFinished( + virtual void OnSharedStorageWorkletOperationExecutionFinished( base::Time finished_time, base::TimeDelta execution_time, AccessMethod method,
diff --git a/content/browser/shared_storage/test_shared_storage_observer.cc b/content/browser/shared_storage/test_shared_storage_observer.cc index 8477b22..9a00d180 100644 --- a/content/browser/shared_storage/test_shared_storage_observer.cc +++ b/content/browser/shared_storage/test_shared_storage_observer.cc
@@ -99,7 +99,7 @@ return GlobalRenderFrameHostId(); } -bool TestSharedStorageObserver::ShouldReceiveAllReports() const { +bool TestSharedStorageObserver::ShouldReceiveAllSharedStorageReports() const { return true; } @@ -113,21 +113,23 @@ accesses_.emplace_back(scope, method, main_frame_id, owner_origin, params); } -void TestSharedStorageObserver::OnUrnUuidGenerated(const GURL& urn_uuid) { +void TestSharedStorageObserver::OnSharedStorageSelectUrlUrnUuidGenerated( + const GURL& urn_uuid) { urn_uuids_observed_.push_back(urn_uuid); } -void TestSharedStorageObserver::OnConfigPopulated( +void TestSharedStorageObserver::OnSharedStorageSelectUrlConfigPopulated( const std::optional<FencedFrameConfig>& config) {} -void TestSharedStorageObserver::OnWorkletOperationExecutionFinished( - base::Time finished_time, - base::TimeDelta execution_time, - AccessMethod method, - int operation_id, - int worklet_id, - GlobalRenderFrameHostId main_frame_id, - const std::string& owner_origin) { +void TestSharedStorageObserver:: + OnSharedStorageWorkletOperationExecutionFinished( + base::Time finished_time, + base::TimeDelta execution_time, + AccessMethod method, + int operation_id, + int worklet_id, + GlobalRenderFrameHostId main_frame_id, + const std::string& owner_origin) { operation_finished_infos_.emplace_back(execution_time, method, operation_id, worklet_id, main_frame_id, owner_origin);
diff --git a/content/browser/shared_storage/test_shared_storage_observer.h b/content/browser/shared_storage/test_shared_storage_observer.h index 931a44c..fcdb790 100644 --- a/content/browser/shared_storage/test_shared_storage_observer.h +++ b/content/browser/shared_storage/test_shared_storage_observer.h
@@ -48,7 +48,7 @@ GlobalRenderFrameHostId AssociatedFrameHostId() const override; - bool ShouldReceiveAllReports() const override; + bool ShouldReceiveAllSharedStorageReports() const override; void OnSharedStorageAccessed(base::Time access_time, AccessScope scope, @@ -57,12 +57,12 @@ const std::string& owner_origin, const SharedStorageEventParams& params) override; - void OnUrnUuidGenerated(const GURL& urn_uuid) override; + void OnSharedStorageSelectUrlUrnUuidGenerated(const GURL& urn_uuid) override; - void OnConfigPopulated( + void OnSharedStorageSelectUrlConfigPopulated( const std::optional<FencedFrameConfig>& config) override; - void OnWorkletOperationExecutionFinished( + void OnSharedStorageWorkletOperationExecutionFinished( base::Time finished_time, base::TimeDelta execution_time, AccessMethod method,
diff --git a/content/browser/webid/federated_auth_request_impl.cc b/content/browser/webid/federated_auth_request_impl.cc index 212aa700..ef00c06 100644 --- a/content/browser/webid/federated_auth_request_impl.cc +++ b/content/browser/webid/federated_auth_request_impl.cc
@@ -765,6 +765,11 @@ /*error=*/nullptr, /*is_auto_selected=*/false); fedcm_metrics_.reset(); + // If there's an existing auth request token callback, we will need to + // record metrics for it once it is resolved. + if (auth_request_token_callback_) { + MaybeCreateFedCmMetrics(); + } return; } @@ -778,6 +783,11 @@ /*error=*/nullptr, /*is_auto_selected=*/false); fedcm_metrics_.reset(); + // If there's an existing auth request token callback, we will need to + // record metrics for it once it is resolved. + if (auth_request_token_callback_) { + MaybeCreateFedCmMetrics(); + } return; } @@ -1412,6 +1422,11 @@ std::move(callback).Run(status); disconnect_request_.reset(); fedcm_metrics_.reset(); + // If there's an existing auth request token callback, we will need to record + // metrics for it once it is resolved. + if (auth_request_token_callback_) { + MaybeCreateFedCmMetrics(); + } } void FederatedAuthRequestImpl::OnClientMetadataResponseReceived(
diff --git a/content/browser/webid/federated_auth_request_impl_unittest.cc b/content/browser/webid/federated_auth_request_impl_unittest.cc index 25dce5d..3068121 100644 --- a/content/browser/webid/federated_auth_request_impl_unittest.cc +++ b/content/browser/webid/federated_auth_request_impl_unittest.cc
@@ -22,6 +22,7 @@ #include "components/ukm/test_ukm_recorder.h" #include "content/browser/web_contents/web_contents_impl.h" #include "content/browser/webid/fedcm_metrics.h" +#include "content/browser/webid/federated_auth_disconnect_request.h" #include "content/browser/webid/test/delegated_idp_network_request_manager.h" #include "content/browser/webid/test/federated_auth_request_request_token_callback_helper.h" #include "content/browser/webid/test/mock_api_permission_delegate.h" @@ -39,6 +40,7 @@ #include "content/test/test_render_frame_host.h" #include "content/test/test_render_view_host.h" #include "content/test/test_web_contents.h" +#include "fedcm_metrics.h" #include "mojo/public/cpp/bindings/remote.h" #include "net/http/http_status_code.h" #include "services/metrics/public/cpp/ukm_builders.h" @@ -1345,6 +1347,20 @@ IdentityRequestDialogController::DismissReason::kCloseButton); } + void CompleteDisconnectRequest() { + std::unique_ptr<TestIdpNetworkRequestManager> network_request_manager = + std::make_unique<TestIdpNetworkRequestManager>(); + blink::mojom::IdentityCredentialDisconnectOptionsPtr options = + blink::mojom::IdentityCredentialDisconnectOptions::New(); + federated_auth_request_impl_->disconnect_request_ = + FederatedAuthDisconnectRequest::Create( + std::move(network_request_manager), test_permission_delegate_.get(), + main_test_rfh(), federated_auth_request_impl_->fedcm_metrics_.get(), + std::move(options)); + federated_auth_request_impl_->CompleteDisconnectRequest( + base::DoNothing(), blink::mojom::DisconnectStatus::kSuccess); + } + base::span<const IdentityRequestAccountPtr> all_accounts_for_display() const { return dialog_controller_state_.all_accounts_for_display; } @@ -8120,4 +8136,18 @@ CheckUkmMetrics(FedCmIdpEntry::kEntryName); } +// Test that completing a disconnect request while there is a pending request +// and then later completing the pending request does not crash. +TEST_F(FederatedAuthRequestImplTest, DisconnectWithPendingRequest) { + // Start an auth request. + RunAuthDontWaitForCallback(kDefaultRequestParameters, kConfigurationValid); + + // Complete a disconnect request. + CompleteDisconnectRequest(); + + // Complete the auth request. + WaitForCurrentAuthRequest(); + CheckAuthExpectations(kConfigurationValid, kExpectationSuccess); +} + } // namespace content
diff --git a/content/child/runtime_features.cc b/content/child/runtime_features.cc index 6874fe39..d157689a 100644 --- a/content/child/runtime_features.cc +++ b/content/child/runtime_features.cc
@@ -262,6 +262,7 @@ #if BUILDFLAG(IS_ANDROID) {wf::EnableSmartZoom, raw_ref(features::kSmartZoom)}, #endif + {wf::EnableTouchDragAndDrop, raw_ref(features::kTouchDragAndDrop)}, {wf::EnableTouchDragAndContextMenu, raw_ref(features::kTouchDragAndContextMenu)}, {wf::EnableWebAuthenticationAmbient,
diff --git a/content/public/android/BUILD.gn b/content/public/android/BUILD.gn index f7a261f..c5c07a25 100644 --- a/content/public/android/BUILD.gn +++ b/content/public/android/BUILD.gn
@@ -164,7 +164,6 @@ "$google_play_services_package:google_play_services_auth_api_phone_java", "$google_play_services_package:google_play_services_base_java", "$google_play_services_package:google_play_services_basement_java", - "$google_play_services_package:google_play_services_identity_credentials_java", "$google_play_services_package:google_play_services_tasks_java", "//base:base_java", "//base:process_launcher_java", @@ -368,8 +367,6 @@ "java/src/org/chromium/content/browser/webcontents/WebContentsImpl.java", "java/src/org/chromium/content/browser/webcontents/WebContentsObserverProxy.java", "java/src/org/chromium/content/browser/webid/DigitalCredentialsCreationDelegate.java", - "java/src/org/chromium/content/browser/webid/DigitalCredentialsPresentationDelegate.java", - "java/src/org/chromium/content/browser/webid/IdentityCredentialsDelegate.java", "java/src/org/chromium/content_public/browser/ActionModeCallback.java", "java/src/org/chromium/content_public/browser/ActionModeCallbackHelper.java", "java/src/org/chromium/content_public/browser/AdditionalNavigationParams.java",
diff --git a/content/test/gpu/gpu_tests/test_expectations/webcodecs_expectations.txt b/content/test/gpu/gpu_tests/test_expectations/webcodecs_expectations.txt index 6ec33ae5..122677f 100644 --- a/content/test/gpu/gpu_tests/test_expectations/webcodecs_expectations.txt +++ b/content/test/gpu/gpu_tests/test_expectations/webcodecs_expectations.txt
@@ -169,6 +169,13 @@ crbug.com/398904317 [ win11 amd-0x7480 ] WebCodecs_TexImage2d_hw_decoder [ Failure ] crbug.com/398904317 [ win11 amd-0x7480 ] WebCodecs_copyTo_hw_decoder [ Failure ] +# Android Desktop/Brya Failures +crbug.com/416483573 [ android android-brya ] WebCodecs_DrawImage_hw_decoder [ Failure ] +crbug.com/416483573 [ android android-brya ] WebCodecs_EncodingRateControl_avc1.420034_prefer-hardware_variable_1500000 [ Failure ] +crbug.com/416483573 [ android android-brya ] WebCodecs_EncodingRateControl_avc1.420034_prefer-software_variable_1500000 [ Failure ] +crbug.com/416483573 [ android android-brya ] WebCodecs_TexImage2d_hw_decoder [ Failure ] +crbug.com/416483573 [ android android-brya ] WebCodecs_copyTo_hw_decoder [ Failure ] + ####################################################################### # Automated Entries After This Point - Do Not Manually Add Below Here # #######################################################################
diff --git a/content/test/gpu/gpu_tests/test_expectations/webgl2_conformance_expectations.txt b/content/test/gpu/gpu_tests/test_expectations/webgl2_conformance_expectations.txt index 6dc4074..a1494cca 100644 --- a/content/test/gpu/gpu_tests/test_expectations/webgl2_conformance_expectations.txt +++ b/content/test/gpu/gpu_tests/test_expectations/webgl2_conformance_expectations.txt
@@ -790,17 +790,10 @@ crbug.com/982292 [ mac nvidia angle-opengl passthrough ] conformance2/glsl3/tricky-loop-conditions.html [ Failure ] crbug.com/982292 [ mac nvidia angle-opengl passthrough ] deqp/functional/gles3/instancedrendering.html [ Failure ] -## Mac ASAN ## - - #################### # Linux failures # #################### - -## Mesa issues ## - - ## Linux NVIDIA ## crbug.com/1115314 [ linux nvidia-0x2184 angle-opengl passthrough ] deqp/functional/gles3/fbocompleteness.html [ Failure ] @@ -823,8 +816,6 @@ # failing. crbug.com/951628 [ android no-passthrough ] conformance/rendering/blending.html [ Failure ] - - # TODO(kbr): flakiness is seen on this configuration with the # passthrough command decoder across many different WebGL conformance # tests. Limit this blanket suppression to Android P in case this is @@ -835,8 +826,6 @@ crbug.com/1176485 [ android qualcomm ] conformance2/glsl3/uint-int-shift-bug.html [ Failure ] crbug.com/1027125 [ android angle-disabled no-passthrough qualcomm ] deqp/functional/gles3/negativetextureapi.html [ Failure ] - - ## Pixel 2 ## crbug.com/906742 [ android-pie android-pixel-2 no-passthrough qualcomm ] conformance2/glsl3/compare-structs-containing-arrays.html [ Failure ] @@ -846,17 +835,30 @@ ## Pixel 4 ## - crbug.com/1175232 [ android android-pixel-4 angle-opengles passthrough ] conformance2/reading/read-pixels-from-fbo-test.html [ Failure ] crbug.com/angleproject/3684 [ android angle-opengles ] conformance2/renderbuffers/multisample-with-full-sample-counts.html [ Failure ] - - ## DrDc being re-enabled ## crbug.com/380250262 [ android android-pixel-2 angle-opengles passthrough ] conformance/canvas/render-after-resize-test.html [ Failure ] crbug.com/380250262 [ android android-pixel-4 angle-opengles passthrough ] conformance/canvas/render-after-resize-test.html [ Failure ] +############################ +# Android Desktop failures # +############################ + +# Brya failures +crbug.com/416483574 [ android android-brya passthrough ] conformance/extensions/webgl-compressed-texture-size-limit.html [ Failure ] +crbug.com/416483574 [ android android-brya passthrough ] conformance/textures/misc/texture-srgb-upload.html [ Failure ] +crbug.com/416408899 [ android android-brya passthrough ] conformance2/canvas/drawingbuffer-storage-test.html [ Failure ] +crbug.com/416408899 [ android android-brya passthrough ] conformance2/extensions/webgl-clip-cull-distance.html [ Failure ] +crbug.com/416408899 [ android android-brya passthrough ] conformance2/extensions/webgl-multi-draw-instanced-base-vertex-base-instance.html [ Failure ] +crbug.com/416408899 [ android android-brya passthrough ] conformance2/reading/read-pixels-from-fbo-test.html [ Failure ] +crbug.com/416408899 [ android android-brya passthrough ] conformance2/renderbuffers/framebuffer-test.html [ Failure ] +crbug.com/416417899 [ android android-brya passthrough ] deqp/functional/gles3/negativebufferapi.html [ Failure ] +crbug.com/416417899 [ android android-brya passthrough ] deqp/functional/gles3/negativetextureapi.html [ Failure ] +crbug.com/416417899 [ android android-brya passthrough ] deqp/functional/gles3/shaderoperator/unary_operator_00.html [ Failure ] + ##################### # ChromeOS failures # #####################
diff --git a/content/test/gpu/gpu_tests/test_expectations/webgl_conformance_expectations.txt b/content/test/gpu/gpu_tests/test_expectations/webgl_conformance_expectations.txt index dedf32f..2a1d714c 100644 --- a/content/test/gpu/gpu_tests/test_expectations/webgl_conformance_expectations.txt +++ b/content/test/gpu/gpu_tests/test_expectations/webgl_conformance_expectations.txt
@@ -643,6 +643,14 @@ crbug.com/331489774 [ android android-sm-s911u1 no-passthrough ] conformance/context/context-attributes-alpha-depth-stencil-antialias.html [ Failure ] +############################ +# Android Desktop failures # +############################ + +# Brya failures +crbug.com/416483574 [ android android-brya ] conformance/extensions/webgl-compressed-texture-size-limit.html [ Failure ] +crbug.com/416483574 [ android android-brya ] conformance/textures/misc/texture-srgb-upload.html [ Failure ] + ############ # ChromeOS #
diff --git a/content/test/test_select_url_fenced_frame_config_observer_impl.cc b/content/test/test_select_url_fenced_frame_config_observer_impl.cc index f6159d19..a4360278 100644 --- a/content/test/test_select_url_fenced_frame_config_observer_impl.cc +++ b/content/test/test_select_url_fenced_frame_config_observer_impl.cc
@@ -21,8 +21,8 @@ return GlobalRenderFrameHostId(); } -bool TestSelectURLFencedFrameConfigObserverImpl::ShouldReceiveAllReports() - const { +bool TestSelectURLFencedFrameConfigObserverImpl:: + ShouldReceiveAllSharedStorageReports() const { return true; } @@ -34,8 +34,8 @@ const std::string& owner_origin, const SharedStorageEventParams& params) {} -void TestSelectURLFencedFrameConfigObserverImpl::OnUrnUuidGenerated( - const GURL& urn_uuid) { +void TestSelectURLFencedFrameConfigObserverImpl:: + OnSharedStorageSelectUrlUrnUuidGenerated(const GURL& urn_uuid) { if (urn_uuid_.has_value()) { // This observer has already observed an urn::uuid. return; @@ -43,8 +43,9 @@ urn_uuid_ = urn_uuid; } -void TestSelectURLFencedFrameConfigObserverImpl::OnConfigPopulated( - const std::optional<FencedFrameConfig>& config) { +void TestSelectURLFencedFrameConfigObserverImpl:: + OnSharedStorageSelectUrlConfigPopulated( + const std::optional<FencedFrameConfig>& config) { if (config_observed_ || !urn_uuid_.has_value() || !config.has_value() || (urn_uuid_.value() != config->urn_uuid())) { // 1. This observer has already observed a config. @@ -58,13 +59,14 @@ } void TestSelectURLFencedFrameConfigObserverImpl:: - OnWorkletOperationExecutionFinished(base::Time finished_time, - base::TimeDelta execution_time, - AccessMethod method, - int operation_id, - int worklet_id, - GlobalRenderFrameHostId main_frame_id, - const std::string& owner_origin) {} + OnSharedStorageWorkletOperationExecutionFinished( + base::Time finished_time, + base::TimeDelta execution_time, + AccessMethod method, + int operation_id, + int worklet_id, + GlobalRenderFrameHostId main_frame_id, + const std::string& owner_origin) {} const std::optional<GURL>& TestSelectURLFencedFrameConfigObserverImpl::GetUrnUuid() const {
diff --git a/content/test/test_select_url_fenced_frame_config_observer_impl.h b/content/test/test_select_url_fenced_frame_config_observer_impl.h index aa1737f..a5c3d6c5 100644 --- a/content/test/test_select_url_fenced_frame_config_observer_impl.h +++ b/content/test/test_select_url_fenced_frame_config_observer_impl.h
@@ -21,7 +21,7 @@ ~TestSelectURLFencedFrameConfigObserverImpl() override; GlobalRenderFrameHostId AssociatedFrameHostId() const override; - bool ShouldReceiveAllReports() const override; + bool ShouldReceiveAllSharedStorageReports() const override; void OnSharedStorageAccessed(base::Time access_time, AccessScope scope, @@ -29,11 +29,11 @@ GlobalRenderFrameHostId main_frame_id, const std::string& owner_origin, const SharedStorageEventParams& params) override; - void OnUrnUuidGenerated(const GURL& urn_uuid) override; - void OnConfigPopulated( + void OnSharedStorageSelectUrlUrnUuidGenerated(const GURL& urn_uuid) override; + void OnSharedStorageSelectUrlConfigPopulated( const std::optional<FencedFrameConfig>& config) override; - void OnWorkletOperationExecutionFinished( + void OnSharedStorageWorkletOperationExecutionFinished( base::Time finished_time, base::TimeDelta execution_time, AccessMethod method,
diff --git a/device/vr/buildflags/buildflags.gni b/device/vr/buildflags/buildflags.gni index f401e277..2c6a90a 100644 --- a/device/vr/buildflags/buildflags.gni +++ b/device/vr/buildflags/buildflags.gni
@@ -12,7 +12,8 @@ # like to support on Android by default. Embedders can still override any of # the particular runtimes if they do not wish to enable them. _is_xr_supported_android = is_android && !is_cast_android && - (current_cpu == "arm" || current_cpu == "arm64") + (current_cpu == "arm" || current_cpu == "arm64" || + current_cpu == "x86" || current_cpu == "x64") declare_args() { enable_cardboard = _is_xr_supported_android @@ -23,7 +24,9 @@ # To build with OpenXR support, the OpenXR Loader needs to be pulled to # third_party/openxr. - enable_openxr = checkout_openxr && (is_win || _is_xr_supported_android) + enable_openxr = checkout_openxr && + (is_win || ((_is_xr_supported_android && + current_cpu == "arm") || current_cpu == "arm64")) } declare_args() {
diff --git a/extensions/browser/api/printer_provider/BUILD.gn b/extensions/browser/api/printer_provider/BUILD.gn index 81d8f4e..77dd1970 100644 --- a/extensions/browser/api/printer_provider/BUILD.gn +++ b/extensions/browser/api/printer_provider/BUILD.gn
@@ -22,15 +22,20 @@ configs += [ "//build/config/compiler:wexit_time_destructors" ] - deps = [ - "//components/device_event_log", + public_deps = [ + "//base", "//components/keyed_service/content", - "//content/public/browser", + "//components/keyed_service/core", + "//extensions/browser:browser_sources", "//extensions/common", "//extensions/common/api", - "//services/device/public/cpp/usb", "//services/device/public/mojom:usb", ] - public_deps = [ "//extensions/browser:browser_sources" ] + deps = [ + "//base:i18n", + "//components/device_event_log", + "//content/public/browser", + "//extensions/common:mojom", + ] }
diff --git a/gpu/BUILD.gn b/gpu/BUILD.gn index 0d5fcb561..b2717ed 100644 --- a/gpu/BUILD.gn +++ b/gpu/BUILD.gn
@@ -80,6 +80,8 @@ sources = [ "command_buffer/client/client_test_helper.cc", "command_buffer/client/client_test_helper.h", + "command_buffer/client/fake_gpu_memory_buffer.cc", + "command_buffer/client/fake_gpu_memory_buffer.h", "command_buffer/client/gles2_interface_stub.cc", "command_buffer/client/gles2_interface_stub.h", "command_buffer/client/gles2_interface_stub_autogen.h", @@ -131,6 +133,7 @@ "//gpu/ipc/common", "//gpu/ipc/service", "//gpu/skia_bindings:skia_bindings", + "//media", "//testing/gmock", "//testing/gtest", "//ui/gfx:test_support",
diff --git a/gpu/command_buffer/client/DEPS b/gpu/command_buffer/client/DEPS index 8c34892..fb3a1cc 100644 --- a/gpu/command_buffer/client/DEPS +++ b/gpu/command_buffer/client/DEPS
@@ -14,4 +14,11 @@ "shared_image_pool_unittest.cc": [ "+components/viz/test/test_context_provider.h", ], + "fake_gpu_memory_buffer.cc": [ + "+media/base/format_utils.h", + "+media/base/video_frame.h", + ], + "fake_gpu_memory_buffer.h": [ + "+media/base/video_types.h", + ], }
diff --git a/media/video/fake_gpu_memory_buffer.cc b/gpu/command_buffer/client/fake_gpu_memory_buffer.cc similarity index 81% rename from media/video/fake_gpu_memory_buffer.cc rename to gpu/command_buffer/client/fake_gpu_memory_buffer.cc index d6bee7c..fd44558 100644 --- a/media/video/fake_gpu_memory_buffer.cc +++ b/gpu/command_buffer/client/fake_gpu_memory_buffer.cc
@@ -7,7 +7,7 @@ #pragma allow_unsafe_buffers #endif -#include "media/video/fake_gpu_memory_buffer.h" +#include "gpu/command_buffer/client/fake_gpu_memory_buffer.h" #include "base/atomic_sequence_num.h" #include "build/build_config.h" @@ -25,7 +25,7 @@ #include <lib/zx/object.h> #endif -namespace media { +namespace gpu { namespace { @@ -46,14 +46,15 @@ const gfx::Size& size, gfx::BufferFormat format, uint64_t modifier) { - std::optional<VideoPixelFormat> video_pixel_format = - GfxBufferFormatToVideoPixelFormat(format); + std::optional<media::VideoPixelFormat> video_pixel_format = + media::GfxBufferFormatToVideoPixelFormat(format); CHECK(video_pixel_format); gfx::NativePixmapHandle native_pixmap_handle; - for (size_t i = 0; i < VideoFrame::NumPlanes(*video_pixel_format); i++) { + for (size_t i = 0; i < media::VideoFrame::NumPlanes(*video_pixel_format); + i++) { const gfx::Size plane_size_in_bytes = - VideoFrame::PlaneSize(*video_pixel_format, i, size); + media::VideoFrame::PlaneSize(*video_pixel_format, i, size); native_pixmap_handle.planes.emplace_back(plane_size_in_bytes.width(), 0, plane_size_in_bytes.GetArea(), GetDummyFD()); @@ -83,13 +84,13 @@ gfx::BufferFormat format, uint64_t modifier) : size_(size), format_(format) { - std::optional<VideoPixelFormat> video_pixel_format = - GfxBufferFormatToVideoPixelFormat(format); + std::optional<media::VideoPixelFormat> video_pixel_format = + media::GfxBufferFormatToVideoPixelFormat(format); CHECK(video_pixel_format); video_pixel_format_ = *video_pixel_format; const size_t allocation_size = - VideoFrame::AllocationSize(video_pixel_format_, size_); + media::VideoFrame::AllocationSize(video_pixel_format_, size_); data_ = std::vector<uint8_t>(allocation_size); handle_.type = gfx::SHARED_MEMORY_BUFFER; @@ -115,11 +116,11 @@ } void* FakeGpuMemoryBuffer::memory(size_t plane) { - DCHECK_LT(plane, VideoFrame::NumPlanes(video_pixel_format_)); + DCHECK_LT(plane, media::VideoFrame::NumPlanes(video_pixel_format_)); auto* data_ptr = data_.data(); for (size_t i = 1; i <= plane; i++) { - data_ptr += - VideoFrame::PlaneSize(video_pixel_format_, i - 1, size_).GetArea(); + data_ptr += media::VideoFrame::PlaneSize(video_pixel_format_, i - 1, size_) + .GetArea(); } return data_ptr; } @@ -135,8 +136,9 @@ } int FakeGpuMemoryBuffer::stride(size_t plane) const { - DCHECK_LT(plane, VideoFrame::NumPlanes(video_pixel_format_)); - return VideoFrame::PlaneSize(video_pixel_format_, plane, size_).width(); + DCHECK_LT(plane, media::VideoFrame::NumPlanes(video_pixel_format_)); + return media::VideoFrame::PlaneSize(video_pixel_format_, plane, size_) + .width(); } gfx::GpuMemoryBufferId FakeGpuMemoryBuffer::GetId() const { @@ -157,4 +159,4 @@ uint64_t tracing_process_id, int importance) const {} -} // namespace media +} // namespace gpu
diff --git a/media/video/fake_gpu_memory_buffer.h b/gpu/command_buffer/client/fake_gpu_memory_buffer.h similarity index 90% rename from media/video/fake_gpu_memory_buffer.h rename to gpu/command_buffer/client/fake_gpu_memory_buffer.h index c6f4c1a..3c0f7ab 100644 --- a/media/video/fake_gpu_memory_buffer.h +++ b/gpu/command_buffer/client/fake_gpu_memory_buffer.h
@@ -2,15 +2,15 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifndef MEDIA_VIDEO_FAKE_GPU_MEMORY_BUFFER_H_ -#define MEDIA_VIDEO_FAKE_GPU_MEMORY_BUFFER_H_ +#ifndef GPU_COMMAND_BUFFER_CLIENT_FAKE_GPU_MEMORY_BUFFER_H_ +#define GPU_COMMAND_BUFFER_CLIENT_FAKE_GPU_MEMORY_BUFFER_H_ #include <memory> #include "media/base/video_types.h" #include "ui/gfx/gpu_memory_buffer.h" -namespace media { +namespace gpu { #if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) // This method is used by tests to create a fake pixmap handle instead of @@ -74,13 +74,13 @@ private: gfx::Size size_; gfx::BufferFormat format_; - VideoPixelFormat video_pixel_format_ = PIXEL_FORMAT_UNKNOWN; + media::VideoPixelFormat video_pixel_format_ = media::PIXEL_FORMAT_UNKNOWN; std::vector<uint8_t> data_; gfx::GpuMemoryBufferHandle handle_; bool premapped_ = true; raw_ptr<MapCallbackController> map_callback_controller_ = nullptr; }; -} // namespace media +} // namespace gpu -#endif // MEDIA_VIDEO_FAKE_GPU_MEMORY_BUFFER_H_ +#endif // GPU_COMMAND_BUFFER_CLIENT_FAKE_GPU_MEMORY_BUFFER_H_
diff --git a/infra/config/generated/builder-owners/bling-engprod@google.com.txt b/infra/config/generated/builder-owners/bling-engprod@google.com.txt index 70ec0e79..0ee923c2 100644 --- a/infra/config/generated/builder-owners/bling-engprod@google.com.txt +++ b/infra/config/generated/builder-owners/bling-engprod@google.com.txt
@@ -17,8 +17,8 @@ ci/mac13-arm64-rel-tests ci/mac14-arm64-rel-tests ci/mac14-tests -ci/mac14-tests-dbg ci/mac15-arm64-rel-tests +ci/mac15-tests-dbg ci/mac15-x64-rel-tests try/ios-vm try/mac-vm
diff --git "a/infra/config/generated/builders/ci/Mac Builder \050dbg\051/properties.json" "b/infra/config/generated/builders/ci/Mac Builder \050dbg\051/properties.json" index 3b0bd0d..539a77f2 100644 --- "a/infra/config/generated/builders/ci/Mac Builder \050dbg\051/properties.json" +++ "b/infra/config/generated/builders/ci/Mac Builder \050dbg\051/properties.json"
@@ -34,7 +34,7 @@ { "builder_id": { "bucket": "ci", - "builder": "mac14-tests-dbg", + "builder": "mac15-tests-dbg", "project": "chromium" }, "builder_spec": { @@ -73,7 +73,7 @@ "builder_ids_in_scope_for_testing": [ { "bucket": "ci", - "builder": "mac14-tests-dbg", + "builder": "mac15-tests-dbg", "project": "chromium" } ],
diff --git "a/infra/config/generated/builders/ci/Mac Builder \050dbg\051/targets/chromium.mac.json" "b/infra/config/generated/builders/ci/Mac Builder \050dbg\051/targets/chromium.mac.json" index a6396c59..cdccbe07 100644 --- "a/infra/config/generated/builders/ci/Mac Builder \050dbg\051/targets/chromium.mac.json" +++ "b/infra/config/generated/builders/ci/Mac Builder \050dbg\051/targets/chromium.mac.json"
@@ -4,7 +4,7 @@ "all" ] }, - "mac14-tests-dbg": { + "mac15-tests-dbg": { "gtest_tests": [ { "merge": { @@ -14,7 +14,7 @@ "swarming": { "dimensions": { "cpu": "x86-64", - "os": "Mac-14|Mac-15" + "os": "Mac-15" }, "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" }, @@ -29,7 +29,7 @@ "swarming": { "dimensions": { "cpu": "x86-64", - "os": "Mac-14|Mac-15" + "os": "Mac-15" }, "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" }, @@ -44,7 +44,7 @@ "swarming": { "dimensions": { "cpu": "x86-64", - "os": "Mac-14|Mac-15" + "os": "Mac-15" }, "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" }, @@ -60,7 +60,7 @@ "swarming": { "dimensions": { "cpu": "x86-64", - "os": "Mac-14|Mac-15" + "os": "Mac-15" }, "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" }, @@ -75,7 +75,7 @@ "swarming": { "dimensions": { "cpu": "x86-64", - "os": "Mac-14|Mac-15" + "os": "Mac-15" }, "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" }, @@ -90,7 +90,7 @@ "swarming": { "dimensions": { "cpu": "x86-64", - "os": "Mac-14|Mac-15" + "os": "Mac-15" }, "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" }, @@ -105,7 +105,7 @@ "swarming": { "dimensions": { "cpu": "x86-64", - "os": "Mac-14|Mac-15" + "os": "Mac-15" }, "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" }, @@ -120,7 +120,7 @@ "swarming": { "dimensions": { "cpu": "x86-64", - "os": "Mac-14|Mac-15" + "os": "Mac-15" }, "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" }, @@ -143,7 +143,7 @@ "swarming": { "dimensions": { "cpu": "x86-64", - "os": "Mac-14|Mac-15" + "os": "Mac-15" }, "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" }, @@ -158,7 +158,7 @@ "swarming": { "dimensions": { "cpu": "x86-64", - "os": "Mac-14|Mac-15" + "os": "Mac-15" }, "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" }, @@ -173,7 +173,7 @@ "swarming": { "dimensions": { "cpu": "x86-64", - "os": "Mac-14|Mac-15" + "os": "Mac-15" }, "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" }, @@ -182,6 +182,25 @@ }, { "args": [ + "--test-launcher-filter-file=../../testing/buildbot/filters/mac.mac-rel.browser_tests.filter" + ], + "merge": { + "script": "//testing/merge_scripts/standard_gtest_merge.py" + }, + "name": "browser_tests", + "swarming": { + "dimensions": { + "cpu": "x86-64", + "os": "Mac-15" + }, + "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com", + "shards": 20 + }, + "test": "browser_tests", + "test_id_prefix": "ninja://chrome/test:browser_tests/" + }, + { + "args": [ "--gtest_filter=-*UsingRealWebcam*" ], "merge": { @@ -191,7 +210,7 @@ "swarming": { "dimensions": { "cpu": "x86-64", - "os": "Mac-14|Mac-15" + "os": "Mac-15" }, "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" }, @@ -206,7 +225,7 @@ "swarming": { "dimensions": { "cpu": "x86-64", - "os": "Mac-14|Mac-15" + "os": "Mac-15" }, "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" }, @@ -221,7 +240,7 @@ "swarming": { "dimensions": { "cpu": "x86-64", - "os": "Mac-14|Mac-15" + "os": "Mac-15" }, "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" }, @@ -236,7 +255,7 @@ "swarming": { "dimensions": { "cpu": "x86-64", - "os": "Mac-14|Mac-15" + "os": "Mac-15" }, "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" }, @@ -251,7 +270,7 @@ "swarming": { "dimensions": { "cpu": "x86-64", - "os": "Mac-14|Mac-15" + "os": "Mac-15" }, "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" }, @@ -266,7 +285,7 @@ "swarming": { "dimensions": { "cpu": "x86-64", - "os": "Mac-14|Mac-15" + "os": "Mac-15" }, "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" }, @@ -281,7 +300,7 @@ "swarming": { "dimensions": { "cpu": "x86-64", - "os": "Mac-14|Mac-15" + "os": "Mac-15" }, "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com", "shards": 3 @@ -297,7 +316,7 @@ "swarming": { "dimensions": { "cpu": "x86-64", - "os": "Mac-14|Mac-15" + "os": "Mac-15" }, "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com", "shards": 18 @@ -313,7 +332,7 @@ "swarming": { "dimensions": { "cpu": "x86-64", - "os": "Mac-14|Mac-15" + "os": "Mac-15" }, "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" }, @@ -328,7 +347,7 @@ "swarming": { "dimensions": { "cpu": "x86-64", - "os": "Mac-14|Mac-15" + "os": "Mac-15" }, "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" }, @@ -343,7 +362,7 @@ "swarming": { "dimensions": { "cpu": "x86-64", - "os": "Mac-14|Mac-15" + "os": "Mac-15" }, "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" }, @@ -358,7 +377,7 @@ "swarming": { "dimensions": { "cpu": "x86-64", - "os": "Mac-14|Mac-15" + "os": "Mac-15" }, "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" }, @@ -373,7 +392,7 @@ "swarming": { "dimensions": { "cpu": "x86-64", - "os": "Mac-14|Mac-15" + "os": "Mac-15" }, "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" }, @@ -388,7 +407,7 @@ "swarming": { "dimensions": { "cpu": "x86-64", - "os": "Mac-14|Mac-15" + "os": "Mac-15" }, "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" }, @@ -403,7 +422,7 @@ "swarming": { "dimensions": { "cpu": "x86-64", - "os": "Mac-14|Mac-15" + "os": "Mac-15" }, "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" }, @@ -419,7 +438,7 @@ "swarming": { "dimensions": { "cpu": "x86-64", - "os": "Mac-14|Mac-15" + "os": "Mac-15" }, "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" }, @@ -434,7 +453,7 @@ "swarming": { "dimensions": { "cpu": "x86-64", - "os": "Mac-14|Mac-15" + "os": "Mac-15" }, "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" }, @@ -449,7 +468,7 @@ "swarming": { "dimensions": { "cpu": "x86-64", - "os": "Mac-14|Mac-15" + "os": "Mac-15" }, "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" }, @@ -464,7 +483,7 @@ "swarming": { "dimensions": { "cpu": "x86-64", - "os": "Mac-14|Mac-15" + "os": "Mac-15" }, "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" }, @@ -479,7 +498,7 @@ "swarming": { "dimensions": { "cpu": "x86-64", - "os": "Mac-14|Mac-15" + "os": "Mac-15" }, "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" }, @@ -494,7 +513,7 @@ "swarming": { "dimensions": { "cpu": "x86-64", - "os": "Mac-14|Mac-15" + "os": "Mac-15" }, "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" }, @@ -509,7 +528,7 @@ "swarming": { "dimensions": { "cpu": "x86-64", - "os": "Mac-14|Mac-15" + "os": "Mac-15" }, "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" }, @@ -524,7 +543,7 @@ "swarming": { "dimensions": { "cpu": "x86-64", - "os": "Mac-14|Mac-15" + "os": "Mac-15" }, "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" }, @@ -539,7 +558,7 @@ "swarming": { "dimensions": { "cpu": "x86-64", - "os": "Mac-14|Mac-15" + "os": "Mac-15" }, "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" }, @@ -554,7 +573,7 @@ "swarming": { "dimensions": { "cpu": "x86-64", - "os": "Mac-14|Mac-15" + "os": "Mac-15" }, "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" }, @@ -569,7 +588,7 @@ "swarming": { "dimensions": { "cpu": "x86-64", - "os": "Mac-14|Mac-15" + "os": "Mac-15" }, "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" }, @@ -584,7 +603,7 @@ "swarming": { "dimensions": { "cpu": "x86-64", - "os": "Mac-14|Mac-15" + "os": "Mac-15" }, "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" }, @@ -599,7 +618,7 @@ "swarming": { "dimensions": { "cpu": "x86-64", - "os": "Mac-14|Mac-15" + "os": "Mac-15" }, "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" }, @@ -614,7 +633,7 @@ "swarming": { "dimensions": { "cpu": "x86-64", - "os": "Mac-14|Mac-15" + "os": "Mac-15" }, "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" }, @@ -630,7 +649,7 @@ "swarming": { "dimensions": { "cpu": "x86-64", - "os": "Mac-14|Mac-15" + "os": "Mac-15" }, "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com", "shards": 12 @@ -646,7 +665,7 @@ "swarming": { "dimensions": { "cpu": "x86-64", - "os": "Mac-14|Mac-15" + "os": "Mac-15" }, "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" }, @@ -661,7 +680,7 @@ "swarming": { "dimensions": { "cpu": "x86-64", - "os": "Mac-14|Mac-15" + "os": "Mac-15" }, "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" }, @@ -677,7 +696,7 @@ "swarming": { "dimensions": { "cpu": "x86-64", - "os": "Mac-14|Mac-15" + "os": "Mac-15" }, "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" }, @@ -692,7 +711,7 @@ "swarming": { "dimensions": { "cpu": "x86-64", - "os": "Mac-14|Mac-15" + "os": "Mac-15" }, "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" }, @@ -707,7 +726,7 @@ "swarming": { "dimensions": { "cpu": "x86-64", - "os": "Mac-14|Mac-15" + "os": "Mac-15" }, "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" }, @@ -722,7 +741,7 @@ "swarming": { "dimensions": { "cpu": "x86-64", - "os": "Mac-14|Mac-15" + "os": "Mac-15" }, "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" }, @@ -737,7 +756,7 @@ "swarming": { "dimensions": { "cpu": "x86-64", - "os": "Mac-14|Mac-15" + "os": "Mac-15" }, "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" }, @@ -752,7 +771,7 @@ "swarming": { "dimensions": { "cpu": "x86-64", - "os": "Mac-14|Mac-15" + "os": "Mac-15" }, "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" }, @@ -767,7 +786,7 @@ "swarming": { "dimensions": { "cpu": "x86-64", - "os": "Mac-14|Mac-15" + "os": "Mac-15" }, "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" }, @@ -782,7 +801,7 @@ "swarming": { "dimensions": { "cpu": "x86-64", - "os": "Mac-14|Mac-15" + "os": "Mac-15" }, "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" }, @@ -797,7 +816,7 @@ "swarming": { "dimensions": { "cpu": "x86-64", - "os": "Mac-14|Mac-15" + "os": "Mac-15" }, "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" }, @@ -812,7 +831,7 @@ "swarming": { "dimensions": { "cpu": "x86-64", - "os": "Mac-14|Mac-15" + "os": "Mac-15" }, "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" }, @@ -827,7 +846,7 @@ "swarming": { "dimensions": { "cpu": "x86-64", - "os": "Mac-14|Mac-15" + "os": "Mac-15" }, "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" }, @@ -842,7 +861,7 @@ "swarming": { "dimensions": { "cpu": "x86-64", - "os": "Mac-14|Mac-15" + "os": "Mac-15" }, "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" }, @@ -857,7 +876,7 @@ "swarming": { "dimensions": { "cpu": "x86-64", - "os": "Mac-14|Mac-15" + "os": "Mac-15" }, "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" }, @@ -872,7 +891,7 @@ "swarming": { "dimensions": { "cpu": "x86-64", - "os": "Mac-14|Mac-15" + "os": "Mac-15" }, "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" }, @@ -887,7 +906,7 @@ "swarming": { "dimensions": { "cpu": "x86-64", - "os": "Mac-14|Mac-15" + "os": "Mac-15" }, "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" }, @@ -902,7 +921,7 @@ "swarming": { "dimensions": { "cpu": "x86-64", - "os": "Mac-14|Mac-15" + "os": "Mac-15" }, "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" }, @@ -917,7 +936,7 @@ "swarming": { "dimensions": { "cpu": "x86-64", - "os": "Mac-14|Mac-15" + "os": "Mac-15" }, "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" }, @@ -932,7 +951,7 @@ "swarming": { "dimensions": { "cpu": "x86-64", - "os": "Mac-14|Mac-15" + "os": "Mac-15" }, "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" }, @@ -947,7 +966,7 @@ "swarming": { "dimensions": { "cpu": "x86-64", - "os": "Mac-14|Mac-15" + "os": "Mac-15" }, "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" }, @@ -962,7 +981,7 @@ "swarming": { "dimensions": { "cpu": "x86-64", - "os": "Mac-14|Mac-15" + "os": "Mac-15" }, "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" }, @@ -977,7 +996,7 @@ "swarming": { "dimensions": { "cpu": "x86-64", - "os": "Mac-14|Mac-15" + "os": "Mac-15" }, "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" }, @@ -992,7 +1011,7 @@ "swarming": { "dimensions": { "cpu": "x86-64", - "os": "Mac-14|Mac-15" + "os": "Mac-15" }, "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" }, @@ -1007,7 +1026,7 @@ "swarming": { "dimensions": { "cpu": "x86-64", - "os": "Mac-14|Mac-15" + "os": "Mac-15" }, "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com", "shards": 3 @@ -1023,7 +1042,7 @@ "swarming": { "dimensions": { "cpu": "x86-64", - "os": "Mac-14|Mac-15" + "os": "Mac-15" }, "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" }, @@ -1038,7 +1057,7 @@ "swarming": { "dimensions": { "cpu": "x86-64", - "os": "Mac-14|Mac-15" + "os": "Mac-15" }, "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" }, @@ -1053,7 +1072,7 @@ "swarming": { "dimensions": { "cpu": "x86-64", - "os": "Mac-14|Mac-15" + "os": "Mac-15" }, "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" }, @@ -1068,7 +1087,7 @@ "swarming": { "dimensions": { "cpu": "x86-64", - "os": "Mac-14|Mac-15" + "os": "Mac-15" }, "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com", "shards": 2 @@ -1084,7 +1103,7 @@ "swarming": { "dimensions": { "cpu": "x86-64", - "os": "Mac-14|Mac-15" + "os": "Mac-15" }, "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" }, @@ -1099,7 +1118,7 @@ "swarming": { "dimensions": { "cpu": "x86-64", - "os": "Mac-14|Mac-15" + "os": "Mac-15" }, "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" }, @@ -1114,7 +1133,7 @@ "swarming": { "dimensions": { "cpu": "x86-64", - "os": "Mac-14|Mac-15" + "os": "Mac-15" }, "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" }, @@ -1129,7 +1148,7 @@ "swarming": { "dimensions": { "cpu": "x86-64", - "os": "Mac-14|Mac-15" + "os": "Mac-15" }, "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" }, @@ -1144,7 +1163,7 @@ "swarming": { "dimensions": { "cpu": "x86-64", - "os": "Mac-14|Mac-15" + "os": "Mac-15" }, "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" }, @@ -1159,7 +1178,7 @@ "swarming": { "dimensions": { "cpu": "x86-64", - "os": "Mac-14|Mac-15" + "os": "Mac-15" }, "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" }, @@ -1174,7 +1193,7 @@ "swarming": { "dimensions": { "cpu": "x86-64", - "os": "Mac-14|Mac-15" + "os": "Mac-15" }, "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" }, @@ -1189,7 +1208,7 @@ "swarming": { "dimensions": { "cpu": "x86-64", - "os": "Mac-14|Mac-15" + "os": "Mac-15" }, "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" }, @@ -1209,7 +1228,7 @@ "swarming": { "dimensions": { "cpu": "x86-64", - "os": "Mac-14|Mac-15" + "os": "Mac-15" }, "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" }, @@ -1236,7 +1255,7 @@ "swarming": { "dimensions": { "cpu": "x86-64", - "os": "Mac-14|Mac-15" + "os": "Mac-15" }, "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com", "shards": 16 @@ -1265,7 +1284,7 @@ "swarming": { "dimensions": { "cpu": "x86-64", - "os": "Mac-14|Mac-15" + "os": "Mac-15" }, "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com", "shards": 8 @@ -1292,7 +1311,7 @@ "swarming": { "dimensions": { "cpu": "x86-64", - "os": "Mac-14|Mac-15" + "os": "Mac-15" }, "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" }, @@ -1311,7 +1330,7 @@ "swarming": { "dimensions": { "cpu": "x86-64", - "os": "Mac-14|Mac-15" + "os": "Mac-15" }, "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" }, @@ -1330,7 +1349,7 @@ "swarming": { "dimensions": { "cpu": "x86-64", - "os": "Mac-14|Mac-15" + "os": "Mac-15" }, "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" }, @@ -1348,7 +1367,7 @@ "swarming": { "dimensions": { "cpu": "x86-64", - "os": "Mac-14|Mac-15" + "os": "Mac-15" }, "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" }, @@ -1380,7 +1399,7 @@ "swarming": { "dimensions": { "cpu": "x86-64", - "os": "Mac-14|Mac-15" + "os": "Mac-15" }, "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com", "shards": 18 @@ -1407,7 +1426,7 @@ "swarming": { "dimensions": { "cpu": "x86-64", - "os": "Mac-14|Mac-15" + "os": "Mac-15" }, "hard_timeout": 960, "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com", @@ -1427,7 +1446,7 @@ "swarming": { "dimensions": { "cpu": "x86-64", - "os": "Mac-14|Mac-15" + "os": "Mac-15" }, "idempotent": false, "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" @@ -1450,7 +1469,7 @@ "swarming": { "dimensions": { "cpu": "x86-64", - "os": "Mac-14|Mac-15" + "os": "Mac-15" }, "idempotent": false, "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com", @@ -1473,7 +1492,7 @@ "swarming": { "dimensions": { "cpu": "x86-64", - "os": "Mac-14|Mac-15" + "os": "Mac-15" }, "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" },
diff --git a/infra/config/generated/builders/ci/mac14-tests-dbg/properties.json b/infra/config/generated/builders/ci/mac15-tests-dbg/properties.json similarity index 95% rename from infra/config/generated/builders/ci/mac14-tests-dbg/properties.json rename to infra/config/generated/builders/ci/mac15-tests-dbg/properties.json index d98bae23..f3ad3f2 100644 --- a/infra/config/generated/builders/ci/mac14-tests-dbg/properties.json +++ b/infra/config/generated/builders/ci/mac15-tests-dbg/properties.json
@@ -31,7 +31,7 @@ { "builder_id": { "bucket": "ci", - "builder": "mac14-tests-dbg", + "builder": "mac15-tests-dbg", "project": "chromium" }, "builder_spec": { @@ -63,7 +63,7 @@ "builder_ids": [ { "bucket": "ci", - "builder": "mac14-tests-dbg", + "builder": "mac15-tests-dbg", "project": "chromium" } ], @@ -78,7 +78,7 @@ } ], "retry_failed_shards": true, - "targets_spec_directory": "src/infra/config/generated/builders/ci/mac14-tests-dbg/targets" + "targets_spec_directory": "src/infra/config/generated/builders/ci/mac15-tests-dbg/targets" } }, "$recipe_engine/resultdb/test_presentation": {
diff --git a/infra/config/generated/builders/ci/mac14-tests-dbg/targets/chromium.mac.json b/infra/config/generated/builders/ci/mac15-tests-dbg/targets/chromium.mac.json similarity index 93% rename from infra/config/generated/builders/ci/mac14-tests-dbg/targets/chromium.mac.json rename to infra/config/generated/builders/ci/mac15-tests-dbg/targets/chromium.mac.json index aa2e1bc..f5f6c1c 100644 --- a/infra/config/generated/builders/ci/mac14-tests-dbg/targets/chromium.mac.json +++ b/infra/config/generated/builders/ci/mac15-tests-dbg/targets/chromium.mac.json
@@ -1,5 +1,5 @@ { - "mac14-tests-dbg": { + "mac15-tests-dbg": { "gtest_tests": [ { "merge": { @@ -9,7 +9,7 @@ "swarming": { "dimensions": { "cpu": "x86-64", - "os": "Mac-14|Mac-15" + "os": "Mac-15" }, "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" }, @@ -24,7 +24,7 @@ "swarming": { "dimensions": { "cpu": "x86-64", - "os": "Mac-14|Mac-15" + "os": "Mac-15" }, "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" }, @@ -39,7 +39,7 @@ "swarming": { "dimensions": { "cpu": "x86-64", - "os": "Mac-14|Mac-15" + "os": "Mac-15" }, "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" }, @@ -55,7 +55,7 @@ "swarming": { "dimensions": { "cpu": "x86-64", - "os": "Mac-14|Mac-15" + "os": "Mac-15" }, "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" }, @@ -70,7 +70,7 @@ "swarming": { "dimensions": { "cpu": "x86-64", - "os": "Mac-14|Mac-15" + "os": "Mac-15" }, "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" }, @@ -85,7 +85,7 @@ "swarming": { "dimensions": { "cpu": "x86-64", - "os": "Mac-14|Mac-15" + "os": "Mac-15" }, "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" }, @@ -100,7 +100,7 @@ "swarming": { "dimensions": { "cpu": "x86-64", - "os": "Mac-14|Mac-15" + "os": "Mac-15" }, "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" }, @@ -115,7 +115,7 @@ "swarming": { "dimensions": { "cpu": "x86-64", - "os": "Mac-14|Mac-15" + "os": "Mac-15" }, "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" }, @@ -138,7 +138,7 @@ "swarming": { "dimensions": { "cpu": "x86-64", - "os": "Mac-14|Mac-15" + "os": "Mac-15" }, "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" }, @@ -153,7 +153,7 @@ "swarming": { "dimensions": { "cpu": "x86-64", - "os": "Mac-14|Mac-15" + "os": "Mac-15" }, "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" }, @@ -168,7 +168,7 @@ "swarming": { "dimensions": { "cpu": "x86-64", - "os": "Mac-14|Mac-15" + "os": "Mac-15" }, "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" }, @@ -177,6 +177,25 @@ }, { "args": [ + "--test-launcher-filter-file=../../testing/buildbot/filters/mac.mac-rel.browser_tests.filter" + ], + "merge": { + "script": "//testing/merge_scripts/standard_gtest_merge.py" + }, + "name": "browser_tests", + "swarming": { + "dimensions": { + "cpu": "x86-64", + "os": "Mac-15" + }, + "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com", + "shards": 20 + }, + "test": "browser_tests", + "test_id_prefix": "ninja://chrome/test:browser_tests/" + }, + { + "args": [ "--gtest_filter=-*UsingRealWebcam*" ], "merge": { @@ -186,7 +205,7 @@ "swarming": { "dimensions": { "cpu": "x86-64", - "os": "Mac-14|Mac-15" + "os": "Mac-15" }, "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" }, @@ -201,7 +220,7 @@ "swarming": { "dimensions": { "cpu": "x86-64", - "os": "Mac-14|Mac-15" + "os": "Mac-15" }, "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" }, @@ -216,7 +235,7 @@ "swarming": { "dimensions": { "cpu": "x86-64", - "os": "Mac-14|Mac-15" + "os": "Mac-15" }, "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" }, @@ -231,7 +250,7 @@ "swarming": { "dimensions": { "cpu": "x86-64", - "os": "Mac-14|Mac-15" + "os": "Mac-15" }, "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" }, @@ -246,7 +265,7 @@ "swarming": { "dimensions": { "cpu": "x86-64", - "os": "Mac-14|Mac-15" + "os": "Mac-15" }, "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" }, @@ -261,7 +280,7 @@ "swarming": { "dimensions": { "cpu": "x86-64", - "os": "Mac-14|Mac-15" + "os": "Mac-15" }, "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" }, @@ -276,7 +295,7 @@ "swarming": { "dimensions": { "cpu": "x86-64", - "os": "Mac-14|Mac-15" + "os": "Mac-15" }, "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com", "shards": 3 @@ -292,7 +311,7 @@ "swarming": { "dimensions": { "cpu": "x86-64", - "os": "Mac-14|Mac-15" + "os": "Mac-15" }, "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com", "shards": 18 @@ -308,7 +327,7 @@ "swarming": { "dimensions": { "cpu": "x86-64", - "os": "Mac-14|Mac-15" + "os": "Mac-15" }, "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" }, @@ -323,7 +342,7 @@ "swarming": { "dimensions": { "cpu": "x86-64", - "os": "Mac-14|Mac-15" + "os": "Mac-15" }, "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" }, @@ -338,7 +357,7 @@ "swarming": { "dimensions": { "cpu": "x86-64", - "os": "Mac-14|Mac-15" + "os": "Mac-15" }, "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" }, @@ -353,7 +372,7 @@ "swarming": { "dimensions": { "cpu": "x86-64", - "os": "Mac-14|Mac-15" + "os": "Mac-15" }, "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" }, @@ -368,7 +387,7 @@ "swarming": { "dimensions": { "cpu": "x86-64", - "os": "Mac-14|Mac-15" + "os": "Mac-15" }, "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" }, @@ -383,7 +402,7 @@ "swarming": { "dimensions": { "cpu": "x86-64", - "os": "Mac-14|Mac-15" + "os": "Mac-15" }, "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" }, @@ -398,7 +417,7 @@ "swarming": { "dimensions": { "cpu": "x86-64", - "os": "Mac-14|Mac-15" + "os": "Mac-15" }, "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" }, @@ -414,7 +433,7 @@ "swarming": { "dimensions": { "cpu": "x86-64", - "os": "Mac-14|Mac-15" + "os": "Mac-15" }, "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" }, @@ -429,7 +448,7 @@ "swarming": { "dimensions": { "cpu": "x86-64", - "os": "Mac-14|Mac-15" + "os": "Mac-15" }, "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" }, @@ -444,7 +463,7 @@ "swarming": { "dimensions": { "cpu": "x86-64", - "os": "Mac-14|Mac-15" + "os": "Mac-15" }, "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" }, @@ -459,7 +478,7 @@ "swarming": { "dimensions": { "cpu": "x86-64", - "os": "Mac-14|Mac-15" + "os": "Mac-15" }, "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" }, @@ -474,7 +493,7 @@ "swarming": { "dimensions": { "cpu": "x86-64", - "os": "Mac-14|Mac-15" + "os": "Mac-15" }, "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" }, @@ -489,7 +508,7 @@ "swarming": { "dimensions": { "cpu": "x86-64", - "os": "Mac-14|Mac-15" + "os": "Mac-15" }, "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" }, @@ -504,7 +523,7 @@ "swarming": { "dimensions": { "cpu": "x86-64", - "os": "Mac-14|Mac-15" + "os": "Mac-15" }, "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" }, @@ -519,7 +538,7 @@ "swarming": { "dimensions": { "cpu": "x86-64", - "os": "Mac-14|Mac-15" + "os": "Mac-15" }, "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" }, @@ -534,7 +553,7 @@ "swarming": { "dimensions": { "cpu": "x86-64", - "os": "Mac-14|Mac-15" + "os": "Mac-15" }, "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" }, @@ -549,7 +568,7 @@ "swarming": { "dimensions": { "cpu": "x86-64", - "os": "Mac-14|Mac-15" + "os": "Mac-15" }, "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" }, @@ -564,7 +583,7 @@ "swarming": { "dimensions": { "cpu": "x86-64", - "os": "Mac-14|Mac-15" + "os": "Mac-15" }, "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" }, @@ -579,7 +598,7 @@ "swarming": { "dimensions": { "cpu": "x86-64", - "os": "Mac-14|Mac-15" + "os": "Mac-15" }, "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" }, @@ -594,7 +613,7 @@ "swarming": { "dimensions": { "cpu": "x86-64", - "os": "Mac-14|Mac-15" + "os": "Mac-15" }, "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" }, @@ -609,7 +628,7 @@ "swarming": { "dimensions": { "cpu": "x86-64", - "os": "Mac-14|Mac-15" + "os": "Mac-15" }, "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" }, @@ -625,7 +644,7 @@ "swarming": { "dimensions": { "cpu": "x86-64", - "os": "Mac-14|Mac-15" + "os": "Mac-15" }, "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com", "shards": 12 @@ -641,7 +660,7 @@ "swarming": { "dimensions": { "cpu": "x86-64", - "os": "Mac-14|Mac-15" + "os": "Mac-15" }, "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" }, @@ -656,7 +675,7 @@ "swarming": { "dimensions": { "cpu": "x86-64", - "os": "Mac-14|Mac-15" + "os": "Mac-15" }, "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" }, @@ -672,7 +691,7 @@ "swarming": { "dimensions": { "cpu": "x86-64", - "os": "Mac-14|Mac-15" + "os": "Mac-15" }, "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" }, @@ -687,7 +706,7 @@ "swarming": { "dimensions": { "cpu": "x86-64", - "os": "Mac-14|Mac-15" + "os": "Mac-15" }, "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" }, @@ -702,7 +721,7 @@ "swarming": { "dimensions": { "cpu": "x86-64", - "os": "Mac-14|Mac-15" + "os": "Mac-15" }, "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" }, @@ -717,7 +736,7 @@ "swarming": { "dimensions": { "cpu": "x86-64", - "os": "Mac-14|Mac-15" + "os": "Mac-15" }, "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" }, @@ -732,7 +751,7 @@ "swarming": { "dimensions": { "cpu": "x86-64", - "os": "Mac-14|Mac-15" + "os": "Mac-15" }, "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" }, @@ -747,7 +766,7 @@ "swarming": { "dimensions": { "cpu": "x86-64", - "os": "Mac-14|Mac-15" + "os": "Mac-15" }, "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" }, @@ -762,7 +781,7 @@ "swarming": { "dimensions": { "cpu": "x86-64", - "os": "Mac-14|Mac-15" + "os": "Mac-15" }, "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" }, @@ -777,7 +796,7 @@ "swarming": { "dimensions": { "cpu": "x86-64", - "os": "Mac-14|Mac-15" + "os": "Mac-15" }, "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" }, @@ -792,7 +811,7 @@ "swarming": { "dimensions": { "cpu": "x86-64", - "os": "Mac-14|Mac-15" + "os": "Mac-15" }, "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" }, @@ -807,7 +826,7 @@ "swarming": { "dimensions": { "cpu": "x86-64", - "os": "Mac-14|Mac-15" + "os": "Mac-15" }, "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" }, @@ -822,7 +841,7 @@ "swarming": { "dimensions": { "cpu": "x86-64", - "os": "Mac-14|Mac-15" + "os": "Mac-15" }, "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" }, @@ -837,7 +856,7 @@ "swarming": { "dimensions": { "cpu": "x86-64", - "os": "Mac-14|Mac-15" + "os": "Mac-15" }, "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" }, @@ -852,7 +871,7 @@ "swarming": { "dimensions": { "cpu": "x86-64", - "os": "Mac-14|Mac-15" + "os": "Mac-15" }, "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" }, @@ -867,7 +886,7 @@ "swarming": { "dimensions": { "cpu": "x86-64", - "os": "Mac-14|Mac-15" + "os": "Mac-15" }, "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" }, @@ -882,7 +901,7 @@ "swarming": { "dimensions": { "cpu": "x86-64", - "os": "Mac-14|Mac-15" + "os": "Mac-15" }, "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" }, @@ -897,7 +916,7 @@ "swarming": { "dimensions": { "cpu": "x86-64", - "os": "Mac-14|Mac-15" + "os": "Mac-15" }, "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" }, @@ -912,7 +931,7 @@ "swarming": { "dimensions": { "cpu": "x86-64", - "os": "Mac-14|Mac-15" + "os": "Mac-15" }, "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" }, @@ -927,7 +946,7 @@ "swarming": { "dimensions": { "cpu": "x86-64", - "os": "Mac-14|Mac-15" + "os": "Mac-15" }, "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" }, @@ -942,7 +961,7 @@ "swarming": { "dimensions": { "cpu": "x86-64", - "os": "Mac-14|Mac-15" + "os": "Mac-15" }, "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" }, @@ -957,7 +976,7 @@ "swarming": { "dimensions": { "cpu": "x86-64", - "os": "Mac-14|Mac-15" + "os": "Mac-15" }, "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" }, @@ -972,7 +991,7 @@ "swarming": { "dimensions": { "cpu": "x86-64", - "os": "Mac-14|Mac-15" + "os": "Mac-15" }, "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" }, @@ -987,7 +1006,7 @@ "swarming": { "dimensions": { "cpu": "x86-64", - "os": "Mac-14|Mac-15" + "os": "Mac-15" }, "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" }, @@ -1002,7 +1021,7 @@ "swarming": { "dimensions": { "cpu": "x86-64", - "os": "Mac-14|Mac-15" + "os": "Mac-15" }, "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com", "shards": 3 @@ -1018,7 +1037,7 @@ "swarming": { "dimensions": { "cpu": "x86-64", - "os": "Mac-14|Mac-15" + "os": "Mac-15" }, "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" }, @@ -1033,7 +1052,7 @@ "swarming": { "dimensions": { "cpu": "x86-64", - "os": "Mac-14|Mac-15" + "os": "Mac-15" }, "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" }, @@ -1048,7 +1067,7 @@ "swarming": { "dimensions": { "cpu": "x86-64", - "os": "Mac-14|Mac-15" + "os": "Mac-15" }, "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" }, @@ -1063,7 +1082,7 @@ "swarming": { "dimensions": { "cpu": "x86-64", - "os": "Mac-14|Mac-15" + "os": "Mac-15" }, "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com", "shards": 2 @@ -1079,7 +1098,7 @@ "swarming": { "dimensions": { "cpu": "x86-64", - "os": "Mac-14|Mac-15" + "os": "Mac-15" }, "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" }, @@ -1094,7 +1113,7 @@ "swarming": { "dimensions": { "cpu": "x86-64", - "os": "Mac-14|Mac-15" + "os": "Mac-15" }, "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" }, @@ -1109,7 +1128,7 @@ "swarming": { "dimensions": { "cpu": "x86-64", - "os": "Mac-14|Mac-15" + "os": "Mac-15" }, "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" }, @@ -1124,7 +1143,7 @@ "swarming": { "dimensions": { "cpu": "x86-64", - "os": "Mac-14|Mac-15" + "os": "Mac-15" }, "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" }, @@ -1139,7 +1158,7 @@ "swarming": { "dimensions": { "cpu": "x86-64", - "os": "Mac-14|Mac-15" + "os": "Mac-15" }, "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" }, @@ -1154,7 +1173,7 @@ "swarming": { "dimensions": { "cpu": "x86-64", - "os": "Mac-14|Mac-15" + "os": "Mac-15" }, "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" }, @@ -1169,7 +1188,7 @@ "swarming": { "dimensions": { "cpu": "x86-64", - "os": "Mac-14|Mac-15" + "os": "Mac-15" }, "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" }, @@ -1184,7 +1203,7 @@ "swarming": { "dimensions": { "cpu": "x86-64", - "os": "Mac-14|Mac-15" + "os": "Mac-15" }, "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" }, @@ -1204,7 +1223,7 @@ "swarming": { "dimensions": { "cpu": "x86-64", - "os": "Mac-14|Mac-15" + "os": "Mac-15" }, "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" }, @@ -1231,7 +1250,7 @@ "swarming": { "dimensions": { "cpu": "x86-64", - "os": "Mac-14|Mac-15" + "os": "Mac-15" }, "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com", "shards": 16 @@ -1260,7 +1279,7 @@ "swarming": { "dimensions": { "cpu": "x86-64", - "os": "Mac-14|Mac-15" + "os": "Mac-15" }, "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com", "shards": 8 @@ -1287,7 +1306,7 @@ "swarming": { "dimensions": { "cpu": "x86-64", - "os": "Mac-14|Mac-15" + "os": "Mac-15" }, "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" }, @@ -1306,7 +1325,7 @@ "swarming": { "dimensions": { "cpu": "x86-64", - "os": "Mac-14|Mac-15" + "os": "Mac-15" }, "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" }, @@ -1325,7 +1344,7 @@ "swarming": { "dimensions": { "cpu": "x86-64", - "os": "Mac-14|Mac-15" + "os": "Mac-15" }, "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" }, @@ -1343,7 +1362,7 @@ "swarming": { "dimensions": { "cpu": "x86-64", - "os": "Mac-14|Mac-15" + "os": "Mac-15" }, "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" }, @@ -1375,7 +1394,7 @@ "swarming": { "dimensions": { "cpu": "x86-64", - "os": "Mac-14|Mac-15" + "os": "Mac-15" }, "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com", "shards": 18 @@ -1402,7 +1421,7 @@ "swarming": { "dimensions": { "cpu": "x86-64", - "os": "Mac-14|Mac-15" + "os": "Mac-15" }, "hard_timeout": 960, "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com", @@ -1422,7 +1441,7 @@ "swarming": { "dimensions": { "cpu": "x86-64", - "os": "Mac-14|Mac-15" + "os": "Mac-15" }, "idempotent": false, "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" @@ -1445,7 +1464,7 @@ "swarming": { "dimensions": { "cpu": "x86-64", - "os": "Mac-14|Mac-15" + "os": "Mac-15" }, "idempotent": false, "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com", @@ -1468,7 +1487,7 @@ "swarming": { "dimensions": { "cpu": "x86-64", - "os": "Mac-14|Mac-15" + "os": "Mac-15" }, "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" },
diff --git a/infra/config/generated/builders/try/mac_chromium_compile_dbg_ng/properties.json b/infra/config/generated/builders/try/mac_chromium_compile_dbg_ng/properties.json index 9a803fd..04745d2 100644 --- a/infra/config/generated/builders/try/mac_chromium_compile_dbg_ng/properties.json +++ b/infra/config/generated/builders/try/mac_chromium_compile_dbg_ng/properties.json
@@ -34,7 +34,7 @@ { "builder_id": { "bucket": "ci", - "builder": "mac14-tests-dbg", + "builder": "mac15-tests-dbg", "project": "chromium" }, "builder_spec": { @@ -73,7 +73,7 @@ "builder_ids_in_scope_for_testing": [ { "bucket": "ci", - "builder": "mac14-tests-dbg", + "builder": "mac15-tests-dbg", "project": "chromium" } ],
diff --git a/infra/config/generated/builders/try/mac_chromium_compile_dbg_ng/targets/chromium.mac.json b/infra/config/generated/builders/try/mac_chromium_compile_dbg_ng/targets/chromium.mac.json index a6396c59..cdccbe07 100644 --- a/infra/config/generated/builders/try/mac_chromium_compile_dbg_ng/targets/chromium.mac.json +++ b/infra/config/generated/builders/try/mac_chromium_compile_dbg_ng/targets/chromium.mac.json
@@ -4,7 +4,7 @@ "all" ] }, - "mac14-tests-dbg": { + "mac15-tests-dbg": { "gtest_tests": [ { "merge": { @@ -14,7 +14,7 @@ "swarming": { "dimensions": { "cpu": "x86-64", - "os": "Mac-14|Mac-15" + "os": "Mac-15" }, "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" }, @@ -29,7 +29,7 @@ "swarming": { "dimensions": { "cpu": "x86-64", - "os": "Mac-14|Mac-15" + "os": "Mac-15" }, "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" }, @@ -44,7 +44,7 @@ "swarming": { "dimensions": { "cpu": "x86-64", - "os": "Mac-14|Mac-15" + "os": "Mac-15" }, "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" }, @@ -60,7 +60,7 @@ "swarming": { "dimensions": { "cpu": "x86-64", - "os": "Mac-14|Mac-15" + "os": "Mac-15" }, "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" }, @@ -75,7 +75,7 @@ "swarming": { "dimensions": { "cpu": "x86-64", - "os": "Mac-14|Mac-15" + "os": "Mac-15" }, "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" }, @@ -90,7 +90,7 @@ "swarming": { "dimensions": { "cpu": "x86-64", - "os": "Mac-14|Mac-15" + "os": "Mac-15" }, "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" }, @@ -105,7 +105,7 @@ "swarming": { "dimensions": { "cpu": "x86-64", - "os": "Mac-14|Mac-15" + "os": "Mac-15" }, "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" }, @@ -120,7 +120,7 @@ "swarming": { "dimensions": { "cpu": "x86-64", - "os": "Mac-14|Mac-15" + "os": "Mac-15" }, "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" }, @@ -143,7 +143,7 @@ "swarming": { "dimensions": { "cpu": "x86-64", - "os": "Mac-14|Mac-15" + "os": "Mac-15" }, "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" }, @@ -158,7 +158,7 @@ "swarming": { "dimensions": { "cpu": "x86-64", - "os": "Mac-14|Mac-15" + "os": "Mac-15" }, "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" }, @@ -173,7 +173,7 @@ "swarming": { "dimensions": { "cpu": "x86-64", - "os": "Mac-14|Mac-15" + "os": "Mac-15" }, "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" }, @@ -182,6 +182,25 @@ }, { "args": [ + "--test-launcher-filter-file=../../testing/buildbot/filters/mac.mac-rel.browser_tests.filter" + ], + "merge": { + "script": "//testing/merge_scripts/standard_gtest_merge.py" + }, + "name": "browser_tests", + "swarming": { + "dimensions": { + "cpu": "x86-64", + "os": "Mac-15" + }, + "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com", + "shards": 20 + }, + "test": "browser_tests", + "test_id_prefix": "ninja://chrome/test:browser_tests/" + }, + { + "args": [ "--gtest_filter=-*UsingRealWebcam*" ], "merge": { @@ -191,7 +210,7 @@ "swarming": { "dimensions": { "cpu": "x86-64", - "os": "Mac-14|Mac-15" + "os": "Mac-15" }, "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" }, @@ -206,7 +225,7 @@ "swarming": { "dimensions": { "cpu": "x86-64", - "os": "Mac-14|Mac-15" + "os": "Mac-15" }, "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" }, @@ -221,7 +240,7 @@ "swarming": { "dimensions": { "cpu": "x86-64", - "os": "Mac-14|Mac-15" + "os": "Mac-15" }, "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" }, @@ -236,7 +255,7 @@ "swarming": { "dimensions": { "cpu": "x86-64", - "os": "Mac-14|Mac-15" + "os": "Mac-15" }, "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" }, @@ -251,7 +270,7 @@ "swarming": { "dimensions": { "cpu": "x86-64", - "os": "Mac-14|Mac-15" + "os": "Mac-15" }, "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" }, @@ -266,7 +285,7 @@ "swarming": { "dimensions": { "cpu": "x86-64", - "os": "Mac-14|Mac-15" + "os": "Mac-15" }, "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" }, @@ -281,7 +300,7 @@ "swarming": { "dimensions": { "cpu": "x86-64", - "os": "Mac-14|Mac-15" + "os": "Mac-15" }, "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com", "shards": 3 @@ -297,7 +316,7 @@ "swarming": { "dimensions": { "cpu": "x86-64", - "os": "Mac-14|Mac-15" + "os": "Mac-15" }, "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com", "shards": 18 @@ -313,7 +332,7 @@ "swarming": { "dimensions": { "cpu": "x86-64", - "os": "Mac-14|Mac-15" + "os": "Mac-15" }, "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" }, @@ -328,7 +347,7 @@ "swarming": { "dimensions": { "cpu": "x86-64", - "os": "Mac-14|Mac-15" + "os": "Mac-15" }, "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" }, @@ -343,7 +362,7 @@ "swarming": { "dimensions": { "cpu": "x86-64", - "os": "Mac-14|Mac-15" + "os": "Mac-15" }, "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" }, @@ -358,7 +377,7 @@ "swarming": { "dimensions": { "cpu": "x86-64", - "os": "Mac-14|Mac-15" + "os": "Mac-15" }, "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" }, @@ -373,7 +392,7 @@ "swarming": { "dimensions": { "cpu": "x86-64", - "os": "Mac-14|Mac-15" + "os": "Mac-15" }, "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" }, @@ -388,7 +407,7 @@ "swarming": { "dimensions": { "cpu": "x86-64", - "os": "Mac-14|Mac-15" + "os": "Mac-15" }, "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" }, @@ -403,7 +422,7 @@ "swarming": { "dimensions": { "cpu": "x86-64", - "os": "Mac-14|Mac-15" + "os": "Mac-15" }, "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" }, @@ -419,7 +438,7 @@ "swarming": { "dimensions": { "cpu": "x86-64", - "os": "Mac-14|Mac-15" + "os": "Mac-15" }, "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" }, @@ -434,7 +453,7 @@ "swarming": { "dimensions": { "cpu": "x86-64", - "os": "Mac-14|Mac-15" + "os": "Mac-15" }, "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" }, @@ -449,7 +468,7 @@ "swarming": { "dimensions": { "cpu": "x86-64", - "os": "Mac-14|Mac-15" + "os": "Mac-15" }, "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" }, @@ -464,7 +483,7 @@ "swarming": { "dimensions": { "cpu": "x86-64", - "os": "Mac-14|Mac-15" + "os": "Mac-15" }, "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" }, @@ -479,7 +498,7 @@ "swarming": { "dimensions": { "cpu": "x86-64", - "os": "Mac-14|Mac-15" + "os": "Mac-15" }, "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" }, @@ -494,7 +513,7 @@ "swarming": { "dimensions": { "cpu": "x86-64", - "os": "Mac-14|Mac-15" + "os": "Mac-15" }, "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" }, @@ -509,7 +528,7 @@ "swarming": { "dimensions": { "cpu": "x86-64", - "os": "Mac-14|Mac-15" + "os": "Mac-15" }, "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" }, @@ -524,7 +543,7 @@ "swarming": { "dimensions": { "cpu": "x86-64", - "os": "Mac-14|Mac-15" + "os": "Mac-15" }, "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" }, @@ -539,7 +558,7 @@ "swarming": { "dimensions": { "cpu": "x86-64", - "os": "Mac-14|Mac-15" + "os": "Mac-15" }, "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" }, @@ -554,7 +573,7 @@ "swarming": { "dimensions": { "cpu": "x86-64", - "os": "Mac-14|Mac-15" + "os": "Mac-15" }, "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" }, @@ -569,7 +588,7 @@ "swarming": { "dimensions": { "cpu": "x86-64", - "os": "Mac-14|Mac-15" + "os": "Mac-15" }, "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" }, @@ -584,7 +603,7 @@ "swarming": { "dimensions": { "cpu": "x86-64", - "os": "Mac-14|Mac-15" + "os": "Mac-15" }, "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" }, @@ -599,7 +618,7 @@ "swarming": { "dimensions": { "cpu": "x86-64", - "os": "Mac-14|Mac-15" + "os": "Mac-15" }, "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" }, @@ -614,7 +633,7 @@ "swarming": { "dimensions": { "cpu": "x86-64", - "os": "Mac-14|Mac-15" + "os": "Mac-15" }, "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" }, @@ -630,7 +649,7 @@ "swarming": { "dimensions": { "cpu": "x86-64", - "os": "Mac-14|Mac-15" + "os": "Mac-15" }, "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com", "shards": 12 @@ -646,7 +665,7 @@ "swarming": { "dimensions": { "cpu": "x86-64", - "os": "Mac-14|Mac-15" + "os": "Mac-15" }, "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" }, @@ -661,7 +680,7 @@ "swarming": { "dimensions": { "cpu": "x86-64", - "os": "Mac-14|Mac-15" + "os": "Mac-15" }, "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" }, @@ -677,7 +696,7 @@ "swarming": { "dimensions": { "cpu": "x86-64", - "os": "Mac-14|Mac-15" + "os": "Mac-15" }, "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" }, @@ -692,7 +711,7 @@ "swarming": { "dimensions": { "cpu": "x86-64", - "os": "Mac-14|Mac-15" + "os": "Mac-15" }, "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" }, @@ -707,7 +726,7 @@ "swarming": { "dimensions": { "cpu": "x86-64", - "os": "Mac-14|Mac-15" + "os": "Mac-15" }, "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" }, @@ -722,7 +741,7 @@ "swarming": { "dimensions": { "cpu": "x86-64", - "os": "Mac-14|Mac-15" + "os": "Mac-15" }, "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" }, @@ -737,7 +756,7 @@ "swarming": { "dimensions": { "cpu": "x86-64", - "os": "Mac-14|Mac-15" + "os": "Mac-15" }, "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" }, @@ -752,7 +771,7 @@ "swarming": { "dimensions": { "cpu": "x86-64", - "os": "Mac-14|Mac-15" + "os": "Mac-15" }, "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" }, @@ -767,7 +786,7 @@ "swarming": { "dimensions": { "cpu": "x86-64", - "os": "Mac-14|Mac-15" + "os": "Mac-15" }, "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" }, @@ -782,7 +801,7 @@ "swarming": { "dimensions": { "cpu": "x86-64", - "os": "Mac-14|Mac-15" + "os": "Mac-15" }, "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" }, @@ -797,7 +816,7 @@ "swarming": { "dimensions": { "cpu": "x86-64", - "os": "Mac-14|Mac-15" + "os": "Mac-15" }, "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" }, @@ -812,7 +831,7 @@ "swarming": { "dimensions": { "cpu": "x86-64", - "os": "Mac-14|Mac-15" + "os": "Mac-15" }, "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" }, @@ -827,7 +846,7 @@ "swarming": { "dimensions": { "cpu": "x86-64", - "os": "Mac-14|Mac-15" + "os": "Mac-15" }, "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" }, @@ -842,7 +861,7 @@ "swarming": { "dimensions": { "cpu": "x86-64", - "os": "Mac-14|Mac-15" + "os": "Mac-15" }, "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" }, @@ -857,7 +876,7 @@ "swarming": { "dimensions": { "cpu": "x86-64", - "os": "Mac-14|Mac-15" + "os": "Mac-15" }, "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" }, @@ -872,7 +891,7 @@ "swarming": { "dimensions": { "cpu": "x86-64", - "os": "Mac-14|Mac-15" + "os": "Mac-15" }, "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" }, @@ -887,7 +906,7 @@ "swarming": { "dimensions": { "cpu": "x86-64", - "os": "Mac-14|Mac-15" + "os": "Mac-15" }, "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" }, @@ -902,7 +921,7 @@ "swarming": { "dimensions": { "cpu": "x86-64", - "os": "Mac-14|Mac-15" + "os": "Mac-15" }, "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" }, @@ -917,7 +936,7 @@ "swarming": { "dimensions": { "cpu": "x86-64", - "os": "Mac-14|Mac-15" + "os": "Mac-15" }, "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" }, @@ -932,7 +951,7 @@ "swarming": { "dimensions": { "cpu": "x86-64", - "os": "Mac-14|Mac-15" + "os": "Mac-15" }, "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" }, @@ -947,7 +966,7 @@ "swarming": { "dimensions": { "cpu": "x86-64", - "os": "Mac-14|Mac-15" + "os": "Mac-15" }, "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" }, @@ -962,7 +981,7 @@ "swarming": { "dimensions": { "cpu": "x86-64", - "os": "Mac-14|Mac-15" + "os": "Mac-15" }, "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" }, @@ -977,7 +996,7 @@ "swarming": { "dimensions": { "cpu": "x86-64", - "os": "Mac-14|Mac-15" + "os": "Mac-15" }, "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" }, @@ -992,7 +1011,7 @@ "swarming": { "dimensions": { "cpu": "x86-64", - "os": "Mac-14|Mac-15" + "os": "Mac-15" }, "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" }, @@ -1007,7 +1026,7 @@ "swarming": { "dimensions": { "cpu": "x86-64", - "os": "Mac-14|Mac-15" + "os": "Mac-15" }, "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com", "shards": 3 @@ -1023,7 +1042,7 @@ "swarming": { "dimensions": { "cpu": "x86-64", - "os": "Mac-14|Mac-15" + "os": "Mac-15" }, "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" }, @@ -1038,7 +1057,7 @@ "swarming": { "dimensions": { "cpu": "x86-64", - "os": "Mac-14|Mac-15" + "os": "Mac-15" }, "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" }, @@ -1053,7 +1072,7 @@ "swarming": { "dimensions": { "cpu": "x86-64", - "os": "Mac-14|Mac-15" + "os": "Mac-15" }, "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" }, @@ -1068,7 +1087,7 @@ "swarming": { "dimensions": { "cpu": "x86-64", - "os": "Mac-14|Mac-15" + "os": "Mac-15" }, "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com", "shards": 2 @@ -1084,7 +1103,7 @@ "swarming": { "dimensions": { "cpu": "x86-64", - "os": "Mac-14|Mac-15" + "os": "Mac-15" }, "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" }, @@ -1099,7 +1118,7 @@ "swarming": { "dimensions": { "cpu": "x86-64", - "os": "Mac-14|Mac-15" + "os": "Mac-15" }, "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" }, @@ -1114,7 +1133,7 @@ "swarming": { "dimensions": { "cpu": "x86-64", - "os": "Mac-14|Mac-15" + "os": "Mac-15" }, "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" }, @@ -1129,7 +1148,7 @@ "swarming": { "dimensions": { "cpu": "x86-64", - "os": "Mac-14|Mac-15" + "os": "Mac-15" }, "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" }, @@ -1144,7 +1163,7 @@ "swarming": { "dimensions": { "cpu": "x86-64", - "os": "Mac-14|Mac-15" + "os": "Mac-15" }, "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" }, @@ -1159,7 +1178,7 @@ "swarming": { "dimensions": { "cpu": "x86-64", - "os": "Mac-14|Mac-15" + "os": "Mac-15" }, "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" }, @@ -1174,7 +1193,7 @@ "swarming": { "dimensions": { "cpu": "x86-64", - "os": "Mac-14|Mac-15" + "os": "Mac-15" }, "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" }, @@ -1189,7 +1208,7 @@ "swarming": { "dimensions": { "cpu": "x86-64", - "os": "Mac-14|Mac-15" + "os": "Mac-15" }, "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" }, @@ -1209,7 +1228,7 @@ "swarming": { "dimensions": { "cpu": "x86-64", - "os": "Mac-14|Mac-15" + "os": "Mac-15" }, "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" }, @@ -1236,7 +1255,7 @@ "swarming": { "dimensions": { "cpu": "x86-64", - "os": "Mac-14|Mac-15" + "os": "Mac-15" }, "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com", "shards": 16 @@ -1265,7 +1284,7 @@ "swarming": { "dimensions": { "cpu": "x86-64", - "os": "Mac-14|Mac-15" + "os": "Mac-15" }, "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com", "shards": 8 @@ -1292,7 +1311,7 @@ "swarming": { "dimensions": { "cpu": "x86-64", - "os": "Mac-14|Mac-15" + "os": "Mac-15" }, "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" }, @@ -1311,7 +1330,7 @@ "swarming": { "dimensions": { "cpu": "x86-64", - "os": "Mac-14|Mac-15" + "os": "Mac-15" }, "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" }, @@ -1330,7 +1349,7 @@ "swarming": { "dimensions": { "cpu": "x86-64", - "os": "Mac-14|Mac-15" + "os": "Mac-15" }, "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" }, @@ -1348,7 +1367,7 @@ "swarming": { "dimensions": { "cpu": "x86-64", - "os": "Mac-14|Mac-15" + "os": "Mac-15" }, "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" }, @@ -1380,7 +1399,7 @@ "swarming": { "dimensions": { "cpu": "x86-64", - "os": "Mac-14|Mac-15" + "os": "Mac-15" }, "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com", "shards": 18 @@ -1407,7 +1426,7 @@ "swarming": { "dimensions": { "cpu": "x86-64", - "os": "Mac-14|Mac-15" + "os": "Mac-15" }, "hard_timeout": 960, "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com", @@ -1427,7 +1446,7 @@ "swarming": { "dimensions": { "cpu": "x86-64", - "os": "Mac-14|Mac-15" + "os": "Mac-15" }, "idempotent": false, "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" @@ -1450,7 +1469,7 @@ "swarming": { "dimensions": { "cpu": "x86-64", - "os": "Mac-14|Mac-15" + "os": "Mac-15" }, "idempotent": false, "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com", @@ -1473,7 +1492,7 @@ "swarming": { "dimensions": { "cpu": "x86-64", - "os": "Mac-14|Mac-15" + "os": "Mac-15" }, "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" },
diff --git a/infra/config/generated/builders/try/mac_chromium_dbg_ng/properties.json b/infra/config/generated/builders/try/mac_chromium_dbg_ng/properties.json index 29d0faa..6e6e648 100644 --- a/infra/config/generated/builders/try/mac_chromium_dbg_ng/properties.json +++ b/infra/config/generated/builders/try/mac_chromium_dbg_ng/properties.json
@@ -34,7 +34,7 @@ { "builder_id": { "bucket": "ci", - "builder": "mac14-tests-dbg", + "builder": "mac15-tests-dbg", "project": "chromium" }, "builder_spec": { @@ -73,7 +73,7 @@ "builder_ids_in_scope_for_testing": [ { "bucket": "ci", - "builder": "mac14-tests-dbg", + "builder": "mac15-tests-dbg", "project": "chromium" } ],
diff --git a/infra/config/generated/builders/try/mac_chromium_dbg_ng/targets/chromium.mac.json b/infra/config/generated/builders/try/mac_chromium_dbg_ng/targets/chromium.mac.json index a6396c59..cdccbe07 100644 --- a/infra/config/generated/builders/try/mac_chromium_dbg_ng/targets/chromium.mac.json +++ b/infra/config/generated/builders/try/mac_chromium_dbg_ng/targets/chromium.mac.json
@@ -4,7 +4,7 @@ "all" ] }, - "mac14-tests-dbg": { + "mac15-tests-dbg": { "gtest_tests": [ { "merge": { @@ -14,7 +14,7 @@ "swarming": { "dimensions": { "cpu": "x86-64", - "os": "Mac-14|Mac-15" + "os": "Mac-15" }, "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" }, @@ -29,7 +29,7 @@ "swarming": { "dimensions": { "cpu": "x86-64", - "os": "Mac-14|Mac-15" + "os": "Mac-15" }, "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" }, @@ -44,7 +44,7 @@ "swarming": { "dimensions": { "cpu": "x86-64", - "os": "Mac-14|Mac-15" + "os": "Mac-15" }, "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" }, @@ -60,7 +60,7 @@ "swarming": { "dimensions": { "cpu": "x86-64", - "os": "Mac-14|Mac-15" + "os": "Mac-15" }, "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" }, @@ -75,7 +75,7 @@ "swarming": { "dimensions": { "cpu": "x86-64", - "os": "Mac-14|Mac-15" + "os": "Mac-15" }, "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" }, @@ -90,7 +90,7 @@ "swarming": { "dimensions": { "cpu": "x86-64", - "os": "Mac-14|Mac-15" + "os": "Mac-15" }, "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" }, @@ -105,7 +105,7 @@ "swarming": { "dimensions": { "cpu": "x86-64", - "os": "Mac-14|Mac-15" + "os": "Mac-15" }, "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" }, @@ -120,7 +120,7 @@ "swarming": { "dimensions": { "cpu": "x86-64", - "os": "Mac-14|Mac-15" + "os": "Mac-15" }, "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" }, @@ -143,7 +143,7 @@ "swarming": { "dimensions": { "cpu": "x86-64", - "os": "Mac-14|Mac-15" + "os": "Mac-15" }, "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" }, @@ -158,7 +158,7 @@ "swarming": { "dimensions": { "cpu": "x86-64", - "os": "Mac-14|Mac-15" + "os": "Mac-15" }, "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" }, @@ -173,7 +173,7 @@ "swarming": { "dimensions": { "cpu": "x86-64", - "os": "Mac-14|Mac-15" + "os": "Mac-15" }, "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" }, @@ -182,6 +182,25 @@ }, { "args": [ + "--test-launcher-filter-file=../../testing/buildbot/filters/mac.mac-rel.browser_tests.filter" + ], + "merge": { + "script": "//testing/merge_scripts/standard_gtest_merge.py" + }, + "name": "browser_tests", + "swarming": { + "dimensions": { + "cpu": "x86-64", + "os": "Mac-15" + }, + "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com", + "shards": 20 + }, + "test": "browser_tests", + "test_id_prefix": "ninja://chrome/test:browser_tests/" + }, + { + "args": [ "--gtest_filter=-*UsingRealWebcam*" ], "merge": { @@ -191,7 +210,7 @@ "swarming": { "dimensions": { "cpu": "x86-64", - "os": "Mac-14|Mac-15" + "os": "Mac-15" }, "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" }, @@ -206,7 +225,7 @@ "swarming": { "dimensions": { "cpu": "x86-64", - "os": "Mac-14|Mac-15" + "os": "Mac-15" }, "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" }, @@ -221,7 +240,7 @@ "swarming": { "dimensions": { "cpu": "x86-64", - "os": "Mac-14|Mac-15" + "os": "Mac-15" }, "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" }, @@ -236,7 +255,7 @@ "swarming": { "dimensions": { "cpu": "x86-64", - "os": "Mac-14|Mac-15" + "os": "Mac-15" }, "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" }, @@ -251,7 +270,7 @@ "swarming": { "dimensions": { "cpu": "x86-64", - "os": "Mac-14|Mac-15" + "os": "Mac-15" }, "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" }, @@ -266,7 +285,7 @@ "swarming": { "dimensions": { "cpu": "x86-64", - "os": "Mac-14|Mac-15" + "os": "Mac-15" }, "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" }, @@ -281,7 +300,7 @@ "swarming": { "dimensions": { "cpu": "x86-64", - "os": "Mac-14|Mac-15" + "os": "Mac-15" }, "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com", "shards": 3 @@ -297,7 +316,7 @@ "swarming": { "dimensions": { "cpu": "x86-64", - "os": "Mac-14|Mac-15" + "os": "Mac-15" }, "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com", "shards": 18 @@ -313,7 +332,7 @@ "swarming": { "dimensions": { "cpu": "x86-64", - "os": "Mac-14|Mac-15" + "os": "Mac-15" }, "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" }, @@ -328,7 +347,7 @@ "swarming": { "dimensions": { "cpu": "x86-64", - "os": "Mac-14|Mac-15" + "os": "Mac-15" }, "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" }, @@ -343,7 +362,7 @@ "swarming": { "dimensions": { "cpu": "x86-64", - "os": "Mac-14|Mac-15" + "os": "Mac-15" }, "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" }, @@ -358,7 +377,7 @@ "swarming": { "dimensions": { "cpu": "x86-64", - "os": "Mac-14|Mac-15" + "os": "Mac-15" }, "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" }, @@ -373,7 +392,7 @@ "swarming": { "dimensions": { "cpu": "x86-64", - "os": "Mac-14|Mac-15" + "os": "Mac-15" }, "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" }, @@ -388,7 +407,7 @@ "swarming": { "dimensions": { "cpu": "x86-64", - "os": "Mac-14|Mac-15" + "os": "Mac-15" }, "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" }, @@ -403,7 +422,7 @@ "swarming": { "dimensions": { "cpu": "x86-64", - "os": "Mac-14|Mac-15" + "os": "Mac-15" }, "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" }, @@ -419,7 +438,7 @@ "swarming": { "dimensions": { "cpu": "x86-64", - "os": "Mac-14|Mac-15" + "os": "Mac-15" }, "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" }, @@ -434,7 +453,7 @@ "swarming": { "dimensions": { "cpu": "x86-64", - "os": "Mac-14|Mac-15" + "os": "Mac-15" }, "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" }, @@ -449,7 +468,7 @@ "swarming": { "dimensions": { "cpu": "x86-64", - "os": "Mac-14|Mac-15" + "os": "Mac-15" }, "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" }, @@ -464,7 +483,7 @@ "swarming": { "dimensions": { "cpu": "x86-64", - "os": "Mac-14|Mac-15" + "os": "Mac-15" }, "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" }, @@ -479,7 +498,7 @@ "swarming": { "dimensions": { "cpu": "x86-64", - "os": "Mac-14|Mac-15" + "os": "Mac-15" }, "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" }, @@ -494,7 +513,7 @@ "swarming": { "dimensions": { "cpu": "x86-64", - "os": "Mac-14|Mac-15" + "os": "Mac-15" }, "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" }, @@ -509,7 +528,7 @@ "swarming": { "dimensions": { "cpu": "x86-64", - "os": "Mac-14|Mac-15" + "os": "Mac-15" }, "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" }, @@ -524,7 +543,7 @@ "swarming": { "dimensions": { "cpu": "x86-64", - "os": "Mac-14|Mac-15" + "os": "Mac-15" }, "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" }, @@ -539,7 +558,7 @@ "swarming": { "dimensions": { "cpu": "x86-64", - "os": "Mac-14|Mac-15" + "os": "Mac-15" }, "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" }, @@ -554,7 +573,7 @@ "swarming": { "dimensions": { "cpu": "x86-64", - "os": "Mac-14|Mac-15" + "os": "Mac-15" }, "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" }, @@ -569,7 +588,7 @@ "swarming": { "dimensions": { "cpu": "x86-64", - "os": "Mac-14|Mac-15" + "os": "Mac-15" }, "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" }, @@ -584,7 +603,7 @@ "swarming": { "dimensions": { "cpu": "x86-64", - "os": "Mac-14|Mac-15" + "os": "Mac-15" }, "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" }, @@ -599,7 +618,7 @@ "swarming": { "dimensions": { "cpu": "x86-64", - "os": "Mac-14|Mac-15" + "os": "Mac-15" }, "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" }, @@ -614,7 +633,7 @@ "swarming": { "dimensions": { "cpu": "x86-64", - "os": "Mac-14|Mac-15" + "os": "Mac-15" }, "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" }, @@ -630,7 +649,7 @@ "swarming": { "dimensions": { "cpu": "x86-64", - "os": "Mac-14|Mac-15" + "os": "Mac-15" }, "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com", "shards": 12 @@ -646,7 +665,7 @@ "swarming": { "dimensions": { "cpu": "x86-64", - "os": "Mac-14|Mac-15" + "os": "Mac-15" }, "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" }, @@ -661,7 +680,7 @@ "swarming": { "dimensions": { "cpu": "x86-64", - "os": "Mac-14|Mac-15" + "os": "Mac-15" }, "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" }, @@ -677,7 +696,7 @@ "swarming": { "dimensions": { "cpu": "x86-64", - "os": "Mac-14|Mac-15" + "os": "Mac-15" }, "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" }, @@ -692,7 +711,7 @@ "swarming": { "dimensions": { "cpu": "x86-64", - "os": "Mac-14|Mac-15" + "os": "Mac-15" }, "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" }, @@ -707,7 +726,7 @@ "swarming": { "dimensions": { "cpu": "x86-64", - "os": "Mac-14|Mac-15" + "os": "Mac-15" }, "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" }, @@ -722,7 +741,7 @@ "swarming": { "dimensions": { "cpu": "x86-64", - "os": "Mac-14|Mac-15" + "os": "Mac-15" }, "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" }, @@ -737,7 +756,7 @@ "swarming": { "dimensions": { "cpu": "x86-64", - "os": "Mac-14|Mac-15" + "os": "Mac-15" }, "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" }, @@ -752,7 +771,7 @@ "swarming": { "dimensions": { "cpu": "x86-64", - "os": "Mac-14|Mac-15" + "os": "Mac-15" }, "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" }, @@ -767,7 +786,7 @@ "swarming": { "dimensions": { "cpu": "x86-64", - "os": "Mac-14|Mac-15" + "os": "Mac-15" }, "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" }, @@ -782,7 +801,7 @@ "swarming": { "dimensions": { "cpu": "x86-64", - "os": "Mac-14|Mac-15" + "os": "Mac-15" }, "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" }, @@ -797,7 +816,7 @@ "swarming": { "dimensions": { "cpu": "x86-64", - "os": "Mac-14|Mac-15" + "os": "Mac-15" }, "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" }, @@ -812,7 +831,7 @@ "swarming": { "dimensions": { "cpu": "x86-64", - "os": "Mac-14|Mac-15" + "os": "Mac-15" }, "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" }, @@ -827,7 +846,7 @@ "swarming": { "dimensions": { "cpu": "x86-64", - "os": "Mac-14|Mac-15" + "os": "Mac-15" }, "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" }, @@ -842,7 +861,7 @@ "swarming": { "dimensions": { "cpu": "x86-64", - "os": "Mac-14|Mac-15" + "os": "Mac-15" }, "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" }, @@ -857,7 +876,7 @@ "swarming": { "dimensions": { "cpu": "x86-64", - "os": "Mac-14|Mac-15" + "os": "Mac-15" }, "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" }, @@ -872,7 +891,7 @@ "swarming": { "dimensions": { "cpu": "x86-64", - "os": "Mac-14|Mac-15" + "os": "Mac-15" }, "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" }, @@ -887,7 +906,7 @@ "swarming": { "dimensions": { "cpu": "x86-64", - "os": "Mac-14|Mac-15" + "os": "Mac-15" }, "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" }, @@ -902,7 +921,7 @@ "swarming": { "dimensions": { "cpu": "x86-64", - "os": "Mac-14|Mac-15" + "os": "Mac-15" }, "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" }, @@ -917,7 +936,7 @@ "swarming": { "dimensions": { "cpu": "x86-64", - "os": "Mac-14|Mac-15" + "os": "Mac-15" }, "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" }, @@ -932,7 +951,7 @@ "swarming": { "dimensions": { "cpu": "x86-64", - "os": "Mac-14|Mac-15" + "os": "Mac-15" }, "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" }, @@ -947,7 +966,7 @@ "swarming": { "dimensions": { "cpu": "x86-64", - "os": "Mac-14|Mac-15" + "os": "Mac-15" }, "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" }, @@ -962,7 +981,7 @@ "swarming": { "dimensions": { "cpu": "x86-64", - "os": "Mac-14|Mac-15" + "os": "Mac-15" }, "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" }, @@ -977,7 +996,7 @@ "swarming": { "dimensions": { "cpu": "x86-64", - "os": "Mac-14|Mac-15" + "os": "Mac-15" }, "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" }, @@ -992,7 +1011,7 @@ "swarming": { "dimensions": { "cpu": "x86-64", - "os": "Mac-14|Mac-15" + "os": "Mac-15" }, "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" }, @@ -1007,7 +1026,7 @@ "swarming": { "dimensions": { "cpu": "x86-64", - "os": "Mac-14|Mac-15" + "os": "Mac-15" }, "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com", "shards": 3 @@ -1023,7 +1042,7 @@ "swarming": { "dimensions": { "cpu": "x86-64", - "os": "Mac-14|Mac-15" + "os": "Mac-15" }, "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" }, @@ -1038,7 +1057,7 @@ "swarming": { "dimensions": { "cpu": "x86-64", - "os": "Mac-14|Mac-15" + "os": "Mac-15" }, "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" }, @@ -1053,7 +1072,7 @@ "swarming": { "dimensions": { "cpu": "x86-64", - "os": "Mac-14|Mac-15" + "os": "Mac-15" }, "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" }, @@ -1068,7 +1087,7 @@ "swarming": { "dimensions": { "cpu": "x86-64", - "os": "Mac-14|Mac-15" + "os": "Mac-15" }, "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com", "shards": 2 @@ -1084,7 +1103,7 @@ "swarming": { "dimensions": { "cpu": "x86-64", - "os": "Mac-14|Mac-15" + "os": "Mac-15" }, "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" }, @@ -1099,7 +1118,7 @@ "swarming": { "dimensions": { "cpu": "x86-64", - "os": "Mac-14|Mac-15" + "os": "Mac-15" }, "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" }, @@ -1114,7 +1133,7 @@ "swarming": { "dimensions": { "cpu": "x86-64", - "os": "Mac-14|Mac-15" + "os": "Mac-15" }, "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" }, @@ -1129,7 +1148,7 @@ "swarming": { "dimensions": { "cpu": "x86-64", - "os": "Mac-14|Mac-15" + "os": "Mac-15" }, "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" }, @@ -1144,7 +1163,7 @@ "swarming": { "dimensions": { "cpu": "x86-64", - "os": "Mac-14|Mac-15" + "os": "Mac-15" }, "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" }, @@ -1159,7 +1178,7 @@ "swarming": { "dimensions": { "cpu": "x86-64", - "os": "Mac-14|Mac-15" + "os": "Mac-15" }, "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" }, @@ -1174,7 +1193,7 @@ "swarming": { "dimensions": { "cpu": "x86-64", - "os": "Mac-14|Mac-15" + "os": "Mac-15" }, "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" }, @@ -1189,7 +1208,7 @@ "swarming": { "dimensions": { "cpu": "x86-64", - "os": "Mac-14|Mac-15" + "os": "Mac-15" }, "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" }, @@ -1209,7 +1228,7 @@ "swarming": { "dimensions": { "cpu": "x86-64", - "os": "Mac-14|Mac-15" + "os": "Mac-15" }, "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" }, @@ -1236,7 +1255,7 @@ "swarming": { "dimensions": { "cpu": "x86-64", - "os": "Mac-14|Mac-15" + "os": "Mac-15" }, "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com", "shards": 16 @@ -1265,7 +1284,7 @@ "swarming": { "dimensions": { "cpu": "x86-64", - "os": "Mac-14|Mac-15" + "os": "Mac-15" }, "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com", "shards": 8 @@ -1292,7 +1311,7 @@ "swarming": { "dimensions": { "cpu": "x86-64", - "os": "Mac-14|Mac-15" + "os": "Mac-15" }, "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" }, @@ -1311,7 +1330,7 @@ "swarming": { "dimensions": { "cpu": "x86-64", - "os": "Mac-14|Mac-15" + "os": "Mac-15" }, "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" }, @@ -1330,7 +1349,7 @@ "swarming": { "dimensions": { "cpu": "x86-64", - "os": "Mac-14|Mac-15" + "os": "Mac-15" }, "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" }, @@ -1348,7 +1367,7 @@ "swarming": { "dimensions": { "cpu": "x86-64", - "os": "Mac-14|Mac-15" + "os": "Mac-15" }, "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" }, @@ -1380,7 +1399,7 @@ "swarming": { "dimensions": { "cpu": "x86-64", - "os": "Mac-14|Mac-15" + "os": "Mac-15" }, "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com", "shards": 18 @@ -1407,7 +1426,7 @@ "swarming": { "dimensions": { "cpu": "x86-64", - "os": "Mac-14|Mac-15" + "os": "Mac-15" }, "hard_timeout": 960, "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com", @@ -1427,7 +1446,7 @@ "swarming": { "dimensions": { "cpu": "x86-64", - "os": "Mac-14|Mac-15" + "os": "Mac-15" }, "idempotent": false, "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" @@ -1450,7 +1469,7 @@ "swarming": { "dimensions": { "cpu": "x86-64", - "os": "Mac-14|Mac-15" + "os": "Mac-15" }, "idempotent": false, "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com", @@ -1473,7 +1492,7 @@ "swarming": { "dimensions": { "cpu": "x86-64", - "os": "Mac-14|Mac-15" + "os": "Mac-15" }, "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" },
diff --git a/infra/config/generated/health-specs/health-specs.json b/infra/config/generated/health-specs/health-specs.json index ee42c28..692947c 100644 --- a/infra/config/generated/health-specs/health-specs.json +++ b/infra/config/generated/health-specs/health-specs.json
@@ -12062,27 +12062,6 @@ } ] }, - "mac14-tests-dbg": { - "contact_team_email": "bling-engprod@google.com", - "problem_specs": [ - { - "name": "Unhealthy", - "period_days": 7, - "score": 5, - "thresholds": { - "_default": "_default" - } - }, - { - "name": "Low Value", - "period_days": 90, - "score": 1, - "thresholds": { - "_default": "_default" - } - } - ] - }, "mac14-x64-updater-tester-rel": { "contact_team_email": "omaha@google.com", "problem_specs": [ @@ -12167,6 +12146,27 @@ } ] }, + "mac15-tests-dbg": { + "contact_team_email": "bling-engprod@google.com", + "problem_specs": [ + { + "name": "Unhealthy", + "period_days": 7, + "score": 5, + "thresholds": { + "_default": "_default" + } + }, + { + "name": "Low Value", + "period_days": 90, + "score": 1, + "thresholds": { + "_default": "_default" + } + } + ] + }, "mac15-x64-rel-tests": { "contact_team_email": "bling-engprod@google.com", "problem_specs": [
diff --git a/infra/config/generated/luci/cr-buildbucket.cfg b/infra/config/generated/luci/cr-buildbucket.cfg index da49efb..722e8fa 100644 --- a/infra/config/generated/luci/cr-buildbucket.cfg +++ b/infra/config/generated/luci/cr-buildbucket.cfg
@@ -67485,116 +67485,6 @@ } } builders { - name: "mac14-tests-dbg" - swarming_host: "chromium-swarm.appspot.com" - dimensions: "builderless:1" - dimensions: "cores:8" - dimensions: "cpu:x86-64" - dimensions: "free_space:standard" - dimensions: "os:Ubuntu-22.04" - dimensions: "pool:luci.chromium.ci" - dimensions: "ssd:0" - exe { - cipd_package: "infra/chromium/bootstrapper/${platform}" - cipd_version: "latest" - cmd: "bootstrapper" - } - properties: - '{' - ' "$bootstrap/exe": {' - ' "exe": {' - ' "cipd_package": "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build",' - ' "cipd_version": "refs/heads/main",' - ' "cmd": [' - ' "luciexe"' - ' ]' - ' }' - ' },' - ' "$bootstrap/properties": {' - ' "properties_file": "infra/config/generated/builders/ci/mac14-tests-dbg/properties.json",' - ' "top_level_project": {' - ' "ref": "refs/heads/main",' - ' "repo": {' - ' "host": "chromium.googlesource.com",' - ' "project": "chromium/src"' - ' }' - ' }' - ' },' - ' "builder_group": "chromium.mac",' - ' "led_builder_is_bootstrapped": true,' - ' "recipe": "chromium"' - '}' - execution_timeout_secs: 10800 - build_numbers: YES - service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com" - experiments { - key: "chromium.use_per_builder_build_dir_name" - value: 100 - } - experiments { - key: "luci.recipes.use_python3" - value: 100 - } - resultdb { - enable: true - bq_exports { - project: "chrome-luci-data" - dataset: "chromium" - table: "ci_test_results" - test_results {} - } - bq_exports { - project: "chrome-luci-data" - dataset: "chromium" - table: "gpu_ci_test_results" - test_results { - predicate { - test_id_regexp: "ninja://(chrome|content)/test:telemetry_gpu_integration_test[^/]*/.+" - } - } - } - bq_exports { - project: "chrome-luci-data" - dataset: "chromium" - table: "blink_web_tests_ci_test_results" - test_results { - predicate { - test_id_regexp: "(ninja://[^/]*blink_web_tests/.+)|(ninja://[^/]*_wpt_tests/.+)|(ninja://[^/]*headless_shell_wpt/.+)" - } - } - } - history_options { - use_invocation_timestamp: true - } - } - description_html: "Runs Mac 14 tests with debug config.<br/>This builder is mirrored by any of the following try builders:<br/><ul><li><a href=\"https://ci.chromium.org/p/chromium/builders/try/mac_chromium_compile_dbg_ng\">mac_chromium_compile_dbg_ng</a></li><li><a href=\"https://ci.chromium.org/p/chromium/builders/try/mac_chromium_dbg_ng\">mac_chromium_dbg_ng</a></li></ul><br/>Builder owner: <a href=mailto:bling-engprod@google.com>bling-engprod@google.com</a>" - shadow_builder_adjustments { - service_account: "chromium-try-builder@chops-service-accounts.iam.gserviceaccount.com" - pool: "luci.chromium.try" - dimensions: "free_space:" - dimensions: "pool:luci.chromium.try" - } - contact_team_email: "bling-engprod@google.com" - custom_metric_definitions { - name: "/chrome/infra/browser/builds/cached_count" - predicates: "has(build.output.properties.is_cached)" - predicates: "string(build.output.properties.is_cached) == \"true\"" - } - custom_metric_definitions { - name: "/chrome/infra/browser/builds/ran_tests_retry_shard_count" - predicates: "has(build.output.properties.ran_tests_retry_shard)" - } - custom_metric_definitions { - name: "/chrome/infra/browser/builds/ran_tests_without_patch_count" - predicates: "has(build.output.properties.ran_tests_without_patch)" - } - custom_metric_definitions { - name: "/chrome/infra/browser/builds/uncached_count" - predicates: "has(build.output.properties.is_cached)" - predicates: "string(build.output.properties.is_cached) == \"false\"" - } - } - builders { name: "mac14-x64-updater-tester-rel" swarming_host: "chromium-swarm.appspot.com" dimensions: "builderless:1" @@ -68035,6 +67925,116 @@ } } builders { + name: "mac15-tests-dbg" + swarming_host: "chromium-swarm.appspot.com" + dimensions: "builderless:1" + dimensions: "cores:8" + dimensions: "cpu:x86-64" + dimensions: "free_space:standard" + dimensions: "os:Ubuntu-22.04" + dimensions: "pool:luci.chromium.ci" + dimensions: "ssd:0" + exe { + cipd_package: "infra/chromium/bootstrapper/${platform}" + cipd_version: "latest" + cmd: "bootstrapper" + } + properties: + '{' + ' "$bootstrap/exe": {' + ' "exe": {' + ' "cipd_package": "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build",' + ' "cipd_version": "refs/heads/main",' + ' "cmd": [' + ' "luciexe"' + ' ]' + ' }' + ' },' + ' "$bootstrap/properties": {' + ' "properties_file": "infra/config/generated/builders/ci/mac15-tests-dbg/properties.json",' + ' "top_level_project": {' + ' "ref": "refs/heads/main",' + ' "repo": {' + ' "host": "chromium.googlesource.com",' + ' "project": "chromium/src"' + ' }' + ' }' + ' },' + ' "builder_group": "chromium.mac",' + ' "led_builder_is_bootstrapped": true,' + ' "recipe": "chromium"' + '}' + execution_timeout_secs: 10800 + build_numbers: YES + service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com" + experiments { + key: "chromium.use_per_builder_build_dir_name" + value: 100 + } + experiments { + key: "luci.recipes.use_python3" + value: 100 + } + resultdb { + enable: true + bq_exports { + project: "chrome-luci-data" + dataset: "chromium" + table: "ci_test_results" + test_results {} + } + bq_exports { + project: "chrome-luci-data" + dataset: "chromium" + table: "gpu_ci_test_results" + test_results { + predicate { + test_id_regexp: "ninja://(chrome|content)/test:telemetry_gpu_integration_test[^/]*/.+" + } + } + } + bq_exports { + project: "chrome-luci-data" + dataset: "chromium" + table: "blink_web_tests_ci_test_results" + test_results { + predicate { + test_id_regexp: "(ninja://[^/]*blink_web_tests/.+)|(ninja://[^/]*_wpt_tests/.+)|(ninja://[^/]*headless_shell_wpt/.+)" + } + } + } + history_options { + use_invocation_timestamp: true + } + } + description_html: "Runs Mac 15 tests with debug config.<br/>This builder is mirrored by any of the following try builders:<br/><ul><li><a href=\"https://ci.chromium.org/p/chromium/builders/try/mac_chromium_compile_dbg_ng\">mac_chromium_compile_dbg_ng</a></li><li><a href=\"https://ci.chromium.org/p/chromium/builders/try/mac_chromium_dbg_ng\">mac_chromium_dbg_ng</a></li></ul><br/>Builder owner: <a href=mailto:bling-engprod@google.com>bling-engprod@google.com</a>" + shadow_builder_adjustments { + service_account: "chromium-try-builder@chops-service-accounts.iam.gserviceaccount.com" + pool: "luci.chromium.try" + dimensions: "free_space:" + dimensions: "pool:luci.chromium.try" + } + contact_team_email: "bling-engprod@google.com" + custom_metric_definitions { + name: "/chrome/infra/browser/builds/cached_count" + predicates: "has(build.output.properties.is_cached)" + predicates: "string(build.output.properties.is_cached) == \"true\"" + } + custom_metric_definitions { + name: "/chrome/infra/browser/builds/ran_tests_retry_shard_count" + predicates: "has(build.output.properties.ran_tests_retry_shard)" + } + custom_metric_definitions { + name: "/chrome/infra/browser/builds/ran_tests_without_patch_count" + predicates: "has(build.output.properties.ran_tests_without_patch)" + } + custom_metric_definitions { + name: "/chrome/infra/browser/builds/uncached_count" + predicates: "has(build.output.properties.is_cached)" + predicates: "string(build.output.properties.is_cached) == \"false\"" + } + } + builders { name: "mac15-x64-rel-tests" swarming_host: "chromium-swarm.appspot.com" dimensions: "builderless:1" @@ -126308,7 +126308,7 @@ use_invocation_timestamp: true } } - description_html: "This builder mirrors the following CI builders:<br/><ul><li><a href=\"https://ci.chromium.org/p/chromium/builders/ci/Mac Builder (dbg)\">Mac Builder (dbg)</a></li><li><a href=\"https://ci.chromium.org/p/chromium/builders/ci/mac14-tests-dbg\">mac14-tests-dbg</a></li></ul>" + description_html: "This builder mirrors the following CI builders:<br/><ul><li><a href=\"https://ci.chromium.org/p/chromium/builders/ci/Mac Builder (dbg)\">Mac Builder (dbg)</a></li><li><a href=\"https://ci.chromium.org/p/chromium/builders/ci/mac15-tests-dbg\">mac15-tests-dbg</a></li></ul>" custom_metric_definitions { name: "/chrome/infra/browser/builds/cached_count" predicates: "has(build.output.properties.is_cached)"
diff --git a/infra/config/generated/luci/luci-milo.cfg b/infra/config/generated/luci/luci-milo.cfg index b0893d88..01eb3959 100644 --- a/infra/config/generated/luci/luci-milo.cfg +++ b/infra/config/generated/luci/luci-milo.cfg
@@ -687,9 +687,9 @@ short_name: "bld" } builders { - name: "buildbucket/luci.chromium.ci/mac14-tests-dbg" + name: "buildbucket/luci.chromium.ci/mac15-tests-dbg" category: "chromium.mac|debug" - short_name: "14" + short_name: "15" } builders { name: "buildbucket/luci.chromium.ci/mac-arm64-dbg" @@ -1453,9 +1453,9 @@ short_name: "bld" } builders { - name: "buildbucket/luci.chromium.ci/mac14-tests-dbg" + name: "buildbucket/luci.chromium.ci/mac15-tests-dbg" category: "chromium.mac|debug" - short_name: "14" + short_name: "15" } builders { name: "buildbucket/luci.chromium.ci/ios-simulator" @@ -19016,9 +19016,9 @@ short_name: "bld" } builders { - name: "buildbucket/luci.chromium.ci/mac14-tests-dbg" + name: "buildbucket/luci.chromium.ci/mac15-tests-dbg" category: "debug" - short_name: "14" + short_name: "15" } builders { name: "buildbucket/luci.chromium.ci/mac-arm64-dbg"
diff --git a/infra/config/generated/luci/luci-notify.cfg b/infra/config/generated/luci/luci-notify.cfg index b00da9f..ab70beec 100644 --- a/infra/config/generated/luci/luci-notify.cfg +++ b/infra/config/generated/luci/luci-notify.cfg
@@ -3914,7 +3914,7 @@ } builders { bucket: "ci" - name: "mac14-tests-dbg" + name: "mac15-tests-dbg" } tree_closers { tree_status_host: "chromium-status.appspot.com"
diff --git a/infra/config/generated/luci/luci-scheduler.cfg b/infra/config/generated/luci/luci-scheduler.cfg index d045afeb..efce624 100644 --- a/infra/config/generated/luci/luci-scheduler.cfg +++ b/infra/config/generated/luci/luci-scheduler.cfg
@@ -5953,15 +5953,6 @@ } } job { - id: "mac14-tests-dbg" - realm: "ci" - buildbucket { - server: "cr-buildbucket.appspot.com" - bucket: "ci" - builder: "mac14-tests-dbg" - } -} -job { id: "mac14-x64-updater-tester-rel" realm: "ci" buildbucket { @@ -5998,6 +5989,15 @@ } } job { + id: "mac15-tests-dbg" + realm: "ci" + buildbucket { + server: "cr-buildbucket.appspot.com" + bucket: "ci" + builder: "mac15-tests-dbg" + } +} +job { id: "mac15-x64-rel-tests" realm: "ci" buildbucket {
diff --git a/infra/config/generated/luci/realms.cfg b/infra/config/generated/luci/realms.cfg index 4cc0ff91..8083e8e 100644 --- a/infra/config/generated/luci/realms.cfg +++ b/infra/config/generated/luci/realms.cfg
@@ -293,11 +293,11 @@ values: "mac14-arm64-rel-tests" values: "mac14-arm64-updater-tester-dbg" values: "mac14-tests" - values: "mac14-tests-dbg" values: "mac14-x64-updater-tester-rel" values: "mac15-arm64-rel-tests" values: "mac15-arm64-updater-tester-dbg" values: "mac15-arm64-updater-tester-rel" + values: "mac15-tests-dbg" values: "mac15-x64-rel-tests" values: "win10-32-on-64-enterprise-companion-tester-dbg" values: "win10-32-on-64-enterprise-companion-tester-rel"
diff --git a/infra/config/subprojects/chromium/ci/chromium.mac.star b/infra/config/subprojects/chromium/ci/chromium.mac.star index cbca26ca..6cb9c26 100644 --- a/infra/config/subprojects/chromium/ci/chromium.mac.star +++ b/infra/config/subprojects/chromium/ci/chromium.mac.star
@@ -916,9 +916,9 @@ ) ci.thin_tester( - name = "mac14-tests-dbg", + name = "mac15-tests-dbg", branch_selector = branches.selector.MAC_BRANCHES, - description_html = "Runs Mac 14 tests with debug config.", + description_html = "Runs Mac 15 tests with debug config.", triggered_by = ["ci/Mac Builder (dbg)"], builder_spec = builder_config.builder_spec( execution_mode = builder_config.execution_mode.TEST, @@ -943,7 +943,7 @@ "chromium_dbg_isolated_scripts", ], mixins = [ - "mac_14_x64", + "mac_15_x64", ], per_test_modifications = { "blink_web_tests": targets.mixin( @@ -959,15 +959,14 @@ shards = 8, ), ), - "browser_tests": targets.remove( - reason = "TODO(crbug.com/338402345): Enable if there is enough capacity after Mac 14 upgrades.", + "browser_tests": targets.mixin( + args = [ + "--test-launcher-filter-file=../../testing/buildbot/filters/mac.mac-rel.browser_tests.filter", + ], + swarming = targets.swarming( + shards = 20, + ), ), - # "browser_tests": targets.mixin( - # # crbug.com/1196416 - # args = [ - # "--test-launcher-filter-file=../../testing/buildbot/filters/mac.mac-rel.browser_tests.filter", - # ], - # ), "content_browsertests": targets.mixin( # https://crbug.com/1279504 swarming = targets.swarming( @@ -1005,7 +1004,7 @@ gardener_rotations = args.ignore_default(None), console_view_entry = consoles.console_view_entry( category = "debug", - short_name = "14", + short_name = "15", ), cq_mirrors_console_view = "mirrors", contact_team_email = "bling-engprod@google.com",
diff --git a/infra/config/subprojects/chromium/try/tryserver.chromium.mac.star b/infra/config/subprojects/chromium/try/tryserver.chromium.mac.star index 49e011c8..741aa0e 100644 --- a/infra/config/subprojects/chromium/try/tryserver.chromium.mac.star +++ b/infra/config/subprojects/chromium/try/tryserver.chromium.mac.star
@@ -553,7 +553,7 @@ name = "mac_chromium_dbg_ng", mirrors = [ "ci/Mac Builder (dbg)", - "ci/mac14-tests-dbg", + "ci/mac15-tests-dbg", ], gn_args = "ci/Mac Builder (dbg)", cpu = cpu.ARM64,
diff --git a/internal b/internal index 9cbd738..5de18f0 160000 --- a/internal +++ b/internal
@@ -1 +1 @@ -Subproject commit 9cbd7389395536e4bc8d2fbcd1f0b3ab1e1adf4b +Subproject commit 5de18f0aacc3865198ffcb45372a83f6b0b9b27b
diff --git a/ios/chrome/app/strings/ios_strings.grd b/ios/chrome/app/strings/ios_strings.grd index b2786d2..c3a6a9b 100644 --- a/ios/chrome/app/strings/ios_strings.grd +++ b/ios/chrome/app/strings/ios_strings.grd
@@ -1570,6 +1570,9 @@ <message name="IDS_IOS_CONTENT_SUGGESTIONS_SHOPCARD_TRACK_PRICE_HIDE_ALT" desc="Alternative button text indicating Track Price ShopCard module can be removed from Magic Stack"> Hide "Track prices from your tabs" </message> + <message name="IDS_IOS_CONTENT_SUGGESTIONS_SHOPCARD_TRACK_PRICE_NO_PUSH_PERMISSION_SNACKBAR" desc="Text for Snackbar if user taps 'Track' on Track price ShopCard in Magic Stack but notification permissions are not granted."> + Product added to your list of tracked products. + </message> <message name="IDS_IOS_CONTENT_SUGGESTIONS_SHOPCARD_TRACK_PRICE_SUCCESS_SNACKBAR" desc="Text for Snackbar if user taps 'Track' on Track price ShopCard in Magic Stack"> You'll get notified if the price drops on any site. </message>
diff --git a/ios/chrome/app/strings/ios_strings_grd/IDS_IOS_CONTENT_SUGGESTIONS_SHOPCARD_TRACK_PRICE_NO_PUSH_PERMISSION_SNACKBAR.png.sha1 b/ios/chrome/app/strings/ios_strings_grd/IDS_IOS_CONTENT_SUGGESTIONS_SHOPCARD_TRACK_PRICE_NO_PUSH_PERMISSION_SNACKBAR.png.sha1 new file mode 100644 index 0000000..bd8028d --- /dev/null +++ b/ios/chrome/app/strings/ios_strings_grd/IDS_IOS_CONTENT_SUGGESTIONS_SHOPCARD_TRACK_PRICE_NO_PUSH_PERMISSION_SNACKBAR.png.sha1
@@ -0,0 +1 @@ +66780bec010e531ab849860c5eaa3ed2e12858e6 \ No newline at end of file
diff --git a/ios/chrome/browser/content_suggestions/ui_bundled/tab_resumption/tab_resumption_mediator.mm b/ios/chrome/browser/content_suggestions/ui_bundled/tab_resumption/tab_resumption_mediator.mm index 4bbe0e5c..ebf1b36a 100644 --- a/ios/chrome/browser/content_suggestions/ui_bundled/tab_resumption/tab_resumption_mediator.mm +++ b/ios/chrome/browser/content_suggestions/ui_bundled/tab_resumption/tab_resumption_mediator.mm
@@ -107,6 +107,12 @@ kTabResumptionShowItemImmediately); } +enum class ShopCardTrackItemResult { + kTrackSuccess, + kTrackSuccesNoNotification, + kTrackError, +}; + // Salient images should come from gstatic.com. const char kGStatic[] = ".gstatic.com"; @@ -491,22 +497,25 @@ [PushNotificationUtil requestPushNotificationPermission:^( BOOL granted, BOOL promptShown, NSError* error) { web::GetUIThreadTaskRunner({})->PostTask( - FROM_HERE, base::BindOnce( - [](__typeof(self) strongSelf, TabResumptionItem* item, - BOOL granted, BOOL promptShown, NSError* error) { - if (error || !granted) { - [strongSelf onTracked:NO item:item]; - return; - } - [strongSelf - onNotificationPermissionVerifiedOrGranted:item]; - }, - weakSelf, item, granted, promptShown, error)); + FROM_HERE, + base::BindOnce( + [](__typeof(self) strongSelf, TabResumptionItem* item, BOOL granted, + BOOL promptShown, NSError* error) { + if (error) { + [strongSelf onTracked:ShopCardTrackItemResult::kTrackError + item:item]; + return; + } + [strongSelf onNotificationPermissionVerifiedOrGranted:item + granted:granted]; + }, + weakSelf, item, granted, promptShown, error)); }]; [self.delegate removeTabResumptionModule]; } -- (void)onNotificationPermissionVerifiedOrGranted:(TabResumptionItem*)item { +- (void)onNotificationPermissionVerifiedOrGranted:(TabResumptionItem*)item + granted:(BOOL)granted { id<SystemIdentity> identity = _authenticationService->GetPrimaryIdentity(signin::ConsentLevel::kSignin); _pushNotificationService->SetPreference( @@ -517,16 +526,25 @@ bool isNewBookmark = bookmark == nullptr; __weak TabResumptionMediator* weakSelf = self; - auto completionHandler = - ^(TabResumptionItem* tabResumptionItem, bool success) { - [weakSelf onTracked:success item:tabResumptionItem]; - }; + auto completionHandler = ^(TabResumptionItem* tabResumptionItem, + bool success) { + if (success) { + [weakSelf + onTracked:granted + ? ShopCardTrackItemResult::kTrackSuccess + : ShopCardTrackItemResult::kTrackSuccesNoNotification + item:tabResumptionItem]; + } else { + [weakSelf onTracked:ShopCardTrackItemResult::kTrackError + item:tabResumptionItem]; + } + }; if (!bookmark) { const bookmarks::BookmarkNode* defaultFolder = _bookmarkModel->account_mobile_node(); if (!defaultFolder) { - [self onTracked:NO item:item]; + [self onTracked:ShopCardTrackItemResult::kTrackError item:item]; return; } bookmark = _bookmarkModel->AddURL( @@ -540,16 +558,16 @@ item.shopCardData.productInfo); } -- (void)onTracked:(BOOL)success item:(TabResumptionItem*)item { - [self.dispatcher showSnackbarMessage:[self snackbarMessage:success - item:item]]; +- (void)onTracked:(ShopCardTrackItemResult)result + item:(TabResumptionItem*)item { + [self.dispatcher showSnackbarMessage:[self snackbarMessage:result item:item]]; } -- (MDCSnackbarMessage*)snackbarMessage:(BOOL)success +- (MDCSnackbarMessage*)snackbarMessage:(ShopCardTrackItemResult)result item:(TabResumptionItem*)item { MDCSnackbarMessageAction* action = [[MDCSnackbarMessageAction alloc] init]; - if (success) { + if (result != ShopCardTrackItemResult::kTrackError) { // Tracking was successful. Give option to go to price tracking menu. action.handler = ^{ [self.dispatcher showPriceTrackedItems]; @@ -557,11 +575,11 @@ } else { // Failed to track - try again. action.handler = ^{ - [self onNotificationPermissionVerifiedOrGranted:item]; + [self trackShopCardItem:item]; }; } - if (success) { + if (result != ShopCardTrackItemResult::kTrackError) { action.title = l10n_util::GetNSString( IDS_IOS_CONTENT_SUGGESTIONS_SHOPCARD_TRACK_PRICE_SUCCESS_SNACKBAR_ACTION); action.accessibilityLabel = l10n_util::GetNSString( @@ -576,9 +594,12 @@ } MDCSnackbarMessage* message; - if (success) { + if (result == ShopCardTrackItemResult::kTrackSuccess) { message = CreateSnackbarMessage(l10n_util::GetNSString( IDS_IOS_CONTENT_SUGGESTIONS_SHOPCARD_TRACK_PRICE_SUCCESS_SNACKBAR)); + } else if (result == ShopCardTrackItemResult::kTrackSuccesNoNotification) { + message = CreateSnackbarMessage(l10n_util::GetNSString( + IDS_IOS_CONTENT_SUGGESTIONS_SHOPCARD_TRACK_PRICE_NO_PUSH_PERMISSION_SNACKBAR)); } else { message = CreateSnackbarMessage(l10n_util::GetNSString( IDS_IOS_CONTENT_SUGGESTIONS_SHOPCARD_TRACK_PRICE_FAILURE_SNACKBAR));
diff --git a/ios/chrome/browser/default_promo/ui_bundled/post_restore/BUILD.gn b/ios/chrome/browser/default_promo/ui_bundled/post_restore/BUILD.gn index 99aec49..f5d1ca09 100644 --- a/ios/chrome/browser/default_promo/ui_bundled/post_restore/BUILD.gn +++ b/ios/chrome/browser/default_promo/ui_bundled/post_restore/BUILD.gn
@@ -28,6 +28,7 @@ deps = [ "//ios/chrome/app/strings", "//ios/chrome/test/earl_grey:eg_test_support+eg2", + "//ios/chrome/test/earl_grey:switches", "//ios/testing/earl_grey:eg_test_support+eg2", "//ui/base", ]
diff --git a/ios/chrome/browser/default_promo/ui_bundled/post_restore/post_restore_default_browser_egtest.mm b/ios/chrome/browser/default_promo/ui_bundled/post_restore/post_restore_default_browser_egtest.mm index 4173ac63..0abebcac 100644 --- a/ios/chrome/browser/default_promo/ui_bundled/post_restore/post_restore_default_browser_egtest.mm +++ b/ios/chrome/browser/default_promo/ui_bundled/post_restore/post_restore_default_browser_egtest.mm
@@ -8,6 +8,7 @@ #import "ios/chrome/test/earl_grey/chrome_earl_grey.h" #import "ios/chrome/test/earl_grey/chrome_earl_grey_ui.h" #import "ios/chrome/test/earl_grey/chrome_test_case.h" +#import "ios/chrome/test/earl_grey/test_switches.h" #import "ios/testing/earl_grey/app_launch_manager.h" #import "ios/testing/earl_grey/earl_grey_test.h" #import "ios/testing/earl_grey/matchers.h" @@ -15,6 +16,7 @@ namespace { base::TimeDelta kPromoAppearanceTimeout = base::Seconds(7); +NSString* const kDefaultBrowserKey = @"DefaultBrowserUtils"; // Matcher for the title. id<GREYMatcher> TitleMatcher() { @@ -41,21 +43,6 @@ @implementation PostRestoreDefaultBrowserPromoTestCase -- (void)setUp { - [[self class] testForStartup]; - [super setUp]; -} - -- (AppLaunchConfiguration)appConfigurationForTestCase { - AppLaunchConfiguration config; - // Override trigger requirements to force the promo to appear. - config.additional_args.push_back("-NextPromoForDisplayOverride"); - config.additional_args.push_back( - "promos_manager::Promo::PostRestoreDefaultBrowserAlert"); - config.relaunch_policy = ForceRelaunchByCleanShutdown; - return config; -} - #pragma mark - Helpers - (void)checkThatCommonElementsAreVisible { @@ -76,17 +63,50 @@ assertWithMatcher:grey_notVisible()]; } +- (void)simulateRestore { + AppLaunchConfiguration config; + config.relaunch_policy = ForceRelaunchByCleanShutdown; + config.additional_args.push_back(std::string("-") + + test_switches::kSimulatePostDeviceRestore); + + // The post-restore default browser alert is a promo, and promo are + // implemented as IPH. All IPH are disabled by default in EGtests. This flag + // enable this particular IPH. + config.iph_feature_enabled = "IPH_iOSPromoPostRestoreDefaultBrowser"; + + // Relaunch the app to take the configuration into account. + [[AppLaunchManager sharedManager] ensureAppLaunchedWithConfiguration:config]; +} + #pragma mark - Tests -// Verifies that the secondary action button opens dismisses the promo. -// TODO(crbug.com/40929054): re-enable once test is no longer flaky. -- (void)DISABLED_testDismiss { +// Verifies that the promo appears for users who had Chrome set as their default +// browser before a restore. Verifies that secondary action button dismisses the +// promo. +- (void)testPromoAppears { + // Simulate setting Chrome as default browser. + NSMutableDictionary<NSString*, NSObject*>* storage = [[ChromeEarlGrey + userDefaultsObjectForKey:kDefaultBrowserKey] mutableCopy]; + storage[@"lastHTTPURLOpenTime"] = [NSDate date]; + [ChromeEarlGrey setUserDefaultsObject:storage forKey:kDefaultBrowserKey]; + + [self simulateRestore]; + [ChromeEarlGrey waitForUIElementToAppearWithMatcher:TitleMatcher() timeout:kPromoAppearanceTimeout]; [self checkThatCommonElementsAreVisible]; [[EarlGrey selectElementWithMatcher:SecondaryActionMatcher()] performAction:grey_tap()]; [self checkThatCommonElementsAreNotVisible]; + + [ChromeEarlGrey removeUserDefaultsObjectForKey:kDefaultBrowserKey]; +} + +// Verifies that the promo does not appear after a restore if the user did not +// previously set Chrome as their default browser. +- (void)testPromoDoesNotAppear_defaultBrowserNotEnabled { + [self simulateRestore]; + [self checkThatCommonElementsAreNotVisible]; } @end
diff --git a/ios/chrome/browser/flags/ios_chrome_flag_descriptions.cc b/ios/chrome/browser/flags/ios_chrome_flag_descriptions.cc index 3f9df4e..e67c4e0 100644 --- a/ios/chrome/browser/flags/ios_chrome_flag_descriptions.cc +++ b/ios/chrome/browser/flags/ios_chrome_flag_descriptions.cc
@@ -511,6 +511,11 @@ const char kAnimatedDefaultBrowserPromoInFREDescription[] = "When enabled, the Default Browser Promo in the FRE will be animated."; +const char kFeedbackIncludeGWSVariationsName[] = + "Include GWS variations in feedback"; +const char kFeedbackIncludeGWSVariationsDescription[] = + "Includes GWS variations in Chrome feedback reports."; + const char kFullscreenImprovementName[] = "Improve fullscreen"; const char kFullscreenImprovementDescription[] = "When enabled, fullscreen should have a better stability.";
diff --git a/ios/chrome/browser/flags/ios_chrome_flag_descriptions.h b/ios/chrome/browser/flags/ios_chrome_flag_descriptions.h index 15c6288..a51d96ad 100644 --- a/ios/chrome/browser/flags/ios_chrome_flag_descriptions.h +++ b/ios/chrome/browser/flags/ios_chrome_flag_descriptions.h
@@ -294,6 +294,9 @@ extern const char kAnimatedDefaultBrowserPromoInFREName[]; extern const char kAnimatedDefaultBrowserPromoInFREDescription[]; +extern const char kFeedbackIncludeGWSVariationsName[]; +extern const char kFeedbackIncludeGWSVariationsDescription[]; + extern const char kFullscreenImprovementName[]; extern const char kFullscreenImprovementDescription[];
diff --git a/ios/chrome/browser/settings/ui_bundled/password/BUILD.gn b/ios/chrome/browser/settings/ui_bundled/password/BUILD.gn index 9461d9f..c12c1dd5a 100644 --- a/ios/chrome/browser/settings/ui_bundled/password/BUILD.gn +++ b/ios/chrome/browser/settings/ui_bundled/password/BUILD.gn
@@ -198,6 +198,7 @@ "//components/affiliations/core/browser:test_support", "//components/feature_engagement/public", "//components/feature_engagement/test:test_support", + "//components/google/core/common", "//components/password_manager/core/browser", "//components/password_manager/core/browser:password_form", "//components/password_manager/core/browser:test_support", @@ -209,15 +210,18 @@ "//ios/chrome/browser/affiliations/model", "//ios/chrome/browser/favicon/model", "//ios/chrome/browser/feature_engagement/model", + "//ios/chrome/browser/net/model:crurl", "//ios/chrome/browser/passwords/model", "//ios/chrome/browser/passwords/model:save_passwords_consumer", "//ios/chrome/browser/passwords/model:store_factory", "//ios/chrome/browser/settings/ui_bundled/cells", "//ios/chrome/browser/settings/ui_bundled/password:features", "//ios/chrome/browser/settings/ui_bundled/utils", + "//ios/chrome/browser/shared/model/application_context", "//ios/chrome/browser/shared/model/browser/test:test_support", "//ios/chrome/browser/shared/model/profile", "//ios/chrome/browser/shared/model/profile/test", + "//ios/chrome/browser/shared/public/features", "//ios/chrome/browser/shared/ui/table_view:test_support", "//ios/chrome/browser/shared/ui/table_view/cells", "//ios/chrome/browser/shared/ui/util",
diff --git a/ios/chrome/browser/settings/ui_bundled/password/password_manager_view_controller.mm b/ios/chrome/browser/settings/ui_bundled/password/password_manager_view_controller.mm index 721d51f2..c677c4c 100644 --- a/ios/chrome/browser/settings/ui_bundled/password/password_manager_view_controller.mm +++ b/ios/chrome/browser/settings/ui_bundled/password/password_manager_view_controller.mm
@@ -679,34 +679,12 @@ #pragma mark - Items - (TableViewLinkHeaderFooterItem*)manageAccountLinkItem { if (_manageAccountLinkItem) { - // When `_savingPasswordsToAccount` is being changed we perform - // `reloadData`. However, during the reloading of data - // `_manageAccountLinkItem` might be already initialized, and this branch of - // code will return without updating `_manageAccountLinkItem.text`. - // TODO(crbug.com/407605858): Fix the described issue. return _manageAccountLinkItem; } _manageAccountLinkItem = [[TableViewLinkHeaderFooterItem alloc] initWithType:ItemTypeLinkHeader]; - - if (_savingPasswordsToAccount) { - _manageAccountLinkItem.text = l10n_util::GetNSString( - IOSPasskeysM2Enabled() - ? IDS_IOS_SAVE_PASSWORDS_PASSKEYS_MANAGE_ACCOUNT_HEADER - : IDS_IOS_SAVE_PASSWORDS_MANAGE_ACCOUNT_HEADER); - - _manageAccountLinkItem.urls = @[ [[CrURL alloc] - initWithGURL: - google_util::AppendGoogleLocaleParam( - GURL(password_manager::kPasswordManagerHelpCenteriOSURL), - GetApplicationContext()->GetApplicationLocale())] ]; - } else { - _manageAccountLinkItem.text = - l10n_util::GetNSString(IDS_IOS_PASSWORD_MANAGER_HEADER_NOT_SYNCING); - _manageAccountLinkItem.urls = @[]; - } - + [self populateManageAccountLinkItemContent]; return _manageAccountLinkItem; } @@ -916,7 +894,14 @@ return; } _savingPasswordsToAccount = savingPasswordsToAccount; - [self reloadData]; + // No need to reload before the view is loaded, as loading the view triggers a + // data reload. + if (self.viewLoaded) { + [self populateManageAccountLinkItemContent]; + // TODO(crbug.com/416468488): Check if it is possible to only update the + // items affected by this mutation instead of reloading data. + [self reloadData]; + } } - (void)setAffiliatedGroups: @@ -1214,6 +1199,29 @@ #pragma mark - Private methods +// Populates the text and urls content of the ManageAccountLinkItem. +- (void)populateManageAccountLinkItemContent { + if (!_manageAccountLinkItem) { + return; + } + if (_savingPasswordsToAccount) { + _manageAccountLinkItem.text = l10n_util::GetNSString( + IOSPasskeysM2Enabled() + ? IDS_IOS_SAVE_PASSWORDS_PASSKEYS_MANAGE_ACCOUNT_HEADER + : IDS_IOS_SAVE_PASSWORDS_MANAGE_ACCOUNT_HEADER); + + _manageAccountLinkItem.urls = @[ [[CrURL alloc] + initWithGURL: + google_util::AppendGoogleLocaleParam( + GURL(password_manager::kPasswordManagerHelpCenteriOSURL), + GetApplicationContext()->GetApplicationLocale())] ]; + } else { + _manageAccountLinkItem.text = + l10n_util::GetNSString(IDS_IOS_PASSWORD_MANAGER_HEADER_NOT_SYNCING); + _manageAccountLinkItem.urls = @[]; + } +} + // Shows loading spinner background view. - (void)showLoadingSpinnerBackground { if (!self.spinnerView) {
diff --git a/ios/chrome/browser/settings/ui_bundled/password/password_manager_view_controller_unittest.mm b/ios/chrome/browser/settings/ui_bundled/password/password_manager_view_controller_unittest.mm index 8d99d2a..051bf19 100644 --- a/ios/chrome/browser/settings/ui_bundled/password/password_manager_view_controller_unittest.mm +++ b/ios/chrome/browser/settings/ui_bundled/password/password_manager_view_controller_unittest.mm
@@ -15,6 +15,7 @@ #import "base/test/scoped_feature_list.h" #import "components/affiliations/core/browser/fake_affiliation_service.h" #import "components/feature_engagement/public/feature_constants.h" +#import "components/google/core/common/google_util.h" #import "components/keyed_service/core/service_access_type.h" #import "components/password_manager/core/browser/leak_detection/mock_bulk_leak_check_service.h" #import "components/password_manager/core/browser/password_form.h" @@ -23,6 +24,7 @@ #import "ios/chrome/browser/affiliations/model/ios_chrome_affiliation_service_factory.h" #import "ios/chrome/browser/favicon/model/favicon_loader.h" #import "ios/chrome/browser/favicon/model/ios_chrome_favicon_loader_factory.h" +#import "ios/chrome/browser/net/model/crurl.h" #import "ios/chrome/browser/passwords/model/ios_chrome_bulk_leak_check_service_factory.h" #import "ios/chrome/browser/passwords/model/ios_chrome_password_check_manager.h" #import "ios/chrome/browser/passwords/model/ios_chrome_password_check_manager_factory.h" @@ -39,8 +41,10 @@ #import "ios/chrome/browser/settings/ui_bundled/password/passwords_mediator.h" #import "ios/chrome/browser/settings/ui_bundled/password/passwords_settings_commands.h" #import "ios/chrome/browser/settings/ui_bundled/password/passwords_table_view_constants.h" +#import "ios/chrome/browser/shared/model/application_context/application_context.h" #import "ios/chrome/browser/shared/model/browser/test/test_browser.h" #import "ios/chrome/browser/shared/model/profile/test/test_profile_ios.h" +#import "ios/chrome/browser/shared/public/features/features.h" #import "ios/chrome/browser/shared/ui/table_view/cells/table_view_detail_text_item.h" #import "ios/chrome/browser/shared/ui/table_view/cells/table_view_text_item.h" #import "ios/chrome/browser/shared/ui/table_view/legacy_chrome_table_view_controller_test.h" @@ -1394,4 +1398,38 @@ [GetPasswordManagerViewController() settingsWillBeDismissed]; } +// Tests that the content of the ManageAccountHeader is being updated when +// `setSavingPasswordsToAccount` changes. +TEST_F(PasswordManagerViewControllerTest, ManageAccountHeaderIsBeingUpdated) { + AddSavedForm1(); + + [GetPasswordManagerViewController() setSavingPasswordsToAccount:NO]; + + TableViewModel* model = GetPasswordManagerViewController().tableViewModel; + TableViewLinkHeaderFooterItem* header = + base::apple::ObjCCastStrict<TableViewLinkHeaderFooterItem>([model + headerForSectionWithIdentifier:SectionIdentifierManageAccountHeader]); + + EXPECT_NSEQ( + l10n_util::GetNSString(IDS_IOS_PASSWORD_MANAGER_HEADER_NOT_SYNCING), + header.text); + EXPECT_NSEQ(@[], header.urls); + + [GetPasswordManagerViewController() setSavingPasswordsToAccount:YES]; + + EXPECT_NSEQ(l10n_util::GetNSString( + IOSPasskeysM2Enabled() + ? IDS_IOS_SAVE_PASSWORDS_PASSKEYS_MANAGE_ACCOUNT_HEADER + : IDS_IOS_SAVE_PASSWORDS_MANAGE_ACCOUNT_HEADER), + header.text); + EXPECT_EQ(1U, [header.urls count]); + CrURL* expectedHeaderUrl = [[CrURL alloc] + initWithGURL:google_util::AppendGoogleLocaleParam( + GURL(password_manager::kPasswordManagerHelpCenteriOSURL), + GetApplicationContext()->GetApplicationLocale())]; + EXPECT_NSEQ(header.urls[0].nsurl, expectedHeaderUrl.nsurl); + + [GetPasswordManagerViewController() settingsWillBeDismissed]; +} + } // namespace
diff --git a/ios/chrome/browser/shared/public/features/features.h b/ios/chrome/browser/shared/public/features/features.h index 73a6bad..81efbdf 100644 --- a/ios/chrome/browser/shared/public/features/features.h +++ b/ios/chrome/browser/shared/public/features/features.h
@@ -1058,4 +1058,10 @@ // Whether the Guided Tour variant of `kBestOfAppFRE` is enabled. bool IsBestOfAppGuidedTourEnabled(); +// Feature flag to include GWS variations in feedback. +BASE_DECLARE_FEATURE(kFeedbackIncludeGWSVariations); + +// Whether the feature to include GWS variations in feedback is enabled. +bool IsFeedbackIncludeGWSVariationsEnabled(); + #endif // IOS_CHROME_BROWSER_SHARED_PUBLIC_FEATURES_FEATURES_H_
diff --git a/ios/chrome/browser/shared/public/features/features.mm b/ios/chrome/browser/shared/public/features/features.mm index 831c7aa..ca72ced0 100644 --- a/ios/chrome/browser/shared/public/features/features.mm +++ b/ios/chrome/browser/shared/public/features/features.mm
@@ -1269,3 +1269,11 @@ return base::GetFieldTrialParamValueByFeature(kBestOfAppFRE, "variant") == "4"; } + +BASE_FEATURE(kFeedbackIncludeGWSVariations, + "FeedbackIncludeGWSVariations", + base::FEATURE_DISABLED_BY_DEFAULT); + +bool IsFeedbackIncludeGWSVariationsEnabled() { + return base::FeatureList::IsEnabled(kFeedbackIncludeGWSVariations); +}
diff --git a/ios/chrome/browser/tabs/model/BUILD.gn b/ios/chrome/browser/tabs/model/BUILD.gn index 0fc75fca..c614d91 100644 --- a/ios/chrome/browser/tabs/model/BUILD.gn +++ b/ios/chrome/browser/tabs/model/BUILD.gn
@@ -54,6 +54,8 @@ "//components/sessions", "//components/strings", "//components/supervised_user/core/common", + "//components/webauthn/ios", + "//components/webauthn/ios:features", "//ios/chrome/browser/app_launcher/model", "//ios/chrome/browser/autofill/model", "//ios/chrome/browser/autofill/model:model_internal", @@ -133,6 +135,7 @@ "//ios/chrome/browser/web/model/print", "//ios/chrome/browser/web/model/web_performance_metrics", "//ios/chrome/browser/web_selection/model", + "//ios/chrome/browser/webauthn/model", "//ios/chrome/browser/webui/model", "//ios/components/security_interstitials", "//ios/components/security_interstitials/https_only_mode",
diff --git a/ios/chrome/browser/tabs/model/tab_helper_util.mm b/ios/chrome/browser/tabs/model/tab_helper_util.mm index 0211d41a..9c5be760 100644 --- a/ios/chrome/browser/tabs/model/tab_helper_util.mm +++ b/ios/chrome/browser/tabs/model/tab_helper_util.mm
@@ -18,6 +18,8 @@ #import "components/safe_browsing/ios/browser/safe_browsing_url_allow_list.h" #import "components/supervised_user/core/common/features.h" #import "components/ukm/ios/ukm_url_recorder.h" +#import "components/webauthn/ios/features.h" +#import "components/webauthn/ios/passkey_tab_helper.h" #import "ios/chrome/browser/app_launcher/model/app_launcher_abuse_detector.h" #import "ios/chrome/browser/app_launcher/model/app_launcher_tab_helper.h" #import "ios/chrome/browser/autofill/model/autofill_tab_helper.h" @@ -116,6 +118,7 @@ #import "ios/chrome/browser/web/model/sad_tab_tab_helper.h" #import "ios/chrome/browser/web/model/web_performance_metrics/web_performance_metrics_tab_helper.h" #import "ios/chrome/browser/web_selection/model/web_selection_tab_helper.h" +#import "ios/chrome/browser/webauthn/model/ios_passkey_model_factory.h" #import "ios/chrome/browser/webui/model/net_export_tab_helper.h" #import "ios/components/security_interstitials/https_only_mode/feature.h" #import "ios/components/security_interstitials/https_only_mode/https_only_mode_container.h" @@ -305,6 +308,11 @@ PasswordTabHelper::FromWebState(web_state)->GetSuggestionProvider(), AutofillTabHelper::FromWebState(web_state)->GetSuggestionProvider(), ]); + + if (base::FeatureList::IsEnabled(kIOSPasskeyShim)) { + PasskeyTabHelper::CreateForWebState( + web_state, IOSPasskeyModelFactory::GetForProfile(profile)); + } } if (!for_lens_overlay) {
diff --git a/media/base/video_frame_unittest.cc b/media/base/video_frame_unittest.cc index 8af9ae7..04c0c91 100644 --- a/media/base/video_frame_unittest.cc +++ b/media/base/video_frame_unittest.cc
@@ -29,7 +29,6 @@ #include "media/base/color_plane_layout.h" #include "media/base/limits.h" #include "media/base/simple_sync_token_client.h" -#include "media/video/fake_gpu_memory_buffer.h" #include "testing/gtest/include/gtest/gtest.h" #include "third_party/libyuv/include/libyuv.h"
diff --git a/media/capture/video/linux/v4l2_capture_delegate_gpu_helper_unittest.cc b/media/capture/video/linux/v4l2_capture_delegate_gpu_helper_unittest.cc index ab34124..952b339 100644 --- a/media/capture/video/linux/v4l2_capture_delegate_gpu_helper_unittest.cc +++ b/media/capture/video/linux/v4l2_capture_delegate_gpu_helper_unittest.cc
@@ -11,10 +11,10 @@ #include "base/command_line.h" #include "base/test/bind.h" #include "base/test/task_environment.h" +#include "gpu/command_buffer/client/fake_gpu_memory_buffer.h" #include "gpu/command_buffer/client/test_shared_image_interface.h" #include "media/base/test_data_util.h" #include "media/capture/video/video_capture_gpu_channel_host.h" -#include "media/video/fake_gpu_memory_buffer.h" #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" @@ -106,7 +106,7 @@ : public VideoCaptureDevice::Client::Buffer::HandleProvider { public: MockCaptureHandleProvider(const gfx::Size& size, gfx::BufferFormat format) { - gmb_handle_ = CreatePixmapHandleForTesting(size, format); + gmb_handle_ = gpu::CreatePixmapHandleForTesting(size, format); } // Duplicate as an writable (unsafe) shared memory region. base::UnsafeSharedMemoryRegion DuplicateAsUnsafeRegion() override {
diff --git a/media/capture/video/linux/v4l2_capture_delegate_unittest.cc b/media/capture/video/linux/v4l2_capture_delegate_unittest.cc index 74e888f..56f4c7a 100644 --- a/media/capture/video/linux/v4l2_capture_delegate_unittest.cc +++ b/media/capture/video/linux/v4l2_capture_delegate_unittest.cc
@@ -26,11 +26,11 @@ #if BUILDFLAG(IS_LINUX) #include "base/command_line.h" +#include "gpu/command_buffer/client/fake_gpu_memory_buffer.h" #include "media/capture/capture_switches.h" #include "media/capture/video/linux/fake_device_provider.h" #include "media/capture/video/linux/fake_v4l2_impl.h" #include "media/capture/video/linux/video_capture_device_linux.h" -#include "media/video/fake_gpu_memory_buffer.h" #endif // BUILDFLAG(IS_LINUX) using base::test::RunClosure; @@ -318,7 +318,7 @@ : public VideoCaptureDevice::Client::Buffer::HandleProvider { public: MockCaptureHandleProvider(const gfx::Size& size, gfx::BufferFormat format) { - gmb_handle_ = CreatePixmapHandleForTesting(size, format); + gmb_handle_ = gpu::CreatePixmapHandleForTesting(size, format); } // Duplicate as an writable (unsafe) shared memory region. base::UnsafeSharedMemoryRegion DuplicateAsUnsafeRegion() override {
diff --git a/media/gpu/android/ndk_video_encode_accelerator_tests.cc b/media/gpu/android/ndk_video_encode_accelerator_tests.cc index 1b3a3633..47902d1 100644 --- a/media/gpu/android/ndk_video_encode_accelerator_tests.cc +++ b/media/gpu/android/ndk_video_encode_accelerator_tests.cc
@@ -27,7 +27,6 @@ #include "media/base/video_util.h" #include "media/parsers/h264_parser.h" #include "media/parsers/vp9_parser.h" -#include "media/video/fake_gpu_memory_buffer.h" #include "testing/gtest/include/gtest/gtest.h" #include "third_party/libyuv/include/libyuv.h" #include "third_party/libyuv/include/libyuv/convert_from.h"
diff --git a/media/gpu/buffer_validation_unittest.cc b/media/gpu/buffer_validation_unittest.cc index 3ce797b..549f814e 100644 --- a/media/gpu/buffer_validation_unittest.cc +++ b/media/gpu/buffer_validation_unittest.cc
@@ -10,7 +10,6 @@ #include "base/functional/bind.h" #include "media/base/video_frame.h" #include "media/base/video_types.h" -#include "media/video/fake_gpu_memory_buffer.h" #include "testing/gtest/include/gtest/gtest.h" #include "ui/gfx/buffer_types.h" #include "ui/gfx/gpu_memory_buffer.h"
diff --git a/media/gpu/chromeos/platform_video_frame_utils_unittest.cc b/media/gpu/chromeos/platform_video_frame_utils_unittest.cc index b08f851..b80fd3a0 100644 --- a/media/gpu/chromeos/platform_video_frame_utils_unittest.cc +++ b/media/gpu/chromeos/platform_video_frame_utils_unittest.cc
@@ -24,7 +24,6 @@ #include "media/base/video_frame.h" #include "media/base/video_frame_layout.h" #include "media/base/video_types.h" -#include "media/video/fake_gpu_memory_buffer.h" #include "testing/gtest/include/gtest/gtest.h" #include "ui/gfx/buffer_types.h" #include "ui/gfx/geometry/rect.h"
diff --git a/media/gpu/vaapi/vaapi_video_encode_accelerator_unittest.cc b/media/gpu/vaapi/vaapi_video_encode_accelerator_unittest.cc index afafae0..1eb4ccd 100644 --- a/media/gpu/vaapi/vaapi_video_encode_accelerator_unittest.cc +++ b/media/gpu/vaapi/vaapi_video_encode_accelerator_unittest.cc
@@ -16,6 +16,7 @@ #include "base/test/task_environment.h" #include "build/build_config.h" #include "components/viz/common/resources/shared_image_format_utils.h" +#include "gpu/command_buffer/client/fake_gpu_memory_buffer.h" #include "gpu/command_buffer/client/test_shared_image_interface.h" #include "media/base/media_util.h" #include "media/base/mock_media_log.h" @@ -26,7 +27,6 @@ #include "media/gpu/vaapi/vaapi_wrapper.h" #include "media/gpu/vaapi/vp9_vaapi_video_encoder_delegate.h" #include "media/gpu/vp9_picture.h" -#include "media/video/fake_gpu_memory_buffer.h" #include "media/video/video_encode_accelerator.h" #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" @@ -750,7 +750,7 @@ auto buffer_format = gfx::BufferFormat::YUV_420_BIPLANAR; // Create a mappable shared image. auto pixmap_handle = - CreatePixmapHandleForTesting(kDefaultEncodeSize, buffer_format); + gpu::CreatePixmapHandleForTesting(kDefaultEncodeSize, buffer_format); auto shared_image = test_sii->CreateSharedImage( {viz::GetSharedImageFormat(buffer_format), kDefaultEncodeSize, gfx::ColorSpace(), gpu::SharedImageUsageSet(si_usage),
diff --git a/media/video/BUILD.gn b/media/video/BUILD.gn index ec8dc6e2..07f47ea8 100644 --- a/media/video/BUILD.gn +++ b/media/video/BUILD.gn
@@ -92,8 +92,6 @@ visibility = [ "//media:test_support" ] testonly = true sources = [ - "fake_gpu_memory_buffer.cc", - "fake_gpu_memory_buffer.h", "fake_video_encode_accelerator.cc", "fake_video_encode_accelerator.h", "mock_gpu_memory_buffer_video_frame_pool.cc",
diff --git a/media/video/renderable_gpu_memory_buffer_video_frame_pool_unittest.cc b/media/video/renderable_gpu_memory_buffer_video_frame_pool_unittest.cc index 248d0f5..9f889f3 100644 --- a/media/video/renderable_gpu_memory_buffer_video_frame_pool_unittest.cc +++ b/media/video/renderable_gpu_memory_buffer_video_frame_pool_unittest.cc
@@ -20,7 +20,6 @@ #include "media/base/format_utils.h" #include "media/base/media_switches.h" #include "media/base/video_frame.h" -#include "media/video/fake_gpu_memory_buffer.h" #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h"
diff --git a/mojo/core/BUILD.gn b/mojo/core/BUILD.gn index 539d923..9d66816 100644 --- a/mojo/core/BUILD.gn +++ b/mojo/core/BUILD.gn
@@ -81,6 +81,7 @@ "ipcz_driver/shared_buffer_mapping.cc", "ipcz_driver/transmissible_platform_handle.cc", "ipcz_driver/transport.cc", + "ipcz_driver/validate_enum.h", "ipcz_driver/wrapped_platform_handle.cc", "platform_handle_in_transit.cc", "platform_handle_in_transit.h",
diff --git a/mojo/core/embedder/features.cc b/mojo/core/embedder/features.cc index 3909ef5..f31fef30 100644 --- a/mojo/core/embedder/features.cc +++ b/mojo/core/embedder/features.cc
@@ -51,5 +51,14 @@ base::FEATURE_DISABLED_BY_DEFAULT); #endif +#if BUILDFLAG(IS_WIN) +// If enabled, then only handles of types Section, File, Directory and +// DxgkSharedResource are allowed to traverse a process boundary to an untrusted +// process via mojo. +BASE_FEATURE(kMojoHandleTypeProtections, + "MojoHandleTypeProtections", + base::FEATURE_ENABLED_BY_DEFAULT); +#endif // BUILDFLAG(IS_WIN) + } // namespace core } // namespace mojo
diff --git a/mojo/core/embedder/features.h b/mojo/core/embedder/features.h index 0677c8a..617e971 100644 --- a/mojo/core/embedder/features.h +++ b/mojo/core/embedder/features.h
@@ -49,6 +49,11 @@ BASE_DECLARE_FEATURE(kMojoUseBinder); #endif +#if BUILDFLAG(IS_WIN) +COMPONENT_EXPORT(MOJO_CORE_EMBEDDER_FEATURES) +BASE_DECLARE_FEATURE(kMojoHandleTypeProtections); +#endif // BUILDFLAG(IS_WIN) + } // namespace core } // namespace mojo
diff --git a/mojo/core/ipcz_driver/data_pipe.cc b/mojo/core/ipcz_driver/data_pipe.cc index 8e74e630..b286db2 100644 --- a/mojo/core/ipcz_driver/data_pipe.cc +++ b/mojo/core/ipcz_driver/data_pipe.cc
@@ -24,6 +24,7 @@ #include "base/synchronization/lock.h" #include "mojo/core/ipcz_api.h" #include "mojo/core/ipcz_driver/ring_buffer.h" +#include "mojo/core/ipcz_driver/validate_enum.h" #include "third_party/ipcz/include/ipcz/ipcz.h" namespace mojo::core::ipcz_driver { @@ -534,6 +535,10 @@ return nullptr; } + if (!ValidateEnum(header.endpoint_type)) { + return nullptr; + } + scoped_refptr<SharedBuffer> buffer = SharedBuffer::Deserialize(data.subspan(header_size), handles); if (!buffer) {
diff --git a/mojo/core/ipcz_driver/data_pipe.h b/mojo/core/ipcz_driver/data_pipe.h index f72f636c..ae0329a 100644 --- a/mojo/core/ipcz_driver/data_pipe.h +++ b/mojo/core/ipcz_driver/data_pipe.h
@@ -41,6 +41,9 @@ enum class EndpointType : uint32_t { kProducer, kConsumer, + // For ValidateEnum(). + kMinValue = kProducer, + kMaxValue = kConsumer, }; struct Config {
diff --git a/mojo/core/ipcz_driver/object.h b/mojo/core/ipcz_driver/object.h index 44c103e..89caf783 100644 --- a/mojo/core/ipcz_driver/object.h +++ b/mojo/core/ipcz_driver/object.h
@@ -65,6 +65,10 @@ // See Envelope for details. kEnvelope, + + // For ValidateEnum(). + kMinValue = kTransport, + kMaxValue = kEnvelope, }; explicit ObjectBase(Type type);
diff --git a/mojo/core/ipcz_driver/shared_buffer.cc b/mojo/core/ipcz_driver/shared_buffer.cc index dc5d34b9..28ab7a3 100644 --- a/mojo/core/ipcz_driver/shared_buffer.cc +++ b/mojo/core/ipcz_driver/shared_buffer.cc
@@ -15,8 +15,10 @@ #include "base/files/scoped_file.h" #include "base/memory/ref_counted.h" +#include "base/notreached.h" #include "base/unguessable_token.h" #include "build/build_config.h" +#include "mojo/core/ipcz_driver/validate_enum.h" #include "mojo/public/cpp/platform/platform_handle.h" #include "third_party/ipcz/include/ipcz/ipcz.h" @@ -29,6 +31,10 @@ kReadOnly, kWritable, kUnsafe, + + // For ValidateEnum(). + kMinValue = kReadOnly, + kMaxValue = kUnsafe, }; // The wire representation of a serialized shared buffer. @@ -251,6 +257,9 @@ if (header_size < sizeof(BufferHeader) || header_size % 8 != 0) { return nullptr; } + if (!ValidateEnum(header.mode)) { + return nullptr; + } base::subtle::PlatformSharedMemoryRegion::Mode mode; switch (header.mode) { @@ -264,7 +273,7 @@ mode = base::subtle::PlatformSharedMemoryRegion::Mode::kUnsafe; break; default: - return nullptr; + NOTREACHED(); } std::optional<base::UnguessableToken> guid =
diff --git a/mojo/core/ipcz_driver/transport.cc b/mojo/core/ipcz_driver/transport.cc index bca98fd..51ee4158 100644 --- a/mojo/core/ipcz_driver/transport.cc +++ b/mojo/core/ipcz_driver/transport.cc
@@ -17,6 +17,7 @@ #include "base/functional/overloaded.h" #include "base/memory/scoped_refptr.h" #include "base/no_destructor.h" +#include "base/notreached.h" #include "base/numerics/safe_conversions.h" #include "base/process/process.h" #include "base/task/single_thread_task_runner.h" @@ -27,6 +28,7 @@ #include "mojo/core/ipcz_driver/object.h" #include "mojo/core/ipcz_driver/shared_buffer.h" #include "mojo/core/ipcz_driver/transmissible_platform_handle.h" +#include "mojo/core/ipcz_driver/validate_enum.h" #include "mojo/core/ipcz_driver/wrapped_platform_handle.h" #include "mojo/public/cpp/platform/platform_channel.h" #include "mojo/public/cpp/platform/platform_channel_endpoint.h" @@ -73,6 +75,10 @@ // these handles as-is. Only brokers should be trusted to send handles that // already belong to the recipient. kRecipient, + + // For ValidateEnum(). + kMinValue = kSender, + kMaxValue = kRecipient, }; // HANDLE value size varies by architecture. We always encode them with 64 bits. @@ -159,7 +165,7 @@ DCHECK(remote_process.IsValid()); #if BUILDFLAG(IS_WIN) if (remote_process_trust == Transport::ProcessTrust::kUntrusted) { - DcheckIfFileHandleIsUnsafe(handle.GetHandle().get()); + MaybeCheckIfHandleIsUnsafe(handle.GetHandle().get()); } #endif @@ -511,17 +517,33 @@ } const auto& header = *reinterpret_cast<const ObjectHeader*>(bytes.data()); + // Validate header fields. const uint32_t header_size = header.size; if (header_size < sizeof(ObjectHeader) || header_size > bytes.size()) { return IPCZ_RESULT_INVALID_ARGUMENT; } +#if BUILDFLAG(IS_WIN) + const HandleOwner handle_owner = header.handle_owner; + if (!ValidateEnum(handle_owner)) { + return IPCZ_RESULT_INVALID_ARGUMENT; + } +#endif + if (!ValidateEnum(header.type)) { + return IPCZ_RESULT_INVALID_ARGUMENT; + } + // Return early for objects that cannot be deserialized. + if (!(header.type == ObjectBase::kTransport || + header.type == ObjectBase::kSharedBuffer || + header.type == ObjectBase::kTransmissiblePlatformHandle || + header.type == ObjectBase::kWrappedPlatformHandle || + header.type == ObjectBase::kDataPipe)) { + return IPCZ_RESULT_UNIMPLEMENTED; + } #if BUILDFLAG(IS_WIN) CHECK(handles.empty()); - size_t num_handles = header.num_handles; - const HandleOwner handle_owner = header.handle_owner; - - size_t available_bytes = bytes.size() - header_size; + const size_t num_handles = header.num_handles; + const size_t available_bytes = bytes.size() - header_size; const size_t max_handles = available_bytes / sizeof(HandleData); if (num_handles > max_handles) { return IPCZ_RESULT_INVALID_ARGUMENT; @@ -534,7 +556,7 @@ auto object_data = bytes.subspan(header_size + handle_data_size); #else auto object_data = bytes.subspan(header_size); - size_t num_handles = handles.size(); + const size_t num_handles = handles.size(); #endif // A small amount of stack storage is reserved to avoid heap allocation in the @@ -559,15 +581,12 @@ auto object_handles = base::span(platform_handles); switch (header.type) { - case ObjectBase::kTransport: { + case ObjectBase::kTransport: object = Deserialize(*this, object_data, object_handles); break; - } - case ObjectBase::kSharedBuffer: object = SharedBuffer::Deserialize(object_data, object_handles); break; - case ObjectBase::kTransmissiblePlatformHandle: object = TransmissiblePlatformHandle::Deserialize(object_data, object_handles); @@ -582,7 +601,8 @@ break; default: - return IPCZ_RESULT_UNIMPLEMENTED; + // Validated at head of function so this should not be reached. + NOTREACHED(); } if (!object) { @@ -652,8 +672,7 @@ } #endif // Reject transports with out of range enum value in destination_type. - if (!(header.destination_type == kBroker || - header.destination_type == kNonBroker)) { + if (!ValidateEnum(header.destination_type)) { return nullptr; }
diff --git a/mojo/core/ipcz_driver/transport.h b/mojo/core/ipcz_driver/transport.h index 6e88d69c..e90011f 100644 --- a/mojo/core/ipcz_driver/transport.h +++ b/mojo/core/ipcz_driver/transport.h
@@ -34,6 +34,9 @@ enum EndpointType : uint32_t { kBroker, kNonBroker, + // For ValidateEnum(). + kMinValue = kBroker, + kMaxValue = kNonBroker, }; // Is the remote process trusted, only tracked on Windows. Not directly
diff --git a/mojo/core/ipcz_driver/transport_test.cc b/mojo/core/ipcz_driver/transport_test.cc index f37157e..91f2938e 100644 --- a/mojo/core/ipcz_driver/transport_test.cc +++ b/mojo/core/ipcz_driver/transport_test.cc
@@ -23,12 +23,15 @@ #include "base/memory/raw_ref.h" #include "base/memory/scoped_refptr.h" #include "base/memory/unsafe_shared_memory_region.h" +#include "base/path_service.h" #include "base/synchronization/condition_variable.h" #include "base/synchronization/lock.h" #include "base/synchronization/waitable_event.h" #include "base/test/gtest_util.h" +#include "base/test/scoped_feature_list.h" #include "base/test/task_environment.h" #include "build/build_config.h" +#include "mojo/core/embedder/features.h" #include "mojo/core/ipcz_driver/driver.h" #include "mojo/core/ipcz_driver/shared_buffer.h" #include "mojo/core/ipcz_driver/transmissible_platform_handle.h" @@ -269,7 +272,7 @@ }); } -DEFINE_TEST_CLIENT_TEST_WITH_PIPE(MalformedTransportClient, +DEFINE_TEST_CLIENT_TEST_WITH_PIPE(MalformedObjectsClient, MojoIpczTransportTest, h) { // Offsets of enums that should be validated on receipt. Serialized objects @@ -280,9 +283,16 @@ constexpr size_t object_type_offset = 4; // offsetof(TransportHeader, destination_type) + sizeof(ObjectHeader) #if BUILDFLAG(IS_WIN) + // offsetof(TransportHeader, destination_type) + sizeof(ObjectHeader) constexpr size_t transport_destination_type_offset = 0x18; + // offsetof(BufferHeader, mode) + sizeof(ObjectHeader) + constexpr size_t shared_bufffer_mode_offset = 0x20; + // offsetof(WrappedPlatformHandleHeader, type) + sizeof(ObjectHeader) + constexpr size_t wrapped_platform_type_offset = 0x1c; #else constexpr size_t transport_destination_type_offset = 0x08; + constexpr size_t shared_bufffer_mode_offset = 0x10; + constexpr size_t wrapped_platform_type_offset = 0x0c; #endif scoped_refptr<Transport> transport = ReceiveTransport(h); @@ -318,13 +328,38 @@ EXPECT_EQ("got null", listener.WaitForNextMessage().as_string()); } + { + auto shared_buffer = SharedBuffer::MakeForRegion( + base::UnsafeSharedMemoryRegion::Create(128)); + TestMessage msg = SerializeObjectFor(*transport, std::move(shared_buffer)); + // Peek into the message to break the encoded mode. + msg.bytes[shared_bufffer_mode_offset] = 22; + msg.Transmit(*transport); + EXPECT_EQ("got null", listener.WaitForNextMessage().as_string()); + } + + { + base::ScopedTempDir temp_dir; + CHECK(temp_dir.CreateUniqueTempDir()); + base::File read_only_file = base::File( + temp_dir.GetPath().AppendASCII("testfile-for-malformed-object"), + base::File::FLAG_CREATE | base::File::FLAG_WRITE); + auto wrapper = base::MakeRefCounted<WrappedPlatformHandle>(PlatformHandle( + base::ScopedPlatformFile(read_only_file.TakePlatformFile()))); + TestMessage msg = SerializeObjectFor(*transport, std::move(wrapper)); + // Peek into the message to break the encoded wrapper type. + msg.bytes[wrapped_platform_type_offset] = 22; + msg.Transmit(*transport); + EXPECT_EQ("got null", listener.WaitForNextMessage().as_string()); + } + TestMessage("done").Transmit(*transport); EXPECT_EQ(MOJO_RESULT_OK, MojoClose(h)); } -TEST_F(MojoIpczTransportTest, MalformedTransport) { +TEST_F(MojoIpczTransportTest, MalformedObjects) { RunTestClientWithController( - "MalformedTransportClient", [&](ClientController& c) { + "MalformedObjectsClient", [&](ClientController& c) { scoped_refptr<Transport> transport = CreateAndSendTransport(c.pipe(), c.process()); @@ -337,7 +372,13 @@ scoped_refptr<ObjectBase> object; const IpczResult result = transport->DeserializeObject( base::span(message.bytes), base::span(message.handles), object); - EXPECT_EQ(result, IPCZ_RESULT_UNIMPLEMENTED); + EXPECT_EQ(result, IPCZ_RESULT_INVALID_ARGUMENT); +#if !BUILDFLAG(IS_WIN) + // Adopt and free memory tracking this handle, as DeserializeObject + // does not get far enough in to do so itself - this is ok to fake up + // in this test as it validates that invalid messages are rejected. + TransmissiblePlatformHandle::TakeFromHandle(message.handles[0]); +#endif // !BUILDFLAG(IS_WIN) TestMessage("got null").Transmit(*transport); } @@ -351,6 +392,27 @@ TestMessage("got null").Transmit(*transport); } + { + // Shared memory mode is invalid so the object should be rejected. + TestMessage message = listener.WaitForNextMessage(); + scoped_refptr<ObjectBase> object; + const IpczResult result = transport->DeserializeObject( + base::span(message.bytes), base::span(message.handles), object); + EXPECT_EQ(result, IPCZ_RESULT_INVALID_ARGUMENT); + TestMessage("got null").Transmit(*transport); + } + + { + // Wrapped platform handle type is invalid so the object should be + // rejected. + TestMessage message = listener.WaitForNextMessage(); + scoped_refptr<ObjectBase> object; + const IpczResult result = transport->DeserializeObject( + base::span(message.bytes), base::span(message.handles), object); + EXPECT_EQ(result, IPCZ_RESULT_INVALID_ARGUMENT); + TestMessage("got null").Transmit(*transport); + } + EXPECT_EQ("done", listener.WaitForNextMessage().as_string()); listener.WaitForDisconnect(); }); @@ -766,6 +828,80 @@ }); } +DEFINE_TEST_CLIENT_TEST_WITH_PIPE(TransmitThreadClient, + MojoIpczTransportTest, + h) { + scoped_refptr<Transport> transport = ReceiveTransport(h); + + TransportListener listener(*transport); + + scoped_refptr<WrappedPlatformHandle> wrapper = + DeserializeObjectFrom<WrappedPlatformHandle>( + *transport, listener.WaitForNextMessage()); + CHECK(wrapper); + auto handle = wrapper->TakeHandle().TakeHandle(); + EXPECT_EQ(MOJO_RESULT_OK, MojoClose(h)); +} + +class MojoIpczTransportHandleTest + : public MojoIpczTransportTest, + public ::testing::WithParamInterface</*feature_enabled=*/bool> { + public: + MojoIpczTransportHandleTest() { + features_.InitWithFeatureState(core::kMojoHandleTypeProtections, + GetParam()); + } + + private: + base::test::ScopedFeatureList features_; +}; + +// Tests that only the allowlisted set of object types can be transmitted. See +// `MaybeCheckIfHandleIsUnsafe` for the allowlist. An object of type "Thread" is +// used here. +TEST_P(MojoIpczTransportHandleTest, TransmitThread) { + RunTestClientWithController("TransmitThreadClient", [&](ClientController& c) { + scoped_refptr<Transport> transport = CreateAndSendTransport( + c.pipe(), c.process(), Transport::ProcessTrust::kUntrusted); + + TransportListener listener(*transport); + HANDLE thread; + // Create a real Thread handle, not a psuedohandle. Psuedohandles are + // blocked elsewhere. + CHECK(::DuplicateHandle(::GetCurrentProcess(), ::GetCurrentThread(), + ::GetCurrentProcess(), &thread, + /*dwDesiredAccess=*/0, /*bInheritHandle=*/FALSE, + DUPLICATE_SAME_ACCESS)); + auto thread_wrapper = base::MakeRefCounted<WrappedPlatformHandle>( + PlatformHandle(base::win::ScopedHandle(thread))); + if (GetParam()) { + EXPECT_NOTREACHED_DEATH({ + SerializeObjectFor(*transport, std::move(thread_wrapper)) + .Transmit(*transport); + }); + // Handler will never get this message as the controller has crashed in + // death check, so send over a valid handle in the form of a file to + // unblock the handler. + SerializeFileFor( + *transport, base::File(base::PathService::CheckedGet(base::FILE_EXE), + base::File::FLAG_OPEN | base::File::FLAG_READ)) + .Transmit(*transport); + } else { + SerializeObjectFor(*transport, std::move(thread_wrapper)) + .Transmit(*transport); + } + listener.WaitForDisconnect(); + }); +} + +INSTANTIATE_TEST_SUITE_P(/*empty prefix*/, + MojoIpczTransportHandleTest, + testing::Bool(), + [](auto& info) { + return info.param ? "FeatureEnabled" + : "FeatureDisabled"; + }); + #endif // BUILDFLAG(IS_WIN) DEFINE_TEST_CLIENT_TEST_WITH_PIPE(TransportFromUntrustedClient,
diff --git a/mojo/core/ipcz_driver/validate_enum.h b/mojo/core/ipcz_driver/validate_enum.h new file mode 100644 index 0000000..44249f9 --- /dev/null +++ b/mojo/core/ipcz_driver/validate_enum.h
@@ -0,0 +1,23 @@ +// 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 MOJO_CORE_IPCZ_DRIVER_VALIDATE_ENUM_H_ +#define MOJO_CORE_IPCZ_DRIVER_VALIDATE_ENUM_H_ + +namespace mojo::core::ipcz_driver { + +// Enum must have kMinValue and kMaxValue members and be non-sparse. Use this to +// validate that values from the wire are inside the expected range on receipt. +template <typename T> +bool ValidateEnum(T& value) { + using UnderlyingType = std::underlying_type<T>::type; + return static_cast<UnderlyingType>(value) >= + static_cast<UnderlyingType>(T::kMinValue) && + static_cast<UnderlyingType>(value) <= + static_cast<UnderlyingType>(T::kMaxValue); +} + +} // namespace mojo::core::ipcz_driver + +#endif // MOJO_CORE_IPCZ_DRIVER_VALIDATE_ENUM_H_
diff --git a/mojo/core/ipcz_driver/wrapped_platform_handle.cc b/mojo/core/ipcz_driver/wrapped_platform_handle.cc index 8f15cedc..9dbf38b6e 100644 --- a/mojo/core/ipcz_driver/wrapped_platform_handle.cc +++ b/mojo/core/ipcz_driver/wrapped_platform_handle.cc
@@ -11,6 +11,7 @@ #include "base/files/scoped_file.h" #include "base/notreached.h" #include "build/build_config.h" +#include "mojo/core/ipcz_driver/validate_enum.h" #include "mojo/public/cpp/platform/platform_handle.h" #include "third_party/ipcz/include/ipcz/ipcz.h" @@ -50,6 +51,14 @@ // back to a file descriptor. kIndirectFileDescriptor, #endif + + // For ValidateEnum(). + kMinValue = kTransmissible, +#if BUILDFLAG(IS_FUCHSIA) || BUILDFLAG(IS_APPLE) + kMaxValue = kIndirectFileDescriptor, +#else + kMaxValue = kTransmissible, +#endif }; // Header for a serialized WrappedPlatformHandle object. @@ -175,6 +184,9 @@ if (header_size < sizeof(header) || header_size % 8 != 0) { return nullptr; } + if (!ValidateEnum(header.type)) { + return nullptr; + } PlatformHandle handle = std::move(handles[0]); switch (header.type) { @@ -188,7 +200,8 @@ #endif default: - return nullptr; + // Validated at head of function. + NOTREACHED(); } if (!handle.is_valid()) {
diff --git a/mojo/core/platform_handle_in_transit.cc b/mojo/core/platform_handle_in_transit.cc index 670dca4..51729ad 100644 --- a/mojo/core/platform_handle_in_transit.cc +++ b/mojo/core/platform_handle_in_transit.cc
@@ -33,7 +33,7 @@ base::ProcessHandle to_process, PlatformHandleInTransit::TransferTargetTrustLevel trust) { if (trust == PlatformHandleInTransit::kUntrustedTarget) { - DcheckIfFileHandleIsUnsafe(handle); + MaybeCheckIfHandleIsUnsafe(handle); } // Duplicating INVALID_HANDLE_VALUE passes a process handle. If you intend to
diff --git a/mojo/public/cpp/platform/platform_handle_security_util_win.cc b/mojo/public/cpp/platform/platform_handle_security_util_win.cc index d2f8fa2f..987186d9 100644 --- a/mojo/public/cpp/platform/platform_handle_security_util_win.cc +++ b/mojo/public/cpp/platform/platform_handle_security_util_win.cc
@@ -6,19 +6,25 @@ #include <windows.h> +#include <algorithm> #include <optional> +#include "base/check.h" #include "base/containers/span.h" #include "base/dcheck_is_on.h" +#include "base/debug/alias.h" #include "base/debug/stack_trace.h" #include "base/feature_list.h" #include "base/features.h" #include "base/functional/callback_helpers.h" #include "base/logging.h" #include "base/no_destructor.h" +#include "base/notreached.h" #include "base/win/nt_status.h" #include "base/win/scoped_handle.h" #include "base/win/security_util.h" +#include "base/win/win_util.h" +#include "mojo/core/embedder/features.h" namespace mojo { @@ -69,8 +75,31 @@ } // namespace -void DcheckIfFileHandleIsUnsafe(HANDLE handle) { +void MaybeCheckIfHandleIsUnsafe(HANDLE handle) { + if (base::FeatureList::IsEnabled(core::kMojoHandleTypeProtections)) { + // This check should be inexpensive as it calls directly to NtQueryObject. + const auto type = base::win::GetObjectTypeName(handle); + + if (type.has_value()) { + constexpr static const auto kSupported = std::to_array<const wchar_t*>({ + L"Section", + L"File", + L"Directory", + L"DxgkSharedResource", + }); + + if (std::find_if(kSupported.begin(), kSupported.end(), + [&type](const wchar_t* entry) { + return *type == entry; + }) == kSupported.end()) { + base::debug::Alias(&handle); + DEBUG_ALIAS_FOR_WCHARCSTR(type_for_debugging, type->c_str(), 64); + NOTREACHED() << "Cannot transfer handle of type " << *type; + } + } + } #if DCHECK_IS_ON() + // More expensive checks go here. if (GetFileType(handle) != FILE_TYPE_DISK) { return; }
diff --git a/mojo/public/cpp/platform/platform_handle_security_util_win.h b/mojo/public/cpp/platform/platform_handle_security_util_win.h index b44602e1..ccecded 100644 --- a/mojo/public/cpp/platform/platform_handle_security_util_win.h +++ b/mojo/public/cpp/platform/platform_handle_security_util_win.h
@@ -14,10 +14,11 @@ using FileHandleSecurityErrorCallback = base::RepeatingCallback<bool()>; // This function DCHECKs if `handle` is to a writeable file that can be mapped -// executable. If so, this is a security risk. Does nothing in non-DCHECK -// builds. +// executable. If so, this is a security risk. In non-DCHECK builds only basic +// validation is done to verify that the handle is of a valid type to be +// transferred across processes. COMPONENT_EXPORT(MOJO_CPP_PLATFORM) -void DcheckIfFileHandleIsUnsafe(HANDLE handle); +void MaybeCheckIfHandleIsUnsafe(HANDLE handle); // Sets a callback for testing that will be called before DCHECKing inside // DcheckIfFileHandleIsUnsafe because of an insecure handle. If the callback has
diff --git a/mojo/public/tools/fuzzers/mojolpm.gni b/mojo/public/tools/fuzzers/mojolpm.gni index 12fb1e8..41394c7 100644 --- a/mojo/public/tools/fuzzers/mojolpm.gni +++ b/mojo/public/tools/fuzzers/mojolpm.gni
@@ -276,6 +276,9 @@ inputs = jinja2_sources deps = [] + if (defined(invoker.deps)) { + deps += invoker.deps + } if (defined(invoker.proto_deps)) { deps += invoker.proto_deps } @@ -289,6 +292,7 @@ "-f", rebase_path(invoker.interface_file, root_build_dir), ] + inputs += [ invoker.interface_file ] } args += [ "--output_file_format",
diff --git a/pdf/pdf_ink_module.cc b/pdf/pdf_ink_module.cc index 727ce88..0ad1fb74 100644 --- a/pdf/pdf_ink_module.cc +++ b/pdf/pdf_ink_module.cc
@@ -453,17 +453,16 @@ CHECK(mouse_up_result); } - if (features::kPdfInk2TextHighlighting.Get() && - state.brush_type == PdfInkBrush::Type::kHighlighter && - client_->IsSelectableTextOrLinkArea(position)) { + if (IsHighlightingTextAtPosition(state, position)) { return StartTextHighlight(position, event.ClickCount(), event.TimeStamp()); } + + return StartStroke(position, event.TimeStamp(), + ink::StrokeInput::ToolType::kMouse); } - return is_drawing_stroke() - ? StartStroke(position, event.TimeStamp(), - ink::StrokeInput::ToolType::kMouse) - : StartEraseStroke(position, ink::StrokeInput::ToolType::kMouse); + + return StartEraseStroke(position, ink::StrokeInput::ToolType::kMouse); } bool PdfInkModule::OnMouseUp(const blink::WebMouseEvent& event) { @@ -775,9 +774,7 @@ state.input_last_event.reset(); bool set_drawing_brush = MaybeSetDrawingBrush(); - if (features::kPdfInk2TextHighlighting.Get() && - state.brush_type == PdfInkBrush::Type::kHighlighter && - client_->IsSelectableTextOrLinkArea(position)) { + if (IsHighlightingTextAtPosition(state, position)) { client_->UpdateInkCursor(ui::mojom::CursorType::kIBeam); } else if (set_drawing_brush) { MaybeSetCursor(); @@ -1318,6 +1315,14 @@ // so the backend doesn't CHECK when it's sent from the frontend. } +bool PdfInkModule::IsHighlightingTextAtPosition( + const DrawingStrokeState& state, + const gfx::PointF& position) const { + return features::kPdfInk2TextHighlighting.Get() && + state.brush_type == PdfInkBrush::Type::kHighlighter && + client_->IsSelectableTextOrLinkArea(position); +} + PdfInkBrush& PdfInkModule::GetDrawingBrush() { // Use the const PdfInkBrush getter and remove the const qualifier to avoid // duplicate getter logic.
diff --git a/pdf/pdf_ink_module.h b/pdf/pdf_ink_module.h index cf02d0f..fa8febc 100644 --- a/pdf/pdf_ink_module.h +++ b/pdf/pdf_ink_module.h
@@ -420,6 +420,13 @@ return std::get<TextHighlightState>(current_tool_state_); } + // Returns true when the user is using a highlighter over selectable text at + // `position`. + // + // - Only returns true when the text highlighting feature is enabled. + bool IsHighlightingTextAtPosition(const DrawingStrokeState& state, + const gfx::PointF& position) const; + // Returns the current brush. Must be in a drawing stroke state. PdfInkBrush& GetDrawingBrush(); const PdfInkBrush& GetDrawingBrush() const;
diff --git a/services/network/mdns_responder.cc b/services/network/mdns_responder.cc index cddf397..6ab9d537 100644 --- a/services/network/mdns_responder.cc +++ b/services/network/mdns_responder.cc
@@ -2,11 +2,6 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifdef UNSAFE_BUFFERS_BUILD -// TODO(crbug.com/390223051): Remove C-library calls to fix the errors. -#pragma allow_unsafe_libc_calls -#endif - #include "services/network/mdns_responder.h" #include <algorithm> @@ -364,7 +359,7 @@ DCHECK(response.io_buffer() != nullptr); auto buf = base::MakeRefCounted<net::IOBufferWithSize>(response.io_buffer_size()); - memcpy(buf->data(), response.io_buffer()->data(), response.io_buffer_size()); + buf->span().copy_from(response.io_buffer()->first(response.io_buffer_size())); return buf; } @@ -382,7 +377,7 @@ DCHECK(response.io_buffer() != nullptr); auto buf = base::MakeRefCounted<net::IOBufferWithSize>(response.io_buffer_size()); - memcpy(buf->data(), response.io_buffer()->data(), response.io_buffer_size()); + buf->span().copy_from(response.io_buffer()->first(response.io_buffer_size())); return buf; } @@ -404,7 +399,7 @@ DCHECK(response.io_buffer() != nullptr); auto buf = base::MakeRefCounted<net::IOBufferWithSize>(response.io_buffer_size()); - memcpy(buf->data(), response.io_buffer()->data(), response.io_buffer_size()); + buf->span().copy_from(response.io_buffer()->first(response.io_buffer_size())); return buf; }
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 e3609a1..9ccd457 100644 --- a/services/on_device_model/ml/on_device_model_executor.cc +++ b/services/on_device_model/ml/on_device_model_executor.cc
@@ -311,12 +311,6 @@ std::move(response), std::move(on_complete), std::move(cloned)); ChromeMLExecutionOutputFn output_fn = responder_->CreateOutputFn(); ChromeMLConstraint constraint = 0; - if (options->response_json_schema && - !options->response_json_schema->empty()) { - options->constraint = - on_device_model::mojom::ResponseConstraint::NewJsonSchema( - *options->response_json_schema); - } if (options->constraint) { constraint = executor_->CreateConstraint(*options->constraint); if (!constraint) {
diff --git a/services/on_device_model/ml/session_accessor.cc b/services/on_device_model/ml/session_accessor.cc index dec12b5..8704e5e 100644 --- a/services/on_device_model/ml/session_accessor.cc +++ b/services/on_device_model/ml/session_accessor.cc
@@ -156,17 +156,6 @@ // this fallback path. if (!params) { params = on_device_model::mojom::SessionParams::New(); - // If session params are not provided but adaptation params are, inherit - // values from adaptation. - if (adaptation_params) { - if (adaptation_params->enable_image_input) { - params->capabilities.Put(on_device_model::CapabilityFlags::kImageInput); - } - if (adaptation_params->enable_audio_input) { - params->capabilities.Put(on_device_model::CapabilityFlags::kAudioInput); - } - params->max_tokens = adaptation_params->max_tokens; - } params->top_k = GetTopK(std::nullopt); params->temperature = GetTemperature(std::nullopt); } else {
diff --git a/services/on_device_model/ml/ts_model.cc b/services/on_device_model/ml/ts_model.cc index bf6885c..a747188 100644 --- a/services/on_device_model/ml/ts_model.cc +++ b/services/on_device_model/ml/ts_model.cc
@@ -64,6 +64,7 @@ ClassifyTextSafetyCallback callback) override; void DetectLanguage(const std::string& text, DetectLanguageCallback callback) override; + void Clone(mojo::PendingReceiver<mojom::TextSafetySession> session) override; mojom::SafetyInfoPtr ClassifyTextSafety(const std::string& text); mojom::LanguageDetectionResultPtr DetectLanguage(std::string_view text); @@ -144,6 +145,10 @@ std::move(callback).Run(DetectLanguage(text)); } +void TsModel::Clone(mojo::PendingReceiver<mojom::TextSafetySession> session) { + StartSession(std::move(session)); +} + DISABLE_CFI_DLSYM mojom::SafetyInfoPtr TsModel::ClassifyTextSafety(const std::string& text) { if (!model_) {
diff --git a/services/on_device_model/on_device_model_service.cc b/services/on_device_model/on_device_model_service.cc index 06fac0be6..2d1044b 100644 --- a/services/on_device_model/on_device_model_service.cc +++ b/services/on_device_model/on_device_model_service.cc
@@ -303,13 +303,6 @@ return; } - // TODO(crbug.com/403383823): Remove these deprecated fields. - if (options->top_k.has_value() || options->temperature.has_value()) { - receiver_.ReportBadMessage( - "Passing sampling params per generation is deprecated."); - return; - } - auto generate_internal = base::BindOnce( &SessionWrapper::GenerateInternal, weak_ptr_factory_.GetWeakPtr(), std::move(options), std::move(response));
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 802fbfee..bd8a90c8 100644 --- a/services/on_device_model/on_device_model_service_unittest.cc +++ b/services/on_device_model/on_device_model_service_unittest.cc
@@ -10,7 +10,6 @@ #include "base/test/task_environment.h" #include "base/test/test_future.h" #include "mojo/public/cpp/bindings/remote.h" -#include "mojo/public/cpp/test_support/test_utils.h" #include "services/on_device_model/fake/fake_chrome_ml_api.h" #include "services/on_device_model/fake/on_device_model_fake.h" #include "services/on_device_model/ml/chrome_ml_types.h" @@ -225,25 +224,6 @@ "Context: more\n", "Context: cheddar\n")); } -TEST_F(OnDeviceModelServiceTest, GenerateWithSamplingParamsIsNotAllowed) { - auto model = LoadModel(); - - TestResponseHolder response; - mojo::Remote<mojom::Session> session; - model->StartSession(session.BindNewPipeAndPassReceiver(), nullptr); - session->Append(MakeInput("cheese"), {}); - - // Sampling params should be passed at session creation, not to Generate(). - auto generate_options = mojom::GenerateOptions::New(); - generate_options->top_k = 2; - generate_options->temperature = 0.8; - - mojo::test::BadMessageObserver bad_message_observer; - session->Generate(std::move(generate_options), response.BindRemote()); - EXPECT_THAT(bad_message_observer.WaitForBadMessage(), - testing::HasSubstr("deprecated")); -} - TEST_F(OnDeviceModelServiceTest, CloneContextAndContinue) { auto model = LoadModel(); @@ -556,40 +536,6 @@ "Context: User: bye\n")); } -TEST_F(OnDeviceModelServiceTest, AppendWithImagesAdaptation) { - auto model = LoadModel(); - auto params = mojom::LoadAdaptationParams::New(); - params->enable_image_input = true; - auto adaptation = LoadAdaptationWithParams(*model, std::move(params)); - - mojo::Remote<mojom::Session> session; - auto session_params = mojom::SessionParams::New(); - session_params->capabilities.Put(CapabilityFlags::kImageInput); - adaptation->StartSession(session.BindNewPipeAndPassReceiver(), - std::move(session_params)); - - std::vector<ml::InputPiece> pieces; - pieces.push_back("bleu"); - - SkBitmap moldy_cheese; - moldy_cheese.allocPixels( - SkImageInfo::Make(63, 42, kRGBA_8888_SkColorType, kOpaque_SkAlphaType), - 0); - moldy_cheese.eraseColor(SK_ColorBLUE); - pieces.push_back(moldy_cheese); - - pieces.push_back("cheese"); - - session->Append(MakeInput(std::move(pieces)), {}); - - TestResponseHolder response; - session->Generate(mojom::GenerateOptions::New(), response.BindRemote()); - response.WaitForCompletion(); - - EXPECT_THAT(response.responses(), - ElementsAre("Context: bleu[Bitmap of size 63x42]cheese\n")); -} - TEST_F(OnDeviceModelServiceTest, AppendWithImages) { auto model = LoadModel(); mojo::Remote<mojom::Session> session; @@ -662,6 +608,34 @@ EXPECT_THAT(resp2->class_scores, ElementsAre(0.2, 0.2)); } +TEST_F(OnDeviceModelServiceTest, CloneTextSafety) { + FakeFile ts_data("fake_ts_data"); + FakeFile ts_sp_model("fake_ts_sp_model"); + TextSafetyLoaderParams params; + params.ts_paths.emplace(); + params.ts_paths->data = ts_data.Path(); + params.ts_paths->sp_model = ts_sp_model.Path(); + mojo::Remote<mojom::TextSafetyModel> model; + service()->LoadTextSafetyModel(LoadTextSafetyParams(params), + model.BindNewPipeAndPassReceiver()); + + mojo::Remote<mojom::TextSafetySession> session; + model->StartSession(session.BindNewPipeAndPassReceiver()); + { + base::test::TestFuture<mojom::SafetyInfoPtr> future; + session->ClassifyTextSafety("unsafe text", future.GetCallback()); + EXPECT_THAT(future.Take()->class_scores, ElementsAre(0.8, 0.8)); + } + + mojo::Remote<mojom::TextSafetySession> clone; + session->Clone(clone.BindNewPipeAndPassReceiver()); + { + base::test::TestFuture<mojom::SafetyInfoPtr> future; + clone->ClassifyTextSafety("unsafe text", future.GetCallback()); + EXPECT_THAT(future.Take()->class_scores, ElementsAre(0.8, 0.8)); + } +} + TEST_F(OnDeviceModelServiceTest, PerformanceHint) { auto model = LoadModel(ml::ModelBackendType::kGpuBackend, ml::ModelPerformanceHint::kFastestInference);
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 a72ba5c6..f64fd34 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
@@ -223,13 +223,6 @@ chunk->text = "Context: " + text + "\n"; remote->OnResponse(std::move(chunk)); } - if (options->top_k > 1) { - auto chunk = mojom::ResponseChunk::New(); - chunk->text += "TopK: " + base::NumberToString(*options->top_k) + - ", Temp: " + base::NumberToString(*options->temperature) + - "\n"; - remote->OnResponse(std::move(chunk)); - } } else { for (const auto& text : settings_->model_execute_result) { output_token_count += text.size(); @@ -363,6 +356,11 @@ std::move(callback).Run(DummyDetectLanguage(text)); } +void FakeTsModel::Clone( + mojo::PendingReceiver<mojom::TextSafetySession> session) { + StartSession(std::move(session)); +} + FakeTsHolder::FakeTsHolder() = default; FakeTsHolder::~FakeTsHolder() = default;
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 1e6dacd41..2496132 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
@@ -176,6 +176,7 @@ ClassifyTextSafetyCallback callback) override; void DetectLanguage(const std::string& text, DetectLanguageCallback callback) override; + void Clone(mojo::PendingReceiver<mojom::TextSafetySession> session) override; private: bool has_safety_model_ = false;
diff --git a/services/on_device_model/public/mojom/on_device_model.mojom b/services/on_device_model/public/mojom/on_device_model.mojom index 286630b0..1667823 100644 --- a/services/on_device_model/public/mojom/on_device_model.mojom +++ b/services/on_device_model/public/mojom/on_device_model.mojom
@@ -93,16 +93,6 @@ struct LoadAdaptationParams { // Assets for an adaptation. AdaptationAssets assets; - - // Deprecated: use SessionParams.max_tokens instead. - uint32 max_tokens = 0; - - // Deprecated: use SessionParams.capabilities.image_input instead. - [MinVersion=1] - bool enable_image_input = false; - // Deprecated: use SessionParams.capabilities.audio_input instead. - [MinVersion=2] - bool enable_audio_input = false; }; // A set of capabilities that a model can have. @@ -192,9 +182,6 @@ // The maximum number of tokens that should be processed. If zero, will // process all tokens from this input. uint32 max_tokens = 0; - - // Deprecated. Does nothing. - uint32 token_offset = 0; }; [Stable, Extensible] @@ -218,15 +205,6 @@ // sequence length. uint32 max_output_tokens = 0; - // Deprecated: use SessionParams.top_k instead. - uint32? top_k; - // Deprecated: use SessionParams.temperature instead. - float? temperature; - - // Deprecated: use `constraint` instead. - [MinVersion=1] - string? response_json_schema; - // Specifies any constraints on the output for this Generate() call. Note that // only one of the JSON schema or regex constraints should be provided. [MinVersion=2] @@ -319,6 +297,9 @@ // Detects the language of the text using the language classifier. Returns // null if there is no classifier available. DetectLanguage@2(string text) => (LanguageDetectionResult? result); + + // Clones the session and any configuration. + Clone@3(pending_receiver<TextSafetySession> session); }; // A loaded text safety model, that can be shared by multiple features.
diff --git a/testing/scripts/run_performance_tests.py b/testing/scripts/run_performance_tests.py index 2717b48..91a8ec6c 100755 --- a/testing/scripts/run_performance_tests.py +++ b/testing/scripts/run_performance_tests.py
@@ -31,7 +31,7 @@ TESTING: To test changes to this script, please run unit tests: $ cd testing/scripts -$ python3 -m unittest run_performance_tests_unittest.py +$ vpython3 -m unittest run_performance_tests_unittest.py Run end-to-end tests: $ cd tools/perf @@ -803,6 +803,15 @@ self.options.passthrough_args.remove(wpr_arg) return [_create_network_json('wpr', path=archive, wpr_go_bin=wpr_go)] + def _check_for_embedder_arg(self): + embedder_arg = _get_arg(self.options.passthrough_args, '--embedder=') + if embedder_arg: + embedder_package_name = embedder_arg.split('=', 1)[1] + # This will affect browser arg, but is not to be passed by itself + self.options.passthrough_args.remove(embedder_arg) + return embedder_package_name + return None + def _find_browser(self, browser_arg): # Replacing --browser with the generated self.browser. self.options.passthrough_args = [ @@ -821,7 +830,9 @@ if not possible_browser: raise ValueError(f'Unable to find Chrome browser of type: {browser_arg}') if self.is_android: - browser_app = possible_browser.settings.package + # Check for an arg with embedder package name to override browser (WV) + browser_app = (self._check_for_embedder_arg() + or possible_browser.settings.package) android_json = self.ANDROID_HJSON % (browser_app, ADB_TOOL) self.browser = self.CHROME_BROWSER % android_json else:
diff --git a/testing/scripts/run_performance_tests_unittest.py b/testing/scripts/run_performance_tests_unittest.py index 42ca2fa0a..195178e2 100644 --- a/testing/scripts/run_performance_tests_unittest.py +++ b/testing/scripts/run_performance_tests_unittest.py
@@ -151,7 +151,9 @@ mock_copyfile.assert_called_with('file', 'dir/benchmarks_shard_map.json') # pylint: enable=no-self-use - @mock.patch.object(run_performance_tests.CrossbenchTest, 'execute_benchmark') + @mock.patch.object(run_performance_tests.CrossbenchTest, + 'execute_benchmark', + return_value=0) def testCrossbenchTestBenchmarksArg(self, mock_execute_benchmark): fake_args = _create_crossbench_args() options = run_performance_tests.parse_arguments(fake_args) @@ -179,7 +181,9 @@ with self.assertRaises(Exception): run_performance_tests.CrossbenchTest(options, 'dir').execute() - @mock.patch.object(run_performance_tests, '_run_benchmarks_on_shardmap') + @mock.patch.object(run_performance_tests, + '_run_benchmarks_on_shardmap', + return_value=0) @mock.patch.object(os.path, 'dirname') @mock.patch.object(run_performance_tests, 'load_map_file') def testCrossbenchTestShardMapFile(self, mock_load_map_file, mock_direname, @@ -200,7 +204,9 @@ expected_options, 'dir', []) - @mock.patch.object(run_performance_tests, '_run_benchmarks_on_shardmap') + @mock.patch.object(run_performance_tests, + '_run_benchmarks_on_shardmap', + return_value=0) @mock.patch.object(os.path, 'dirname') @mock.patch.object(run_performance_tests, 'load_map_string') def testCrossbenchTestShardMapString(self, mock_load_map_string, @@ -333,7 +339,7 @@ expected_dict = { 'type': 'local', 'path': local_fileserver, - 'url': 'http://localhost:8000' + 'url': 'http://localhost:0' } crosebench_test = run_performance_tests.CrossbenchTest(options, 'dir') @@ -349,7 +355,7 @@ expected_dict = { 'type': 'local', 'path': local_fileserver, - 'url': 'http://localhost:8000' + 'url': 'http://localhost:0' } crosebench_test = run_performance_tests.CrossbenchTest(options, 'dir') @@ -360,10 +366,10 @@ @mock.patch.object(binary_manager, 'FetchPath') def testCrossbenchGetDefaultWpr(self, mock_fetch_path): mock_fetch_path.return_value = 'wpr_go_path' - fake_args = _create_crossbench_args() + ['--wpr'] + fake_args = _create_crossbench_args() + ['--wpr=fake.wprgo'] options = run_performance_tests.parse_arguments(fake_args) data_dir = run_performance_tests.PAGE_SETS_DATA - archive = str(data_dir / 'crossbench_android_speedometer_3.0_000.wprgo') + archive = str(data_dir / 'fake.wprgo') expected_dict = { 'type': 'wpr', 'path': archive, @@ -393,6 +399,19 @@ network_dict = json.loads(crosebench_test.network[0].split('=', 1)[1]) self.assertDictEqual(network_dict, expected_dict) + def testCrossbenchFindBrowserFromEmbedder(self): + fake_args = ( + _create_crossbench_args('android-webview-trichrome-google-bundle') + + ['--embedder=org.foo.bar']) + options = run_performance_tests.parse_arguments(fake_args) + + crossbench_test = run_performance_tests.CrossbenchTest(options, 'dir') + + expected_hjson = crossbench_test.ANDROID_HJSON % ( + 'org.foo.bar', run_performance_tests.ADB_TOOL) + expected_browser = crossbench_test.CHROME_BROWSER % expected_hjson + self.assertEqual(crossbench_test.network, expected_browser) + def _create_crossbench_args(browser='./chrome'): return [
diff --git a/testing/variations/fieldtrial_testing_config.json b/testing/variations/fieldtrial_testing_config.json index 5a8c902..e3f4ca651 100644 --- a/testing/variations/fieldtrial_testing_config.json +++ b/testing/variations/fieldtrial_testing_config.json
@@ -440,6 +440,22 @@ ] } ], + "AndroidAutofillLazyFrameworkWrapper": [ + { + "platforms": [ + "android", + "android_webview" + ], + "experiments": [ + { + "name": "Enabled", + "enable_features": [ + "AndroidAutofillLazyFrameworkWrapper" + ] + } + ] + } + ], "AndroidBookmarkBar": [ { "platforms": [ @@ -19582,6 +19598,21 @@ ] } ], + "ReportingEventsForAndroid": [ + { + "platforms": [ + "android" + ], + "experiments": [ + { + "name": "Enabled", + "enable_features": [ + "EnterpriseSecurityEventReportingOnAndroid" + ] + } + ] + } + ], "ReportingServiceAlwaysFlush": [ { "platforms": [ @@ -23897,7 +23928,6 @@ "android", "android_webview", "android_weblayer", - "chromeos", "fuchsia", "ios", "linux",
diff --git a/third_party/angle b/third_party/angle index 5b08297..00845fd 160000 --- a/third_party/angle +++ b/third_party/angle
@@ -1 +1 @@ -Subproject commit 5b08297d9c01207db50f399083c05c8f168d6b88 +Subproject commit 00845fd649a450a00e61215f44bb4fc95d515c97
diff --git a/third_party/blink/common/web_preferences/web_preferences.cc b/third_party/blink/common/web_preferences/web_preferences.cc index ce40870f..f4c3d012 100644 --- a/third_party/blink/common/web_preferences/web_preferences.cc +++ b/third_party/blink/common/web_preferences/web_preferences.cc
@@ -12,17 +12,6 @@ #include "third_party/blink/public/mojom/webpreferences/web_preferences.mojom.h" #include "ui/base/ui_base_switches_util.h" -namespace { - -bool IsTouchDragDropEnabled() { - // Cache the enabled state so it isn't queried on every WebPreferences - // creation. Note that this means unit tests can't override the state. - static const bool enabled = switches::IsTouchDragDropEnabled(); - return enabled; -} - -} // namespace - namespace blink { namespace web_pref { @@ -32,8 +21,7 @@ // "Zyyy" is the ISO 15924 script code for undetermined script aka Common. const char kCommonScript[] = "Zyyy"; -WebPreferences::WebPreferences() - : touch_drag_drop_enabled(IsTouchDragDropEnabled()) { +WebPreferences::WebPreferences() { standard_font_family_map[web_pref::kCommonScript] = u"Times New Roman"; #if BUILDFLAG(IS_MAC) fixed_font_family_map[web_pref::kCommonScript] = u"Menlo";
diff --git a/third_party/blink/public/common/web_preferences/web_preferences.h b/third_party/blink/public/common/web_preferences/web_preferences.h index 15117b45..2d9ef567 100644 --- a/third_party/blink/public/common/web_preferences/web_preferences.h +++ b/third_party/blink/public/common/web_preferences/web_preferences.h
@@ -417,7 +417,7 @@ // Whether touch input can trigger HTML drag-and-drop operations. The // default value depends on the platform. - bool touch_drag_drop_enabled; // Set in web_preferences.cc + bool touch_drag_drop_enabled = false; // Whether the end of a drag fires a contextmenu event and possibly shows a // context-menu (depends on how the event is handled). Currently touch-drags
diff --git a/third_party/blink/public/mojom/use_counter/metrics/web_feature.mojom b/third_party/blink/public/mojom/use_counter/metrics/web_feature.mojom index e1cbc7f..d2e465e 100644 --- a/third_party/blink/public/mojom/use_counter/metrics/web_feature.mojom +++ b/third_party/blink/public/mojom/use_counter/metrics/web_feature.mojom
@@ -4856,6 +4856,7 @@ kEditContextTextFormatUpdateAddListener = 5552, kEditContextTextFormatUpdateFireEvent = 5553, kEditContextTextFormatUpdateTextFormatThicknessOrStyleNotNone = 5554, + kC2PAManifest = 5555, // Add new features immediately above this line. Don't change assigned // numbers of any item, and don't reuse removed slots. Also don't add extra
diff --git a/third_party/blink/public/mojom/use_counter/metrics/webdx_feature.mojom b/third_party/blink/public/mojom/use_counter/metrics/webdx_feature.mojom index 977d5580..ad952a3 100644 --- a/third_party/blink/public/mojom/use_counter/metrics/webdx_feature.mojom +++ b/third_party/blink/public/mojom/use_counter/metrics/webdx_feature.mojom
@@ -389,7 +389,7 @@ kTextUnderlineOffset = 331, kWindowManagement = 332, kRequestclose = 333, - kDRAFT_Uint8ArrayToFromBase64AndHex = 334, + kUint8arrayBase64Hex = 334, kLineBreak = 335, // Add new features immediately above this line. Don't change assigned
diff --git a/third_party/blink/public/strings/blink_strings.grd b/third_party/blink/public/strings/blink_strings.grd index 2e67454f..a327a19 100644 --- a/third_party/blink/public/strings/blink_strings.grd +++ b/third_party/blink/public/strings/blink_strings.grd
@@ -377,6 +377,10 @@ <message name="IDS_PRETTY_PRINT_JSON" desc="Label for the checkbox that enables 'pretty-printing' of JSON documents.To pretty-print JSON, carriage returns and spaces are inserted to cleanly format the JSON and make it readable."> Pretty-print </message> + <message name="IDS_PARTIAL_INTEREST_TARGET_ACTIVATION_HINT" desc="Hint text added to the page for popovers activated by elements with the 'interesttarget' attribute, which have been activated in partial interest mode. This is an instruction for which key(s) to press in order to activate the popover in full interest mode."> + Press <ph name="HOT_KEY">$1<ex>Alt + ⬆️</ex></ph> to activate + </message> + <!-- The following IDS_FORM_VALIDATION_* messages were taken from Mozilla's dom.properties file.
diff --git a/third_party/blink/public/strings/blink_strings_grd/IDS_PARTIAL_INTEREST_TARGET_ACTIVATION_HINT.png.sha1 b/third_party/blink/public/strings/blink_strings_grd/IDS_PARTIAL_INTEREST_TARGET_ACTIVATION_HINT.png.sha1 new file mode 100644 index 0000000..870bfdf --- /dev/null +++ b/third_party/blink/public/strings/blink_strings_grd/IDS_PARTIAL_INTEREST_TARGET_ACTIVATION_HINT.png.sha1
@@ -0,0 +1 @@ +fbf618cf7c2d3dc2a35ce1be80eedf145b677e56 \ No newline at end of file
diff --git a/third_party/blink/renderer/bindings/core/v8/use_counter_callback.cc b/third_party/blink/renderer/bindings/core/v8/use_counter_callback.cc index 80990ef..6409b482 100644 --- a/third_party/blink/renderer/bindings/core/v8/use_counter_callback.cc +++ b/third_party/blink/renderer/bindings/core/v8/use_counter_callback.cc
@@ -467,7 +467,7 @@ webdx_feature = WebDXFeature::kDRAFT_ExplicitResourceManagement; break; case v8::Isolate::kUint8ArrayToFromBase64AndHex: - webdx_feature = WebDXFeature::kDRAFT_Uint8ArrayToFromBase64AndHex; + webdx_feature = WebDXFeature::kUint8arrayBase64Hex; break; default: // This can happen if V8 has added counters that this version of Blink
diff --git a/third_party/blink/renderer/core/css/css.dict b/third_party/blink/renderer/core/css/css.dict index 229481d..2ea7c27 100644 --- a/third_party/blink/renderer/core/css/css.dict +++ b/third_party/blink/renderer/core/css/css.dict
@@ -940,6 +940,7 @@ "accentcolor" "accentcolortext" "-internal-auto-base-select" +"-internal-partial-interest-content" # Math functions (TODO: We're missing a lot of them) "calc"
diff --git a/third_party/blink/renderer/core/css/css_properties.json5 b/third_party/blink/renderer/core/css/css_properties.json5 index c3dfa4c..cc8657b 100644 --- a/third_party/blink/renderer/core/css/css_properties.json5 +++ b/third_party/blink/renderer/core/css/css_properties.json5
@@ -3074,6 +3074,7 @@ { name: "cursor", property_methods: ["ParseSingleValue", "CSSValueFromComputedStyleInternal"], + field_group: "inherited", inherited: true, independent: true, field_template: "keyword",
diff --git a/third_party/blink/renderer/core/css/css_value_keywords.json5 b/third_party/blink/renderer/core/css/css_value_keywords.json5 index 3f25b6b..a843480 100644 --- a/third_party/blink/renderer/core/css/css_value_keywords.json5 +++ b/third_party/blink/renderer/core/css/css_value_keywords.json5
@@ -908,6 +908,9 @@ // <select>. It should be renamed when a standardized name is chosen. "base-select", "-internal-auto-base", + // Used to set the `content` property of the pseudo element "hint" given to + // elements with partial interest (via `interesttarget` keyboard activation). + "-internal-partial-interest-content", // // background-clip/background-origin
diff --git a/third_party/blink/renderer/core/css/properties/longhands/longhands_custom.cc b/third_party/blink/renderer/core/css/properties/longhands/longhands_custom.cc index 4bc9534e..cefbbeb 100644 --- a/third_party/blink/renderer/core/css/properties/longhands/longhands_custom.cc +++ b/third_party/blink/renderer/core/css/properties/longhands/longhands_custom.cc
@@ -3,6 +3,7 @@ // found in the LICENSE file. #include "base/numerics/clamped_math.h" +#include "third_party/blink/public/strings/grit/blink_strings.h" #include "third_party/blink/renderer/core/css/basic_shape_functions.h" #include "third_party/blink/renderer/core/css/css_anchor_query_enums.h" #include "third_party/blink/renderer/core/css/css_axis_value.h" @@ -81,6 +82,7 @@ #include "third_party/blink/renderer/platform/heap/garbage_collected.h" #include "third_party/blink/renderer/platform/instrumentation/use_counter.h" #include "third_party/blink/renderer/platform/runtime_enabled_features.h" +#include "third_party/blink/renderer/platform/text/platform_locale.h" #include "third_party/blink/renderer/platform/text/quotes_data.h" // Implementations of methods in Longhand subclasses that aren't generated. @@ -2940,6 +2942,11 @@ stream.Peek().Id())) { return css_parsing_utils::ConsumeIdent(stream); } + if (context.GetMode() == kUASheetMode && + css_parsing_utils::IdentMatches< + CSSValueID::kInternalPartialInterestContent>(stream.Peek().Id())) { + return css_parsing_utils::ConsumeIdent(stream); + } CSSValueList* values = CSSValueList::CreateSpaceSeparated(); CSSValueList* outer_list = CSSValueList::CreateSlashSeparated(); @@ -3083,9 +3090,23 @@ ComputedStyleBuilder& builder = state.StyleBuilder(); if (auto* identifier_value = DynamicTo<CSSIdentifierValue>(value)) { DCHECK(identifier_value->GetValueID() == CSSValueID::kNormal || - identifier_value->GetValueID() == CSSValueID::kNone); + identifier_value->GetValueID() == CSSValueID::kNone || + identifier_value->GetValueID() == + CSSValueID::kInternalPartialInterestContent); if (identifier_value->GetValueID() == CSSValueID::kNone) { builder.SetContent(MakeGarbageCollected<NoneContentData>()); + } else if (identifier_value->GetValueID() == + CSSValueID::kInternalPartialInterestContent && + RuntimeEnabledFeatures::HTMLInterestTargetAttributeEnabled( + state.GetDocument().GetExecutionContext())) { + String hint_text = state.GetDocument().GetCachedLocale().QueryString( + IDS_PARTIAL_INTEREST_TARGET_ACTIVATION_HINT, + Element::GetPartialInterestTargetActivationHotkey()); + auto* content = + MakeGarbageCollected<TextContentData>("{" + hint_text + "}"); + auto* alt_content = MakeGarbageCollected<AltTextContentData>(hint_text); + content->SetNext(alt_content); + builder.SetContent(content); } else { builder.SetContent(nullptr); }
diff --git a/third_party/blink/renderer/core/css/selector_checker.cc b/third_party/blink/renderer/core/css/selector_checker.cc index d7270263..ddcd99a 100644 --- a/third_party/blink/renderer/core/css/selector_checker.cc +++ b/third_party/blink/renderer/core/css/selector_checker.cc
@@ -1877,7 +1877,9 @@ DCHECK(RuntimeEnabledFeatures::HTMLInterestTargetAttributeEnabled( element.GetDocument().GetExecutionContext())); return element.GetInterestState() == - Element::InterestState::kPartialInterest; + Element::InterestState::kPartialInterest || + element.GetInterestState() == + Element::InterestState::kPotentialPartialInterest; case CSSSelector::kPseudoTargetOfInterest: { DCHECK(RuntimeEnabledFeatures::HTMLInterestTargetAttributeEnabled( element.GetDocument().GetExecutionContext())); @@ -1892,8 +1894,10 @@ Element* invoker = element.GetInterestInvoker(); DCHECK(!invoker || invoker->GetInterestState() != Element::InterestState::kNoInterest); - return invoker && invoker->GetInterestState() == - Element::InterestState::kPartialInterest; + return invoker && (invoker->GetInterestState() == + Element::InterestState::kPartialInterest || + invoker->GetInterestState() == + Element::InterestState::kPotentialPartialInterest); } case CSSSelector::kPseudoHasSlotted: DCHECK(RuntimeEnabledFeatures::CSSPseudoHasSlottedEnabled());
diff --git a/third_party/blink/renderer/core/dom/element.cc b/third_party/blink/renderer/core/dom/element.cc index 780a330..fd9d0c26 100644 --- a/third_party/blink/renderer/core/dom/element.cc +++ b/third_party/blink/renderer/core/dom/element.cc
@@ -1547,7 +1547,8 @@ return popover; } -bool Element::InterestGained(Element& interest_target) { +bool Element::InterestGained(Element& interest_target, + InterestState new_state) { CHECK(RuntimeEnabledFeatures::HTMLInterestTargetAttributeEnabled( GetDocument().GetExecutionContext())); CHECK(IsInTreeScope()); @@ -1558,6 +1559,17 @@ if (interest_event->defaultPrevented()) { return false; } + + // This is now the target's interest invoker + CHECK(!interest_target.GetInterestInvoker()); + interest_target.EnsureElementRareData() + .EnsureInterestInvokerTargetData() + .setInterestInvoker(this); + ChangeInterestState(&interest_target, new_state); + DCHECK(!GetDocument().CurrentInterestTargetElements().Contains(this)); + GetDocument().CurrentInterestTargetElements().insert(this); + + // If the target is a popover, invoke it. if (auto* popover = DynamicTo<HTMLElement>(interest_target); popover && popover->PopoverType() != PopoverValueType::kNone) { if (popover->IsPopoverReady(PopoverTriggerAction::kShow, @@ -1581,6 +1593,17 @@ if (lose_interest_event->defaultPrevented()) { return false; } + + // If the target still thinks this invoker is its invoker, remove it. + if (auto* targets_invoker = interest_target.GetInterestInvoker(); + targets_invoker && targets_invoker == this) { + interest_target.EnsureElementRareData().RemoveInterestInvokerTargetData(); + ChangeInterestState(&interest_target, InterestState::kNoInterest); + DCHECK(GetDocument().CurrentInterestTargetElements().Contains(this)); + GetDocument().CurrentInterestTargetElements().erase(this); + } + + // If the target is a popover, hide it. if (auto* popover = DynamicTo<HTMLElement>(interest_target); popover && popover->PopoverType() != PopoverValueType::kNone) { if (popover->IsPopoverReady(PopoverTriggerAction::kHide, @@ -1596,8 +1619,25 @@ return true; } +// static +// This should return a string that describes the hot-key implemented below in +// Element::DefaultEventHandler. +String Element::GetPartialInterestTargetActivationHotkey() { +#if BUILDFLAG(IS_MAC) + // On a Mac, the "Alt" key is denoted with the symbol ⌥, for "Option". The + // most typical display uses the ↑ for the up arrow. So this text resolves + // to "⌥↑". + return u"\u2325\u2191"; +#else + // Other operating systems use "Alt" for the alt key, even in most other + // languages. And ⬆️ is more often used for the up arrow. So this text + // resolves to "Alt + ⬆️". + return u"Alt + \u2b06\ufe0f"; +#endif +} + void Element::DefaultEventHandler(Event& event) { - if (GetInterestState() != Element::InterestState::kNoInterest) [[unlikely]] { + if (GetInterestState() != InterestState::kNoInterest) [[unlikely]] { CHECK(RuntimeEnabledFeatures::HTMLInterestTargetAttributeEnabled( GetDocument().GetExecutionContext())); if (auto* keyboard_event = DynamicTo<KeyboardEvent>(event); @@ -1605,12 +1645,15 @@ const int modifiers = keyboard_event->GetModifiers() & blink::WebInputEvent::kKeyModifiers; auto* target = InterestTargetElement(); - if (GetInterestState() == Element::InterestState::kPartialInterest && + DCHECK_NE(GetInterestState(), InterestState::kPotentialPartialInterest); + if (GetInterestState() == InterestState::kPartialInterest && keyboard_event->key() == keywords::kArrowUp && modifiers == WebInputEvent::kAltKey) { - // Hitting the hotkey (Alt-UpArrow) on an invoker that has partial - // interest causes interest to be "upgraded" to full interest. It also - // focuses the first focusable element within the target. + // Hitting the hotkey (Alt/Option-UpArrow) on an invoker that has + // partial interest causes interest to be "upgraded" to full interest. + // It also focuses the first focusable element within the target. + // NOTE: this hotkey must be kept in sync with the string description + // returned by `GetPartialInterestTargetActivationHotkey()`. ChangeInterestState(target, InterestState::kFullInterest); if (Element* first_focusable = target->GetFocusDelegate()) { first_focusable->Focus(); @@ -7266,6 +7309,21 @@ return IsScrollableNode(this); } +bool Element::ContainsKeyboardFocusableElementsSlow( + UpdateBehavior update_behavior) const { + for (const Node* node = FlatTreeTraversal::FirstChild(*this); node; + node = FlatTreeTraversal::Next(*node, this)) { + auto* element = DynamicTo<Element>(node); + if (!element) { + continue; + } + if (element->IsKeyboardFocusableSlow(update_behavior)) { + return true; + } + } + return false; +} + // This can be slow, because it can require a tree walk. It might be // a good idea to cache this bit on the element to avoid having to // recompute it. That would require marking that bit dirty whenever @@ -7282,15 +7340,9 @@ return true; } - for (Node* node = FlatTreeTraversal::FirstChild(*this); node; - node = FlatTreeTraversal::Next(*node, this)) { - if (Element* element = DynamicTo<Element>(node)) { - if (element->IsKeyboardFocusableSlow(update_behavior)) { - return false; - } - } - } - return true; + // If the scroller already contains something focusable, then we return false, + // since it's not a "special" keyboard focusable scroller. + return !ContainsKeyboardFocusableElementsSlow(update_behavior); } // TODO(crbug.com/326681249): Should `tabindex` take precedence? @@ -10749,6 +10801,9 @@ // gained or lost. Fire the event and run any default actions. switch (new_state) { case InterestState::kPartialInterest: + NOTREACHED() << "Partial interest should never be gained directly"; + + case InterestState::kPotentialPartialInterest: case InterestState::kFullInterest: if (Element* existing_invoker = target->GetInterestInvoker()) { // We're gaining interest, but the target already has an active interest @@ -10781,36 +10836,11 @@ } } } - if (!invoker->InterestGained(*target)) { - return false; // event was cancelled. - } - // This is now the target's interest invoker - CHECK(!target->GetInterestInvoker()); - target->EnsureElementRareData() - .EnsureInterestInvokerTargetData() - .setInterestInvoker(invoker); - invoker->ChangeInterestState(target, new_state); - DCHECK(!invoker->GetDocument().CurrentInterestTargetElements().Contains( - invoker)); - invoker->GetDocument().CurrentInterestTargetElements().insert(invoker); - break; + return invoker->InterestGained(*target, new_state); case InterestState::kNoInterest: - if (!invoker->InterestLost(*target)) { - return false; // event was cancelled. - } - // If the target still thinks this invoker is its invoker, remove it. - if (auto* targets_invoker = target->GetInterestInvoker(); - targets_invoker && targets_invoker == invoker) { - target->EnsureElementRareData().RemoveInterestInvokerTargetData(); - invoker->ChangeInterestState(target, InterestState::kNoInterest); - DCHECK(invoker->GetDocument().CurrentInterestTargetElements().Contains( - invoker)); - invoker->GetDocument().CurrentInterestTargetElements().erase(invoker); - } - break; + return invoker->InterestLost(*target); } - return true; } void Element::ScheduleInterestGainedTask(InterestState new_state) { @@ -10882,7 +10912,7 @@ return nullptr; } DCHECK_EQ(invoker->InterestTargetElement(), this); - DCHECK_NE(invoker->GetInterestState(), Element::InterestState::kNoInterest); + DCHECK_NE(invoker->GetInterestState(), InterestState::kNoInterest); DCHECK_EQ(invoker->InterestTargetElement(), this); return invoker; } @@ -10952,6 +10982,8 @@ // invoker for this element. auto* upstream_data = upstream_invoker->GetInvokerData(); upstream_data->CancelInterestLostTask(); + DCHECK_NE(upstream_data->GetInterestState(), + InterestState::kPotentialPartialInterest); if (upstream_data->GetInterestState() == InterestState::kPartialInterest) { // Hovering (or focusing, if the developer allowed that) triggers full @@ -10960,13 +10992,17 @@ InterestState::kFullInterest); } } - if (InterestTargetElement()) [[unlikely]] { + if (auto* target = InterestTargetElement()) [[unlikely]] { // This is an interest invoker that was just hovered or focused. Schedule // an InterestGained task, with a new state of "full interest" (for - // hover), or "partial interest" (for focus). - ScheduleInterestGainedTask(source == InterestTargetSource::kHover - ? InterestState::kFullInterest - : InterestState::kPartialInterest); + // hover), or "potential partial interest" (for focus). + auto* target_popover = DynamicTo<HTMLElement>(target); + bool might_need_partial_interest = + source == InterestTargetSource::kFocusElementChain && + target_popover && target_popover->HasPopoverAttribute(); + ScheduleInterestGainedTask(might_need_partial_interest + ? InterestState::kPotentialPartialInterest + : InterestState::kFullInterest); } } else { DCHECK(source == InterestTargetSource::kDeHover ||
diff --git a/third_party/blink/renderer/core/dom/element.h b/third_party/blink/renderer/core/dom/element.h index be85231..205f548 100644 --- a/third_party/blink/renderer/core/dom/element.h +++ b/third_party/blink/renderer/core/dom/element.h
@@ -1139,12 +1139,32 @@ // wasn't triggered by this invoker, this will return nullptr.) HTMLElement* GetOpenPopoverTarget() const; + // Represents the current state of an interest invoker. + enum class InterestState { + // No interest. + kNoInterest, + // This is a transient interest state, used for an interest invoker pointing + // to a popover that has been activated via keyboard focus. It potentially + // has partial interest, but that can only be determined once the popover + // actually opens, so that focusability can be tested. Once the popover is + // open, the invoker's interest_state will be updated to one of the other + // states. It can actually get to any of the states: + // - partial interest if there are focusable elements + // - full interest otherwise + // - no interest if the showPopover is cancelled for any reason + kPotentialPartialInterest, + // Invoker has partial interest (for sure). + kPartialInterest, + // Invoker has full interest. + kFullInterest, + }; + // Implementation of the `interesttarget` feature. These are called on the // element with the `interesttarget` attribute, and not on the target itself. // These are called when interest is actually gained or lost on the element, // e.g. after any hover-delays. They return true if the event was *not* // cancelled, and the action was performed. - bool InterestGained(Element& interest_target); + bool InterestGained(Element& interest_target, InterestState new_state); bool InterestLost(Element& interest_target); // Returns the target of the `interesttarget` attribute, if any, and only if // the element supports this attribute. For example, `interesttarget` is not @@ -1153,11 +1173,6 @@ // Returns the active interest invoker for which this element is the target, // or nullptr otherwise. Element* GetInterestInvoker() const; - enum class InterestState { - kNoInterest, - kPartialInterest, - kFullInterest, - }; // Returns the current state of "interest" in an element that is an interest // invoker. InterestState GetInterestState(); @@ -1171,6 +1186,14 @@ // happens. void ShowInterestNow(); + // Returns true if any of its (non-inclusive) flat tree descendants is + // keyboard focusable. Note that this is quite slow, since it traverses the + // entire subtree, and calls `IsKeyboardFocusableSlow()` on each element. + // See the comment next to IsFocusable() above for a description of + // update_behavior. + bool ContainsKeyboardFocusableElementsSlow( + UpdateBehavior update_behavior) const; + // The implementations of |innerText()| and |GetInnerTextWithoutUpdate()| are // found in "element_inner_text.cc". // Avoids layout update. @@ -1616,10 +1639,12 @@ InvokerData& EnsureInvokerData(); InvokerData* GetInvokerData() const; + void ChangeInterestState(Element* target, InterestState new_state); void RemoveInterestInvokerTargetData(); InterestInvokerTargetData& EnsureInterestInvokerTargetData(); InterestInvokerTargetData* GetInterestInvokerTargetData() const; + static String GetPartialInterestTargetActivationHotkey(); void DefaultEventHandler(Event&) override; @@ -2189,7 +2214,6 @@ // These schedule interest gained/lost events, for `interesttarget` invokers. void ScheduleInterestGainedTask(InterestState); void ScheduleInterestLostTask(); - void ChangeInterestState(Element* target, InterestState new_state); static bool GainOrLoseInterest(Element* invoker, Element* target, InterestState new_state);
diff --git a/third_party/blink/renderer/core/dom/layout_tree_builder.cc b/third_party/blink/renderer/core/dom/layout_tree_builder.cc index cfb756a..d32f50c 100644 --- a/third_party/blink/renderer/core/dom/layout_tree_builder.cc +++ b/third_party/blink/renderer/core/dom/layout_tree_builder.cc
@@ -127,12 +127,11 @@ return; } - // Make sure the LayoutObject already knows it is going to be added to a - // LayoutFlowThread before we set the style for the first time. Otherwise code - // using IsInsideFlowThread() in the StyleWillChange and StyleDidChange will - // fail. - new_layout_object->SetIsInsideFlowThread( - parent_layout_object->IsInsideFlowThread()); + // Make sure the LayoutObject already knows it's a descendant of a multicol + // container before we set the style for the first time. Otherwise code using + // IsInsideMulticol() in the StyleWillChange and StyleDidChange will fail. + new_layout_object->SetIsInsideMulticol( + parent_layout_object->IsInsideMulticol()); LayoutObject* next_layout_object = NextLayoutObject(); node_->SetLayoutObject(new_layout_object); @@ -200,12 +199,10 @@ return; } - // Make sure the LayoutObject already knows it is going to be added to a - // LayoutFlowThread before we set the style for the first time. Otherwise code - // using IsInsideFlowThread() in the StyleWillChange and StyleDidChange will - // fail. - new_layout_object->SetIsInsideFlowThread( - context_.parent->IsInsideFlowThread()); + // Make sure the LayoutObject already knows it's a descendant of a multicol + // container before we set the style for the first time. Otherwise code using + // IsInsideMulticol() in the StyleWillChange and StyleDidChange will fail. + new_layout_object->SetIsInsideMulticol(context_.parent->IsInsideMulticol()); node_->SetLayoutObject(new_layout_object); DCHECK(!new_layout_object->Style());
diff --git a/third_party/blink/renderer/core/exported/web_view_test.cc b/third_party/blink/renderer/core/exported/web_view_test.cc index 2514d55..55f900ee 100644 --- a/third_party/blink/renderer/core/exported/web_view_test.cc +++ b/third_party/blink/renderer/core/exported/web_view_test.cc
@@ -2840,6 +2840,7 @@ WebGestureEvent event(type, WebInputEvent::kNoModifiers, WebInputEvent::GetStaticTimeStampForTests(), WebGestureDevice::kTouchscreen); + event.SetPositionInWidget(gfx::PointF(center)); web_view_helper_.GetWebView()->MainFrameWidget()->HandleInputEvent( @@ -6495,4 +6496,44 @@ EXPECT_EQ("visible 4", log_element.TextContent()); } +// Verifies that the drag controller stores the drag's pointer id to be used +// by synthetic events. +TEST_F(WebViewTest, TouchDragSetsDragPointerId) { + RegisterMockedHttpURLLoad("long_press_draggable_div.html"); + + WebViewImpl* web_view = web_view_helper_.InitializeAndLoad( + base_url_ + "long_press_draggable_div.html"); + + web_view->SettingsImpl()->SetTouchDragDropEnabled(true); + web_view->SettingsImpl()->SetTouchDragEndContextMenu(true); + web_view->MainFrameViewWidget()->Resize(gfx::Size(500, 300)); + UpdateAllLifecyclePhases(); + RunPendingTasks(); + + WebPointerEvent pointer_down( + WebInputEvent::Type::kPointerDown, + WebPointerProperties(1, WebPointerProperties::PointerType::kTouch), 5, 5); + pointer_down.SetPositionInWidget(250, 8); + pointer_down.SetPositionInScreen(250, 8); + web_view->MainFrameWidget()->HandleInputEvent( + WebCoalescedInputEvent(pointer_down, ui::LatencyInfo())); + web_view->MainFrameWidget()->DispatchBufferedTouchEvents(); + + WebString target_id = WebString::FromUTF8("target"); + + // Simulate long press to start dragging. + EXPECT_TRUE(SimulateGestureAtElementById( + WebInputEvent::Type::kGestureLongPress, target_id)); + EXPECT_EQ("dragstart", web_view->MainFrameImpl()->GetDocument().Title()); + // Starting a drag should make the drag controller cache the pointer id, and + // it should be reset after it ends. + EXPECT_TRUE( + web_view->GetPage()->GetDragController().drag_pointer_id().has_value()); + web_view->MainFrameViewWidget()->DragSourceEndedAt( + gfx::PointF(), gfx::PointF(), ui::mojom::blink::DragOperation::kNone, + base::DoNothing()); + EXPECT_FALSE( + web_view->GetPage()->GetDragController().drag_pointer_id().has_value()); +} + } // namespace blink
diff --git a/third_party/blink/renderer/core/frame/web_frame_widget_impl.cc b/third_party/blink/renderer/core/frame/web_frame_widget_impl.cc index 4fdaa70..3bb80c4 100644 --- a/third_party/blink/renderer/core/frame/web_frame_widget_impl.cc +++ b/third_party/blink/renderer/core/frame/web_frame_widget_impl.cc
@@ -649,7 +649,13 @@ WebInputEvent::Type::kMouseMove, GetPage()->GetVisualViewport().ViewportToRootFrame(point_in_viewport), screen_point, WebPointerProperties::Button::kLeft, 0, - WebInputEvent::kNoModifiers, base::TimeTicks::Now()); + WebInputEvent::kNoModifiers, base::TimeTicks::Now(), kMenuSourceNone, + local_root_->GetFrame() + ->GetPage() + ->GetDragController() + .drag_pointer_id() + .value_or(WebMouseEvent::kMousePointerId)); + fake_mouse_move.SetFrameScale(1); local_root_->GetFrame()->GetEventHandler().DragSourceEndedAt(fake_mouse_move, operation);
diff --git a/third_party/blink/renderer/core/html/build.gni b/third_party/blink/renderer/core/html/build.gni index 66c4801..dc1c04a3 100644 --- a/third_party/blink/renderer/core/html/build.gni +++ b/third_party/blink/renderer/core/html/build.gni
@@ -236,6 +236,8 @@ "forms/reset_input_type.h", "forms/search_input_type.cc", "forms/search_input_type.h", + "forms/select_mutation_observer.cc", + "forms/select_mutation_observer.h", "forms/select_type.cc", "forms/select_type.h", "forms/slider_thumb_element.cc",
diff --git a/third_party/blink/renderer/core/html/forms/html_select_element.cc b/third_party/blink/renderer/core/html/forms/html_select_element.cc index 845218f..c2a8247e 100644 --- a/third_party/blink/renderer/core/html/forms/html_select_element.cc +++ b/third_party/blink/renderer/core/html/forms/html_select_element.cc
@@ -33,7 +33,6 @@ #include "third_party/blink/public/mojom/input/focus_type.mojom-blink.h" #include "third_party/blink/public/platform/task_type.h" #include "third_party/blink/public/strings/grit/blink_strings.h" -#include "third_party/blink/renderer/bindings/core/v8/v8_mutation_observer_init.h" #include "third_party/blink/renderer/bindings/core/v8/v8_union_htmlelement_long.h" #include "third_party/blink/renderer/bindings/core/v8/v8_union_htmloptgroupelement_htmloptionelement.h" #include "third_party/blink/renderer/core/accessibility/ax_object_cache.h" @@ -46,47 +45,34 @@ #include "third_party/blink/renderer/core/dom/events/scoped_event_queue.h" #include "third_party/blink/renderer/core/dom/events/simulated_click_options.h" #include "third_party/blink/renderer/core/dom/focus_params.h" -#include "third_party/blink/renderer/core/dom/mutation_observer.h" -#include "third_party/blink/renderer/core/dom/mutation_record.h" #include "third_party/blink/renderer/core/dom/node_lists_node_data.h" #include "third_party/blink/renderer/core/dom/node_traversal.h" #include "third_party/blink/renderer/core/events/keyboard_event.h" #include "third_party/blink/renderer/core/frame/local_frame.h" -#include "third_party/blink/renderer/core/html/custom/custom_element.h" #include "third_party/blink/renderer/core/html/forms/form_controller.h" #include "third_party/blink/renderer/core/html/forms/form_data.h" #include "third_party/blink/renderer/core/html/forms/html_button_element.h" -#include "third_party/blink/renderer/core/html/forms/html_data_list_element.h" #include "third_party/blink/renderer/core/html/forms/html_form_element.h" -#include "third_party/blink/renderer/core/html/forms/html_legend_element.h" #include "third_party/blink/renderer/core/html/forms/html_opt_group_element.h" #include "third_party/blink/renderer/core/html/forms/html_option_element.h" #include "third_party/blink/renderer/core/html/forms/html_options_collection.h" #include "third_party/blink/renderer/core/html/forms/html_selected_content_element.h" +#include "third_party/blink/renderer/core/html/forms/select_mutation_observer.h" #include "third_party/blink/renderer/core/html/forms/select_type.h" #include "third_party/blink/renderer/core/html/html_div_element.h" #include "third_party/blink/renderer/core/html/html_hr_element.h" -#include "third_party/blink/renderer/core/html/html_no_script_element.h" -#include "third_party/blink/renderer/core/html/html_object_element.h" -#include "third_party/blink/renderer/core/html/html_script_element.h" -#include "third_party/blink/renderer/core/html/html_slot_element.h" -#include "third_party/blink/renderer/core/html/html_span_element.h" -#include "third_party/blink/renderer/core/html/html_template_element.h" #include "third_party/blink/renderer/core/html/parser/html_parser_idioms.h" #include "third_party/blink/renderer/core/html/shadow/shadow_element_names.h" #include "third_party/blink/renderer/core/html_names.h" #include "third_party/blink/renderer/core/inspector/console_message.h" -#include "third_party/blink/renderer/core/inspector/inspector_audits_issue.h" #include "third_party/blink/renderer/core/layout/flex/layout_flexible_box.h" #include "third_party/blink/renderer/core/layout/hit_test_request.h" #include "third_party/blink/renderer/core/layout/hit_test_result.h" #include "third_party/blink/renderer/core/layout/layout_block_flow.h" #include "third_party/blink/renderer/core/layout/layout_theme.h" -#include "third_party/blink/renderer/core/mathml_names.h" #include "third_party/blink/renderer/core/page/chrome_client.h" #include "third_party/blink/renderer/core/page/page.h" #include "third_party/blink/renderer/core/page/spatial_navigation.h" -#include "third_party/blink/renderer/core/svg_names.h" #include "third_party/blink/renderer/platform/bindings/exception_state.h" #include "third_party/blink/renderer/platform/heap/garbage_collected.h" #include "third_party/blink/renderer/platform/instrumentation/tracing/trace_event.h" @@ -104,530 +90,6 @@ // absent. const int kDefaultListBoxSize = 4; -class SelectDescendantsObserver : public MutationObserver::Delegate { - public: - explicit SelectDescendantsObserver(HTMLSelectElement& select) - : select_(select), observer_(MutationObserver::Create(this)) { - CHECK(HTMLSelectElement::CustomizableSelectEnabled(&select)); - DCHECK(select_->IsAppearanceBase()); - - MutationObserverInit* init = MutationObserverInit::Create(); - init->setChildList(true); - init->setSubtree(true); - init->setAttributes(true); - observer_->observe(select_, init, ASSERT_NO_EXCEPTION); - // Traverse descendants that have been added to the select so far. - TraverseNodeDescendants(select_); - } - - ExecutionContext* GetExecutionContext() const override { - return select_->GetExecutionContext(); - } - - void Deliver(const MutationRecordVector& records, - MutationObserver&) override { - for (const auto& record : records) { - if (record->type() == "childList") { - CheckAddedNodes(record); - CheckRemovedNodes(record); - } else if (record->type() == "attributes") { - if (record->attributeName() == html_names::kTabindexAttr || - record->attributeName() == html_names::kContenteditableAttr) { - AddDescendantDisallowedErrorToNode(*record->target()); - } else if ((RuntimeEnabledFeatures:: - SelectAccessibilityReparentInputEnabled() || - RuntimeEnabledFeatures:: - SelectAccessibilityNestedInputEnabled()) && - record->attributeName() == html_names::kTypeAttr) { - if (auto* input = DynamicTo<HTMLInputElement>(record->target())) { - if (input->IsTextField()) { - select_->AddDescendantTextInput(input); - } else { - select_->RemoveDescendantTextInput(input); - // If the type attribute was changed in a way that makes the - // <input> no longer an allowed descendant, then we should emit an - // error. - AddDescendantDisallowedErrorToNode(*input); - } - } - } - } - } - } - - void Disconnect() { observer_->disconnect(); } - - void Trace(Visitor* visitor) const override { - visitor->Trace(select_); - visitor->Trace(observer_); - MutationObserver::Delegate::Trace(visitor); - } - - private: - void CheckAddedNodes(MutationRecord* record) { - DCHECK(record); - auto* added_nodes = record->addedNodes(); - for (unsigned i = 0; i < added_nodes->length(); ++i) { - auto* descendant = added_nodes->item(i); - DCHECK(descendant); - if (IsWhitespaceOrEmpty(*descendant)) { - continue; - } - MaybeAddDescendantTextInput(descendant); - AddDescendantDisallowedErrorToNode(*descendant); - // Check the added node's descendants, if any. - TraverseNodeDescendants(descendant); - } - } - - void CheckRemovedNodes(MutationRecord* record) { - DCHECK(record); - auto* removed_nodes = record->removedNodes(); - DCHECK(removed_nodes); - for (unsigned i = 0; i < removed_nodes->length(); ++i) { - auto* descendant = removed_nodes->item(i); - DCHECK(descendant); - if (IsWhitespaceOrEmpty(*descendant)) { - continue; - } - MaybeRemoveDescendantTextInput(descendant); - if (!IsAllowedInteractiveElement(*descendant)) { - select_->DecreaseContentModelViolationCount(); - } - // Check the removed node's descendants, if any. - for (Node* nested_descendant = NodeTraversal::FirstWithin(*descendant); - nested_descendant; nested_descendant = NodeTraversal::Next( - *nested_descendant, descendant)) { - MaybeRemoveDescendantTextInput(descendant); - if (!IsWhitespaceOrEmpty(*nested_descendant) && - !IsAllowedInteractiveElement(*nested_descendant)) { - select_->DecreaseContentModelViolationCount(); - } - } - } - } - - void TraverseNodeDescendants(const Node* node) { - for (Node* descendant = NodeTraversal::FirstWithin(*node); descendant; - descendant = NodeTraversal::Next(*descendant, node)) { - if (!IsWhitespaceOrEmpty(*descendant)) { - MaybeAddDescendantTextInput(descendant); - AddDescendantDisallowedErrorToNode(*descendant); - } - } - } - - void MaybeAddDescendantTextInput(Node* node) { - if (RuntimeEnabledFeatures::SelectAccessibilityReparentInputEnabled() || - RuntimeEnabledFeatures::SelectAccessibilityNestedInputEnabled()) { - if (auto* input = DynamicTo<HTMLInputElement>(node); - input && input->IsTextField()) { - select_->AddDescendantTextInput(input); - } - } - } - - void MaybeRemoveDescendantTextInput(Node* node) { - if (RuntimeEnabledFeatures::SelectAccessibilityReparentInputEnabled() || - RuntimeEnabledFeatures::SelectAccessibilityNestedInputEnabled()) { - if (auto* input = DynamicTo<HTMLInputElement>(node); - input && input->IsTextField()) { - select_->RemoveDescendantTextInput(input); - } - } - } - - void AddDescendantDisallowedErrorToNode(Node& node) { - SelectElementAccessibilityIssueReason issue_reason = CheckForIssue(node); - if (issue_reason != SelectElementAccessibilityIssueReason::kValidChild) { - if (!IsAllowedInteractiveElement(node)) { - select_->IncreaseContentModelViolationCount(); - } - if (RuntimeEnabledFeatures:: - CustomizableSelectElementAccessibilityIssuesEnabled()) { - Document& document = select_->GetDocument(); - AuditsIssue::ReportSelectElementAccessibilityIssue( - &document, node.GetDomNodeId(), issue_reason, - /* has_disallowed_attributes = */ HasTabIndexAttribute(node) || - IsContenteditable(node)); - } - node.AddConsoleMessage( - mojom::blink::ConsoleMessageSource::kRecommendation, - mojom::blink::ConsoleMessageLevel::kError, - GetMessageForReason(issue_reason)); - RecordIssueByType(issue_reason); - } - } - - String GetMessageForReason( - SelectElementAccessibilityIssueReason issue_reason) { - switch (issue_reason) { - case SelectElementAccessibilityIssueReason::kDisallowedSelectChild: - return FormatElementMessage( - "<select>", "a ", - "an <optgroup> with a <legend> element or <option> elements"); - case SelectElementAccessibilityIssueReason::kDisallowedOptGroupChild: - return FormatElementMessage("<optgroup>", "an ", - "the <legend> or <option> elements"); - case SelectElementAccessibilityIssueReason:: - kNonPhrasingContentOptionChild: - return "Non-phrasing content was found within an <option> element. The " - "<option> element allows only non-interactive phrasing content, " - "text, and <div> elements as its children. The semantics of " - "non-phrasing content elements do not make sense as children of " - "an <option>, and such semantics will largely be ignored by " - "assistive technology since they are inappropriate in this " - "context. Consider removing or changing such elements to one of " - "the allowed phrasing content elements."; - case SelectElementAccessibilityIssueReason:: - kInteractiveContentOptionChild: - return FormatInteractiveElementMessage("<option>", "an ", - g_empty_string); - case SelectElementAccessibilityIssueReason:: - kInteractiveContentLegendChild: - return FormatInteractiveElementMessage( - "<legend>", "a ", - "Interactive elements are not allowed children of a <legend> " - "element when used within an <optgroup> element. "); - case SelectElementAccessibilityIssueReason::kValidChild: - default: - NOTREACHED(); - } - } - - String FormatElementMessage(const String& element, - const String& article, - const String& example) { - return "An element which is not allowed in the content model of the " + - element + " element was found within " + article + element + - " element. These elements will not consistently be accessible to " - "people navigating by keyboard or using assistive technology. If " - "using disallowed elements for layout structure and styling, " - "consider using the allowed <div> element instead. Any text " - "existing within the " + - element + - " element should either be removed or relocated to a valid element " - "that allows text descendants, e.g., " + - example + "."; - } - - String FormatInteractiveElementMessage(const String& element, - const String& article, - const String& context) { - return "An interactive element which is not allowed in the content model " - "of the " + - element + " element was found within " + article + element + - " element. " + context + - "These elements will not consistently be accessible to people " - "navigating by keyboard or using assistive technology."; - } - - bool IsAllowedInteractiveElement(Node& node) { - if (IsA<HTMLButtonElement>(node)) { - // The <button> must have a parent (not being inserted as a child of - // `HTMLSelectedContentElement`) and must be the first child of the - // <select>. - const Node* parent = node.parentNode(); - return parent && IsA<HTMLSelectElement>(*parent) && - !ElementTraversal::PreviousSibling(node); - } - if (RuntimeEnabledFeatures::SelectAccessibilityReparentInputEnabled() || - RuntimeEnabledFeatures::SelectAccessibilityNestedInputEnabled()) { - // <select>s are allowed to have one <input> before the options. We should - // probably find a way to figure out if the <input> is actually placed - // before the <option>s or not. - - if (auto* input = DynamicTo<HTMLInputElement>(node)) { - if (input->IsTextField()) { - select_->AddDescendantTextInput(input); - } - if (input == select_->FirstDescendantTextInput()) { - return true; - } - } - } - // If the node isn't a <button> but it is an interactive element, we return - // false as interactive elements are disallowed. - return !IsInteractiveElement(node); - } - - bool IsInteractiveElement(const Node& node) { - if (HasTabIndexAttribute(node)) { - return true; - } - if (auto* html_element = DynamicTo<HTMLElement>(node)) { - return IsContenteditable(node) || html_element->IsInteractiveContent(); - } - return false; - } - - void RecordIssueByType(SelectElementAccessibilityIssueReason issue_reason) { - switch (issue_reason) { - case SelectElementAccessibilityIssueReason::kDisallowedSelectChild: - UseCounter::Count(select_->GetDocument(), - WebFeature::kDisallowedSelectChild); - break; - case SelectElementAccessibilityIssueReason::kDisallowedOptGroupChild: - UseCounter::Count(select_->GetDocument(), - WebFeature::kDisallowedOptGroupChild); - break; - case SelectElementAccessibilityIssueReason:: - kNonPhrasingContentOptionChild: - UseCounter::Count(select_->GetDocument(), - WebFeature::kNonPhrasingContentOptionChild); - break; - case SelectElementAccessibilityIssueReason:: - kInteractiveContentOptionChild: - UseCounter::Count(select_->GetDocument(), - WebFeature::kInteractiveContentOptionChild); - break; - case SelectElementAccessibilityIssueReason:: - kInteractiveContentLegendChild: - UseCounter::Count(select_->GetDocument(), - WebFeature::kInteractiveContentLegendChild); - break; - case SelectElementAccessibilityIssueReason::kValidChild: - default: - NOTREACHED(); - } - } - - SelectElementAccessibilityIssueReason CheckForIssue(const Node& descendant) { - if (descendant.getNodeType() == Node::kCommentNode || - IsAutonomousCustomElement(descendant)) { - return SelectElementAccessibilityIssueReason::kValidChild; - } - // Get the parent of the descendant. - const Node* parent = descendant.parentNode(); - // If the node has no parent, assume it is being appended to a - // `HTMLSelectedContentElement`. - if (!parent) { - return CheckDescedantOfOption(descendant); - } - if (!IsA<HTMLElement>(*parent)) { - if (parent->IsSVGElement()) { - return SelectElementAccessibilityIssueReason::kValidChild; - } - return SelectElementAccessibilityIssueReason::kDisallowedSelectChild; - } - if (IsA<HTMLSelectElement>(*parent)) { - if (IsAllowedDescendantOfSelect(descendant, *parent)) { - return SelectElementAccessibilityIssueReason::kValidChild; - } - return SelectElementAccessibilityIssueReason::kDisallowedSelectChild; - } - if (IsA<HTMLOptGroupElement>(*parent)) { - if (IsAllowedDescendantOfOptgroup(descendant, *parent)) { - return SelectElementAccessibilityIssueReason::kValidChild; - } - return SelectElementAccessibilityIssueReason::kDisallowedOptGroupChild; - } - if (IsA<HTMLOptionElement>(*parent) || - IsA<HTMLSelectedContentElement>(*parent) || - (IsAllowedPhrasingContent(*parent) && !IsA<HTMLSpanElement>(*parent))) { - return CheckDescedantOfOption(descendant); - } - if (IsA<HTMLDivElement>(*parent) || IsA<HTMLSpanElement>(*parent) || - IsAutonomousCustomElement(*parent)) { - return TraverseAncestorsAndCheckDescendant(descendant); - } - if ((IsA<HTMLNoScriptElement>(*parent) || IsA<HTMLScriptElement>(*parent) || - IsA<HTMLTemplateElement>(*parent)) && - !descendant.IsTextNode()) { - return TraverseAncestorsAndCheckDescendant(descendant); - } - if (IsA<HTMLButtonElement>(*parent)) { - if (IsAllowedDescendantOfButton(descendant)) { - return SelectElementAccessibilityIssueReason::kValidChild; - } - return SelectElementAccessibilityIssueReason::kDisallowedSelectChild; - } - if (IsA<HTMLLegendElement>(*parent)) { - if (IsAllowedPhrasingContent(descendant) && - !HasTabIndexAttribute(descendant) && !IsContenteditable(descendant)) { - return SelectElementAccessibilityIssueReason::kValidChild; - } - return SelectElementAccessibilityIssueReason:: - kInteractiveContentLegendChild; - } - return SelectElementAccessibilityIssueReason::kDisallowedSelectChild; - } - - bool IsAllowedDescendantOfSelect(const Node& descendant, const Node& parent) { - if (RuntimeEnabledFeatures::SelectAccessibilityReparentInputEnabled() || - RuntimeEnabledFeatures::SelectAccessibilityNestedInputEnabled()) { - // <select>s are allowed to have one text <input>, although it should be - // placed before any of the <option>s. - if (select_->FirstDescendantTextInput() == descendant) { - return true; - } - } - // <button> has to be the first direct descendant of the <select>. - return (IsA<HTMLButtonElement>(descendant) && - IsA<HTMLSelectElement>(parent) && - !ElementTraversal::PreviousSibling(descendant)) || - IsA<HTMLOptionElement>(descendant) || - IsA<HTMLOptGroupElement>(descendant) || - IsA<HTMLHRElement>(descendant) || IsA<HTMLDivElement>(descendant) || - IsA<HTMLSpanElement>(descendant) || - IsA<HTMLNoScriptElement>(descendant) || - IsA<HTMLScriptElement>(descendant) || - IsA<HTMLTemplateElement>(descendant); - } - - bool IsAllowedDescendantOfOptgroup(const Node& descendant, - const Node& parent) { - // <legend> has to be the first direct descendant of the <optgroup>. - return (IsA<HTMLLegendElement>(descendant) && - IsA<HTMLOptGroupElement>(parent) && - !ElementTraversal::PreviousSibling(descendant)) || - IsA<HTMLOptionElement>(descendant) || - IsA<HTMLDivElement>(descendant) || - IsA<HTMLSpanElement>(descendant) || - IsA<HTMLNoScriptElement>(descendant) || - IsA<HTMLScriptElement>(descendant) || - IsA<HTMLTemplateElement>(descendant); - } - - bool IsAllowedDescendantOfButton(const Node& descendant) { - return IsA<HTMLSelectedContentElement>(descendant) || - CheckDescedantOfOption(descendant) == - SelectElementAccessibilityIssueReason::kValidChild; - } - - SelectElementAccessibilityIssueReason CheckDescedantOfOption( - const Node& descendant) { - if (!IsA<HTMLDivElement>(descendant) && - !IsAllowedPhrasingContent(descendant) && - !IsAutonomousCustomElement(descendant)) { - return SelectElementAccessibilityIssueReason:: - kNonPhrasingContentOptionChild; - } - // Check tabindex and contenteditable attributes of the descendant as well. - if (!HasTabIndexAttribute(descendant) && !IsContenteditable(descendant)) { - return SelectElementAccessibilityIssueReason::kValidChild; - } - return SelectElementAccessibilityIssueReason:: - kInteractiveContentOptionChild; - } - - bool HasTabIndexAttribute(const Node& node) { - if (auto* element = DynamicTo<Element>(node)) { - return element->FastHasAttribute(html_names::kTabindexAttr); - } - return false; - } - - bool IsContenteditable(const Node& node) { - if (auto* html_element = DynamicTo<HTMLElement>(node)) { - ContentEditableType normalized_value = - html_element->contentEditableNormalized(); - return normalized_value == ContentEditableType::kContentEditable || - normalized_value == ContentEditableType::kPlaintextOnly; - } - return false; - } - - SelectElementAccessibilityIssueReason TraverseAncestorsAndCheckDescendant( - const Node& descendant) { - // As we've already checked the descendant's parent, we can directly look at - // the grandparent. - const Node* parent = descendant.parentNode(); - for (const Node* ancestor = parent->parentNode(); ancestor; - ancestor = ancestor->parentNode()) { - if (IsA<HTMLOptionElement>(*ancestor) || - IsA<HTMLSelectedContentElement>(*ancestor)) { - return CheckDescedantOfOption(descendant); - } - if (IsA<HTMLOptGroupElement>(*ancestor)) { - if (IsAllowedDescendantOfOptgroup(descendant, *parent)) { - return SelectElementAccessibilityIssueReason::kValidChild; - } - return SelectElementAccessibilityIssueReason::kDisallowedOptGroupChild; - } - if (IsA<HTMLSelectElement>(*ancestor) && - IsAllowedDescendantOfSelect(descendant, *parent)) { - return SelectElementAccessibilityIssueReason::kValidChild; - } - if (IsA<HTMLButtonElement>(*ancestor) && - IsAllowedDescendantOfButton(descendant)) { - return SelectElementAccessibilityIssueReason::kValidChild; - } - } - return SelectElementAccessibilityIssueReason::kDisallowedSelectChild; - } - - bool IsWhitespaceOrEmpty(const Node& node) { - return node.IsTextNode() && - node.textContent().ContainsOnlyWhitespaceOrEmpty(); - } - - // Phrasing content that isn't Interactive content. <datalist>, <object> - // elements are excluded as well. - bool IsAllowedPhrasingContent(const Node& node) { - DEFINE_STATIC_LOCAL(HashSet<QualifiedName>, phrasing_content_names, - ({ - html_names::kATag, html_names::kAbbrTag, - html_names::kAreaTag, html_names::kAudioTag, - html_names::kBTag, html_names::kBdiTag, - html_names::kBdoTag, html_names::kBrTag, - html_names::kButtonTag, html_names::kCanvasTag, - html_names::kCiteTag, html_names::kCodeTag, - html_names::kDataTag, html_names::kDatalistTag, - html_names::kDelTag, html_names::kDfnTag, - html_names::kEmTag, html_names::kEmbedTag, - html_names::kITag, html_names::kIFrameTag, - html_names::kImgTag, html_names::kInputTag, - html_names::kInsTag, html_names::kKbdTag, - html_names::kLabelTag, html_names::kLinkTag, - html_names::kMapTag, html_names::kMarkTag, - mathml_names::kMathTag, html_names::kMetaTag, - html_names::kMeterTag, html_names::kNoscriptTag, - html_names::kObjectTag, html_names::kOutputTag, - html_names::kPictureTag, html_names::kProgressTag, - html_names::kQTag, html_names::kRubyTag, - html_names::kSTag, html_names::kSampTag, - html_names::kScriptTag, html_names::kSelectTag, - html_names::kSlotTag, html_names::kSmallTag, - html_names::kSpanTag, html_names::kStrongTag, - html_names::kSubTag, html_names::kSupTag, - svg_names::kSVGTag, html_names::kTemplateTag, - html_names::kTextareaTag, html_names::kTimeTag, - html_names::kUTag, html_names::kVarTag, - html_names::kVideoTag, html_names::kWbrTag, - })); - if (node.IsTextNode()) { - return true; - } - if (IsA<HTMLDataListElement>(node) || IsA<HTMLObjectElement>(node)) { - return false; - } - if (const auto* element = DynamicTo<Element>(node)) { - if (phrasing_content_names.Contains(element->TagQName())) { - if (auto* html_element = DynamicTo<HTMLElement>(element)) { - return !html_element->IsInteractiveContent(); - } - return element->IsSVGElement(); - } - } - return false; - } - - bool IsAutonomousCustomElement(const Node& node) { - if (node.IsCustomElement()) { - if (auto* element = DynamicTo<Element>(node)) { - if (CustomElement::IsValidName(element->localName())) { - return true; - } - } - } - return false; - } - - Member<HTMLSelectElement> select_; - Member<MutationObserver> observer_; -}; - HTMLSelectElement::HTMLSelectElement(Document& document) : HTMLFormControlElementWithState(html_names::kSelectTag, document), type_ahead_(this), @@ -1736,7 +1198,7 @@ if (UsesMenuList() && isConnected() && IsAppearanceBase()) { if (!descendants_observer_) { descendants_observer_ = - MakeGarbageCollected<SelectDescendantsObserver>(*this); + MakeGarbageCollected<SelectMutationObserver>(*this); } } else if (descendants_observer_) { descendants_observer_->Disconnect();
diff --git a/third_party/blink/renderer/core/html/forms/html_select_element.h b/third_party/blink/renderer/core/html/forms/html_select_element.h index 3ab56757..b00f716 100644 --- a/third_party/blink/renderer/core/html/forms/html_select_element.h +++ b/third_party/blink/renderer/core/html/forms/html_select_element.h
@@ -54,7 +54,7 @@ class V8UnionHTMLElementOrLong; class V8UnionHTMLOptGroupElementOrHTMLOptionElement; class HTMLSelectedContentElement; -class SelectDescendantsObserver; +class SelectMutationObserver; enum class SelectPopupHideBehavior { kNormal, @@ -450,7 +450,7 @@ Member<SelectType> select_type_; int index_to_select_on_cancel_; - Member<SelectDescendantsObserver> descendants_observer_; + Member<SelectMutationObserver> descendants_observer_; unsigned content_model_violations_count_ = 0U; friend class ListBoxSelectType;
diff --git a/third_party/blink/renderer/core/html/forms/select_mutation_observer.cc b/third_party/blink/renderer/core/html/forms/select_mutation_observer.cc new file mode 100644 index 0000000..94d45e9 --- /dev/null +++ b/third_party/blink/renderer/core/html/forms/select_mutation_observer.cc
@@ -0,0 +1,540 @@ +// Copyright 2025 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "third_party/blink/renderer/core/html/forms/select_mutation_observer.h" + +#include "third_party/blink/renderer/bindings/core/v8/v8_mutation_observer_init.h" +#include "third_party/blink/renderer/core/dom/mutation_record.h" +#include "third_party/blink/renderer/core/frame/web_feature.h" +#include "third_party/blink/renderer/core/html/custom/custom_element.h" +#include "third_party/blink/renderer/core/html/forms/html_button_element.h" +#include "third_party/blink/renderer/core/html/forms/html_data_list_element.h" +#include "third_party/blink/renderer/core/html/forms/html_legend_element.h" +#include "third_party/blink/renderer/core/html/forms/html_opt_group_element.h" +#include "third_party/blink/renderer/core/html/forms/html_option_element.h" +#include "third_party/blink/renderer/core/html/html_div_element.h" +#include "third_party/blink/renderer/core/html/html_hr_element.h" +#include "third_party/blink/renderer/core/html/html_no_script_element.h" +#include "third_party/blink/renderer/core/html/html_object_element.h" +#include "third_party/blink/renderer/core/html/html_script_element.h" +#include "third_party/blink/renderer/core/html/html_span_element.h" +#include "third_party/blink/renderer/core/html/html_template_element.h" +#include "third_party/blink/renderer/core/mathml_names.h" +#include "third_party/blink/renderer/core/svg_names.h" + +namespace blink { + +SelectMutationObserver::SelectMutationObserver(HTMLSelectElement& select) + : select_(select), observer_(MutationObserver::Create(this)) { + CHECK(HTMLSelectElement::CustomizableSelectEnabled(&select)); + DCHECK(select_->IsAppearanceBase()); + + MutationObserverInit* init = MutationObserverInit::Create(); + init->setChildList(true); + init->setSubtree(true); + init->setAttributes(true); + observer_->observe(select_, init, ASSERT_NO_EXCEPTION); + // Traverse descendants that have been added to the select so far. + TraverseNodeDescendants(select_); +} + +ExecutionContext* SelectMutationObserver::GetExecutionContext() const { + return select_->GetExecutionContext(); +} + +void SelectMutationObserver::Deliver(const MutationRecordVector& records, + MutationObserver&) { + for (const auto& record : records) { + if (record->type() == "childList") { + CheckAddedNodes(record); + CheckRemovedNodes(record); + } else if (record->type() == "attributes") { + if (record->attributeName() == html_names::kTabindexAttr || + record->attributeName() == html_names::kContenteditableAttr) { + AddDescendantDisallowedErrorToNode(*record->target()); + } else if ((RuntimeEnabledFeatures:: + SelectAccessibilityReparentInputEnabled() || + RuntimeEnabledFeatures::SelectAccessibilityNestedInputEnabled()) + && + record->attributeName() == html_names::kTypeAttr) { + if (auto* input = DynamicTo<HTMLInputElement>(record->target())) { + if (input->IsTextField()) { + select_->AddDescendantTextInput(input); + } else { + select_->RemoveDescendantTextInput(input); + // If the type attribute was changed in a way that makes the + // <input> no longer an allowed descendant, then we should emit an + // error. + AddDescendantDisallowedErrorToNode(*input); + } + } + } + } + } +} + +void SelectMutationObserver::Disconnect() { + observer_->disconnect(); +} + +void SelectMutationObserver::Trace(Visitor* visitor) const { + visitor->Trace(select_); + visitor->Trace(observer_); + MutationObserver::Delegate::Trace(visitor); +} + +void SelectMutationObserver::CheckAddedNodes(MutationRecord* record) { + DCHECK(record); + auto* added_nodes = record->addedNodes(); + for (unsigned i = 0; i < added_nodes->length(); ++i) { + auto* descendant = added_nodes->item(i); + DCHECK(descendant); + if (IsWhitespaceOrEmpty(*descendant)) { + continue; + } + MaybeAddDescendantTextInput(descendant); + AddDescendantDisallowedErrorToNode(*descendant); + // Check the added node's descendants, if any. + TraverseNodeDescendants(descendant); + } +} + +void SelectMutationObserver::CheckRemovedNodes(MutationRecord* record) { + DCHECK(record); + auto* removed_nodes = record->removedNodes(); + DCHECK(removed_nodes); + for (unsigned i = 0; i < removed_nodes->length(); ++i) { + auto* descendant = removed_nodes->item(i); + DCHECK(descendant); + if (IsWhitespaceOrEmpty(*descendant)) { + continue; + } + MaybeRemoveDescendantTextInput(descendant); + if (!IsAllowedInteractiveElement(*descendant)) { + select_->DecreaseContentModelViolationCount(); + } + // Check the removed node's descendants, if any. + for (Node* nested_descendant = NodeTraversal::FirstWithin(*descendant); + nested_descendant; nested_descendant = NodeTraversal::Next( + *nested_descendant, descendant)) { + MaybeRemoveDescendantTextInput(descendant); + if (!IsWhitespaceOrEmpty(*nested_descendant) && + !IsAllowedInteractiveElement(*nested_descendant)) { + select_->DecreaseContentModelViolationCount(); + } + } + } +} + +void SelectMutationObserver::TraverseNodeDescendants(const Node* node) { + for (Node* descendant = NodeTraversal::FirstWithin(*node); descendant; + descendant = NodeTraversal::Next(*descendant, node)) { + if (!IsWhitespaceOrEmpty(*descendant)) { + MaybeAddDescendantTextInput(descendant); + AddDescendantDisallowedErrorToNode(*descendant); + } + } +} + +void SelectMutationObserver::MaybeAddDescendantTextInput(Node* node) { + if (RuntimeEnabledFeatures::SelectAccessibilityReparentInputEnabled() || RuntimeEnabledFeatures::SelectAccessibilityNestedInputEnabled()) { + if (auto* input = DynamicTo<HTMLInputElement>(node); + input && input->IsTextField()) { + select_->AddDescendantTextInput(input); + } + } +} + +void SelectMutationObserver::MaybeRemoveDescendantTextInput(Node* node) { + if (RuntimeEnabledFeatures::SelectAccessibilityReparentInputEnabled() || RuntimeEnabledFeatures::SelectAccessibilityNestedInputEnabled()) { + if (auto* input = DynamicTo<HTMLInputElement>(node); + input && input->IsTextField()) { + select_->RemoveDescendantTextInput(input); + } + } +} + +void SelectMutationObserver::AddDescendantDisallowedErrorToNode(Node& node) { + SelectElementAccessibilityIssueReason issue_reason = CheckForIssue(node); + if (issue_reason != SelectElementAccessibilityIssueReason::kValidChild) { + if (!IsAllowedInteractiveElement(node)) { + select_->IncreaseContentModelViolationCount(); + } + if (RuntimeEnabledFeatures:: + CustomizableSelectElementAccessibilityIssuesEnabled()) { + Document& document = select_->GetDocument(); + AuditsIssue::ReportSelectElementAccessibilityIssue( + &document, node.GetDomNodeId(), issue_reason, + /* has_disallowed_attributes = */ HasTabIndexAttribute(node) || + IsContenteditable(node)); + } + node.AddConsoleMessage(mojom::blink::ConsoleMessageSource::kRecommendation, + mojom::blink::ConsoleMessageLevel::kError, + GetMessageForReason(issue_reason)); + RecordIssueByType(issue_reason); + } +} + +String SelectMutationObserver::GetMessageForReason( + SelectElementAccessibilityIssueReason issue_reason) { + switch (issue_reason) { + case SelectElementAccessibilityIssueReason::kDisallowedSelectChild: + return FormatElementMessage( + "<select>", "a ", + "an <optgroup> with a <legend> element or <option> elements"); + case SelectElementAccessibilityIssueReason::kDisallowedOptGroupChild: + return FormatElementMessage("<optgroup>", "an ", + "the <legend> or <option> elements"); + case SelectElementAccessibilityIssueReason::kNonPhrasingContentOptionChild: + return "Non-phrasing content was found within an <option> element. The " + "<option> element allows only non-interactive phrasing content, " + "text, and <div> elements as its children. The semantics of " + "non-phrasing content elements do not make sense as children of " + "an <option>, and such semantics will largely be ignored by " + "assistive technology since they are inappropriate in this " + "context. Consider removing or changing such elements to one of " + "the allowed phrasing content elements."; + case SelectElementAccessibilityIssueReason::kInteractiveContentOptionChild: + return FormatInteractiveElementMessage("<option>", "an ", g_empty_string); + case SelectElementAccessibilityIssueReason::kInteractiveContentLegendChild: + return FormatInteractiveElementMessage( + "<legend>", "a ", + "Interactive elements are not allowed children of a <legend> " + "element when used within an <optgroup> element. "); + case SelectElementAccessibilityIssueReason::kValidChild: + default: + NOTREACHED(); + } +} + +String SelectMutationObserver::FormatElementMessage(const String& element, + const String& article, + const String& example) { + return "An element which is not allowed in the content model of the " + + element + " element was found within " + article + element + + " element. These elements will not consistently be accessible to " + "people navigating by keyboard or using assistive technology. If " + "using disallowed elements for layout structure and styling, " + "consider using the allowed <div> element instead. Any text " + "existing within the " + + element + + " element should either be removed or relocated to a valid element " + "that allows text descendants, e.g., " + + example + "."; +} + +String SelectMutationObserver::FormatInteractiveElementMessage( + const String& element, + const String& article, + const String& context) { + return "An interactive element which is not allowed in the content model " + "of the " + + element + " element was found within " + article + element + + " element. " + context + + "These elements will not consistently be accessible to people " + "navigating by keyboard or using assistive technology."; +} + +bool SelectMutationObserver::IsAllowedInteractiveElement(Node& node) { + if (IsA<HTMLButtonElement>(node)) { + // The <button> must have a parent (not being inserted as a child of + // `HTMLSelectedContentElement`) and must be the first child of the + // <select>. + const Node* parent = node.parentNode(); + return parent && IsA<HTMLSelectElement>(*parent) && + !ElementTraversal::PreviousSibling(node); + } + if (RuntimeEnabledFeatures::SelectAccessibilityReparentInputEnabled() || RuntimeEnabledFeatures::SelectAccessibilityNestedInputEnabled()) { + // <select>s are allowed to have one <input> before the options. We should + // probably find a way to figure out if the <input> is actually placed + // before the <option>s or not. + + if (auto* input = DynamicTo<HTMLInputElement>(node)) { + if (input->IsTextField()) { + select_->AddDescendantTextInput(input); + } + if (input == select_->FirstDescendantTextInput()) { + return true; + } + } + } + // If the node isn't a <button> but it is an interactive element, we return + // false as interactive elements are disallowed. + return !IsInteractiveElement(node); +} + +bool SelectMutationObserver::IsInteractiveElement(const Node& node) { + if (HasTabIndexAttribute(node)) { + return true; + } + if (auto* html_element = DynamicTo<HTMLElement>(node)) { + return IsContenteditable(node) || html_element->IsInteractiveContent(); + } + return false; +} + +void SelectMutationObserver::RecordIssueByType( + SelectElementAccessibilityIssueReason issue_reason) { + switch (issue_reason) { + case SelectElementAccessibilityIssueReason::kDisallowedSelectChild: + UseCounter::Count(select_->GetDocument(), + WebFeature::kDisallowedSelectChild); + break; + case SelectElementAccessibilityIssueReason::kDisallowedOptGroupChild: + UseCounter::Count(select_->GetDocument(), + WebFeature::kDisallowedOptGroupChild); + break; + case SelectElementAccessibilityIssueReason::kNonPhrasingContentOptionChild: + UseCounter::Count(select_->GetDocument(), + WebFeature::kNonPhrasingContentOptionChild); + break; + case SelectElementAccessibilityIssueReason::kInteractiveContentOptionChild: + UseCounter::Count(select_->GetDocument(), + WebFeature::kInteractiveContentOptionChild); + break; + case SelectElementAccessibilityIssueReason::kInteractiveContentLegendChild: + UseCounter::Count(select_->GetDocument(), + WebFeature::kInteractiveContentLegendChild); + break; + case SelectElementAccessibilityIssueReason::kValidChild: + default: + NOTREACHED(); + } +} + +SelectElementAccessibilityIssueReason SelectMutationObserver::CheckForIssue( + const Node& descendant) { + if (descendant.getNodeType() == Node::kCommentNode || + IsAutonomousCustomElement(descendant)) { + return SelectElementAccessibilityIssueReason::kValidChild; + } + // Get the parent of the descendant. + const Node* parent = descendant.parentNode(); + // If the node has no parent, assume it is being appended to a + // `HTMLSelectedContentElement`. + if (!parent) { + return CheckDescedantOfOption(descendant); + } + if (!IsA<HTMLElement>(*parent)) { + if (parent->IsSVGElement()) { + return SelectElementAccessibilityIssueReason::kValidChild; + } + return SelectElementAccessibilityIssueReason::kDisallowedSelectChild; + } + if (IsA<HTMLSelectElement>(*parent)) { + if (IsAllowedDescendantOfSelect(descendant, *parent)) { + return SelectElementAccessibilityIssueReason::kValidChild; + } + return SelectElementAccessibilityIssueReason::kDisallowedSelectChild; + } + if (IsA<HTMLOptGroupElement>(*parent)) { + if (IsAllowedDescendantOfOptgroup(descendant, *parent)) { + return SelectElementAccessibilityIssueReason::kValidChild; + } + return SelectElementAccessibilityIssueReason::kDisallowedOptGroupChild; + } + if (IsA<HTMLOptionElement>(*parent) || + IsA<HTMLSelectedContentElement>(*parent) || + (IsAllowedPhrasingContent(*parent) && !IsA<HTMLSpanElement>(*parent))) { + return CheckDescedantOfOption(descendant); + } + if (IsA<HTMLDivElement>(*parent) || IsA<HTMLSpanElement>(*parent) || + IsAutonomousCustomElement(*parent)) { + return TraverseAncestorsAndCheckDescendant(descendant); + } + if ((IsA<HTMLNoScriptElement>(*parent) || IsA<HTMLScriptElement>(*parent) || + IsA<HTMLTemplateElement>(*parent)) && + !descendant.IsTextNode()) { + return TraverseAncestorsAndCheckDescendant(descendant); + } + if (IsA<HTMLButtonElement>(*parent)) { + if (IsAllowedDescendantOfButton(descendant)) { + return SelectElementAccessibilityIssueReason::kValidChild; + } + return SelectElementAccessibilityIssueReason::kDisallowedSelectChild; + } + if (IsA<HTMLLegendElement>(*parent)) { + if (IsAllowedPhrasingContent(descendant) && + !HasTabIndexAttribute(descendant) && !IsContenteditable(descendant)) { + return SelectElementAccessibilityIssueReason::kValidChild; + } + return SelectElementAccessibilityIssueReason:: + kInteractiveContentLegendChild; + } + return SelectElementAccessibilityIssueReason::kDisallowedSelectChild; +} + +bool SelectMutationObserver::IsAllowedDescendantOfSelect(const Node& descendant, + const Node& parent) { + if (RuntimeEnabledFeatures::SelectAccessibilityReparentInputEnabled() || RuntimeEnabledFeatures::SelectAccessibilityNestedInputEnabled()) { + // <select>s are allowed to have one text <input>, although it should be + // placed before any of the <option>s. + if (select_->FirstDescendantTextInput() == descendant) { + return true; + } + } + // <button> has to be the first direct descendant of the <select>. + return (IsA<HTMLButtonElement>(descendant) && + IsA<HTMLSelectElement>(parent) && + !ElementTraversal::PreviousSibling(descendant)) || + IsA<HTMLOptionElement>(descendant) || + IsA<HTMLOptGroupElement>(descendant) || + IsA<HTMLHRElement>(descendant) || IsA<HTMLDivElement>(descendant) || + IsA<HTMLSpanElement>(descendant) || + IsA<HTMLNoScriptElement>(descendant) || + IsA<HTMLScriptElement>(descendant) || + IsA<HTMLTemplateElement>(descendant); +} + +bool SelectMutationObserver::IsAllowedDescendantOfOptgroup( + const Node& descendant, + const Node& parent) { + // <legend> has to be the first direct descendant of the <optgroup>. + return (IsA<HTMLLegendElement>(descendant) && + IsA<HTMLOptGroupElement>(parent) && + !ElementTraversal::PreviousSibling(descendant)) || + IsA<HTMLOptionElement>(descendant) || + IsA<HTMLDivElement>(descendant) || IsA<HTMLSpanElement>(descendant) || + IsA<HTMLNoScriptElement>(descendant) || + IsA<HTMLScriptElement>(descendant) || + IsA<HTMLTemplateElement>(descendant); +} + +bool SelectMutationObserver::IsAllowedDescendantOfButton( + const Node& descendant) { + return IsA<HTMLSelectedContentElement>(descendant) || + CheckDescedantOfOption(descendant) == + SelectElementAccessibilityIssueReason::kValidChild; +} + +SelectElementAccessibilityIssueReason +SelectMutationObserver::CheckDescedantOfOption(const Node& descendant) { + if (!IsA<HTMLDivElement>(descendant) && + !IsAllowedPhrasingContent(descendant) && + !IsAutonomousCustomElement(descendant)) { + return SelectElementAccessibilityIssueReason:: + kNonPhrasingContentOptionChild; + } + // Check tabindex and contenteditable attributes of the descendant as well. + if (!HasTabIndexAttribute(descendant) && !IsContenteditable(descendant)) { + return SelectElementAccessibilityIssueReason::kValidChild; + } + return SelectElementAccessibilityIssueReason::kInteractiveContentOptionChild; +} + +bool SelectMutationObserver::HasTabIndexAttribute(const Node& node) { + if (auto* element = DynamicTo<Element>(node)) { + return element->FastHasAttribute(html_names::kTabindexAttr); + } + return false; +} + +bool SelectMutationObserver::IsContenteditable(const Node& node) { + if (auto* html_element = DynamicTo<HTMLElement>(node)) { + ContentEditableType normalized_value = + html_element->contentEditableNormalized(); + return normalized_value == ContentEditableType::kContentEditable || + normalized_value == ContentEditableType::kPlaintextOnly; + } + return false; +} + +SelectElementAccessibilityIssueReason +SelectMutationObserver::TraverseAncestorsAndCheckDescendant( + const Node& descendant) { + // As we've already checked the descendant's parent, we can directly look at + // the grandparent. + const Node* parent = descendant.parentNode(); + for (const Node* ancestor = parent->parentNode(); ancestor; + ancestor = ancestor->parentNode()) { + if (IsA<HTMLOptionElement>(*ancestor) || + IsA<HTMLSelectedContentElement>(*ancestor)) { + return CheckDescedantOfOption(descendant); + } + if (IsA<HTMLOptGroupElement>(*ancestor)) { + if (IsAllowedDescendantOfOptgroup(descendant, *parent)) { + return SelectElementAccessibilityIssueReason::kValidChild; + } + return SelectElementAccessibilityIssueReason::kDisallowedOptGroupChild; + } + if (IsA<HTMLSelectElement>(*ancestor) && + IsAllowedDescendantOfSelect(descendant, *parent)) { + return SelectElementAccessibilityIssueReason::kValidChild; + } + if (IsA<HTMLButtonElement>(*ancestor) && + IsAllowedDescendantOfButton(descendant)) { + return SelectElementAccessibilityIssueReason::kValidChild; + } + } + return SelectElementAccessibilityIssueReason::kDisallowedSelectChild; +} + +bool SelectMutationObserver::IsWhitespaceOrEmpty(const Node& node) { + return node.IsTextNode() && + node.textContent().ContainsOnlyWhitespaceOrEmpty(); +} + +// Phrasing content that isn't Interactive content. <datalist>, <object> +// elements are excluded as well. +bool SelectMutationObserver::IsAllowedPhrasingContent(const Node& node) { + DEFINE_STATIC_LOCAL(HashSet<QualifiedName>, phrasing_content_names, + ({ + html_names::kATag, html_names::kAbbrTag, + html_names::kAreaTag, html_names::kAudioTag, + html_names::kBTag, html_names::kBdiTag, + html_names::kBdoTag, html_names::kBrTag, + html_names::kButtonTag, html_names::kCanvasTag, + html_names::kCiteTag, html_names::kCodeTag, + html_names::kDataTag, html_names::kDatalistTag, + html_names::kDelTag, html_names::kDfnTag, + html_names::kEmTag, html_names::kEmbedTag, + html_names::kITag, html_names::kIFrameTag, + html_names::kImgTag, html_names::kInputTag, + html_names::kInsTag, html_names::kKbdTag, + html_names::kLabelTag, html_names::kLinkTag, + html_names::kMapTag, html_names::kMarkTag, + mathml_names::kMathTag, html_names::kMetaTag, + html_names::kMeterTag, html_names::kNoscriptTag, + html_names::kObjectTag, html_names::kOutputTag, + html_names::kPictureTag, html_names::kProgressTag, + html_names::kQTag, html_names::kRubyTag, + html_names::kSTag, html_names::kSampTag, + html_names::kScriptTag, html_names::kSelectTag, + html_names::kSlotTag, html_names::kSmallTag, + html_names::kSpanTag, html_names::kStrongTag, + html_names::kSubTag, html_names::kSupTag, + svg_names::kSVGTag, html_names::kTemplateTag, + html_names::kTextareaTag, html_names::kTimeTag, + html_names::kUTag, html_names::kVarTag, + html_names::kVideoTag, html_names::kWbrTag, + })); + if (node.IsTextNode()) { + return true; + } + if (IsA<HTMLDataListElement>(node) || IsA<HTMLObjectElement>(node)) { + return false; + } + if (const auto* element = DynamicTo<Element>(node)) { + if (phrasing_content_names.Contains(element->TagQName())) { + if (auto* html_element = DynamicTo<HTMLElement>(element)) { + return !html_element->IsInteractiveContent(); + } + return element->IsSVGElement(); + } + } + return false; +} + +bool SelectMutationObserver::IsAutonomousCustomElement(const Node& node) { + if (node.IsCustomElement()) { + if (auto* element = DynamicTo<Element>(node)) { + if (CustomElement::IsValidName(element->localName())) { + return true; + } + } + } + return false; +} + +} // namespace blink
diff --git a/third_party/blink/renderer/core/html/forms/select_mutation_observer.h b/third_party/blink/renderer/core/html/forms/select_mutation_observer.h new file mode 100644 index 0000000..17367002 --- /dev/null +++ b/third_party/blink/renderer/core/html/forms/select_mutation_observer.h
@@ -0,0 +1,58 @@ +// Copyright 2025 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "third_party/blink/renderer/core/dom/mutation_observer.h" +#include "third_party/blink/renderer/core/html/forms/html_select_element.h" +#include "third_party/blink/renderer/core/inspector/inspector_audits_issue.h" + +namespace blink { + +class SelectMutationObserver : public MutationObserver::Delegate { + public: + explicit SelectMutationObserver(HTMLSelectElement& select); + + ExecutionContext* GetExecutionContext() const override; + void Deliver(const MutationRecordVector& records, MutationObserver&) override; + void Trace(Visitor* visitor) const override; + + void Disconnect(); + + private: + void CheckAddedNodes(MutationRecord* record); + void CheckRemovedNodes(MutationRecord* record); + void TraverseNodeDescendants(const Node* node); + void MaybeAddDescendantTextInput(Node* node); + void MaybeRemoveDescendantTextInput(Node* node); + void AddDescendantDisallowedErrorToNode(Node& node); + String GetMessageForReason( + SelectElementAccessibilityIssueReason issue_reason); + String FormatElementMessage(const String& element, + const String& article, + const String& example); + String FormatInteractiveElementMessage(const String& element, + const String& article, + const String& context); + bool IsAllowedInteractiveElement(Node& node); + bool IsInteractiveElement(const Node& node); + void RecordIssueByType(SelectElementAccessibilityIssueReason issue_reason); + SelectElementAccessibilityIssueReason CheckForIssue(const Node& descendant); + bool IsAllowedDescendantOfSelect(const Node& descendant, const Node& parent); + bool IsAllowedDescendantOfOptgroup(const Node& descendant, + const Node& parent); + bool IsAllowedDescendantOfButton(const Node& descendant); + SelectElementAccessibilityIssueReason CheckDescedantOfOption( + const Node& descendant); + bool HasTabIndexAttribute(const Node& node); + bool IsContenteditable(const Node& node); + SelectElementAccessibilityIssueReason TraverseAncestorsAndCheckDescendant( + const Node& descendant); + bool IsWhitespaceOrEmpty(const Node& node); + bool IsAllowedPhrasingContent(const Node& node); + bool IsAutonomousCustomElement(const Node& node); + + Member<HTMLSelectElement> select_; + Member<MutationObserver> observer_; +}; + +} // namespace blink
diff --git a/third_party/blink/renderer/core/html/html_element.cc b/third_party/blink/renderer/core/html/html_element.cc index c5f6e87..c72d9a7 100644 --- a/third_party/blink/renderer/core/html/html_element.cc +++ b/third_party/blink/renderer/core/html/html_element.cc
@@ -1447,6 +1447,17 @@ void HTMLElement::ShowPopoverInternal(Element* invoker, ExceptionState* exception_state) { + auto is_potential_partial_interest = [](Element* invoker) { + return invoker && invoker->GetInvokerData() && + invoker->GetInvokerData()->GetInterestState() == + InterestState::kPotentialPartialInterest; + }; + auto abandon_partial_interest = [this, &invoker, + &is_potential_partial_interest]() { + if (is_potential_partial_interest(invoker)) { + invoker->ChangeInterestState(this, InterestState::kNoInterest); + } + }; if (!IsPopoverReady(PopoverTriggerAction::kShow, exception_state, /*include_event_handler_text=*/false, /*document=*/nullptr)) { @@ -1454,6 +1465,7 @@ << " Callers which aren't supposed to throw exceptions should not call " "ShowPopoverInternal when the Popover isn't in a valid state to be " "shown."; + abandon_partial_interest(); return; } @@ -1477,8 +1489,10 @@ CHECK_EQ(event->oldState(), "closed"); CHECK_EQ(event->newState(), "open"); event->SetTarget(this); - if (DispatchEvent(*event) != DispatchEventResult::kNotCanceled) + if (DispatchEvent(*event) != DispatchEventResult::kNotCanceled) { + abandon_partial_interest(); return; + } // The 'beforetoggle' event handler could have changed this popover, e.g. by // changing its type, removing it from the document, moving it to another @@ -1486,6 +1500,7 @@ if (!IsPopoverReady(PopoverTriggerAction::kShow, exception_state, /*include_event_handler_text=*/true, &original_document)) { + abandon_partial_interest(); return; } @@ -1552,11 +1567,13 @@ "The value of the popover attribute was changed while hiding the " "popover."); } + abandon_partial_interest(); return; } if (!IsPopoverReady(PopoverTriggerAction::kShow, exception_state, /*include_event_handler_text=*/true, &original_document)) { + abandon_partial_interest(); return; } @@ -1609,6 +1626,19 @@ GetPopoverData()->setPreviouslyFocusedElement(originally_focused_element); } + // Now that the popover has been shown, we can check the focusability of its + // contents, to evaluate whether we need partial interest, or should go + // directly to full interest. + if (is_potential_partial_interest(invoker)) { + bool is_focusable = + IsKeyboardFocusableSlow(UpdateBehavior::kAssertNoLayoutUpdates) || + ContainsKeyboardFocusableElementsSlow( + UpdateBehavior::kAssertNoLayoutUpdates); + invoker->ChangeInterestState(this, is_focusable + ? InterestState::kPartialInterest + : InterestState::kFullInterest); + } + // Queue the "opening" toggle event. String old_state = "closed"; ToggleEvent* after_event;
diff --git a/third_party/blink/renderer/core/html/resources/html.css b/third_party/blink/renderer/core/html/resources/html.css index 1d9fca4..4ad8b8a 100644 --- a/third_party/blink/renderer/core/html/resources/html.css +++ b/third_party/blink/renderer/core/html/resources/html.css
@@ -1395,8 +1395,10 @@ outline: auto 1px -webkit-focus-ring-color } -[interesttarget]:focus-visible { - outline: solid 3px -webkit-focus-ring-color; +[popover]:target-of-partial-interest::after { + content: -internal-partial-interest-content; + display: block; + font-size: 0.8em; } button[interesttarget] {
diff --git a/third_party/blink/renderer/core/input/gesture_manager.cc b/third_party/blink/renderer/core/input/gesture_manager.cc index 839aea0..c0d8fc2 100644 --- a/third_party/blink/renderer/core/input/gesture_manager.cc +++ b/third_party/blink/renderer/core/input/gesture_manager.cc
@@ -413,8 +413,9 @@ // timeout which causes GestureRecognizer to suppress long-press detection. if (TouchDragAndContextMenuEnabled(frame_) && RuntimeEnabledFeatures::TouchDragOnShortPressEnabled()) { - drag_in_progress_ = - mouse_event_manager_->HandleDragDropIfPossible(targeted_event); + drag_in_progress_ = mouse_event_manager_->HandleDragDropIfPossible( + targeted_event, + GetPointerIdFromWebGestureEvent(targeted_event.Event())); } return drag_in_progress_ ? WebInputEventResult::kHandledSystem : WebInputEventResult::kNotHandled; @@ -439,8 +440,9 @@ if (TouchDragAndContextMenuEnabled(frame_)) { if (!RuntimeEnabledFeatures::TouchDragOnShortPressEnabled()) { - drag_in_progress_ = - mouse_event_manager_->HandleDragDropIfPossible(targeted_event); + drag_in_progress_ = mouse_event_manager_->HandleDragDropIfPossible( + targeted_event, + GetPointerIdFromWebGestureEvent(targeted_event.Event())); } } else if (frame_->GetSettings() && frame_->GetSettings()->GetTouchDragDropEnabled() && @@ -450,7 +452,9 @@ !hit_test_result.AbsoluteImageURL().IsNull() || !hit_test_result.AbsoluteMediaURL().IsNull(); if (!hit_test_contains_links && - mouse_event_manager_->HandleDragDropIfPossible(targeted_event)) { + mouse_event_manager_->HandleDragDropIfPossible( + targeted_event, + GetPointerIdFromWebGestureEvent(targeted_event.Event()))) { gesture_context_menu_deferred_ = true; return WebInputEventResult::kHandledSystem; }
diff --git a/third_party/blink/renderer/core/input/mouse_event_manager.cc b/third_party/blink/renderer/core/input/mouse_event_manager.cc index 5c394ed4..aa0c7b88 100644 --- a/third_party/blink/renderer/core/input/mouse_event_manager.cc +++ b/third_party/blink/renderer/core/input/mouse_event_manager.cc
@@ -741,7 +741,8 @@ } bool MouseEventManager::HandleDragDropIfPossible( - const GestureEventWithHitTestResults& targeted_event) { + const GestureEventWithHitTestResults& targeted_event, + PointerId pointer_id) { const WebGestureEvent& gesture_event = targeted_event.Event(); unsigned modifiers = gesture_event.GetModifiers(); @@ -757,7 +758,7 @@ WebPointerProperties::Button::kLeft, 1, modifiers | WebInputEvent::Modifiers::kLeftButtonDown | WebInputEvent::Modifiers::kIsCompatibilityEventForTouch, - base::TimeTicks::Now()); + base::TimeTicks::Now(), pointer_id); HitTestRequest request(HitTestRequest::kReadOnly); MouseEventWithHitTestResults mev = event_handling_util::PerformMouseEventHitTest(frame_, request,
diff --git a/third_party/blink/renderer/core/input/mouse_event_manager.h b/third_party/blink/renderer/core/input/mouse_event_manager.h index 368d447c..201e747b 100644 --- a/third_party/blink/renderer/core/input/mouse_event_manager.h +++ b/third_party/blink/renderer/core/input/mouse_event_manager.h
@@ -90,7 +90,8 @@ void SetLastKnownMousePosition(const WebMouseEvent&); void SetLastMousePositionAsUnknown(); - bool HandleDragDropIfPossible(const GestureEventWithHitTestResults&); + bool HandleDragDropIfPossible(const GestureEventWithHitTestResults&, + PointerId pointer_id); WebInputEventResult HandleMouseDraggedEvent( const MouseEventWithHitTestResults&);
diff --git a/third_party/blink/renderer/core/layout/anchor_position_scroll_data.cc b/third_party/blink/renderer/core/layout/anchor_position_scroll_data.cc index ae090f6..69e0589 100644 --- a/third_party/blink/renderer/core/layout/anchor_position_scroll_data.cc +++ b/third_party/blink/renderer/core/layout/anchor_position_scroll_data.cc
@@ -5,10 +5,11 @@ #include "third_party/blink/renderer/core/layout/anchor_position_scroll_data.h" #include "third_party/blink/renderer/core/dom/document.h" -#include "third_party/blink/renderer/core/frame/local_frame_view.h" +#include "third_party/blink/renderer/core/frame/visual_viewport.h" #include "third_party/blink/renderer/core/layout/anchor_position_visibility_observer.h" #include "third_party/blink/renderer/core/layout/layout_view.h" #include "third_party/blink/renderer/core/layout/non_overflowing_scroll_range.h" +#include "third_party/blink/renderer/core/page/page.h" #include "third_party/blink/renderer/core/paint/paint_layer.h" #include "third_party/blink/renderer/core/paint/paint_layer_scrollable_area.h" @@ -83,6 +84,23 @@ return container; }; + auto may_need_scroll_adjustment = [](const LayoutBox* box) -> bool { + if (RuntimeEnabledFeatures:: + AnchorPositionAdjustmentWithoutOverflowEnabled()) { + if (box->IsLayoutView()) { + // We may need to adjust scroll for overscroll effects, even if there + // is no scrollable overflow. + if (box->GetDocument() + .GetPage() + ->GetVisualViewport() + .GetOverscrollType() == OverscrollType::kTransform) { + return true; + } + } + } + return box->HasScrollableOverflow(); + }; + const auto* anchor_element = DynamicTo<Element>(anchor.GetNode()); CHECK(anchor_element); result.anchor_element = anchor_element; @@ -103,8 +121,7 @@ const PaintLayerScrollableArea* scrollable_area = To<LayoutBox>(container)->GetScrollableArea(); if (container != anchor && container != bounding_container && - // No need to adjust if the scroll container can't scroll anything. - To<LayoutBox>(container)->HasScrollableOverflow()) { + may_need_scroll_adjustment(To<LayoutBox>(container))) { result.adjustment_container_ids.push_back( scrollable_area->GetScrollElementId()); result.accumulated_adjustment += PhysicalOffset::FromVector2dFFloor(
diff --git a/third_party/blink/renderer/core/layout/layout_block_flow.cc b/third_party/blink/renderer/core/layout/layout_block_flow.cc index 77098d3..d0c38dd 100644 --- a/third_party/blink/renderer/core/layout/layout_block_flow.cc +++ b/third_party/blink/renderer/core/layout/layout_block_flow.cc
@@ -409,11 +409,11 @@ DCHECK_EQ(sibling_that_may_be_deleted->ChildrenInline(), ChildrenInline()); // Take all the children out of the |next| block and put them in the |prev| - // block. If there are paint layers involved, or if we're part of a flow - // thread, we need to notify the layout tree about the movement. + // block. If there are paint layers involved, or if we're part of a multicol + // container, we need to notify the layout tree about the movement. bool full_remove_insert = sibling_that_may_be_deleted->HasLayer() || HasLayer() || - sibling_that_may_be_deleted->IsInsideFlowThread(); + sibling_that_may_be_deleted->IsInsideMulticol(); sibling_that_may_be_deleted->MoveAllChildrenIncludingFloatsTo( this, full_remove_insert); // Delete the now-empty block's lines and nuke it.
diff --git a/third_party/blink/renderer/core/layout/layout_flow_thread.cc b/third_party/blink/renderer/core/layout/layout_flow_thread.cc index cccd7fe..5a1aa602 100644 --- a/third_party/blink/renderer/core/layout/layout_flow_thread.cc +++ b/third_party/blink/renderer/core/layout/layout_flow_thread.cc
@@ -52,7 +52,7 @@ LayoutFlowThread* LayoutFlowThread::LocateFlowThreadContainingBlockOf( const LayoutObject& descendant, AncestorSearchConstraint constraint) { - DCHECK(descendant.IsInsideFlowThread()); + DCHECK(descendant.IsInsideMulticol()); LayoutObject* curr = const_cast<LayoutObject*>(&descendant); bool inner_is_ng_object = curr->IsLayoutNGObject(); while (curr) {
diff --git a/third_party/blink/renderer/core/layout/layout_multi_column_flow_thread.cc b/third_party/blink/renderer/core/layout/layout_multi_column_flow_thread.cc index bbc1945..d1b5de5 100644 --- a/third_party/blink/renderer/core/layout/layout_multi_column_flow_thread.cc +++ b/third_party/blink/renderer/core/layout/layout_multi_column_flow_thread.cc
@@ -49,7 +49,7 @@ : last_set_worked_on_(nullptr), column_count_(1), is_being_evacuated_(false) { - SetIsInsideFlowThread(true); + SetIsInsideMulticol(true); } LayoutMultiColumnFlowThread::~LayoutMultiColumnFlowThread() = default; @@ -527,8 +527,9 @@ LayoutMultiColumnFlowThread* LayoutMultiColumnFlowThread::EnclosingFlowThread( AncestorSearchConstraint constraint) const { NOT_DESTROYED(); - if (!MultiColumnBlockFlow()->IsInsideFlowThread()) + if (!MultiColumnBlockFlow()->IsInsideMulticol()) { return nullptr; + } return To<LayoutMultiColumnFlowThread>( LocateFlowThreadContainingBlockOf(*MultiColumnBlockFlow(), constraint)); }
diff --git a/third_party/blink/renderer/core/layout/layout_object.cc b/third_party/blink/renderer/core/layout/layout_object.cc index 5df440c..7ebadec3 100644 --- a/third_party/blink/renderer/core/layout/layout_object.cc +++ b/third_party/blink/renderer/core/layout/layout_object.cc
@@ -546,8 +546,8 @@ } } -void LayoutObject::SetIsInsideFlowThreadIncludingDescendants( - bool inside_flow_thread) { +void LayoutObject::SetIsInsideMulticolIncludingDescendants( + bool inside_multicol) { NOT_DESTROYED(); LayoutObject* next; for (LayoutObject* object = this; object; object = next) { @@ -558,8 +558,8 @@ continue; } next = object->NextInPreOrder(this); - DCHECK_NE(inside_flow_thread, object->IsInsideFlowThread()); - object->SetIsInsideFlowThread(inside_flow_thread); + DCHECK_NE(inside_multicol, object->IsInsideMulticol()); + object->SetIsInsideMulticol(inside_multicol); } } @@ -1313,7 +1313,7 @@ LayoutFlowThread* LayoutObject::LocateFlowThreadContainingBlock() const { NOT_DESTROYED(); - DCHECK(IsInsideFlowThread()); + DCHECK(IsInsideMulticol()); return LayoutFlowThread::LocateFlowThreadContainingBlockOf( *this, LayoutFlowThread::kAnyAncestor); } @@ -4005,8 +4005,9 @@ void LayoutObject::RemoveFromLayoutFlowThread() { NOT_DESTROYED(); - if (!IsInsideFlowThread()) + if (!IsInsideMulticol()) { return; + } // Sometimes we remove the element from the flow, but it's not destroyed at // that time. @@ -4042,7 +4043,7 @@ if (layout_flow_thread && layout_flow_thread != this) layout_flow_thread->FlowThreadDescendantWillBeRemoved(this); - SetIsInsideFlowThread(false); + SetIsInsideMulticol(false); CHECK(!SpannerPlaceholder()); }
diff --git a/third_party/blink/renderer/core/layout/layout_object.h b/third_party/blink/renderer/core/layout/layout_object.h index 71a8508..6b637b7a 100644 --- a/third_party/blink/renderer/core/layout/layout_object.h +++ b/third_party/blink/renderer/core/layout/layout_object.h
@@ -467,8 +467,9 @@ // one. This function follows the containing block chain. LayoutFlowThread* FlowThreadContainingBlock() const { NOT_DESTROYED(); - if (!IsInsideFlowThread()) + if (!IsInsideMulticol()) { return nullptr; + } return LocateFlowThreadContainingBlock(); } @@ -760,9 +761,10 @@ // if we're not a LayoutFlowThread. // A LayoutFlowThread is always considered to be inside itself, so it never // has to change its state in response to parent changes. - bool inside_flow_thread = parent && parent->IsInsideFlowThread(); - if (inside_flow_thread != IsInsideFlowThread() && !IsLayoutFlowThread()) - SetIsInsideFlowThreadIncludingDescendants(inside_flow_thread); + bool inside_multicol = parent && parent->IsInsideMulticol(); + if (inside_multicol != IsInsideMulticol() && !IsLayoutFlowThread()) { + SetIsInsideMulticolIncludingDescendants(inside_multicol); + } } ////////////////////////////////////////// @@ -1148,15 +1150,20 @@ always_create_line_boxes); } - void SetIsInsideFlowThreadIncludingDescendants(bool); + void SetIsInsideMulticolIncludingDescendants(bool); - bool IsInsideFlowThread() const { + // Return true if there's a multicol container in the ancestry. Note that this + // doesn't have to mean that this object actually participates in the + // fragmentation context established by the multicol container, since this + // object may be inside an out-of-flow positioned subtree that's not contained + // by the multicol container, or even inside a monolithic subtree. + bool IsInsideMulticol() const { NOT_DESTROYED(); - return bitfields_.IsInsideFlowThread(); + return bitfields_.IsInsideMulticol(); } - void SetIsInsideFlowThread(bool inside_flow_thread) { + void SetIsInsideMulticol(bool b) { NOT_DESTROYED(); - bitfields_.SetIsInsideFlowThread(inside_flow_thread); + bitfields_.SetIsInsideMulticol(b); } // Remove this object and all descendants from the containing @@ -1167,8 +1174,7 @@ // false if it's definitely *not* inside one. bool MightBeInsideFragmentationContext() const { NOT_DESTROYED(); - return IsInsideFlowThread() || - (GetDocument().Printing() && !IsLayoutView()); + return IsInsideMulticol() || (GetDocument().Printing() && !IsLayoutView()); } // FIXME: Until all SVG layoutObjects can be subclasses of @@ -3743,7 +3749,7 @@ can_contain_absolute_position_objects_(false), can_contain_fixed_position_objects_(false), ever_had_layout_(false), - is_inside_flow_thread_(false), + is_inside_multicol_(false), subtree_change_listener_registered_(false), notified_of_subtree_change_(false), consumes_subtree_change_notification_(false), @@ -3926,7 +3932,7 @@ ADD_BOOLEAN_BITFIELD(ever_had_layout_, EverHadLayout); - ADD_BOOLEAN_BITFIELD(is_inside_flow_thread_, IsInsideFlowThread); + ADD_BOOLEAN_BITFIELD(is_inside_multicol_, IsInsideMulticol); ADD_BOOLEAN_BITFIELD(subtree_change_listener_registered_, SubtreeChangeListenerRegistered);
diff --git a/third_party/blink/renderer/core/loader/frame_fetch_context.cc b/third_party/blink/renderer/core/loader/frame_fetch_context.cc index 422b608..9416525d 100644 --- a/third_party/blink/renderer/core/loader/frame_fetch_context.cc +++ b/third_party/blink/renderer/core/loader/frame_fetch_context.cc
@@ -1315,7 +1315,7 @@ } ExecutionContext* FrameFetchContext::GetExecutionContext() const { - return document_->GetExecutionContext(); + return document_ ? document_->GetExecutionContext() : nullptr; } std::optional<ResourceRequestBlockedReason> FrameFetchContext::CanRequest(
diff --git a/third_party/blink/renderer/core/loader/image_loader.cc b/third_party/blink/renderer/core/loader/image_loader.cc index a6b7199..a27611fb 100644 --- a/third_party/blink/renderer/core/loader/image_loader.cc +++ b/third_party/blink/renderer/core/loader/image_loader.cc
@@ -799,6 +799,7 @@ } content->RecordDecodedImageType(&element_->GetDocument()); + content->RecordDecodedImageC2PA(&element_->GetDocument()); CHECK(!pending_load_event_.IsActive()); pending_load_event_ = PostCancellableTask(
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 2a57f39..fa680b5 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
@@ -741,4 +741,10 @@ bitmap_image->RecordDecodedImageType(use_counter); } +void ImageResourceContent::RecordDecodedImageC2PA(UseCounter* use_counter) { + if (auto* bitmap_image = DynamicTo<BitmapImage>(image_.get())) { + bitmap_image->RecordDecodedImageC2PA(use_counter); + } +} + } // namespace blink
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 6bc5f84a..b7e3ae8 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
@@ -230,6 +230,10 @@ // BitmapImage. |use_counter| may be a null pointer. void RecordDecodedImageType(UseCounter* use_counter); + // Records the presence of a C2PManifest if the image is a BitmapImage. + // |use_counter| may be a null pointer. + void RecordDecodedImageC2PA(UseCounter* use_counter); + private: using CanDeferInvalidation = ImageResourceObserver::CanDeferInvalidation;
diff --git a/third_party/blink/renderer/core/page/drag_controller.cc b/third_party/blink/renderer/core/page/drag_controller.cc index 66201ad..884de88 100644 --- a/third_party/blink/renderer/core/page/drag_controller.cc +++ b/third_party/blink/renderer/core/page/drag_controller.cc
@@ -220,6 +220,7 @@ void DragController::DragEnded() { drag_initiator_ = nullptr; did_initiate_drag_ = false; + drag_pointer_id_.reset(); page_->GetDragCaret().Clear(); // When dragging occurs, the mousedown event is triggered, causing the caret's // blinking state to be suspended. Therefore, it is necessary to reset the @@ -1366,6 +1367,7 @@ drag_obj_rect, effective_drag_initiation_location, frame, state, hit_test_result, drag_initiation_location, mouse_dragged_point); + drag_pointer_id_ = drag_event.id; DoSystemDrag(drag_image.get(), drag_obj_rect, effective_drag_initiation_location, state.drag_data_transfer_.Get(), frame);
diff --git a/third_party/blink/renderer/core/page/drag_controller.h b/third_party/blink/renderer/core/page/drag_controller.h index 08bbf5d..aae98d6 100644 --- a/third_party/blink/renderer/core/page/drag_controller.h +++ b/third_party/blink/renderer/core/page/drag_controller.h
@@ -26,6 +26,9 @@ #ifndef THIRD_PARTY_BLINK_RENDERER_CORE_PAGE_DRAG_CONTROLLER_H_ #define THIRD_PARTY_BLINK_RENDERER_CORE_PAGE_DRAG_CONTROLLER_H_ +#include <optional> + +#include "third_party/blink/public/common/input/pointer_id.h" #include "third_party/blink/renderer/core/core_export.h" #include "third_party/blink/renderer/core/dom/events/event_target.h" #include "third_party/blink/renderer/core/execution_context/execution_context_lifecycle_observer.h" @@ -121,6 +124,8 @@ void Trace(Visitor*) const final; + std::optional<PointerId> drag_pointer_id() const { return drag_pointer_id_; } + private: DispatchEventResult DispatchTextInputEventFor(LocalFrame*, DragData*); bool CanProcessDrag(DragData*, LocalFrame& local_root); @@ -162,6 +167,9 @@ DragDestinationAction drag_destination_action_; bool did_initiate_drag_; + // Used to set the correct pointer id to synthetic events. Principally added + // to track touch drag and drop when `TouchDragEndContextMenu` is enabled. + std::optional<PointerId> drag_pointer_id_; }; } // namespace blink
diff --git a/third_party/blink/renderer/core/page/scrolling/root_scroller_controller.cc b/third_party/blink/renderer/core/page/scrolling/root_scroller_controller.cc index 55655ad..fc88c48 100644 --- a/third_party/blink/renderer/core/page/scrolling/root_scroller_controller.cc +++ b/third_party/blink/renderer/core/page/scrolling/root_scroller_controller.cc
@@ -224,9 +224,10 @@ if (!element.GetLayoutObject()->IsBox()) return false; - // Ignore anything inside a FlowThread (multi-col, paginated, etc.). - if (element.GetLayoutObject()->IsInsideFlowThread()) + // Ignore anything inside that might be inside multicol layout. + if (element.GetLayoutObject()->IsInsideMulticol()) { return false; + } if (!element.GetLayoutObject()->IsScrollContainer() && !element.IsFrameOwnerElement()) @@ -265,9 +266,10 @@ if (!element.GetLayoutObject()->IsBox()) return false; - // Ignore anything inside a FlowThread (multi-col, paginated, etc.). - if (element.GetLayoutObject()->IsInsideFlowThread()) + // Ignore anything inside that might be inside multicol layout. + if (element.GetLayoutObject()->IsInsideMulticol()) { return false; + } PaintLayerScrollableArea* scrollable_area = GetScrollableArea(element); if (!scrollable_area || !scrollable_area->ScrollsOverflow())
diff --git a/third_party/blink/renderer/core/paint/compositing/compositing_reason_finder_test.cc b/third_party/blink/renderer/core/paint/compositing/compositing_reason_finder_test.cc index b1808b6..a10eb3f 100644 --- a/third_party/blink/renderer/core/paint/compositing/compositing_reason_finder_test.cc +++ b/third_party/blink/renderer/core/paint/compositing/compositing_reason_finder_test.cc
@@ -110,7 +110,7 @@ } // Tests that an anchored-positioned fixpos element should overscroll if the -// anchor cab be overscrolled, so that it keeps "attached" to the anchor. +// anchor can be overscrolled, so that it keeps "attached" to the anchor. TEST_P(CompositingReasonFinderTest, FixedPosAnchorPosOverscroll) { SetBodyInnerHTML(R"HTML( <style> @@ -137,11 +137,29 @@ *GetLayoutObjectByElementId("target"))); visual_viewport.SetOverscrollTypeForTesting(OverscrollType::kTransform); + auto expected_reasons_with_overflow = + CompositingReason::kFixedPosition | CompositingReason::kAnchorPosition; UpdateAllLifecyclePhasesForTest(); - EXPECT_REASONS( - CompositingReason::kFixedPosition | CompositingReason::kAnchorPosition, - CompositingReasonFinder::DirectReasonsForPaintProperties( - *GetLayoutObjectByElementId("target"))); + EXPECT_REASONS(expected_reasons_with_overflow, + CompositingReasonFinder::DirectReasonsForPaintProperties( + *GetLayoutObjectByElementId("target"))); + + // Adjust the body so that it does not scroll, but is still affected by + // elastic overscroll effects. + GetDocument().body()->setAttribute(html_names::kStyleAttr, + AtomicString("height: 50vh")); + UpdateAllLifecyclePhasesForTest(); + // When AnchorPositionAdjustmentWithoutOverflow is enabled, the behavior + // should be the same as the non-overflow case because overflow effects are + // the same regardless of actual scrollable overflow. + auto expected_reasons_without_overflow = + RuntimeEnabledFeatures::AnchorPositionAdjustmentWithoutOverflowEnabled() + ? expected_reasons_with_overflow + : CompositingReason::kFixedPosition | + CompositingReason::kUndoOverscroll; + EXPECT_REASONS(expected_reasons_without_overflow, + CompositingReasonFinder::DirectReasonsForPaintProperties( + *GetLayoutObjectByElementId("target"))); } // Tests that an anchored-positioned fixpos element should not overscroll if @@ -185,6 +203,7 @@ CompositingReasonFinder::DirectReasonsForPaintProperties( *GetLayoutObjectByElementId("target"))); } + TEST_P(CompositingReasonFinderTest, OnlyAnchoredStickyPositionPromoted) { SetBodyInnerHTML(R"HTML( <style>
diff --git a/third_party/blink/renderer/core/paint/inline_box_fragment_painter.cc b/third_party/blink/renderer/core/paint/inline_box_fragment_painter.cc index 137c6d8..8f91f45 100644 --- a/third_party/blink/renderer/core/paint/inline_box_fragment_painter.cc +++ b/third_party/blink/renderer/core/paint/inline_box_fragment_painter.cc
@@ -45,7 +45,7 @@ // if it's possible that this object participates in a fragmentation context. // This will give false positives, but that should be harmless, given the way // the return value is used by the caller. - if (layout_object.IsInsideFlowThread()) [[unlikely]] { + if (layout_object.IsInsideMulticol()) [[unlikely]] { return true; } return false;
diff --git a/third_party/blink/renderer/core/paint/paint_layer.cc b/third_party/blink/renderer/core/paint/paint_layer.cc index c428a3b..3a2c22e 100644 --- a/third_party/blink/renderer/core/paint/paint_layer.cc +++ b/third_party/blink/renderer/core/paint/paint_layer.cc
@@ -595,14 +595,15 @@ PaintLayer* PaintLayer::ContainingLayer() const { LayoutObject& layout_object = GetLayoutObject(); if (layout_object.IsOutOfFlowPositioned()) { - // In NG, the containing block chain goes directly from a column spanner to - // the multi-column container. Thus, for an OOF nested inside a spanner, we - // need to find its containing layer through its containing block to handle - // this case correctly. Therefore, we technically only need to take this - // path for OOFs inside an NG spanner. However, doing so for all OOF - // descendants of a multicol container is reasonable enough. - if (layout_object.IsInsideFlowThread()) + // The containing block chain goes directly from a column spanner to the + // multi-column container. Thus, for an OOF nested inside a spanner, we need + // to find its containing layer through its containing block to handle this + // case correctly. Therefore, we technically only need to take this path for + // OOFs inside a spanner. However, doing so for all OOF descendants of a + // multicol container is reasonable enough. + if (layout_object.IsInsideMulticol()) { return SlowContainingLayer(layout_object); + } auto can_contain_this_layer = layout_object.IsFixedPositioned() ? &LayoutObject::CanContainFixedPositionObjects
diff --git a/third_party/blink/renderer/core/paint/paint_layer.h b/third_party/blink/renderer/core/paint/paint_layer.h index 1908d37..ae53a3a 100644 --- a/third_party/blink/renderer/core/paint/paint_layer.h +++ b/third_party/blink/renderer/core/paint/paint_layer.h
@@ -424,7 +424,6 @@ bool HasBackdropFilterDescendant() const { DCHECK(!needs_descendant_dependent_flags_update_); return has_backdrop_filter_descendant_; - ; } // See
diff --git a/third_party/blink/renderer/core/streams/writable_stream.cc b/third_party/blink/renderer/core/streams/writable_stream.cc index 01fa8d8..89835a9 100644 --- a/third_party/blink/renderer/core/streams/writable_stream.cc +++ b/third_party/blink/renderer/core/streams/writable_stream.cc
@@ -434,6 +434,8 @@ auto* resolver = MakeGarbageCollected<ScriptPromiseResolver<IDLUndefined>>(script_state); + resolver->SuppressDetachCheck(); + // 6. Set stream.[[closeRequest]] to promise. stream->SetCloseRequest(resolver);
diff --git a/third_party/blink/renderer/core/style/computed_style.cc b/third_party/blink/renderer/core/style/computed_style.cc index 148bfd23..2b3ffbd 100644 --- a/third_party/blink/renderer/core/style/computed_style.cc +++ b/third_party/blink/renderer/core/style/computed_style.cc
@@ -124,7 +124,7 @@ private: Member<void*> pointers[10]; - unsigned bitfields[6]; + unsigned bitfields[5]; }; struct SameSizeAsComputedStyle : public SameSizeAsComputedStyleBase {
diff --git a/third_party/blink/renderer/modules/mediastream/DEPS b/third_party/blink/renderer/modules/mediastream/DEPS index a5cbdb0..afe287a 100644 --- a/third_party/blink/renderer/modules/mediastream/DEPS +++ b/third_party/blink/renderer/modules/mediastream/DEPS
@@ -62,7 +62,6 @@ "+base/numerics/ranges.h", "+base/run_loop.h", "+cc/layers/layer.h", - "+media/video/fake_gpu_memory_buffer.h", "+media/video/mock_gpu_memory_buffer_video_frame_pool.h", "+media/video/mock_gpu_video_accelerator_factories.h",
diff --git a/third_party/blink/renderer/modules/mediastream/web_media_player_ms_test.cc b/third_party/blink/renderer/modules/mediastream/web_media_player_ms_test.cc index ac2fc03..0a1eb0b 100644 --- a/third_party/blink/renderer/modules/mediastream/web_media_player_ms_test.cc +++ b/third_party/blink/renderer/modules/mediastream/web_media_player_ms_test.cc
@@ -28,7 +28,6 @@ #include "media/base/media_util.h" #include "media/base/test_helpers.h" #include "media/base/video_frame.h" -#include "media/video/fake_gpu_memory_buffer.h" #include "media/video/mock_gpu_memory_buffer_video_frame_pool.h" #include "media/video/mock_gpu_video_accelerator_factories.h" #include "third_party/blink/public/platform/scheduler/test/renderer_scheduler_test_support.h"
diff --git a/third_party/blink/renderer/modules/ml/BUILD.gn b/third_party/blink/renderer/modules/ml/BUILD.gn index c0b8a2f..3729607f 100644 --- a/third_party/blink/renderer/modules/ml/BUILD.gn +++ b/third_party/blink/renderer/modules/ml/BUILD.gn
@@ -37,6 +37,7 @@ deps = [ "//services/webnn/public/cpp", "//services/webnn/public/mojom:mojom_blink", + "//third_party/blink/renderer/modules/webgpu", ] }
diff --git a/third_party/blink/renderer/modules/ml/ml_context.cc b/third_party/blink/renderer/modules/ml/ml_context.cc index 6864942..aff31ec 100644 --- a/third_party/blink/renderer/modules/ml/ml_context.cc +++ b/third_party/blink/renderer/modules/ml/ml_context.cc
@@ -1027,7 +1027,7 @@ // this mapping. static_assert(base::to_underlying(webnn::MLTensorUsageFlags::kMaxValue) == 2); webnn::MLTensorUsage usage; - if (descriptor->importableToWebGPU()) { + if (descriptor->exportableToGPU()) { usage.Put(webnn::MLTensorUsageFlags::kWebGpuInterop); } if (descriptor->readable()) { @@ -1193,4 +1193,36 @@ resolver->Resolve(tensor); } +ScriptPromise<GPUBuffer> MLContext::exportToGPU( + ScriptState* script_state, + GPUDevice* device, + MLTensor* tensor, + ExceptionState& exception_state) { + webnn::ScopedTrace scoped_trace("MLContext::exportToGPU"); + if (!script_state->ContextIsValid()) { + exception_state.ThrowDOMException(DOMExceptionCode::kInvalidStateError, + "Invalid script state"); + return EmptyPromise(); + } + if (!context_remote_.is_bound()) { + exception_state.ThrowDOMException(DOMExceptionCode::kInvalidStateError, + "Context is lost."); + return EmptyPromise(); + } + if (tensor->context() != this) { + exception_state.ThrowTypeError( + "The source tensor was not created by this context."); + return EmptyPromise(); + } + if (!tensor->exportableToGPU()) { + exception_state.ThrowTypeError( + "The source tensor cannot be exported to WebGPU."); + return EmptyPromise(); + } + // TODO(crbug.com/345352987): Implement MLTensor's exportToGPU. + exception_state.ThrowDOMException(DOMExceptionCode::kNotSupportedError, + "MLContext::exportToGPU is not supported."); + return EmptyPromise(); +} + } // namespace blink
diff --git a/third_party/blink/renderer/modules/ml/ml_context.h b/third_party/blink/renderer/modules/ml/ml_context.h index 9ae502d..20be025 100644 --- a/third_party/blink/renderer/modules/ml/ml_context.h +++ b/third_party/blink/renderer/modules/ml/ml_context.h
@@ -41,6 +41,8 @@ class MLTensorDescriptor; class MLContextLostInfo; class MLOpSupportLimits; +class GPUBuffer; +class GPUDevice; class MODULES_EXPORT MLContext : public ScriptWrappable { DEFINE_WRAPPERTYPEINFO(); @@ -95,6 +97,11 @@ const MLNamedTensors& outputs, ExceptionState& exception_state); + ScriptPromise<GPUBuffer> exportToGPU(ScriptState* script_state, + GPUDevice* device, + MLTensor* tensor, + ExceptionState& exception_state); + MLGraphBuilder* CreateWebNNGraphBuilder(ScriptState* script_state, ExceptionState& exception_state);
diff --git a/third_party/blink/renderer/modules/ml/ml_context.idl b/third_party/blink/renderer/modules/ml/ml_context.idl index 8433a5c3..0dcdf6eb 100644 --- a/third_party/blink/renderer/modules/ml/ml_context.idl +++ b/third_party/blink/renderer/modules/ml/ml_context.idl
@@ -331,4 +331,13 @@ RuntimeEnabled=MachineLearningNeuralNetwork, CallWith=ScriptState ] MLOpSupportLimits opSupportLimits(); + + // TODO(crbug.com/345352987): remove device once MLContext(gpuDevice) is + // implemented. + [ + RuntimeEnabled=MachineLearningNeuralNetwork, + CallWith=ScriptState, + RaisesException + ] Promise<GPUBuffer> exportToGPU( + GPUDevice device, MLTensor tensor); };
diff --git a/third_party/blink/renderer/modules/ml/webnn/ml_tensor.cc b/third_party/blink/renderer/modules/ml/webnn/ml_tensor.cc index 58a43be..b3023fd3 100644 --- a/third_party/blink/renderer/modules/ml/webnn/ml_tensor.cc +++ b/third_party/blink/renderer/modules/ml/webnn/ml_tensor.cc
@@ -64,7 +64,7 @@ return Vector<uint32_t>(descriptor_.shape()); } -bool MLTensor::importableToWebGPU() const { +bool MLTensor::exportableToGPU() const { return usage_.Has(webnn::MLTensorUsageFlags::kWebGpuInterop); }
diff --git a/third_party/blink/renderer/modules/ml/webnn/ml_tensor.h b/third_party/blink/renderer/modules/ml/webnn/ml_tensor.h index 82debb7..976b04d5 100644 --- a/third_party/blink/renderer/modules/ml/webnn/ml_tensor.h +++ b/third_party/blink/renderer/modules/ml/webnn/ml_tensor.h
@@ -57,7 +57,7 @@ // ml_tensor.idl V8MLOperandDataType dataType() const; Vector<uint32_t> shape() const; - bool importableToWebGPU() const; + bool exportableToGPU() const; bool readable() const; bool writable() const;
diff --git a/third_party/blink/renderer/modules/ml/webnn/ml_tensor.idl b/third_party/blink/renderer/modules/ml/webnn/ml_tensor.idl index fd171bd5..c86b419 100644 --- a/third_party/blink/renderer/modules/ml/webnn/ml_tensor.idl +++ b/third_party/blink/renderer/modules/ml/webnn/ml_tensor.idl
@@ -10,7 +10,7 @@ ] interface MLTensor { readonly attribute MLOperandDataType dataType; readonly attribute FrozenArray<unsigned long> shape; - readonly attribute boolean importableToWebGPU; + readonly attribute boolean exportableToGPU; readonly attribute boolean readable; readonly attribute boolean writable;
diff --git a/third_party/blink/renderer/modules/ml/webnn/ml_tensor_descriptor.idl b/third_party/blink/renderer/modules/ml/webnn/ml_tensor_descriptor.idl index 312847c..15105fe 100644 --- a/third_party/blink/renderer/modules/ml/webnn/ml_tensor_descriptor.idl +++ b/third_party/blink/renderer/modules/ml/webnn/ml_tensor_descriptor.idl
@@ -5,7 +5,7 @@ // https://www.w3.org/TR/webnn/#api-mltensor dictionary MLTensorDescriptor : MLOperandDescriptor { - boolean importableToWebGPU = false; + boolean exportableToGPU = false; boolean readable = false; boolean writable = false; }; \ No newline at end of file
diff --git a/third_party/blink/renderer/platform/graphics/bitmap_image.cc b/third_party/blink/renderer/platform/graphics/bitmap_image.cc index 338776d..02d3a2b 100644 --- a/third_party/blink/renderer/platform/graphics/bitmap_image.cc +++ b/third_party/blink/renderer/platform/graphics/bitmap_image.cc
@@ -175,6 +175,12 @@ use_counter); } +void BitmapImage::RecordDecodedImageC2PA(UseCounter* use_counter) { + if (decoder_->HasC2PAManifest()) { + BitmapImageMetrics::CountDecodedImageC2PA(use_counter); + } +} + bool BitmapImage::GetHotSpot(gfx::Point& hot_spot) const { return decoder_ && decoder_->HotSpot(hot_spot); }
diff --git a/third_party/blink/renderer/platform/graphics/bitmap_image.h b/third_party/blink/renderer/platform/graphics/bitmap_image.h index 63d14d2..9c602abb 100644 --- a/third_party/blink/renderer/platform/graphics/bitmap_image.h +++ b/third_party/blink/renderer/platform/graphics/bitmap_image.h
@@ -108,6 +108,9 @@ // pointer. void RecordDecodedImageType(UseCounter* use_counter); + // Records the presence of a C2PA Manifest in a UseCounter. + void RecordDecodedImageC2PA(UseCounter* use_counter); + protected: bool IsSizeAvailable() override;
diff --git a/third_party/blink/renderer/platform/graphics/bitmap_image_metrics.cc b/third_party/blink/renderer/platform/graphics/bitmap_image_metrics.cc index a77ae3c..ba168f7e 100644 --- a/third_party/blink/renderer/platform/graphics/bitmap_image_metrics.cc +++ b/third_party/blink/renderer/platform/graphics/bitmap_image_metrics.cc
@@ -59,6 +59,12 @@ } } +void BitmapImageMetrics::CountDecodedImageC2PA(UseCounter* use_counter) { + if (use_counter) { + use_counter->CountUse(WebFeature::kC2PAManifest); + } +} + void BitmapImageMetrics::CountDecodedImageDensity(const String& type, int image_min_side, uint64_t density_centi_bpp,
diff --git a/third_party/blink/renderer/platform/graphics/bitmap_image_metrics.h b/third_party/blink/renderer/platform/graphics/bitmap_image_metrics.h index df3c3fda..31ce1db 100644 --- a/third_party/blink/renderer/platform/graphics/bitmap_image_metrics.h +++ b/third_party/blink/renderer/platform/graphics/bitmap_image_metrics.h
@@ -45,6 +45,8 @@ // |use_counter| may be a null pointer. static void CountDecodedImageType(const WTF::String& type, UseCounter* use_counter); + // |use_counter| may be a null pointer. + static void CountDecodedImageC2PA(UseCounter* use_counter); // Report the image compression density in 0.01 bits per pixel for an image // with a smallest side (width or length) of |image_min_side| and total size // in bytes |image_size_bytes|. Only certain image types and minimum image
diff --git a/third_party/blink/renderer/platform/graphics/canvas_resource_provider.cc b/third_party/blink/renderer/platform/graphics/canvas_resource_provider.cc index 380d6b87..22468747 100644 --- a/third_party/blink/renderer/platform/graphics/canvas_resource_provider.cc +++ b/third_party/blink/renderer/platform/graphics/canvas_resource_provider.cc
@@ -677,7 +677,6 @@ is_cleared_ = true; RasterRecordOOP(std::move(last_recording), needs_clear, resource()->GetClientSharedImage()->mailbox()); - resource()->GetSyncToken(); } bool ShouldReplaceTargetBuffer(
diff --git a/third_party/blink/renderer/platform/graphics/compositing/property_tree_manager.cc b/third_party/blink/renderer/platform/graphics/compositing/property_tree_manager.cc index d6b576d..ef60508 100644 --- a/third_party/blink/renderer/platform/graphics/compositing/property_tree_manager.cc +++ b/third_party/blink/renderer/platform/graphics/compositing/property_tree_manager.cc
@@ -1388,9 +1388,10 @@ // Otherwise all layers count as controlled layers of the parent. effect_layer_counts[effect->parent_id] += effect_layer_counts[id]; has_child_surface[effect->parent_id] |= has_child_surface[id]; - has_text[effect->parent_id] |= has_text[id]; } + has_text[effect->parent_id] |= has_text[id]; + #if DCHECK_IS_ON() // Mark we have visited this effect. effect_layer_counts[id] = -1;
diff --git a/third_party/blink/renderer/platform/graphics/deferred_image_decoder.cc b/third_party/blink/renderer/platform/graphics/deferred_image_decoder.cc index 5217b72f..1c74af635 100644 --- a/third_party/blink/renderer/platform/graphics/deferred_image_decoder.cc +++ b/third_party/blink/renderer/platform/graphics/deferred_image_decoder.cc
@@ -101,8 +101,8 @@ can_yuv_decode_(false), has_hot_spot_(false), image_is_high_bit_depth_(false), - complete_frame_content_id_(PaintImage::GetNextContentId()) { -} + has_c2pa_manifest_(false), + complete_frame_content_id_(PaintImage::GetNextContentId()) {} DeferredImageDecoder::~DeferredImageDecoder() { } @@ -258,6 +258,11 @@ : frame_data_.size(); } +bool DeferredImageDecoder::HasC2PAManifest() const { + return metadata_decoder_ ? metadata_decoder_->HasC2PAManifest() + : has_c2pa_manifest_; +} + int DeferredImageDecoder::RepetitionCount() const { return metadata_decoder_ ? metadata_decoder_->RepetitionCount() : repetition_count_; @@ -332,6 +337,7 @@ size_ = metadata_decoder_->Size(); image_is_high_bit_depth_ = metadata_decoder_->ImageIsHighBitDepth(); + has_c2pa_manifest_ = metadata_decoder_->HasC2PAManifest(); has_hot_spot_ = metadata_decoder_->HotSpot(hot_spot_); filename_extension_ = metadata_decoder_->FilenameExtension(); mime_type_ = metadata_decoder_->MimeType();
diff --git a/third_party/blink/renderer/platform/graphics/deferred_image_decoder.h b/third_party/blink/renderer/platform/graphics/deferred_image_decoder.h index cc031fdc..4d63759 100644 --- a/third_party/blink/renderer/platform/graphics/deferred_image_decoder.h +++ b/third_party/blink/renderer/platform/graphics/deferred_image_decoder.h
@@ -81,6 +81,7 @@ gfx::Size FrameSizeAtIndex(wtf_size_t index) const; wtf_size_t FrameCount(); bool ImageIsHighBitDepth() const { return image_is_high_bit_depth_; } + bool HasC2PAManifest() const; int RepetitionCount() const; bool FrameIsReceivedAtIndex(wtf_size_t index) const; base::TimeDelta FrameDurationAtIndex(wtf_size_t index) const; @@ -129,6 +130,7 @@ bool can_yuv_decode_; bool has_hot_spot_; bool image_is_high_bit_depth_; + bool has_c2pa_manifest_; sk_sp<SkColorSpace> color_space_for_sk_images_; gfx::Point hot_spot_; const PaintImage::ContentId complete_frame_content_id_;
diff --git a/third_party/blink/renderer/platform/media/DEPS b/third_party/blink/renderer/platform/media/DEPS index 2f251e6..d8515d26 100644 --- a/third_party/blink/renderer/platform/media/DEPS +++ b/third_party/blink/renderer/platform/media/DEPS
@@ -51,7 +51,6 @@ "+gpu/command_buffer/client/test_shared_image_interface.h", "+media/mojo/services", "+media/renderers", - "+media/video/fake_gpu_memory_buffer.h", "+gin/v8_initializer.h", # Allow test support dependencies.
diff --git a/third_party/blink/renderer/platform/media/video_frame_compositor_unittest.cc b/third_party/blink/renderer/platform/media/video_frame_compositor_unittest.cc index c4a70412..77e5b18 100644 --- a/third_party/blink/renderer/platform/media/video_frame_compositor_unittest.cc +++ b/third_party/blink/renderer/platform/media/video_frame_compositor_unittest.cc
@@ -18,7 +18,6 @@ #include "components/viz/common/surfaces/frame_sink_id.h" #include "gpu/command_buffer/client/test_shared_image_interface.h" #include "media/base/video_frame.h" -#include "media/video/fake_gpu_memory_buffer.h" #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" #include "third_party/blink/public/platform/web_video_frame_submitter.h"
diff --git a/third_party/blink/renderer/platform/peerconnection/DEPS b/third_party/blink/renderer/platform/peerconnection/DEPS index b487d8b..ec6d916 100644 --- a/third_party/blink/renderer/platform/peerconnection/DEPS +++ b/third_party/blink/renderer/platform/peerconnection/DEPS
@@ -46,7 +46,6 @@ "+base/threading/thread.h", "+components/webrtc/thread_wrapper.h", "+gpu/command_buffer/common/mailbox.h", - "+media/video/fake_gpu_memory_buffer.h", "+gpu/command_buffer/client/test_shared_image_interface.h", "+media/video/mock_gpu_video_accelerator_factories.h", "+media/video/mock_video_encode_accelerator.h", @@ -61,5 +60,8 @@ "+third_party/blink/renderer/platform/testing/blink_fuzzer_test_support.h", "+third_party/blink/renderer/platform/testing/task_environment.h", "+third_party/blink/renderer/platform/testing/fuzzed_data_provider.h", + ], + "webrtc_video_track_source_test.cc" : [ + "+gpu/command_buffer/client/fake_gpu_memory_buffer.h" ] }
diff --git a/third_party/blink/renderer/platform/peerconnection/rtc_video_encoder_test.cc b/third_party/blink/renderer/platform/peerconnection/rtc_video_encoder_test.cc index 51dcce5..cb223d96 100644 --- a/third_party/blink/renderer/platform/peerconnection/rtc_video_encoder_test.cc +++ b/third_party/blink/renderer/platform/peerconnection/rtc_video_encoder_test.cc
@@ -33,7 +33,6 @@ #include "media/capture/capture_switches.h" #include "media/mojo/clients/mock_mojo_video_encoder_metrics_provider_factory.h" #include "media/mojo/clients/mojo_video_encoder_metrics_provider.h" -#include "media/video/fake_gpu_memory_buffer.h" #include "media/video/mock_gpu_video_accelerator_factories.h" #include "media/video/mock_video_encode_accelerator.h" #include "media/webrtc/webrtc_features.h"
diff --git a/third_party/blink/renderer/platform/peerconnection/webrtc_video_track_source_test.cc b/third_party/blink/renderer/platform/peerconnection/webrtc_video_track_source_test.cc index 84152618..76261e9 100644 --- a/third_party/blink/renderer/platform/peerconnection/webrtc_video_track_source_test.cc +++ b/third_party/blink/renderer/platform/peerconnection/webrtc_video_track_source_test.cc
@@ -12,11 +12,11 @@ #include "base/test/bind.h" #include "base/test/scoped_feature_list.h" #include "base/test/task_environment.h" +#include "gpu/command_buffer/client/fake_gpu_memory_buffer.h" #include "gpu/command_buffer/client/test_shared_image_interface.h" #include "media/base/format_utils.h" #include "media/base/media_switches.h" #include "media/base/video_frame.h" -#include "media/video/fake_gpu_memory_buffer.h" #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" #include "third_party/blink/renderer/platform/testing/video_frame_utils.h" @@ -60,7 +60,7 @@ class WebRtcVideoTrackSourceTest : public ::testing::TestWithParam< std::tuple<media::VideoFrame::StorageType, media::VideoPixelFormat>>, - public media::FakeGpuMemoryBuffer::MapCallbackController { + public gpu::FakeGpuMemoryBuffer::MapCallbackController { public: WebRtcVideoTrackSourceTest() : shared_resources_( @@ -120,8 +120,8 @@ void SendTestFrameWithMappableGMB(const FrameParameters& frame_parameters, base::TimeDelta timestamp, bool premapped) { - std::unique_ptr<media::FakeGpuMemoryBuffer> fake_gmb = - std::make_unique<media::FakeGpuMemoryBuffer>( + std::unique_ptr<gpu::FakeGpuMemoryBuffer> fake_gmb = + std::make_unique<gpu::FakeGpuMemoryBuffer>( frame_parameters.coded_size, media::VideoPixelFormatToGfxBufferFormat( frame_parameters.pixel_format)
diff --git a/third_party/blink/renderer/platform/runtime_enabled_features.json5 b/third_party/blink/renderer/platform/runtime_enabled_features.json5 index c710a7e..8afb9e2 100644 --- a/third_party/blink/renderer/platform/runtime_enabled_features.json5 +++ b/third_party/blink/renderer/platform/runtime_enabled_features.json5
@@ -439,6 +439,13 @@ base_feature: "none", }, { + // Kill switch for https://crbug.com/415834974 which changes anchor + // positioning adjustments to occur even in cases where there is no + // scrollable overflow. + name: "AnchorPositionAdjustmentWithoutOverflow", + status: "stable", + }, + { name: "AndroidDownloadableFontsMatching", base_feature: "none", public: true, @@ -4755,6 +4762,14 @@ base_feature: "none", public: true, }, + // This feature enables drag and drop using touch input devices. Replaces + // the old "--enable-touch-drag-drop" and "--disable-touch-drag-drop" + // switches. + { + name: "TouchDragAndDrop", + base_feature: "none", + public: true, + }, // This feature makes touch dragging to occur at the short-press gesture, // which occurs right before the long-press gesture. This feature assumes // that TouchDragAndContextMenu is enabled.
diff --git a/third_party/blink/renderer/platform/testing/DEPS b/third_party/blink/renderer/platform/testing/DEPS index f7d4cd5..9eb033c5 100644 --- a/third_party/blink/renderer/platform/testing/DEPS +++ b/third_party/blink/renderer/platform/testing/DEPS
@@ -83,7 +83,6 @@ "+base/task/sequence_manager/test/sequence_manager_for_test.h", ], "video_frame_utils\.cc": [ - "+media/video/fake_gpu_memory_buffer.h", "+media/base/format_utils.h", "+components/viz/common/resources/shared_image_format.h", "+components/viz/common/resources/shared_image_format_utils.h",
diff --git a/third_party/blink/renderer/platform/testing/video_frame_utils.cc b/third_party/blink/renderer/platform/testing/video_frame_utils.cc index 278f3066..8665466 100644 --- a/third_party/blink/renderer/platform/testing/video_frame_utils.cc +++ b/third_party/blink/renderer/platform/testing/video_frame_utils.cc
@@ -9,7 +9,6 @@ #include "components/viz/common/resources/shared_image_format_utils.h" #include "gpu/command_buffer/client/test_shared_image_interface.h" #include "media/base/format_utils.h" -#include "media/video/fake_gpu_memory_buffer.h" namespace blink {
diff --git a/third_party/blink/renderer/platform/video_capture/DEPS b/third_party/blink/renderer/platform/video_capture/DEPS index 062714c..ffe7652b 100644 --- a/third_party/blink/renderer/platform/video_capture/DEPS +++ b/third_party/blink/renderer/platform/video_capture/DEPS
@@ -29,7 +29,6 @@ ], "gpu_memory_buffer_test_support.cc": [ "+components/viz/test/test_context_provider.h", - "+media/video/fake_gpu_memory_buffer.h", "+media/video/mock_gpu_video_accelerator_factories.h", ], "gpu_memory_buffer_test_support.h": [
diff --git a/third_party/blink/renderer/platform/video_capture/gpu_memory_buffer_test_support.cc b/third_party/blink/renderer/platform/video_capture/gpu_memory_buffer_test_support.cc index 8fda8cd..9147236 100644 --- a/third_party/blink/renderer/platform/video_capture/gpu_memory_buffer_test_support.cc +++ b/third_party/blink/renderer/platform/video_capture/gpu_memory_buffer_test_support.cc
@@ -5,7 +5,6 @@ #include "third_party/blink/renderer/platform/video_capture/gpu_memory_buffer_test_support.h" #include "components/viz/test/test_context_provider.h" -#include "media/video/fake_gpu_memory_buffer.h" #include "media/video/mock_gpu_video_accelerator_factories.h" #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h"
diff --git a/third_party/blink/web_tests/TestExpectations b/third_party/blink/web_tests/TestExpectations index 7155681..14b6409 100644 --- a/third_party/blink/web_tests/TestExpectations +++ b/third_party/blink/web_tests/TestExpectations
@@ -9354,7 +9354,7 @@ crbug.com/415747471 [ Mac ] virtual/text-antialias/international/bidi-CS-after-AN.html [ Failure ] crbug.com/415747471 [ Mac ] virtual/text-antialias/font-fallback.html [ Failure ] crbug.com/415747471 [ Mac ] virtual/text-antialias/complex-text-opacity.html [ Failure ] -crbug.com/415747471 [ Mac ] svg/text/bidi-textlength.html [ Failure ] +crbug.com/415747471 [ Mac ] svg/text/bidi-textlength.html [ Failure Pass ] # Gardener 2025-05-07 crbug.com/416123241 [ Linux ] fast/forms/select/customizable-select/disallowed-select-descendants-console-message.html [ Failure Pass ]
diff --git a/third_party/blink/web_tests/external/wpt/css/css-transforms/scale-transform-filtered-text-ref.html b/third_party/blink/web_tests/external/wpt/css/css-transforms/scale-transform-filtered-text-ref.html new file mode 100644 index 0000000..e8b564ce --- /dev/null +++ b/third_party/blink/web_tests/external/wpt/css/css-transforms/scale-transform-filtered-text-ref.html
@@ -0,0 +1,16 @@ +<!DOCTYPE html> +<style> + #container { + transform: scale(0.5); + background-color: lightblue; + font-size: 80px; + } + #animated, #content { + will-change: transform; + } +</style> +<div id=container> + <div id=content> + <div id=animated>Text</div> + </div> +</div>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-transforms/scale-transform-filtered-text.html b/third_party/blink/web_tests/external/wpt/css/css-transforms/scale-transform-filtered-text.html new file mode 100644 index 0000000..a479b12 --- /dev/null +++ b/third_party/blink/web_tests/external/wpt/css/css-transforms/scale-transform-filtered-text.html
@@ -0,0 +1,24 @@ +<!DOCTYPE html> +<link rel="author" href="mailto:chrishtr@chromium.org"> +<link rel="help" href="https://drafts.csswg.org/css-transforms-2/"> +<meta name="assert" content="Text is not blurry under w/filter and animation."> +<link rel="match" href="scale-transform-filtered-text-ref.html"> +<meta name=fuzzy content="maxDifference=0-25;totalPixels=0-500"> +<style> + #container { + transform: scale(0.5); + background-color: lightblue; + font-size: 80px; + } + #content { + filter: blur(0px); + } + #animated { + will-change: transform; + } +</style> +<div id=container> + <div id=content> + <div id=animated>Text</div> + </div> +</div>
diff --git a/third_party/blink/web_tests/external/wpt/fedcm/fedcm-disconnect.sub.https.html b/third_party/blink/web_tests/external/wpt/fedcm/fedcm-disconnect.sub.https.html index 2ea2d4a2..04fcd272 100644 --- a/third_party/blink/web_tests/external/wpt/fedcm/fedcm-disconnect.sub.https.html +++ b/third_party/blink/web_tests/external/wpt/fedcm/fedcm-disconnect.sub.https.html
@@ -13,6 +13,7 @@ mark_signed_in, disconnect_options, fedcm_get_and_select_first_account, + fedcm_select_account_promise, request_options_with_mediation_required, alt_manifest_origin, alt_request_options_with_mediation_required, @@ -82,4 +83,20 @@ await IdentityCredential.disconnect(disconnect_options("1")); return IdentityCredential.disconnect(alt_disconnect_options("2")); }, 'Disconnect is bound to each IDP'); + +fedcm_test(async t => { + await mark_signed_in(alt_manifest_origin); + // Get at least one connected account that can be disconnected. + await fedcm_get_and_select_first_account(t, alt_request_options_with_mediation_required()); + + // Pending get request. + const credentialPromise = navigator.credentials.get(alt_request_options_with_mediation_required()); + + // Disconnect the one connected account. + await IdentityCredential.disconnect(alt_disconnect_options("1234")); + + // Select an account to resolve the pending get request. + fedcm_select_account_promise(t, 0); + return credentialPromise; +}, 'Test that disconnect succeeds when there is a pending get request and the get request succeeds after the disconnect'); </script>
diff --git a/third_party/blink/web_tests/external/wpt/html/semantics/the-button-element/interest-target/interesttarget-keyboard-invalidation-ref.html b/third_party/blink/web_tests/external/wpt/html/semantics/the-button-element/interest-target/interesttarget-keyboard-invalidation-ref.html index 0ba07b1..9d52094 100644 --- a/third_party/blink/web_tests/external/wpt/html/semantics/the-button-element/interest-target/interesttarget-keyboard-invalidation-ref.html +++ b/third_party/blink/web_tests/external/wpt/html/semantics/the-button-element/interest-target/interesttarget-keyboard-invalidation-ref.html
@@ -7,21 +7,24 @@ <button id=hasinterest>Button</button> <button class=otherselector>Button</button> <button class=otherselector>Button</button> -<div id=target>Target</div> +<div popover id=target>Target</div> <style> #hasinterest { - background-color: red; - outline: solid 3px -webkit-focus-ring-color; + background-color: purple; } .otherselector { background-color: green; } #target { background-color: yellow; + inset:auto; + top:50px; + left:0; } </style> <script> + target.showPopover(); hasinterest.focus(); </script>
diff --git a/third_party/blink/web_tests/external/wpt/html/semantics/the-button-element/interest-target/interesttarget-keyboard-invalidation.tentative.html b/third_party/blink/web_tests/external/wpt/html/semantics/the-button-element/interest-target/interesttarget-keyboard-invalidation.tentative.html index 8c0ebd7a2..b6d0ff1 100644 --- a/third_party/blink/web_tests/external/wpt/html/semantics/the-button-element/interest-target/interesttarget-keyboard-invalidation.tentative.html +++ b/third_party/blink/web_tests/external/wpt/html/semantics/the-button-element/interest-target/interesttarget-keyboard-invalidation.tentative.html
@@ -13,7 +13,7 @@ <button id=b3 interesttarget=target>Button</button> <button id=b4>Button</button> <button id=b5>Button</button> -<div id=target>Target</div> +<div popover id=target>Target</div> <style> :has-interest { @@ -22,18 +22,24 @@ :has-interest:has-partial-interest { background-color: red; } - :has-partial-interest + button { + /* Test complicated combinators: */ + :has-interest + button { background-color: green; } :root:has(:has-interest) #b5 { background-color: green; } - :target-of-interest:target-of-partial-interest { + :target-of-interest { background-color: yellow; } [interesttarget] { interest-target-delay: 0s; } + #target { + inset:auto; + top:50px; + left:0; + } </style> <script>
diff --git a/third_party/blink/web_tests/external/wpt/html/semantics/the-button-element/interest-target/interesttarget-outline-appearance-ref.html b/third_party/blink/web_tests/external/wpt/html/semantics/the-button-element/interest-target/interesttarget-outline-appearance-ref.html deleted file mode 100644 index 33b0cf6..0000000 --- a/third_party/blink/web_tests/external/wpt/html/semantics/the-button-element/interest-target/interesttarget-outline-appearance-ref.html +++ /dev/null
@@ -1,9 +0,0 @@ -<!DOCTYPE html> -<meta charset="utf-8" /> -<link rel="author" href="mailto:masonf@chromium.org"> - -<button id=non_interesttarget_button>Interesttarget Button</button> - -<script> - document.querySelector('#non_interesttarget_button').focus(); -</script>
diff --git a/third_party/blink/web_tests/external/wpt/html/semantics/the-button-element/interest-target/interesttarget-outline-appearance.tentative.html b/third_party/blink/web_tests/external/wpt/html/semantics/the-button-element/interest-target/interesttarget-outline-appearance.tentative.html deleted file mode 100644 index 28cb2052..0000000 --- a/third_party/blink/web_tests/external/wpt/html/semantics/the-button-element/interest-target/interesttarget-outline-appearance.tentative.html +++ /dev/null
@@ -1,16 +0,0 @@ -<!DOCTYPE html> -<meta charset="utf-8" /> -<link rel="author" href="mailto:masonf@chromium.org"> -<link rel="help" href="https://open-ui.org/components/interest-invokers.explainer/" /> -<link rel="mismatch" href="interesttarget-outline-appearance-ref.html"> - -<button interesttarget=target>Interesttarget Button</button> - -<style> - /* Outline style should apply even when the element doesn't have interest. */ - :has-interest { background-color: red; } -</style> - -<script> - document.querySelector('[interesttarget]').focus(); -</script>
diff --git a/third_party/blink/web_tests/external/wpt/html/semantics/the-button-element/interest-target/interesttarget-partial-interest.tentative.html b/third_party/blink/web_tests/external/wpt/html/semantics/the-button-element/interest-target/interesttarget-partial-interest.tentative.html new file mode 100644 index 0000000..bc7184d4 --- /dev/null +++ b/third_party/blink/web_tests/external/wpt/html/semantics/the-button-element/interest-target/interesttarget-partial-interest.tentative.html
@@ -0,0 +1,52 @@ +<!DOCTYPE html> +<meta charset="utf-8" /> +<link rel="author" href="mailto:masonf@chromium.org"> +<link rel="help" href="https://open-ui.org/components/interest-invokers.explainer/" /> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="/resources/testdriver.js"></script> +<script src="/resources/testdriver-actions.js"></script> +<script src="/resources/testdriver-vendor.js"></script> +<script src="resources/invoker-utils.js"></script> + +<button id=invoker interesttarget=target>Button</button> +<div id=target popover>Popover with <button id=contained>button</button></div> + +<style> + [interesttarget] { + interest-target-delay: 0s; + } +</style> + +<script> + let expectInterest; + let expectPartial; + let eventCount = 0; + function checkInterest(e) { + const notStr = expectInterest ? "" : "not "; + const event = e instanceof Event ? `${e.type} event` : e; + assert_equals(invoker.matches(':has-interest'),expectInterest,`target should ${notStr}gain interest (${event})`); + assert_equals(invoker.matches(':has-partial-interest'),expectInterest&&expectPartial,`Interest should ${expectPartial ? "" : "not "}be partial (${event})`); + assert_equals(target.matches(':popover-open'),expectInterest,`popover should ${notStr}be open (${event})`); + ++eventCount; + } + + promise_test(async function (t) { + invoker.addEventListener('focus',checkInterest); + target.addEventListener('interest',(e) => { + checkInterest(e); + expectInterest = true; + expectPartial = true; + }); + + expectInterest = false; + expectPartial = false; + await focusOn(invoker); + assert_equals(eventCount,2,'focus and interest should both have fired'); + assert_true(expectInterest,'the interest event should set this'); + checkInterest('before hot key'); + await sendShowInterestHotkey(); + expectPartial = false; + checkInterest('after hot key'); + },'Partial interest timing should not be observable'); +</script>
diff --git a/third_party/blink/web_tests/external/wpt/streams/readable-streams/crashtests/garbage-collection.any.js b/third_party/blink/web_tests/external/wpt/streams/readable-streams/crashtests/garbage-collection.any.js index cf10f7f..6e9d80c 100644 --- a/third_party/blink/web_tests/external/wpt/streams/readable-streams/crashtests/garbage-collection.any.js +++ b/third_party/blink/web_tests/external/wpt/streams/readable-streams/crashtests/garbage-collection.any.js
@@ -14,35 +14,6 @@ await garbageCollect(); }, 'Garbage-collecting a stream along with its reader should not crash'); - -// See https://crbug.com/390646657 for details. -promise_test(async () => { - const written = new WritableStream({ - write(chunk) { - return new Promise(resolve => {}); - } - }).getWriter().write('just nod if you can hear me'); - for (let i = 0; i < 5; ++i) - await garbageCollect(); -}, 'Garbage-collecting a stream writer with a pending write should not crash'); - - -promise_test(async () => { - const closed = new WritableStream({ - write(chunk) { } - }).getWriter().closed; - for (let i = 0; i < 5; ++i) - await garbageCollect(); -}, 'Garbage-collecting a stream writer should not crash with closed promise is retained'); - -promise_test(async () => { - const ready = new WritableStream({ - write(chunk) { } - }, {highWaterMark: 0}).getWriter().ready; - for (let i = 0; i < 5; ++i) - await garbageCollect(); -}, 'Garbage-collecting a stream writer should not crash when backpressure is being applied'); - promise_test(async () => { let reader = new ReadableStream({ pull() { }
diff --git a/third_party/blink/web_tests/external/wpt/streams/writable-streams/crashtests/garbage-collection.any.js b/third_party/blink/web_tests/external/wpt/streams/writable-streams/crashtests/garbage-collection.any.js new file mode 100644 index 0000000..a379688 --- /dev/null +++ b/third_party/blink/web_tests/external/wpt/streams/writable-streams/crashtests/garbage-collection.any.js
@@ -0,0 +1,43 @@ +// META: global=window,worker +// META: script=/common/gc.js +'use strict'; + +// See https://crbug.com/390646657 for details. +promise_test(async () => { + const written = new WritableStream({ + write(chunk) { + return new Promise(resolve => {}); + } + }).getWriter().write('just nod if you can hear me'); + for (let i = 0; i < 5; ++i) + await garbageCollect(); +}, 'Garbage-collecting a stream writer with a pending write should not crash'); + +promise_test(async () => { + const closed = new WritableStream({ + write(chunk) { } + }).getWriter().closed; + for (let i = 0; i < 5; ++i) + await garbageCollect(); +}, 'Garbage-collecting a stream writer should not crash with closed promise is retained'); + +promise_test(async () => { + let writer = new WritableStream({ + write(chunk) { return new Promise(resolve => {}); }, + close() { return new Promise(resolve => {}); } + }).getWriter(); + writer.write('is there anyone home?'); + writer.close(); + writer = null; + for (let i = 0; i < 5; ++i) + await garbageCollect(); +}, 'Garbage-collecting a stream writer should not crash with close promise pending'); + +promise_test(async () => { + const ready = new WritableStream({ + write(chunk) { } + }, {highWaterMark: 0}).getWriter().ready; + for (let i = 0; i < 5; ++i) + await garbageCollect(); +}, 'Garbage-collecting a stream writer should not crash when backpressure is being applied'); +
diff --git a/third_party/boringssl/src b/third_party/boringssl/src index 864a235..154bb58 160000 --- a/third_party/boringssl/src +++ b/third_party/boringssl/src
@@ -1 +1 @@ -Subproject commit 864a235afcf4d2575b1eab8de96fbf0d84f6cda9 +Subproject commit 154bb5860a47c160c3be09003908c64c1de11bff
diff --git a/third_party/chromite b/third_party/chromite index 96096c3..bf95ef3 160000 --- a/third_party/chromite +++ b/third_party/chromite
@@ -1 +1 @@ -Subproject commit 96096c3c83cbe537782e84dfc12981f146723f6a +Subproject commit bf95ef3e710a92303a9f6b450e85d27892d26c69
diff --git a/third_party/compiler-rt/src b/third_party/compiler-rt/src index 16c0e42..2fdf452 160000 --- a/third_party/compiler-rt/src +++ b/third_party/compiler-rt/src
@@ -1 +1 @@ -Subproject commit 16c0e42913a45357f0e557dcc0b7055129c78af6 +Subproject commit 2fdf452fd1b09f2e0bb9fdfe8a162c86cc70dbc2
diff --git a/third_party/crossbench b/third_party/crossbench index 73e0cfe..803813f 160000 --- a/third_party/crossbench +++ b/third_party/crossbench
@@ -1 +1 @@ -Subproject commit 73e0cfe22e2699bf14bedb489c0b39ba1bc03702 +Subproject commit 803813fe85b358fa2883c15cd4dec46098e1b4c3
diff --git a/third_party/dawn b/third_party/dawn index dfe3855..19a7153 160000 --- a/third_party/dawn +++ b/third_party/dawn
@@ -1 +1 @@ -Subproject commit dfe3855e5d0b5367a598e60674766ffa1c894c71 +Subproject commit 19a7153574fe7506f098586a12ac21edf0944849
diff --git a/third_party/lit/v3_0/BUILD.gn b/third_party/lit/v3_0/BUILD.gn index 2e4d492..843845e 100644 --- a/third_party/lit/v3_0/BUILD.gn +++ b/third_party/lit/v3_0/BUILD.gn
@@ -45,6 +45,7 @@ "//chrome/browser/resources/media_router/cast_feedback:build_ts", "//chrome/browser/resources/new_tab_footer:build_ts", "//chrome/browser/resources/new_tab_page:build_ts", + "//chrome/browser/resources/new_tab_shared:build_ts", "//chrome/browser/resources/omnibox_popup:build_ts", "//chrome/browser/resources/on_device_internals:build_ts", "//chrome/browser/resources/on_device_translation_internals:build_ts",
diff --git a/third_party/rust/base64/OWNERS b/third_party/rust/base64/OWNERS new file mode 100644 index 0000000..2e7c01b --- /dev/null +++ b/third_party/rust/base64/OWNERS
@@ -0,0 +1,3 @@ +# This encoding-focused crate has been brought into `chromium_crates_io` +# as a dependency of `//third_party/cloud_authenticator`. +file://third_party/cloud_authenticator/OWNERS
diff --git a/third_party/rust/bytes/OWNERS b/third_party/rust/bytes/OWNERS new file mode 100644 index 0000000..2e7c01b --- /dev/null +++ b/third_party/rust/bytes/OWNERS
@@ -0,0 +1,3 @@ +# This encoding-focused crate has been brought into `chromium_crates_io` +# as a dependency of `//third_party/cloud_authenticator`. +file://third_party/cloud_authenticator/OWNERS
diff --git a/third_party/rust/hex/OWNERS b/third_party/rust/hex/OWNERS new file mode 100644 index 0000000..2e7c01b --- /dev/null +++ b/third_party/rust/hex/OWNERS
@@ -0,0 +1,3 @@ +# This encoding-focused crate has been brought into `chromium_crates_io` +# as a dependency of `//third_party/cloud_authenticator`. +file://third_party/cloud_authenticator/OWNERS
diff --git a/third_party/rust/prost/OWNERS b/third_party/rust/prost/OWNERS new file mode 100644 index 0000000..2e7c01b --- /dev/null +++ b/third_party/rust/prost/OWNERS
@@ -0,0 +1,3 @@ +# This encoding-focused crate has been brought into `chromium_crates_io` +# as a dependency of `//third_party/cloud_authenticator`. +file://third_party/cloud_authenticator/OWNERS
diff --git a/third_party/skia b/third_party/skia index 9d53fce..ac2d5cee 160000 --- a/third_party/skia +++ b/third_party/skia
@@ -1 +1 @@ -Subproject commit 9d53fcefe244192b0057c3b4a41bff52c838799d +Subproject commit ac2d5ceecb588e3be2a798896b92d27c91ad6461
diff --git a/third_party/spirv-tools/src b/third_party/spirv-tools/src index e8864ed..f06e0f3 160000 --- a/third_party/spirv-tools/src +++ b/third_party/spirv-tools/src
@@ -1 +1 @@ -Subproject commit e8864edbebe9fb9872c6c95b2363b490c6105a15 +Subproject commit f06e0f3d2e5acfe4b14e714e4103dd1ccdb237e5
diff --git a/third_party/vulkan-deps b/third_party/vulkan-deps index 0b78635..9d02147 160000 --- a/third_party/vulkan-deps +++ b/third_party/vulkan-deps
@@ -1 +1 @@ -Subproject commit 0b7863549d960381696d3cd7485ac6a284122e15 +Subproject commit 9d021471f38e15ccd4d7087f4cf05292e6e671b0
diff --git a/tools/android/nullaway/find_unmarked_chrome_java.py b/tools/android/nullaway/find_unmarked_chrome_java.py new file mode 100755 index 0000000..629cea6 --- /dev/null +++ b/tools/android/nullaway/find_unmarked_chrome_java.py
@@ -0,0 +1,168 @@ +#!/usr/bin/env python3 +# Copyright 2025 The Chromium Authors +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +import argparse +import collections +import csv +import dataclasses +import logging +import os +import pathlib +import re +import sys + +_SRC_ROOT = pathlib.Path(__file__).parents[3] +sys.path.insert(1, str(_SRC_ROOT / 'build/android/gyp')) + +import check_for_missing_direct_deps + +_CHROME_JAVA_SOURCES = 'gen/chrome/android/chrome_java.sources' + +_PACKAGE_RE = re.compile(r'^package\s+(.*?)(;|\s*$)', flags=re.MULTILINE) +_STRIP_NESTED_RE = re.compile(r'\$.*') +_PACKAGE_FROM_NAME_RE = re.compile(r'(.*?)\.[A-Z]') + + +@dataclasses.dataclass(frozen=True) +class _JavaClass: + path: str + name: str + null_marked: bool + + +def _read_file(path): + return pathlib.Path(path).read_text() + + +def _analyze_java_file(path): + data = _read_file(path) + m = _PACKAGE_RE.search(data) + package = m.group(1) + name = os.path.splitext(os.path.basename(path))[0] + null_marked = '@NullMarked' in data or '@NullUnmarked' in data + return _JavaClass(path, f'{package}.{name}', null_marked) + + +def _package_from_name(clazz): + return _PACKAGE_FROM_NAME_RE.match(clazz).group(1) + + +def _create_dep_graph(): + # dict of class -> set(referenced classes) + class_graph = check_for_missing_direct_deps._ParseDepGraph( + 'obj/chrome/android/chrome_java.javac.jar') + + # Strip nested classes. + ret = collections.defaultdict(set) + for clazz, deps in class_graph.items(): + clazz = _STRIP_NESTED_RE.sub('', clazz) + ret[clazz].update(_STRIP_NESTED_RE.sub('', d) for d in deps) + return ret + + +def main(): + logging.basicConfig(format='%(message)s', level=logging.INFO) + parser = argparse.ArgumentParser() + parser.add_argument('--csv', action='store_true') + args = parser.parse_args() + + if not os.path.exists('args.gn'): + parser.error('Must be run from within output directory.') + sys.exit(1) + + all_paths = _read_file(_CHROME_JAVA_SOURCES).splitlines() + all_classes = [_analyze_java_file(p) for p in all_paths] + + already_marked = {c.name for c in all_classes if c.null_marked} + not_already_marked = [c.name for c in all_classes if not c.null_marked] + + logging.info('Marked: %d', len(already_marked)) + logging.info('Unmarked: %d', len(not_already_marked)) + + # Find packages that reference only annotated other packages. + dep_graph = _create_dep_graph() + + names_to_class = {x.name: x for x in all_classes} + + # class name -> set(class names they depend on that are in chrome_java) + deps_by_name = collections.defaultdict(set) + for name in not_already_marked: + deps_by_name[name].update(c for c in dep_graph.get(name, []) + if c != name and c in names_to_class) + + # Sort tuples of name -> deps by class name to try and keep them clustered. + unmarked_items = sorted(x for x in deps_by_name.items() + if x[0] not in already_marked) + current_unblocked = [ + x for x in unmarked_items if all(d in already_marked for d in x[1]) + ] + id_set = {id(x) for x in current_unblocked} + still_blocked = [x for x in unmarked_items if id(x) not in id_set] + logging.info('Initially unblocked: %d', len(current_unblocked)) + + # Keep appending classes if all deps are going to be annotated before them. + future_marked = set(already_marked) + future_marked.update(x[0] for x in current_unblocked) + future_unblocked = [] + for i in range(20): + newly_unblocked = [ + x for x in still_blocked if all(d in future_marked for d in x[1]) + ] + logging.info('Unblocked in round %d: %d', i, len(newly_unblocked)) + if not newly_unblocked: + # No more classes where all deps are unblocked (circular deps). + break + future_unblocked.extend(newly_unblocked) + future_marked.update(x[0] for x in newly_unblocked) + still_blocked = [x for x in still_blocked if x[0] not in future_marked] + + logging.info('Future unblocked: %d', len(future_unblocked)) + + # Filter to just blocked deps. + still_blocked = [(c, sorted(d for d in deps if d not in future_marked)) + for c, deps in still_blocked] + # Sort by smallest number of blocked deps. + still_blocked.sort(key=lambda x: len(x[1])) + logging.info('Classes with circular deps: %d', len(still_blocked)) + + # TODO(agrieve): Try and find clusters of circuclar within still_blocked. + # E.g. Sort by len(unique(deps + deps_of_deps + deps_of_deps_of_deps)) + # Then just try and add the first file and all recursive deps until all + # files are seen. + + if args.csv: + writer = csv.writer(sys.stdout) + writer.writerow(('Path', 'Num Deps', 'Has Ciruclar Dep')) + for name, _ in current_unblocked: + clazz = names_to_class[name] + writer.writerow((clazz.path.lstrip('/.'), 0, 'No')) + for name, deps in future_unblocked: + clazz = names_to_class[name] + deps = [x for x in deps if x not in already_marked] + writer.writerow((clazz.path.lstrip('/.'), len(deps), 'No')) + for name, ciruclar_deps in still_blocked: + clazz = names_to_class[name] + writer.writerow( + (clazz.path.lstrip('/.'), len(ciruclar_deps), 'Yes')) + return + + print('Already Unblocked:') + for name, deps in current_unblocked: + print(name, len(deps)) + + print('Future Unblocked:') + for name, deps in future_unblocked: + deps_not_already_null_marked = [ + x for x in deps if x not in already_marked + ] + print(name, len(deps), len(deps_not_already_null_marked)) + + print('Circular Deps:') + for name, deps in still_blocked: + print(name, len(deps)) + + +if __name__ == '__main__': + main()
diff --git a/tools/android/nullaway/run_annotator_on_chrome_java.py b/tools/android/nullaway/run_annotator_on_chrome_java.py index ee7e4e1..aa40bee2 100755 --- a/tools/android/nullaway/run_annotator_on_chrome_java.py +++ b/tools/android/nullaway/run_annotator_on_chrome_java.py
@@ -46,18 +46,12 @@ return action_helpers.parse_gn_list(value) -def main(): - logging.basicConfig(format='%(message)s', level=logging.INFO) - parser = argparse.ArgumentParser() - parser.add_argument('--loud', - action='store_true', - help='Print compiler while annotating output') - args = parser.parse_args() - +def prep_errorprone_run(enable_annotator, parser): + if enable_annotator: + if not os.path.exists(_ANNOTATOR_JAR): + parser.error('Annotator .jar not found. Follow steps to build it.') if not os.path.exists('args.gn'): parser.error('Must be run from within output directory.') - if not os.path.exists(_ANNOTATOR_JAR): - parser.error('Annotator .jar not found. Follow steps to build it.') if not os.path.exists(_CHROME_JAVA_TURBINE_JAR): parser.error('Run "autoninja chrome/android:chrome_java" first.') @@ -67,10 +61,6 @@ java_files = [p for p in java_files if '@NullMarked' in _read_file(p)] sources_path = 'null-away-chrome-java-sources.txt' _write_file(sources_path, '\n'.join(java_files)) - logging.info( - 'Running annotator over %d @NullMarked files within chrome_java', - len(java_files)) - logging.info('This will probably take 3-5 minutes 🐢🐢🐢') classpath = [ 'obj/third_party/android_sdk/android_sdk_java.ijar.jar', @@ -83,7 +73,8 @@ processor_path = _read_build_config_value( 'gen/tools/android/errorprone_plugin/errorprone_plugin.build_config.json', 'classpath') - processor_path.append(_ANNOTATOR_JAR) + if enable_annotator: + processor_path.append(_ANNOTATOR_JAR) contract_annotations = [ 'org.chromium.build.annotations.Contract', @@ -103,7 +94,6 @@ '-Xplugin:ErrorProne', '-XepDisableAllChecks', '-Xep:NullAway:ERROR', - '-Xep:AnnotatorScanner:ERROR', '-XepOpt:NullAway:OnlyNullMarked', '-XepOpt:NullAway:CustomContractAnnotations=' + ','.join(contract_annotations), @@ -113,10 +103,15 @@ '-XepOpt:Nullaway:AcknowledgeAndroidRecent=true', '-XepOpt:NullAway:JSpecifyMode=true', '-XepOpt:NullAway:KnownInitializers=' + ','.join(init_methods), - '-XepOpt:AnnotatorScanner:ConfigPath=../nullaway_scanner.xml', - '-XepOpt:NullAway:SerializeFixMetadata=true', - '-XepOpt:NullAway:FixSerializationConfigPath=../nullaway_config.xml', ] + if enable_annotator: + errorprone_args += [ + '-XepOpt:NullAway:SerializeFixMetadata=true', + '-XepOpt:NullAway:FixSerializationConfigPath=../nullaway_config.xml', + '-Xep:AnnotatorScanner:ERROR', + '-XepOpt:AnnotatorScanner:ConfigPath=../nullaway_scanner.xml', + ] + javac_cmd = [ '../../third_party/jdk/current/bin/javac', '-g', '-parameters', '--release', '17', '-encoding', 'UTF-8', '-sourcepath', ':', @@ -139,6 +134,22 @@ f'@{sources_path}' ] + return java_files, javac_cmd + + +def main(): + logging.basicConfig(format='%(message)s', level=logging.INFO) + parser = argparse.ArgumentParser() + parser.add_argument('--loud', + action='store_true', + help='Print compiler while annotating output') + args = parser.parse_args() + + java_files, javac_cmd = prep_errorprone_run(True, parser) + logging.info('Running annotator over %d @NullMarked files in chrome_java', + len(java_files)) + logging.info('This will probably take 3-5 minutes 🐢🐢🐢') + outdir = os.path.abspath('annotator-out') if os.path.exists(outdir): shutil.rmtree(outdir) @@ -168,7 +179,7 @@ cmd += ['--redirect-build-output-stderr'] result = subprocess.run(cmd).returncode - logging.warning('🪵 Error Prone output (find warnings here):\n%s', + logging.warning('🪵 Error Prone output (find warnings here):\n%s\n', os.path.abspath(compile_logs)) if result:
diff --git a/tools/android/nullaway/run_nullaway_on_chrome_java.py b/tools/android/nullaway/run_nullaway_on_chrome_java.py new file mode 100755 index 0000000..14027e76 --- /dev/null +++ b/tools/android/nullaway/run_nullaway_on_chrome_java.py
@@ -0,0 +1,26 @@ +#!/usr/bin/env python3 +# Copyright 2025 The Chromium Authors +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +import argparse +import logging +import os +import subprocess +import sys + +import run_annotator_on_chrome_java + + +def main(): + logging.basicConfig(format='%(message)s', level=logging.INFO) + parser = argparse.ArgumentParser() + java_files, javac_cmd = run_annotator_on_chrome_java.prep_errorprone_run( + False, parser) + logging.info('Running NullAway on %d @NullMarked files in chrome_java', + len(java_files)) + sys.exit(subprocess.run(javac_cmd).returncode) + + +if __name__ == '__main__': + main()
diff --git a/tools/crates/gnrt/lib/condition.rs b/tools/crates/gnrt/lib/condition.rs index 5a417711..19cebdb0d 100644 --- a/tools/crates/gnrt/lib/condition.rs +++ b/tools/crates/gnrt/lib/condition.rs
@@ -281,6 +281,8 @@ RustTargetArch::Riscv64 => "current_cpu == \"riscv64\"", RustTargetArch::X86 => "current_cpu == \"x86\"", RustTargetArch::X8664 => "current_cpu == \"x64\"", + RustTargetArch::Powerpc64 => "(current_cpu == \"ppc64le\")", + RustTargetArch::S390x => "(current_cpu == \"s390x\")", } }
diff --git a/tools/gritsettings/resource_ids.spec b/tools/gritsettings/resource_ids.spec index b238a0f..9e045a9 100644 --- a/tools/gritsettings/resource_ids.spec +++ b/tools/gritsettings/resource_ids.spec
@@ -516,6 +516,10 @@ "META": {"sizes": {"includes": [10]}}, "includes": [4440], }, + "<(SHARED_INTERMEDIATE_DIR)/chrome/browser/resources/new_tab_shared/resources.grd": { + "META": {"sizes": {"includes": [10]}}, + "includes": [4450], + }, "<(SHARED_INTERMEDIATE_DIR)/chrome/browser/resources/omnibox/resources.grd": { "META": {"sizes": {"includes": [30]}}, "includes": [4460],
diff --git a/tools/mb/mb_config.pyl b/tools/mb/mb_config.pyl index 09f0f274..44ca683 100644 --- a/tools/mb/mb_config.pyl +++ b/tools/mb/mb_config.pyl
@@ -996,7 +996,7 @@ }, 'orderfile': { - 'mixins': ['no_proguard_obfuscation', 'devtools_instrumentation_dumping', 'order_profiling'], + 'mixins': ['no_proguard_obfuscation', 'devtools_instrumentation_dumping', 'order_profiling', 'pgo_profile_override'], }, 'ozone_headless': { @@ -1013,6 +1013,10 @@ 'gn_args': 'chrome_pgo_phase=1' }, + 'pgo_profile_override': { + 'args_file': '//tools/pgo/override_args.gni', + }, + # Note: This is probably not what you want to use. Instead use one of the # chrome_with_codecs or chromeos_with_codecs mixins. 'proprietary_codecs': {
diff --git a/tools/mb/mb_config_expectations/chrome.orderfile.json b/tools/mb/mb_config_expectations/chrome.orderfile.json index 7a467fcd..b7b6072 100644 --- a/tools/mb/mb_config_expectations/chrome.orderfile.json +++ b/tools/mb/mb_config_expectations/chrome.orderfile.json
@@ -1,5 +1,6 @@ { "android-arm32-orderfile": { + "args_file": "//tools/pgo/override_args.gni", "gn_args": { "debuggable_apks": false, "devtools_instrumentation_dumping": true, @@ -18,6 +19,7 @@ } }, "android-arm64-orderfile": { + "args_file": "//tools/pgo/override_args.gni", "gn_args": { "debuggable_apks": false, "devtools_instrumentation_dumping": true,
diff --git a/tools/mb/mb_config_expectations/tryserver.chrome.orderfile.json b/tools/mb/mb_config_expectations/tryserver.chrome.orderfile.json index 7a467fcd..b7b6072 100644 --- a/tools/mb/mb_config_expectations/tryserver.chrome.orderfile.json +++ b/tools/mb/mb_config_expectations/tryserver.chrome.orderfile.json
@@ -1,5 +1,6 @@ { "android-arm32-orderfile": { + "args_file": "//tools/pgo/override_args.gni", "gn_args": { "debuggable_apks": false, "devtools_instrumentation_dumping": true, @@ -18,6 +19,7 @@ } }, "android-arm64-orderfile": { + "args_file": "//tools/pgo/override_args.gni", "gn_args": { "debuggable_apks": false, "devtools_instrumentation_dumping": true,
diff --git a/tools/metrics/histograms/enums.xml b/tools/metrics/histograms/enums.xml index 2dd3772..9f32cf5 100644 --- a/tools/metrics/histograms/enums.xml +++ b/tools/metrics/histograms/enums.xml
@@ -12184,6 +12184,7 @@ <int value="-705464246" label="ChromeCartDomBasedHeuristics:enabled"/> <int value="-705212373" label="IncognitoNtpRevamp:disabled"/> <int value="-704459022" label="ContinuousSearch:enabled"/> + <int value="-704335590" label="TreesInViz:disabled"/> <int value="-704232562" label="UseMonitorColorSpace:enabled"/> <int value="-704105887" label="(Obsolete) EnforceAshExtensionKeeplist:enabled"/> @@ -13423,6 +13424,7 @@ <int value="-229995076" label="AltClickAndSixPackCustomization:disabled"/> <int value="-228933177" label="ToastFramework:disabled"/> <int value="-228847300" label="OmniboxLimitKeywordModeSuggestions:disabled"/> + <int value="-227487160" label="TreesInViz:enabled"/> <int value="-227261557" label="CrosSodaConchLanguages:enabled"/> <int value="-226826278" label="ForceEnableFastCheckoutCapabilities:disabled"/> <int value="-226715863" label="SHA1ServerSignature:enabled"/> @@ -13902,6 +13904,7 @@ <int value="-43650386" label="WindowsFollowCursor:enabled"/> <int value="-43566818" label="WebRtcApmDownmixCaptureAudioMethod:disabled"/> <int value="-43428597" label="ClickToCallDetectionV2:enabled"/> + <int value="-42707046" label="TouchDragAndDrop:enabled"/> <int value="-42175674" label="ShareUsageRankingFixedMore:disabled"/> <int value="-41548966" label="AutocorrectUseReplaceSurroundingText:disabled"/> <int value="-41460305" label="InfobarScrollOptimization:disabled"/> @@ -14432,6 +14435,7 @@ <int value="155977192" label="EnableFileManagerFormatDialog:disabled"/> <int value="156285060" label="UseMultipleOverlays:disabled"/> <int value="156345797" label="BorealisZinkGlDriver:enabled"/> + <int value="156657549" label="DiscountAutofill:enabled"/> <int value="157217034" label="enable-tab-for-desktop-share"/> <int value="157318016" label="AutomaticTabDiscarding:enabled"/> <int value="158490417" label="PhoneHubCameraRoll:enabled"/> @@ -17751,6 +17755,7 @@ <int value="1425103276" label="SharedHighlightingV2:enabled"/> <int value="1425425055" label="WiFiDirect:disabled"/> <int value="1426588012" label="WebXRLayers:enabled"/> + <int value="1427040726" label="DiscountAutofill:disabled"/> <int value="1427427985" label="CacheControlNoStoreEnterBackForwardCache:enabled"/> <int value="1427683927" @@ -19471,6 +19476,7 @@ <int value="2058283872" label="CCTModuleCache:disabled"/> <int value="2058425230" label="LauncherNudgeSessionReset:enabled"/> <int value="2058439723" label="CalculateNativeWinOcclusion:disabled"/> + <int value="2058894797" label="TouchDragAndDrop:disabled"/> <int value="2058921256" label="LeftClickOpensTabGroupBubble:enabled"/> <int value="2059322877" label="new-avatar-menu"/> <int value="2060099975" label="AndroidBottomToolbar:enabled"/>
diff --git a/tools/metrics/histograms/metadata/autofill/enums.xml b/tools/metrics/histograms/metadata/autofill/enums.xml index 4564dc1b..cd53042 100644 --- a/tools/metrics/histograms/metadata/autofill/enums.xml +++ b/tools/metrics/histograms/metadata/autofill/enums.xml
@@ -2674,9 +2674,19 @@ </enum> <enum name="AutofillSilentUpdatesProfileImportType"> - <int value="0" label="Unspecified"/> - <int value="1" label="SilentUpdateForIncompleteProfile"/> - <int value="2" label="UnusableIncompleteProfile"/> + <int value="0" label="ImportTypeUnspecified"/> + <int value="1" label="NewProfile"/> + <int value="2" label="DuplicateImport"/> + <int value="3" label="SilentUpdate"/> + <int value="4" label="ConfirmableMerge"/> + <int value="5" label="SuppressedNewProfile"/> + <int value="6" label="ConfirmableMergeAndSilentUpdate"/> + <int value="7" label="SuppressedConfirmableMerge"/> + <int value="8" label="SuppressedConfirmableMergeAndSilentUpdate"/> + <int value="9" label="SilentUpdateForIncompleteProfile"/> + <int value="10" label="UnusableIncompleteProfile"/> + <int value="11" label="ProfileMigration"/> + <int value="12" label="ProfileMigrationAndSilentUpdate"/> </enum> <enum name="AutofillSingleEntryRemovalMethod">
diff --git a/tools/metrics/histograms/metadata/blink/enums.xml b/tools/metrics/histograms/metadata/blink/enums.xml index 59a462d..ac59ea0a 100644 --- a/tools/metrics/histograms/metadata/blink/enums.xml +++ b/tools/metrics/histograms/metadata/blink/enums.xml
@@ -6180,6 +6180,7 @@ <int value="5553" label="EditContextTextFormatUpdateFireEvent"/> <int value="5554" label="EditContextTextFormatUpdateTextFormatThicknessOrStyleNotNone"/> + <int value="5555" label="C2PAManifest"/> </enum> <!-- LINT.ThenChange(//third_party/blink/public/mojom/use_counter/metrics/web_feature.mojom:WebFeature) --> @@ -8196,7 +8197,7 @@ <int value="331" label="TextUnderlineOffset"/> <int value="332" label="WindowManagement"/> <int value="333" label="Requestclose"/> - <int value="334" label="DRAFT_Uint8ArrayToFromBase64AndHex"/> + <int value="334" label="Uint8arrayBase64Hex"/> <int value="335" label="LineBreak"/> </enum>
diff --git a/tools/metrics/histograms/metadata/bookmarks/enums.xml b/tools/metrics/histograms/metadata/bookmarks/enums.xml index 9247c73..b5ba6cd 100644 --- a/tools/metrics/histograms/metadata/bookmarks/enums.xml +++ b/tools/metrics/histograms/metadata/bookmarks/enums.xml
@@ -110,6 +110,7 @@ <int value="20" label="Help center"/> <int value="21" label="Open bookmark (via double-click / enter)"/> <int value="22" label="Open folder (via double-click / enter)"/> + <int value="23" label="Open in split view"/> </enum> <!-- LINT.IfChange(BookmarkReorderDropTarget) -->
diff --git a/tools/metrics/histograms/metadata/gpu/histograms.xml b/tools/metrics/histograms/metadata/gpu/histograms.xml index 9184ea2..84fa325 100644 --- a/tools/metrics/histograms/metadata/gpu/histograms.xml +++ b/tools/metrics/histograms/metadata/gpu/histograms.xml
@@ -1864,7 +1864,7 @@ </histogram> <histogram name="Viz.FileDescriptorTracking.TimeToCompute" units="microseconds" - expires_after="2025-06-09"> + expires_after="2026-06-09"> <owner>petermcneeley@chromium.org</owner> <owner>chrome-gpu-metric-alerts@chromium.org</owner> <summary> @@ -1878,7 +1878,7 @@ </histogram> <histogram name="Viz.FileDescriptorTracking.{FdStat}" units="units" - expires_after="2025-06-09"> + expires_after="2026-06-09"> <owner>petermcneeley@chromium.org</owner> <owner>chrome-gpu-metric-alerts@chromium.org</owner> <summary>
diff --git a/tools/metrics/histograms/metadata/media/histograms.xml b/tools/metrics/histograms/metadata/media/histograms.xml index 1020252..560b905 100644 --- a/tools/metrics/histograms/metadata/media/histograms.xml +++ b/tools/metrics/histograms/metadata/media/histograms.xml
@@ -6193,7 +6193,7 @@ <histogram name="Media.Video.Capture.Mac.ScreenCaptureSystemPermission" enum="BooleanAllowed" expires_after="2025-07-27"> - <owner>bur@chromium.org</owner> + <owner>avi@chromium.org</owner> <owner>mark@chromium.org</owner> <summary> The Mac system permission state for screen capture. Logged once at browser
diff --git a/tools/metrics/histograms/metadata/session/histograms.xml b/tools/metrics/histograms/metadata/session/histograms.xml index 600a0b9..02f9b0e 100644 --- a/tools/metrics/histograms/metadata/session/histograms.xml +++ b/tools/metrics/histograms/metadata/session/histograms.xml
@@ -50,7 +50,7 @@ <histogram name="Session.BrowserFullscreen.DurationUpTo24H" units="ms" expires_after="2025-09-14"> - <owner>bur@chromium.org</owner> + <owner>avi@chromium.org</owner> <owner>chrome-mac-dev@google.com</owner> <summary> The total time a Chrome browser window spends in fullscreen. Content
diff --git a/tools/metrics/histograms/metadata/web_audio/histograms.xml b/tools/metrics/histograms/metadata/web_audio/histograms.xml index 752dae10..addc1fe0 100644 --- a/tools/metrics/histograms/metadata/web_audio/histograms.xml +++ b/tools/metrics/histograms/metadata/web_audio/histograms.xml
@@ -291,7 +291,7 @@ <histogram name="WebAudio.AudioDestination.{ModeTag}.RenderTimeRatio{LatencyTag}" - units="ms" expires_after="2025-10-05"> + units="%" expires_after="2025-10-05"> <owner>mjwilson@chromium.org</owner> <owner>hongchan@chromium.org</owner> <owner>src/third_party/blink/renderer/modules/webaudio/OWNERS</owner> @@ -305,7 +305,7 @@ <histogram name="WebAudio.AudioDestination.{ModeTag}.RequestRenderGapTimeRatio{LatencyTag}" - units="ms" expires_after="2025-10-05"> + units="%" expires_after="2025-10-05"> <owner>mjwilson@chromium.org</owner> <owner>hongchan@chromium.org</owner> <owner>src/third_party/blink/renderer/modules/webaudio/OWNERS</owner> @@ -320,7 +320,7 @@ <histogram name="WebAudio.AudioDestination.{ModeTag}.RequestRenderTimeRatio{LatencyTag}" - units="ms" expires_after="2025-10-05"> + units="%" expires_after="2025-10-05"> <owner>mjwilson@chromium.org</owner> <owner>hongchan@chromium.org</owner> <owner>src/third_party/blink/renderer/modules/webaudio/OWNERS</owner>
diff --git a/tools/metrics/histograms/metadata/webauthn/enums.xml b/tools/metrics/histograms/metadata/webauthn/enums.xml index 9acfa0e..be28a88d 100644 --- a/tools/metrics/histograms/metadata/webauthn/enums.xml +++ b/tools/metrics/histograms/metadata/webauthn/enums.xml
@@ -300,9 +300,17 @@ Page requested a new WebAuthn credential using the navigator.credentials.create API. </int> + <int value="2" label="GetResolvedGpm"> + WebAuthn credential get request was successfully resolved with Google + Password Manager as an authenticator. + </int> + <int value="3" label="GetResolvedNonGpm"> + WebAuthn credential get request was successfully resolved with different + authenticator than Google Password Manager. + </int> </enum> -<!-- LINT.ThenChange(//components/webauthn/ios/passkey_java_script_feature.mm) --> +<!-- LINT.ThenChange(//components/webauthn/ios/passkey_tab_helper.mm) --> <enum name="WebAuthenticationMacOSPasskeysPermission"> <int value="0" label="Requested permission during a create() call"/>
diff --git a/tools/perf/core/perfetto_binary_roller/binary_deps.json b/tools/perf/core/perfetto_binary_roller/binary_deps.json index 280108e..59327112 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": "6cdfc3b341633fb60786e2a45516e59bb0f2ca8e", - "full_remote_path": "chromium-telemetry/perfetto_binaries/trace_processor_shell/win/f4cde1b052fd591a5c50fe11dbcda61c51c4cccf/trace_processor_shell.exe" + "hash": "b3b39240afb506e27ef5939231c01b089d208e2d", + "full_remote_path": "chromium-telemetry/perfetto_binaries/trace_processor_shell/win/e2d7750acd15d57c6c88e3c8fb8c5a45f424ba96/trace_processor_shell.exe" }, "linux_arm": { "hash": "99f971ca131f6d11c73f4b918099d434bdd8093c",
diff --git a/tools/pgo/override_args.gni b/tools/pgo/override_args.gni new file mode 100644 index 0000000..2619fcf --- /dev/null +++ b/tools/pgo/override_args.gni
@@ -0,0 +1,6 @@ +# This is intended to be used on bots, but these args work locally too. + +# As an example, this file can contain the following (uncommented): +#pgo_override_filename = "chrome-android64-main-1746631188-f62ce3c6980019c1d1229c672a11ade92b49238c-9158358aef7fc7a0f62b5f98b94c2f09b8e14b32.profdata" +#pgo_gs_bucket = "chrome-pgo-trybot-profiles" +#pgo_gs_bucket_path = "pgo_profiles"
diff --git a/tools/typescript/definitions/bookmark_manager_private.d.ts b/tools/typescript/definitions/bookmark_manager_private.d.ts index 9aa0f18..2579edd4 100644 --- a/tools/typescript/definitions/bookmark_manager_private.d.ts +++ b/tools/typescript/definitions/bookmark_manager_private.d.ts
@@ -23,6 +23,11 @@ elements: BookmarkNodeDataElement[]; } + interface OpenInNewTabParams { + active?: boolean; + split?: boolean; + } + export function copy(idList: string[]): Promise<void>; export function cut(idList: string[]): Promise<void>; export function paste(parentId: string, selectedIdList?: string[]): @@ -38,7 +43,8 @@ export function removeTrees(idList: string[]): Promise<void>; export function undo(): void; export function redo(): void; - export function openInNewTab(id: string, active: boolean): void; + export function openInNewTab( + id: string, params?: OpenInNewTabParams): void; export function openInNewWindow(idList: string[], incognito: boolean): void; export function openInNewTabGroup(idList: string[]): void;
diff --git a/ui/android/event_forwarder.cc b/ui/android/event_forwarder.cc index 6d72b93..e6273d1f 100644 --- a/ui/android/event_forwarder.cc +++ b/ui/android/event_forwarder.cc
@@ -10,7 +10,7 @@ #include "base/tracing/protos/chrome_track_event.pbzero.h" #include "ui/android/ui_android_features.h" #include "ui/android/window_android.h" -#include "ui/base/ui_base_switches_util.h" +#include "ui/base/ui_base_features.h" #include "ui/events/android/drag_event_android.h" #include "ui/events/android/gesture_event_android.h" #include "ui/events/android/gesture_event_type.h" @@ -45,7 +45,7 @@ JNIEnv* env = jni_zero::AttachCurrentThread(); java_obj_.Reset( Java_EventForwarder_create(env, reinterpret_cast<intptr_t>(this), - switches::IsTouchDragDropEnabled())); + features::IsTouchDragAndDropEnabled())); } return ScopedJavaLocalRef<jobject>(java_obj_); }
diff --git a/ui/base/cocoa/OWNERS b/ui/base/cocoa/OWNERS index 03d42f4..c91f2277 100644 --- a/ui/base/cocoa/OWNERS +++ b/ui/base/cocoa/OWNERS
@@ -3,7 +3,6 @@ # other stricter requirements. avi@chromium.org -bur@google.com lgrey@chromium.org mark@chromium.org shrike@chromium.org
diff --git a/ui/base/ui_base_features.cc b/ui/base/ui_base_features.cc index 4633086..3f35ae4 100644 --- a/ui/base/ui_base_features.cc +++ b/ui/base/ui_base_features.cc
@@ -286,6 +286,22 @@ return base::FeatureList::IsEnabled(kTouchTextEditingRedesign); } +// This feature enables drag and drop using touch input devices. +BASE_FEATURE(kTouchDragAndDrop, + "TouchDragAndDrop", +#if BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_ANDROID) + base::FEATURE_ENABLED_BY_DEFAULT +#else + base::FEATURE_DISABLED_BY_DEFAULT +#endif +); + +bool IsTouchDragAndDropEnabled() { + static const bool touch_drag_and_drop_enabled = + base::FeatureList::IsEnabled(kTouchDragAndDrop); + return touch_drag_and_drop_enabled; +} + // Enables forced colors mode for web content. BASE_FEATURE(kForcedColors, "ForcedColors", base::FEATURE_ENABLED_BY_DEFAULT);
diff --git a/ui/base/ui_base_features.h b/ui/base/ui_base_features.h index 1cd0cc8..2c8943f6 100644 --- a/ui/base/ui_base_features.h +++ b/ui/base/ui_base_features.h
@@ -68,6 +68,9 @@ COMPONENT_EXPORT(UI_BASE_FEATURES) bool IsTouchTextEditingRedesignEnabled(); +COMPONENT_EXPORT(UI_BASE_FEATURES) BASE_DECLARE_FEATURE(kTouchDragAndDrop); +COMPONENT_EXPORT(UI_BASE_FEATURES) bool IsTouchDragAndDropEnabled(); + // Used to enable forced colors mode for web content. COMPONENT_EXPORT(UI_BASE_FEATURES) BASE_DECLARE_FEATURE(kForcedColors); COMPONENT_EXPORT(UI_BASE_FEATURES) bool IsForcedColorsEnabled();
diff --git a/ui/base/ui_base_switches.cc b/ui/base/ui_base_switches.cc index 2aeb8892..e7ffbbf 100644 --- a/ui/base/ui_base_switches.cc +++ b/ui/base/ui_base_switches.cc
@@ -41,9 +41,6 @@ const char kDisableGtkIme[] = "disable-gtk-ime"; #endif -// Disables touch event based drag and drop. -const char kDisableTouchDragDrop[] = "disable-touch-drag-drop"; - // Disable re-use of non-exact resources to fulfill ResourcePool requests. // Intended only for use in layout or pixel tests to reduce noise. const char kDisallowNonExactResourceReuse[] = @@ -53,9 +50,6 @@ const char kDRMVirtualConnectorIsExternal[] = "drm-virtual-connector-is-external"; -// Enables touch event based drag and drop. -const char kEnableTouchDragDrop[] = "enable-touch-drag-drop"; - // Forces the caption style for WebVTT captions. const char kForceCaptionStyle[] = "force-caption-style";
diff --git a/ui/base/ui_base_switches_util.cc b/ui/base/ui_base_switches_util.cc index c03a219..c7df1e1 100644 --- a/ui/base/ui_base_switches_util.cc +++ b/ui/base/ui_base_switches_util.cc
@@ -33,13 +33,4 @@ #endif } -bool IsTouchDragDropEnabled() { - const auto* const command_line = base::CommandLine::ForCurrentProcess(); -#if BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_ANDROID) - return !command_line->HasSwitch(kDisableTouchDragDrop); -#else - return command_line->HasSwitch(kEnableTouchDragDrop); -#endif -} - } // namespace switches
diff --git a/ui/base/ui_base_switches_util.h b/ui/base/ui_base_switches_util.h index ff0ed4c6..d8263da 100644 --- a/ui/base/ui_base_switches_util.h +++ b/ui/base/ui_base_switches_util.h
@@ -10,7 +10,6 @@ namespace switches { COMPONENT_EXPORT(UI_BASE) bool IsElasticOverscrollEnabled(); -COMPONENT_EXPORT(UI_BASE) bool IsTouchDragDropEnabled(); } // namespace switches
diff --git a/ui/views/controls/textfield/textfield.cc b/ui/views/controls/textfield/textfield.cc index 971a4ea5..c4ef291 100644 --- a/ui/views/controls/textfield/textfield.cc +++ b/ui/views/controls/textfield/textfield.cc
@@ -36,8 +36,6 @@ #include "ui/base/mojom/menu_source_type.mojom.h" #include "ui/base/resource/resource_bundle.h" #include "ui/base/ui_base_features.h" -#include "ui/base/ui_base_switches.h" -#include "ui/base/ui_base_switches_util.h" #include "ui/color/color_id.h" #include "ui/color/color_provider.h" #include "ui/compositor/canvas_painter.h" @@ -882,7 +880,7 @@ // handle drag-drop or context menu. DestroyTouchSelection(); StopSelectionDragging(); - initiating_drag_ = switches::IsTouchDragDropEnabled(); + initiating_drag_ = ::features::IsTouchDragAndDropEnabled(); break; } else { // If long-press happens outside selection, select word and try to
diff --git a/ui/views/controls/textfield/textfield_unittest.cc b/ui/views/controls/textfield/textfield_unittest.cc index f03d3cf..db57da9 100644 --- a/ui/views/controls/textfield/textfield_unittest.cc +++ b/ui/views/controls/textfield/textfield_unittest.cc
@@ -3763,6 +3763,10 @@ // Long_Press gesture in Textfield can initiate a drag and drop now. TEST_F(TextfieldTest, TestLongPressInitiatesDragDrop) { + // Enable touch-drag-drop to make long press effective. + base::test::ScopedFeatureList feature_list; + feature_list.InitAndEnableFeature(::features::kTouchDragAndDrop); + InitTextfield(); textfield_->SetText(u"Hello string world"); @@ -3770,10 +3774,6 @@ textfield_->SetSelectedRange(gfx::Range(6, 12)); const gfx::Point kStringPoint(GetCursorPositionX(9), GetCursorYForTesting()); - // Enable touch-drag-drop to make long press effective. - base::CommandLine::ForCurrentProcess()->AppendSwitch( - switches::kEnableTouchDragDrop); - // Create a long press event in the selected region should start a drag. ui::GestureEvent long_press = CreateTestGestureEvent( kStringPoint.x(), kStringPoint.y(),
diff --git a/ui/views/widget/root_view.cc b/ui/views/widget/root_view.cc index 9aeb23b..e9c418a 100644 --- a/ui/views/widget/root_view.cc +++ b/ui/views/widget/root_view.cc
@@ -22,7 +22,7 @@ #include "ui/base/metadata/metadata_header_macros.h" #include "ui/base/metadata/metadata_impl_macros.h" #include "ui/base/mojom/menu_source_type.mojom.h" -#include "ui/base/ui_base_switches_util.h" +#include "ui/base/ui_base_features.h" #include "ui/compositor/layer.h" #include "ui/events/event.h" #include "ui/events/event_utils.h" @@ -251,7 +251,7 @@ class PostEventDispatchHandler : public ui::EventHandler { public: PostEventDispatchHandler() - : touch_dnd_enabled_(::switches::IsTouchDragDropEnabled()) {} + : touch_dnd_enabled_(::features::IsTouchDragAndDropEnabled()) {} PostEventDispatchHandler(const PostEventDispatchHandler&) = delete; PostEventDispatchHandler& operator=(const PostEventDispatchHandler&) = delete; ~PostEventDispatchHandler() override = default;
diff --git a/ui/views/window/dialog_delegate.h b/ui/views/window/dialog_delegate.h index f53ee492..8005433 100644 --- a/ui/views/window/dialog_delegate.h +++ b/ui/views/window/dialog_delegate.h
@@ -117,7 +117,7 @@ class BnplTosDialog; class CardUnmaskOtpInputDialogViews; class EditAddressProfileView; -class SaveAndFillDialogViews; +class SaveAndFillDialog; class WebauthnDialogView; namespace payments { @@ -754,7 +754,7 @@ friend class ::autofill::BnplTosDialog; friend class ::autofill::CardUnmaskOtpInputDialogViews; friend class ::autofill::EditAddressProfileView; - friend class ::autofill::SaveAndFillDialogViews; + friend class ::autofill::SaveAndFillDialog; friend class ::autofill::WebauthnDialogView; friend class ::autofill::payments::PaymentsWindowUserConsentDialogView; friend class ::autofill::payments::SelectBnplIssuerDialog;
diff --git a/v8 b/v8 index bbcee50..a1ba74a 160000 --- a/v8 +++ b/v8
@@ -1 +1 @@ -Subproject commit bbcee50bb6db5243a0eac31ac5a4453a91239d98 +Subproject commit a1ba74a85fdfad2d8eac3857059676bc6e0fb686