diff --git a/DEPS b/DEPS index 35ba0b24..27ad84b 100644 --- a/DEPS +++ b/DEPS
@@ -248,7 +248,7 @@ # luci-go CIPD package version. # Make sure the revision is uploaded by infra-packagers builder. # https://ci.chromium.org/p/infra-internal/g/infra-packagers/console - 'luci_go': 'git_revision:eef34c694bdab861ded59a7e34ddf4d97e013c90', + 'luci_go': 'git_revision:cd0142e4e2f376bb0bd1b34a3b91167aff5226bf', # This can be overridden, e.g. with custom_vars, to build clang from HEAD # instead of downloading the prebuilt pinned revision. @@ -300,19 +300,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': '063254c1cbbe2562ca28802b862f9d4162ff25f4', + 'src_internal_revision': 'a4e46326699b3f0b96d8071f454f5b762710a6eb', # 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': '5342f227267ef899adb5da0473fe3b513f3c6790', + 'skia_revision': 'c106d7831592fbc7245b6e032164506db3038f2a', # 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': '86883747d3c5aba2f21b1963c6c24931f3d00067', + 'v8_revision': 'ebd5dc00c889238ea48d84b19c1bc0a8233d41b1', # 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': 'b64aa6d5c5bdf4acf7de56dcb52594027f29a3ec', + 'angle_revision': 'f56c8e02ce8b75bba76782d1b13200f0bde6cc63', # Three lines of non-changing comments so that # the commit queue can handle CLs rolling SwiftShader # and whatever else without interference from each other. @@ -324,7 +324,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': 'c86127e656ae8b95622c79ba3d0fb35415502d53', + 'boringssl_revision': 'f0ab91129d1a234dc127335765b794d62dc9ed4d', # 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. @@ -420,7 +420,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': '85717040cc75f7b29ca714b18a31087d2a2b6382', + 'dawn_revision': 'ad166239934f9105fdfbbfe8ea46f8e05ca43181', # Three lines of non-changing comments so that # the commit queue can handle CLs rolling feed # and whatever else without interference from each other. @@ -1158,7 +1158,7 @@ }, 'src/chrome/release_scripts': { - 'url': Var('chrome_git') + '/chrome/tools/release/scripts' + '@' + 'dd1c8ec0edb90fc7d5860a50b690f3bd8026ce14', + 'url': Var('chrome_git') + '/chrome/tools/release/scripts' + '@' + '4f61c195e5c4e456130a2de1ad5362a125ea7a61', 'condition': 'checkout_chrome_release_scripts', }, @@ -1501,7 +1501,7 @@ 'src/clank': { 'url': Var('chrome_git') + '/clank/internal/apps.git' + '@' + - '0b6dec35eaa57d814798237cad2cd20a7453d50d', + '75c07728388055f0e7b7a0496693ee75827e374d', 'condition': 'checkout_android and checkout_src_internal', }, @@ -2855,7 +2855,7 @@ 'dep_type': 'cipd', }, - 'src/third_party/vulkan-deps': '{chromium_git}/vulkan-deps@a7952ef72c943513045046f4f1ab0f91998a9307', + 'src/third_party/vulkan-deps': '{chromium_git}/vulkan-deps@07e305fc66d3132b515ed22bc78dee79bf7eb9fc', 'src/third_party/glslang/src': '{chromium_git}/external/github.com/KhronosGroup/glslang@e57f993cff981c8c3ffd38967e030f04d13781a9', '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@8e82b7cfeca98baae9a01a53511483da7194f854', @@ -2864,7 +2864,7 @@ 'src/third_party/vulkan-loader/src': '{chromium_git}/external/github.com/KhronosGroup/Vulkan-Loader@723d6b4aa35853315c6e021ec86388b3a2559fae', 'src/third_party/vulkan-tools/src': '{chromium_git}/external/github.com/KhronosGroup/Vulkan-Tools@289efccc7560f2b970e2b4e0f50349da87669311', 'src/third_party/vulkan-utility-libraries/src': '{chromium_git}/external/github.com/KhronosGroup/Vulkan-Utility-Libraries@551221d913cc56218fcaddce086ae293d375ac28', - 'src/third_party/vulkan-validation-layers/src': '{chromium_git}/external/github.com/KhronosGroup/Vulkan-ValidationLayers@7c7fd77a6ba3ff7699d042ee1d396aa5803850df', + 'src/third_party/vulkan-validation-layers/src': '{chromium_git}/external/github.com/KhronosGroup/Vulkan-ValidationLayers@fcc588f5171b007d433bf7c02184708dbc7d9d89', 'src/third_party/vulkan_memory_allocator': Var('chromium_git') + '/external/github.com/GPUOpen-LibrariesAndSDKs/VulkanMemoryAllocator.git' + '@' + '56300b29fbfcc693ee6609ddad3fdd5b7a449a21', @@ -2903,7 +2903,7 @@ Var('chromium_git') + '/external/khronosgroup/webgl.git' + '@' + 'c01b768bce4a143e152c1870b6ba99ea6267d2b0', 'src/third_party/webgpu-cts/src': - Var('chromium_git') + '/external/github.com/gpuweb/cts.git' + '@' + 'eb1d526134f6789e9ce6d5e3b654fbfc10043267', + Var('chromium_git') + '/external/github.com/gpuweb/cts.git' + '@' + '5aba8ba55eb2496852d622854a99fac74f074c65', 'src/third_party/webpagereplay': Var('chromium_git') + '/webpagereplay.git' + '@' + Var('webpagereplay_revision'), @@ -2987,7 +2987,7 @@ 'packages': [ { 'package': 'skia/tools/goldctl/mac-arm64', - 'version': 'Sfu8dXj0tJ8uWFOZr90L_rVRcp8Kcy8zLsfelpk6K2YC', + 'version': 'zQOaSHuVEWVro28b0D6FdKiq5f8KhkMG_XOFqAdgoIUC', }, ], 'dep_type': 'cipd', @@ -3113,7 +3113,7 @@ 'packages': [ { 'package': 'chromium/third_party/android_deps/autorolled', - 'version': '-kw2ioUuVwgaTHjBYvcyYBT3SZ24bRuwH_rIhsiZ24IC', + 'version': 'TFiDa5VbziMUligPbDLeDXpPmLBgBdhqIj5CslOiRLMC', }, ], 'condition': 'checkout_android and non_git_source', @@ -4701,7 +4701,7 @@ 'src/ios_internal': { 'url': Var('chrome_git') + '/chrome/ios_internal.git' + '@' + - 'ab1e6c45e8b0ceb3f070f0a39c5271766fc7aa12', + 'a4b8373a27c133f175d81ed8044474c23cfb164b', 'condition': 'checkout_ios and checkout_src_internal', },
diff --git a/PRESUBMIT.py b/PRESUBMIT.py index 42fa7c4..ced056f 100644 --- a/PRESUBMIT.py +++ b/PRESUBMIT.py
@@ -2470,7 +2470,7 @@ 'skylab-test-cros-roller', 'infra-try-recipes-tester', 'chrome-automated-expectation', 'chromium-automated-expectation', 'chrome-branch-day', - 'chromium-autosharder') + 'chrome-cherry-picker', 'chromium-autosharder') ) | set('%s@skia-public.iam.gserviceaccount.com' % s for s in ('chromium-autoroll', 'chromium-release-autoroll') ) | set('%s@skia-corp.google.com.iam.gserviceaccount.com' % s
diff --git a/android_webview/browser/aw_feature_map.cc b/android_webview/browser/aw_feature_map.cc index 7b5e56e7..dd0a78a 100644 --- a/android_webview/browser/aw_feature_map.cc +++ b/android_webview/browser/aw_feature_map.cc
@@ -37,7 +37,6 @@ &features::kWebViewXRequestedWithHeaderControl, &metrics::kAndroidMetricsAsyncMetricLogging, &safe_browsing::kHashPrefixRealTimeLookups, - &features::kWebViewSupervisedUserSiteBlock, &base::features::kCollectAndroidFrameTimelineMetrics, &features::kWebViewMediaIntegrityApiBlinkExtension, &features::kWebViewSeparateResourceContext,
diff --git a/android_webview/common/aw_features.cc b/android_webview/common/aw_features.cc index 1799c8d..20199c2 100644 --- a/android_webview/common/aw_features.cc +++ b/android_webview/common/aw_features.cc
@@ -101,12 +101,6 @@ "WebViewRecordAppDataDirectorySize", base::FEATURE_DISABLED_BY_DEFAULT); -// Enable blocking the loading of mature sites (according to Google SafeSearch) -// on WebViews running on supervised user accounts. -BASE_FEATURE(kWebViewSupervisedUserSiteBlock, - "WebViewSupervisedUserSiteBlock", - base::FEATURE_ENABLED_BY_DEFAULT); - // A Feature used for WebView variations tests. Not used in production. Please // do not clean up this stale feature: we intentionally keep this feature flag // around for testing purposes.
diff --git a/android_webview/common/aw_features.h b/android_webview/common/aw_features.h index 5f88340..d33e154c 100644 --- a/android_webview/common/aw_features.h +++ b/android_webview/common/aw_features.h
@@ -32,7 +32,6 @@ BASE_DECLARE_FEATURE(kWebViewMuteAudio); BASE_DECLARE_FEATURE(kWebViewRecordAppDataDirectorySize); BASE_DECLARE_FEATURE(kWebViewRenderDocument); -BASE_DECLARE_FEATURE(kWebViewSupervisedUserSiteBlock); BASE_DECLARE_FEATURE(kWebViewTestFeature); BASE_DECLARE_FEATURE(kWebViewUseMetricsUploadService); BASE_DECLARE_FEATURE(kWebViewUseMetricsUploadServiceOnlySdkRuntime);
diff --git a/android_webview/java/src/org/chromium/android_webview/common/ProductionSupportedFlagList.java b/android_webview/java/src/org/chromium/android_webview/common/ProductionSupportedFlagList.java index 1d826da..e66a69b 100644 --- a/android_webview/java/src/org/chromium/android_webview/common/ProductionSupportedFlagList.java +++ b/android_webview/java/src/org/chromium/android_webview/common/ProductionSupportedFlagList.java
@@ -666,10 +666,6 @@ AwFeatures.WEBVIEW_AUTO_SAA, "Enable auto granting storage access API requests. This will be done " + "if a relationship is detected between the app and the website."), - Flag.baseFeature( - AwFeatures.WEBVIEW_SUPERVISED_USER_SITE_BLOCK, - "Enable blocking the loading of mature sites on " - + "WebViews running on supervised user accounts"), Flag.baseFeature(GwpAsanFeatures.GWP_ASAN_MALLOC, "GWP-ASan for `malloc()`."), Flag.baseFeature(GwpAsanFeatures.GWP_ASAN_PARTITION_ALLOC, "GWP-ASan for PartitionAlloc."), Flag.baseFeature( @@ -939,10 +935,6 @@ "Redact sensitive content during screen sharing, screen recording, and similar" + " actions"), Flag.baseFeature( - BlinkFeatures.PLZ_DEDICATED_WORKER, - "Enable PlzDedicatedWorker. This affects how some URLs are sent to" - + " WebViewClient.shouldInterceptRequest()"), - Flag.baseFeature( "BlinkUseLargeEmptySlotSpanRingForBufferRoot", "Tuning memory allocator for speed - large empty slot span ring for Blink buffer" + " root"),
diff --git a/android_webview/java/src/org/chromium/android_webview/supervised_user/AwSupervisedUserUrlClassifier.java b/android_webview/java/src/org/chromium/android_webview/supervised_user/AwSupervisedUserUrlClassifier.java index c0ed9cb..1c8420e 100644 --- a/android_webview/java/src/org/chromium/android_webview/supervised_user/AwSupervisedUserUrlClassifier.java +++ b/android_webview/java/src/org/chromium/android_webview/supervised_user/AwSupervisedUserUrlClassifier.java
@@ -10,8 +10,6 @@ import org.jni_zero.JNINamespace; import org.jni_zero.NativeMethods; -import org.chromium.android_webview.AwFeatureMap; -import org.chromium.android_webview.common.AwFeatures; import org.chromium.android_webview.common.AwSupervisedUserUrlClassifierDelegate; import org.chromium.android_webview.common.PlatformServiceBridge; import org.chromium.base.ContextUtils; @@ -51,12 +49,10 @@ synchronized (sInstanceLock) { if (!sInitialized) { - if (AwFeatureMap.isEnabled(AwFeatures.WEBVIEW_SUPERVISED_USER_SITE_BLOCK)) { - AwSupervisedUserUrlClassifierDelegate delegate = - PlatformServiceBridge.getInstance().getUrlClassifierDelegate(); - if (delegate != null) { - sInstance = new AwSupervisedUserUrlClassifier(delegate); - } + AwSupervisedUserUrlClassifierDelegate delegate = + PlatformServiceBridge.getInstance().getUrlClassifierDelegate(); + if (delegate != null) { + sInstance = new AwSupervisedUserUrlClassifier(delegate); } sInitialized = true; }
diff --git a/android_webview/javatests/src/org/chromium/android_webview/test/AwContentsClientShouldInterceptRequestTest.java b/android_webview/javatests/src/org/chromium/android_webview/test/AwContentsClientShouldInterceptRequestTest.java index be93c173..c4ebffd 100644 --- a/android_webview/javatests/src/org/chromium/android_webview/test/AwContentsClientShouldInterceptRequestTest.java +++ b/android_webview/javatests/src/org/chromium/android_webview/test/AwContentsClientShouldInterceptRequestTest.java
@@ -1997,7 +1997,6 @@ @Test @SmallTest @Feature({"AndroidWebView"}) - @CommandLineFlags.Add({"enable-features=PlzDedicatedWorker"}) public void testDedicatedWorkerSubresourceIntercepted() throws Throwable { final String importScriptJs = addJavaScriptToTestServer(mWebServer, "/test-worker.js", ""); final String workerJs =
diff --git a/android_webview/javatests/src/org/chromium/android_webview/test/AwSupervisedUserTest.java b/android_webview/javatests/src/org/chromium/android_webview/test/AwSupervisedUserTest.java index 91d178b..184c0e5 100644 --- a/android_webview/javatests/src/org/chromium/android_webview/test/AwSupervisedUserTest.java +++ b/android_webview/javatests/src/org/chromium/android_webview/test/AwSupervisedUserTest.java
@@ -37,10 +37,8 @@ import org.junit.runners.Parameterized.UseParametersRunnerFactory; import org.chromium.android_webview.AwContents; -import org.chromium.android_webview.AwFeatureMap; import org.chromium.android_webview.JsReplyProxy; import org.chromium.android_webview.WebMessageListener; -import org.chromium.android_webview.common.AwFeatures; import org.chromium.android_webview.common.AwSupervisedUserUrlClassifierDelegate; import org.chromium.android_webview.common.BackgroundThreadExecutor; import org.chromium.android_webview.common.PlatformServiceBridge; @@ -50,7 +48,6 @@ import org.chromium.base.ThreadUtils; import org.chromium.base.test.util.Batch; import org.chromium.base.test.util.CallbackHelper; -import org.chromium.base.test.util.CommandLineFlags; import org.chromium.base.test.util.Criteria; import org.chromium.base.test.util.CriteriaHelper; import org.chromium.base.test.util.Feature; @@ -153,7 +150,6 @@ @Test @SmallTest @Feature({"AndroidWebView"}) - @CommandLineFlags.Add("enable-features=" + AwFeatures.WEBVIEW_SUPERVISED_USER_SITE_BLOCK) public void testAllowedSiteIsLoaded() throws Throwable { String embeddedUrl = setUpWebPage(SAFE_SITE_IFRAME_PATH, SAFE_SITE_IFRAME_TITLE, null); String requestUrl = setUpWebPage(SAFE_SITE_PATH, SAFE_SITE_TITLE, embeddedUrl); @@ -167,7 +163,6 @@ @Test @SmallTest @Feature({"AndroidWebView"}) - @CommandLineFlags.Add("enable-features=" + AwFeatures.WEBVIEW_SUPERVISED_USER_SITE_BLOCK) public void testDisallowedSiteIsBlocked() throws Throwable { String requestUrl = setUpWebPage(MATURE_SITE_PATH, MATURE_SITE_TITLE, null); @@ -179,7 +174,6 @@ @Test @SmallTest @Feature({"AndroidWebView"}) - @CommandLineFlags.Add("enable-features=" + AwFeatures.WEBVIEW_SUPERVISED_USER_SITE_BLOCK) public void testDisallowedEmbeddedSiteIsBlocked() throws Throwable { String embeddedUrl = setUpWebPage(MATURE_SITE_IFRAME_PATH, MATURE_SITE_IFRAME_TITLE, null); String requestUrl = setUpWebPage(SAFE_SITE_PATH, SAFE_SITE_TITLE, embeddedUrl); @@ -193,7 +187,6 @@ @Test @SmallTest @Feature({"AndroidWebView"}) - @CommandLineFlags.Add("enable-features=" + AwFeatures.WEBVIEW_SUPERVISED_USER_SITE_BLOCK) public void testDisallowedSiteRedirectIsBlocked() throws Throwable { String requestUrl = mWebServer.setRedirect(MATURE_SITE_PATH, SAFE_SITE_PATH); @@ -205,7 +198,6 @@ @Test @SmallTest @Feature({"AndroidWebView"}) - @CommandLineFlags.Add("enable-features=" + AwFeatures.WEBVIEW_SUPERVISED_USER_SITE_BLOCK) public void testDisallowedRedirectIsBlocked() throws Throwable { String requestUrl = mWebServer.setRedirect(SAFE_SITE_PATH, MATURE_SITE_PATH); @@ -217,21 +209,6 @@ @Test @SmallTest @Feature({"AndroidWebView"}) - @CommandLineFlags.Add("disable-features=" + AwFeatures.WEBVIEW_SUPERVISED_USER_SITE_BLOCK) - public void testDisallowedSiteIsLoadedFeatureOff() throws Throwable { - String embeddedUrl = setUpWebPage(MATURE_SITE_IFRAME_PATH, MATURE_SITE_IFRAME_TITLE, null); - String requestUrl = setUpWebPage(MATURE_SITE_PATH, MATURE_SITE_TITLE, embeddedUrl); - - loadUrl(requestUrl); - - assertPageTitle(MATURE_SITE_TITLE); - assertIframeTitle(MATURE_SITE_IFRAME_TITLE); - } - - @Test - @SmallTest - @Feature({"AndroidWebView"}) - @CommandLineFlags.Add("enable-features=" + AwFeatures.WEBVIEW_SUPERVISED_USER_SITE_BLOCK) public void testBlocksContentOnlyIfRestrctionRequired() throws Throwable { String embeddedUrl = setUpWebPage(MATURE_SITE_IFRAME_PATH, MATURE_SITE_IFRAME_TITLE, null); String requestUrl = setUpWebPage(MATURE_SITE_PATH, MATURE_SITE_TITLE, embeddedUrl); @@ -263,7 +240,6 @@ @Test @SmallTest @Feature({"AndroidWebView"}) - @CommandLineFlags.Add("enable-features=" + AwFeatures.WEBVIEW_SUPERVISED_USER_SITE_BLOCK) public void testClickLearnMoreLink() throws Throwable { String requestUrl = setUpWebPage(MATURE_SITE_PATH, MATURE_SITE_TITLE, null); try { @@ -293,7 +269,6 @@ @Test @SmallTest @Feature({"AndroidWebView"}) - @CommandLineFlags.Add("enable-features=" + AwFeatures.WEBVIEW_SUPERVISED_USER_SITE_BLOCK) public void testSafeMode() throws Throwable { String embeddedUrl = setUpWebPage(MATURE_SITE_IFRAME_PATH, MATURE_SITE_IFRAME_TITLE, null); String requestUrl = setUpWebPage(MATURE_SITE_PATH, MATURE_SITE_TITLE, embeddedUrl); @@ -415,10 +390,6 @@ } private void resetNeedsRestriction(boolean value) throws Exception { - if (!AwFeatureMap.isEnabled(AwFeatures.WEBVIEW_SUPERVISED_USER_SITE_BLOCK)) { - // Nothing we need to do if the feature is disabled. - return; - } mDelegate.setNeedsRestrictedContentBlockingResponse(value); int count = mDelegate.getNeedsRestrictionHelper().getCallCount(); AwSupervisedUserUrlClassifier classifier = AwSupervisedUserUrlClassifier.getInstance();
diff --git a/android_webview/test/data/web_tests/virtual/stable/webexposed/global-interface-listing-expected.txt b/android_webview/test/data/web_tests/virtual/stable/webexposed/global-interface-listing-expected.txt index 17b244c..869feefa 100644 --- a/android_webview/test/data/web_tests/virtual/stable/webexposed/global-interface-listing-expected.txt +++ b/android_webview/test/data/web_tests/virtual/stable/webexposed/global-interface-listing-expected.txt
@@ -8302,6 +8302,7 @@ getter anchorOffset getter baseNode getter baseOffset + getter direction getter extentNode getter extentOffset getter focusNode @@ -8318,6 +8319,7 @@ method deleteFromDocument method empty method extend + method getComposedRanges method getRangeAt method modify method removeAllRanges
diff --git a/ash/root_window_controller.cc b/ash/root_window_controller.cc index 0e2396f..4548d9b 100644 --- a/ash/root_window_controller.cc +++ b/ash/root_window_controller.cc
@@ -1176,11 +1176,9 @@ non_lock_screen_containers); aura::Window* shutdown_screenshot_container = non_lock_screen_containers; - if (features::IsForestFeatureEnabled()) { - shutdown_screenshot_container = CreateContainer( - kShellWindowId_ShutdownScreenshotContainer, - "ShutdownScreenshotContainer", non_lock_screen_containers); - } + shutdown_screenshot_container = CreateContainer( + kShellWindowId_ShutdownScreenshotContainer, "ShutdownScreenshotContainer", + non_lock_screen_containers); for (const auto& id : desks_util::GetDesksContainersIds()) { aura::Window* container =
diff --git a/ash/system/unified/quick_settings_footer_unittest.cc b/ash/system/unified/quick_settings_footer_unittest.cc index b846f2ff..6a516259 100644 --- a/ash/system/unified/quick_settings_footer_unittest.cc +++ b/ash/system/unified/quick_settings_footer_unittest.cc
@@ -16,6 +16,7 @@ #include "base/memory/raw_ptr.h" #include "base/memory/scoped_refptr.h" #include "base/test/metrics/histogram_tester.h" +#include "base/test/run_until.h" #include "components/user_manager/user_type.h" #include "ui/views/test/views_test_utils.h" #include "ui/views/view_utils.h" @@ -249,11 +250,6 @@ } TEST_F(QuickSettingsFooterTest, SignOutButtonRecordsUmaAndSignsOut) { - // TODO(minch): Re-enable this test. - if (features::IsForestFeatureEnabled()) { - GTEST_SKIP() << "Skipping test body for forest feature."; - } - GetSessionControllerClient()->set_existing_users_count(2); SimulateUserLogin(kRegularUserLoginInfo); SetUpView(); @@ -267,7 +263,9 @@ QsButtonCatalogName::kSignOutButton, /*expected_count=*/1); - EXPECT_EQ(1, GetSessionControllerClient()->request_sign_out_count()); + EXPECT_TRUE(base::test::RunUntil([&]() { + return GetSessionControllerClient()->request_sign_out_count() == 1; + })); } // Settings button is disabled when kSettingsIconDisabled is set.
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 992c8c7..f73621d9 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
@@ -160,6 +160,7 @@ maxOutputTokens: 0, topK: null, temperature: null, + responseJsonSchema: '', }, responseRouter.$.bindNewPipeAndPassRemote(), );
diff --git a/ash/wm/lock_state_controller.cc b/ash/wm/lock_state_controller.cc index 4dbfe953..e9bc5139 100644 --- a/ash/wm/lock_state_controller.cc +++ b/ash/wm/lock_state_controller.cc
@@ -17,7 +17,6 @@ #include "ash/app_list/app_list_controller_impl.h" #include "ash/cancel_mode.h" #include "ash/capture_mode/capture_mode_controller.h" -#include "ash/constants/ash_features.h" #include "ash/constants/ash_pref_names.h" #include "ash/public/cpp/saved_desk_delegate.h" #include "ash/public/cpp/shell_window_ids.h" @@ -465,11 +464,7 @@ } HideAndMaybeLockCursor(/*lock=*/true); - if (features::IsForestFeatureEnabled()) { - SessionStateChangeWithInformedRestore(RequestedSessionState::kShutdown); - } else { - StartSessionStateChange(RequestedSessionState::kShutdown); - } + SessionStateChangeWithInformedRestore(RequestedSessionState::kShutdown); } void LockStateController::RequestCancelableShutdown(ShutdownReason reason) { @@ -477,12 +472,8 @@ shutdown_canceled_ = false; HideAndMaybeLockCursor(/*lock=*/false); - if (features::IsForestFeatureEnabled()) { - SessionStateChangeWithInformedRestore( - RequestedSessionState::kCancelableShutdown); - } else { - StartSessionStateChange(RequestedSessionState::kCancelableShutdown); - } + SessionStateChangeWithInformedRestore( + RequestedSessionState::kCancelableShutdown); } bool LockStateController::ShutdownRequested() const { @@ -499,12 +490,10 @@ SessionStateAnimator::ANIMATION_UNDO_GRAYSCALE_BRIGHTNESS, SessionStateAnimator::ANIMATION_SPEED_REVERT_SHUTDOWN); shutdown_canceled_ = true; - if (features::IsForestFeatureEnabled()) { - // Shutdown maybe canceled before or after image saved. So we need to delete - // both here and `OnImageSaved`. - DeleteInformedRestoreImage(informed_restore_image_callback_for_test_, - GetInformedRestoreImagePath()); - } + // Shutdown maybe canceled before or after image saved. So we need to delete + // both here and `OnImageSaved`. + DeleteInformedRestoreImage(informed_restore_image_callback_for_test_, + GetInformedRestoreImagePath()); cancelable_shutdown_timer_.Stop(); return true; } @@ -512,23 +501,15 @@ void LockStateController::RequestRestart( power_manager::RequestRestartReason reason, const std::string& description) { - if (features::IsForestFeatureEnabled()) { - HideAndMaybeLockCursor(/*lock=*/false); - restart_callback_ = - base::BindOnce(&LockStateController::DoRestart, base::Unretained(this), - reason, description); - SessionStateChangeWithInformedRestore(RequestedSessionState::kRestart); - } else { - chromeos::PowerManagerClient::Get()->RequestRestart(reason, description); - } + HideAndMaybeLockCursor(/*lock=*/false); + restart_callback_ = + base::BindOnce(&LockStateController::DoRestart, base::Unretained(this), + reason, description); + SessionStateChangeWithInformedRestore(RequestedSessionState::kRestart); } void LockStateController::RequestSignOut() { - if (features::IsForestFeatureEnabled()) { - SessionStateChangeWithInformedRestore(RequestedSessionState::kSignOut); - } else { - Shell::Get()->session_controller()->RequestSignOut(); - } + SessionStateChangeWithInformedRestore(RequestedSessionState::kSignOut); } void LockStateController::OnHostCloseRequested(aura::WindowTreeHost* host) {
diff --git a/base/BUILD.gn b/base/BUILD.gn index c4b8c65..0c24c7f 100644 --- a/base/BUILD.gn +++ b/base/BUILD.gn
@@ -1519,8 +1519,6 @@ # TODO(crbug.com/40511454) Use only boringssl when NaCl is removed. sources += [ "hash/md5.h", - "hash/md5_constexpr.h", - "hash/md5_constexpr_internal.h", "hash/sha1.h", ] if (is_nacl) { @@ -3381,7 +3379,6 @@ "gmock_unittest.cc", "hash/hash_unittest.cc", "hash/legacy_hash_unittest.cc", - "hash/md5_constexpr_unittest.cc", "hash/md5_unittest.cc", "hash/sha1_unittest.cc", "i18n/break_iterator_unittest.cc",
diff --git a/base/hash/md5_constexpr.h b/base/hash/md5_constexpr.h deleted file mode 100644 index dd239598..0000000 --- a/base/hash/md5_constexpr.h +++ /dev/null
@@ -1,27 +0,0 @@ -// Copyright 2019 The Chromium Authors -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef BASE_HASH_MD5_CONSTEXPR_H_ -#define BASE_HASH_MD5_CONSTEXPR_H_ - -#include <string_view> - -#include "base/hash/md5_constexpr_internal.h" // IWYU pragma: export - -namespace base { - -// Calculates the first 32/64 bits of the MD5 digest of the provided data, -// returned as a uint32_t/uint64_t. When passing |string| with no explicit -// length the terminating null will not be processed. This abstracts away -// endianness so that the integer will read as the first 4 or 8 bytes of the -// MD5 sum, ensuring that the following outputs are equivalent for -// convenience: -// -// printf("%08x\n", MD5Hash32Constexpr("foo")); -constexpr uint64_t MD5Hash64Constexpr(std::string_view string); -constexpr uint32_t MD5Hash32Constexpr(std::string_view string); - -} // namespace base - -#endif // BASE_HASH_MD5_CONSTEXPR_H_
diff --git a/base/hash/md5_constexpr_unittest.cc b/base/hash/md5_constexpr_unittest.cc deleted file mode 100644 index bb6d553..0000000 --- a/base/hash/md5_constexpr_unittest.cc +++ /dev/null
@@ -1,25 +0,0 @@ -// Copyright 2019 The Chromium Authors -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "base/hash/md5_constexpr.h" - -namespace base { - -// Ensure that everything works at compile-time by comparing to a few -// reference hashes. -constexpr char kMessage0[] = "message digest"; -static_assert(MD5Hash64Constexpr(kMessage0) == 0xF96B697D7CB7938Dull, - "incorrect MD5Hash64 implementation"); - -static_assert(MD5Hash32Constexpr(kMessage0) == 0xF96B697Dul, - "incorrect MD5Hash32 implementation"); - -constexpr char kMessage1[] = "The quick brown fox jumps over the lazy dog"; -static_assert(MD5Hash64Constexpr(kMessage1) == 0x9E107D9D372BB682ull, - "incorrect MD5Hash64 implementation"); - -static_assert(MD5Hash32Constexpr(kMessage1) == 0x9E107D9Dul, - "incorrect MD5Hash32 implementation"); - -} // namespace base
diff --git a/build/android/gyp/util/jar_utils.py b/build/android/gyp/util/jar_utils.py index 0bd4137..1649eab6 100644 --- a/build/android/gyp/util/jar_utils.py +++ b/build/android/gyp/util/jar_utils.py
@@ -15,7 +15,7 @@ _IGNORED_JAR_PATHS = [ # This matches org_ow2_asm_asm_commons and org_ow2_asm_asm_analysis, both of # which fail jdeps (not sure why), see: https://crbug.com/348423879 - 'third_party/android_deps/cipd/libs/org_ow2_asm_asm', + 'cipd/libs/org_ow2_asm_asm', ] def _should_ignore(jar_path: pathlib.Path) -> bool:
diff --git a/build/config/unsafe_buffers_paths.txt b/build/config/unsafe_buffers_paths.txt index 80abde7..29db2f9 100644 --- a/build/config/unsafe_buffers_paths.txt +++ b/build/config/unsafe_buffers_paths.txt
@@ -5,7 +5,7 @@ # See `docs/unsafe_buffers.md`. # Checks to enable -.buffers +.buffers,libc # Directories to exempt from checks -base/allocator/
diff --git a/chrome/android/features/keyboard_accessory/internal/java/src/org/chromium/chrome/browser/keyboard_accessory/AutofillKeyboardAccessoryViewBridge.java b/chrome/android/features/keyboard_accessory/internal/java/src/org/chromium/chrome/browser/keyboard_accessory/AutofillKeyboardAccessoryViewBridge.java index 5ec0d0c9..5a71adf 100644 --- a/chrome/android/features/keyboard_accessory/internal/java/src/org/chromium/chrome/browser/keyboard_accessory/AutofillKeyboardAccessoryViewBridge.java +++ b/chrome/android/features/keyboard_accessory/internal/java/src/org/chromium/chrome/browser/keyboard_accessory/AutofillKeyboardAccessoryViewBridge.java
@@ -184,7 +184,6 @@ .setLabel(label) .setSubLabel(sublabel) .setIconId(drawableId) - .setIsIconAtStart(false) .setSuggestionType(suggestionType) .setIsDeletable(isDeletable) .setFeatureForIph(featureForIph)
diff --git a/chrome/android/features/tab_ui/BUILD.gn b/chrome/android/features/tab_ui/BUILD.gn index 7c41afdd..56d8e7c 100644 --- a/chrome/android/features/tab_ui/BUILD.gn +++ b/chrome/android/features/tab_ui/BUILD.gn
@@ -29,6 +29,8 @@ "java/res/anim/iph_touch_point_background_animation.xml", "java/res/anim/tab_switcher_context_menu_animation_in.xml", "java/res/anim/tab_switcher_context_menu_animation_out.xml", + "java/res/color-night/tab_grid_dialog_bg_color.xml", + "java/res/color/tab_grid_dialog_bg_color.xml", "java/res/drawable/archived_tab_icon.xml", "java/res/drawable/chevron_right.xml", "java/res/drawable/color_picker_color_icon.xml",
diff --git a/chrome/android/features/tab_ui/java/res/color-night/tab_grid_dialog_bg_color.xml b/chrome/android/features/tab_ui/java/res/color-night/tab_grid_dialog_bg_color.xml new file mode 100644 index 0000000..4c9a1ac --- /dev/null +++ b/chrome/android/features/tab_ui/java/res/color-night/tab_grid_dialog_bg_color.xml
@@ -0,0 +1,10 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +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. +--> + +<selector xmlns:android="http://schemas.android.com/apk/res/android"> + <item android:color="?attr/colorSurfaceContainerLow" /> +</selector>
diff --git a/chrome/android/features/tab_ui/java/res/color/tab_grid_dialog_bg_color.xml b/chrome/android/features/tab_ui/java/res/color/tab_grid_dialog_bg_color.xml new file mode 100644 index 0000000..8a60d84 --- /dev/null +++ b/chrome/android/features/tab_ui/java/res/color/tab_grid_dialog_bg_color.xml
@@ -0,0 +1,10 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +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. +--> + +<selector xmlns:android="http://schemas.android.com/apk/res/android"> + <item android:color="?attr/colorSurface" /> +</selector>
diff --git a/chrome/android/features/tab_ui/java/res/values/colors.xml b/chrome/android/features/tab_ui/java/res/values/colors.xml index 2dab8d7..460a3ead5 100644 --- a/chrome/android/features/tab_ui/java/res/values/colors.xml +++ b/chrome/android/features/tab_ui/java/res/values/colors.xml
@@ -26,7 +26,7 @@ <color name="incognito_tab_tile_number_color">@color/baseline_neutral_90</color> <color name="incognito_tab_tile_number_selected_color">@color/baseline_primary_20</color> - <color name="incognito_tab_grid_dialog_background_color">@color/default_bg_color_dark</color> + <color name="incognito_tab_grid_dialog_background_color">@color/gm3_baseline_surface_container_low_dark</color> <color name="incognito_tab_grid_dialog_ungroup_bar_bg_hovered_color">@color/baseline_primary_80</color>
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabUiThemeProvider.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabUiThemeProvider.java index 1299edbe..264a78e 100644 --- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabUiThemeProvider.java +++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabUiThemeProvider.java
@@ -204,7 +204,7 @@ if (isIncognito) { return context.getColor(R.color.incognito_tab_grid_dialog_background_color); } else { - return SemanticColorUtils.getDialogBgColor(context); + return ContextCompat.getColor(context, R.color.tab_grid_dialog_bg_color); } }
diff --git a/chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/TabSwitcherListEditorPTTest.java b/chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/TabSwitcherListEditorPTTest.java index 0b50f81..1ed16775 100644 --- a/chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/TabSwitcherListEditorPTTest.java +++ b/chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/TabSwitcherListEditorPTTest.java
@@ -4,20 +4,27 @@ package org.chromium.chrome.browser.tasks.tab_management; +import static org.mockito.Mockito.when; + import static org.chromium.base.test.transit.TransitAsserts.assertFinalDestination; import static org.chromium.chrome.test.util.TabBinningUtil.group; import androidx.test.filters.MediumTest; +import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.junit.MockitoJUnit; +import org.mockito.junit.MockitoRule; import org.chromium.base.ThreadUtils; import org.chromium.base.test.util.Batch; import org.chromium.base.test.util.CommandLineFlags; import org.chromium.base.test.util.Features; import org.chromium.base.test.util.RequiresRestart; +import org.chromium.chrome.browser.collaboration.CollaborationServiceFactory; import org.chromium.chrome.browser.flags.ChromeFeatureList; import org.chromium.chrome.browser.flags.ChromeSwitches; import org.chromium.chrome.browser.tab.Tab; @@ -38,6 +45,8 @@ import org.chromium.chrome.test.transit.ntp.RegularNewTabPageStation; import org.chromium.chrome.test.transit.page.WebPageStation; import org.chromium.chrome.test.util.TabBinningUtil; +import org.chromium.components.collaboration.CollaborationService; +import org.chromium.components.collaboration.ServiceStatus; import org.chromium.components.tab_groups.TabGroupColorId; import java.util.List; @@ -48,12 +57,28 @@ @Batch(Batch.PER_CLASS) // TODO(https://crbug.com/392634251): Fix line height when elegant text height is used with Roboto // or enable Google Sans (Text) in //chrome/ tests on Android T+. -@Features.DisableFeatures(ChromeFeatureList.ANDROID_ELEGANT_TEXT_HEIGHT) +@Features.DisableFeatures({ + ChromeFeatureList.DATA_SHARING, + ChromeFeatureList.ANDROID_ELEGANT_TEXT_HEIGHT +}) public class TabSwitcherListEditorPTTest { @Rule public AutoResetCtaTransitTestRule mCtaTestRule = ChromeTransitTestRules.autoResetCtaActivityRule(); + @Rule public final MockitoRule mMockitoRule = MockitoJUnit.rule(); + + @Mock private CollaborationService mCollaborationService; + @Mock private ServiceStatus mServiceStatus; + + @Before + public void setUp() { + CollaborationServiceFactory.setForTesting(mCollaborationService); + when(mCollaborationService.getServiceStatus()).thenReturn(mServiceStatus); + when(mServiceStatus.isAllowedToCreate()).thenReturn(false); + when(mServiceStatus.isAllowedToJoin()).thenReturn(false); + } + @Test @MediumTest public void testLeaveEditorViaBackPress() {
diff --git a/chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_management/ArchivedTabsCardViewBinderUnitTest.java b/chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_management/ArchivedTabsCardViewBinderUnitTest.java index c9d68699..e3c4cfd48 100644 --- a/chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_management/ArchivedTabsCardViewBinderUnitTest.java +++ b/chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_management/ArchivedTabsCardViewBinderUnitTest.java
@@ -16,17 +16,19 @@ import android.view.View; import android.widget.TextView; +import androidx.test.ext.junit.rules.ActivityScenarioRule; + import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.junit.MockitoJUnit; import org.mockito.junit.MockitoRule; -import org.robolectric.Robolectric; import org.robolectric.annotation.Config; import org.chromium.base.test.BaseRobolectricTestRunner; import org.chromium.base.test.util.CallbackHelper; +import org.chromium.ui.base.TestActivity; import org.chromium.ui.modelutil.PropertyModel; import org.chromium.ui.modelutil.PropertyModelChangeProcessor; @@ -43,6 +45,10 @@ @Rule public final MockitoRule mMockitoRule = MockitoJUnit.rule(); + @Rule + public final ActivityScenarioRule<TestActivity> mActivityScenarioRule = + new ActivityScenarioRule<>(TestActivity.class); + private Activity mActivity; private View mArchivedTabsCardView; private PropertyModel mModel; @@ -50,7 +56,7 @@ @Before public void setUp() throws Exception { - mActivity = Robolectric.buildActivity(Activity.class).setup().get(); + mActivityScenarioRule.getScenario().onActivity(activity -> mActivity = activity); mArchivedTabsCardView = LayoutInflater.from(mActivity) .inflate(R.layout.archived_tabs_message_card_view, /* root= */ null);
diff --git a/chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_management/ArchivedTabsMessageServiceUnitTest.java b/chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_management/ArchivedTabsMessageServiceUnitTest.java index 83f8c464..80a148a 100644 --- a/chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_management/ArchivedTabsMessageServiceUnitTest.java +++ b/chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_management/ArchivedTabsMessageServiceUnitTest.java
@@ -21,6 +21,8 @@ import android.view.ViewGroup; import android.widget.FrameLayout; +import androidx.test.ext.junit.rules.ActivityScenarioRule; + import org.junit.Before; import org.junit.Rule; import org.junit.Test; @@ -31,7 +33,6 @@ import org.mockito.junit.MockitoJUnit; import org.mockito.junit.MockitoRule; import org.mockito.quality.Strictness; -import org.robolectric.Robolectric; import org.robolectric.annotation.Config; import org.robolectric.shadows.ShadowLooper; @@ -50,6 +51,7 @@ import org.chromium.chrome.browser.ui.edge_to_edge.EdgeToEdgeController; import org.chromium.chrome.browser.ui.messages.snackbar.SnackbarManager; import org.chromium.components.feature_engagement.Tracker; +import org.chromium.ui.base.TestActivity; import org.chromium.ui.modaldialog.ModalDialogManager; import org.chromium.ui.modelutil.PropertyModel; @@ -61,6 +63,10 @@ @Rule public MockitoRule mMockitoRule = MockitoJUnit.rule().strictness(Strictness.LENIENT); + @Rule + public ActivityScenarioRule<TestActivity> mActivityScenarioRule = + new ActivityScenarioRule<>(TestActivity.class); + @Mock private ArchivedTabModelOrchestrator mArchivedTabModelOrchestrator; @Mock private TabArchiveSettings mTabArchiveSettings; @Mock private TabModel mArchivedTabModel; @@ -90,7 +96,7 @@ @Before public void setUp() throws Exception { - mActivity = Robolectric.buildActivity(Activity.class).setup().get(); + mActivityScenarioRule.getScenario().onActivity(activity -> mActivity = activity); mRootView = new FrameLayout(mActivity); doReturn(TIME_DELTA_DAYS).when(mTabArchiveSettings).getArchiveTimeDeltaDays();
diff --git a/chrome/android/java/res/layout/recent_tabs_group_item.xml b/chrome/android/java/res/layout/recent_tabs_group_item.xml index 7c648a3..086dd62 100644 --- a/chrome/android/java/res/layout/recent_tabs_group_item.xml +++ b/chrome/android/java/res/layout/recent_tabs_group_item.xml
@@ -10,8 +10,7 @@ xmlns:tools="http://schemas.android.com/tools" android:id="@+id/recent_tabs_group_view" android:layout_height="wrap_content" - android:layout_width="match_parent" - android:background="?attr/selectableItemBackground" > + android:layout_width="match_parent" > <LinearLayout android:layout_width="match_parent"
diff --git a/chrome/android/java/res/layout/recent_tabs_list_item.xml b/chrome/android/java/res/layout/recent_tabs_list_item.xml index 993f571..ede144b 100644 --- a/chrome/android/java/res/layout/recent_tabs_list_item.xml +++ b/chrome/android/java/res/layout/recent_tabs_list_item.xml
@@ -13,7 +13,6 @@ android:layout_height="wrap_content" android:layout_width="match_parent" android:minHeight="@dimen/recent_tabs_foreign_session_group_item_height" - android:background="?attr/selectableItemBackground" android:paddingStart="@dimen/recent_tabs_list_item_side_padding" android:paddingEnd="@dimen/recent_tabs_list_item_side_padding">
diff --git a/chrome/android/java/res/layout/recent_tabs_page.xml b/chrome/android/java/res/layout/recent_tabs_page.xml index 63c4153c..8bc81f65 100644 --- a/chrome/android/java/res/layout/recent_tabs_page.xml +++ b/chrome/android/java/res/layout/recent_tabs_page.xml
@@ -27,11 +27,10 @@ android:paddingTop="@dimen/recent_tabs_group_view_vertical_padding" android:layout_height="match_parent" android:layout_width="match_parent" - android:cacheColorHint="@macro/default_bg_color" android:childDivider="@null" android:divider="@null" android:background="@macro/default_bg_color" - android:listSelector="@android:color/transparent" + android:listSelector="?attr/selectableItemBackground" android:scrollbarStyle="outsideOverlay" /> </org.chromium.chrome.browser.ntp.NativePageRootFrameLayout>
diff --git a/chrome/android/java/res/values-sw600dp-v30/styles.xml b/chrome/android/java/res/values-sw600dp-v30/styles.xml index 80d39fdb..096724e4 100644 --- a/chrome/android/java/res/values-sw600dp-v30/styles.xml +++ b/chrome/android/java/res/values-sw600dp-v30/styles.xml
@@ -6,6 +6,10 @@ --> <resources> + <style name="Theme.Chromium.TabbedMode" parent="Base.Theme.Chromium.TabbedMode"> + <item name="android:statusBarColor">@macro/default_bg_color</item> + <item name="android:navigationBarColor">@macro/default_bg_color</item> + </style> <style name="Theme.Chromium.Settings" parent="Base.Theme.Chromium.Settings"> <item name="android:statusBarColor">@macro/default_bg_color</item> <item name="android:windowLightStatusBar">@bool/window_light_status_bar</item>
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 926d769d..358d8d3 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
@@ -1179,7 +1179,7 @@ private boolean shouldEnableGroupSharing() { Profile profile = mTabGroupModelFilter.getTabModel().getProfile(); - if (profile == null || profile.isOffTheRecord()) { + if (profile == null || profile.isOffTheRecord() || mTabGroupSyncService == null) { return false; } CollaborationService collaborationService =
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/crash/MinidumpUploadServiceImpl.java b/chrome/android/java/src/org/chromium/chrome/browser/crash/MinidumpUploadServiceImpl.java index 68fc1a7..5b486cbc 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/crash/MinidumpUploadServiceImpl.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/crash/MinidumpUploadServiceImpl.java
@@ -7,7 +7,6 @@ import android.app.job.JobInfo; import android.content.ComponentName; import android.content.Intent; -import android.os.Build; import android.os.PersistableBundle; import android.os.Process; @@ -362,16 +361,14 @@ * Attempts to upload the specified {@param minidumpFile} directly. If Android doesn't allow a * direct upload, then fallback to JobScheduler. * - * Note that the preferred way to upload minidump is only through JobScheduler, use this + * <p>Note that the preferred way to upload minidump is only through JobScheduler, use this * function if you need to upload it urgently. */ static void tryUploadCrashDumpNow(File minidumpFile) { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S - && !(ApplicationStatus.isInitialized() - && ApplicationStatus.hasVisibleActivities())) { - // If we are on API 31+, Android does not allow us to start services from the - // background. If we are in the background, then go through the JobScheduler path - // instead. See crbug.com/1433529 for more details. + if (!(ApplicationStatus.isInitialized() && ApplicationStatus.hasVisibleActivities())) { + // Android does not allow us to start services from the background. If we are in the + // background, then go through the JobScheduler path instead. See crbug.com/1433529 + // and crbug.com/407575680 for crashes caused by not doing this. scheduleUploadJob(); return; }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/BaseCustomTabRootUiCoordinator.java b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/BaseCustomTabRootUiCoordinator.java index 8be39acc..461e4853 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/BaseCustomTabRootUiCoordinator.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/BaseCustomTabRootUiCoordinator.java
@@ -66,6 +66,7 @@ import org.chromium.chrome.browser.lifecycle.ActivityLifecycleDispatcher; import org.chromium.chrome.browser.page_info.ChromePageInfo; import org.chromium.chrome.browser.page_info.ChromePageInfoHighlight; +import org.chromium.chrome.browser.pdf.PdfPageIphController; import org.chromium.chrome.browser.privacy_sandbox.ActivityTypeMapper; import org.chromium.chrome.browser.privacy_sandbox.PrivacySandboxBridge; import org.chromium.chrome.browser.privacy_sandbox.PrivacySandboxDialogController; @@ -127,6 +128,7 @@ private @Nullable BrandingController mBrandingController; private @Nullable DesktopSiteSettingsIphController mDesktopSiteSettingsIphController; + private @Nullable PdfPageIphController mPdfPageIphController; private @Nullable CustomTabHistoryIphController mCustomTabHistoryIphController; private @Nullable ReadAloudIphController mReadAloudIphController; private @Nullable GoogleBottomBarCoordinator mGoogleBottomBarCoordinator; @@ -740,6 +742,11 @@ mDesktopSiteSettingsIphController = null; } + if (mPdfPageIphController != null) { + mPdfPageIphController.destroy(); + mPdfPageIphController = null; + } + if (mReadAloudIphController != null) { mReadAloudIphController.destroy(); mReadAloudIphController = null; @@ -865,6 +872,15 @@ regularProfile, getToolbarManager().getMenuButtonView(), mAppMenuCoordinator.getAppMenuHandler()); + mPdfPageIphController = + PdfPageIphController.create( + mActivity, + mWindowAndroid, + mActivityTabProvider, + profile, + getToolbarManager().getMenuButtonView(), + mAppMenuCoordinator.getAppMenuHandler(), + /* isBrowserApp= */ false); } if (!didShowPrompt
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/pdf/PdfPageIphController.java b/chrome/android/java/src/org/chromium/chrome/browser/pdf/PdfPageIphController.java index 22666028..20ff7ec 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/pdf/PdfPageIphController.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/pdf/PdfPageIphController.java
@@ -4,37 +4,89 @@ package org.chromium.chrome.browser.pdf; +import android.app.Activity; +import android.content.Context; +import android.os.Handler; +import android.os.Looper; +import android.view.View; + import org.chromium.chrome.R; import org.chromium.chrome.browser.ActivityTabProvider; import org.chromium.chrome.browser.ActivityTabProvider.ActivityTabTabObserver; +import org.chromium.chrome.browser.feature_engagement.TrackerFactory; +import org.chromium.chrome.browser.flags.ChromeFeatureList; +import org.chromium.chrome.browser.profiles.Profile; import org.chromium.chrome.browser.tab.Tab; +import org.chromium.chrome.browser.ui.appmenu.AppMenuHandler; +import org.chromium.chrome.browser.user_education.IphCommandBuilder; +import org.chromium.chrome.browser.user_education.UserEducationHelper; +import org.chromium.components.feature_engagement.FeatureConstants; +import org.chromium.components.feature_engagement.Tracker; import org.chromium.ui.base.DeviceFormFactor; import org.chromium.ui.base.WindowAndroid; import org.chromium.url.GURL; -// TODO(crbug.com/405813894): Placeholder class to be implemented. /** Controller to manage PDF page in-product-help messages to users. */ public class PdfPageIphController { + private final UserEducationHelper mUserEducationHelper; private final WindowAndroid mWindowAndroid; + private final AppMenuHandler mAppMenuHandler; + private final View mToolbarMenuButton; private final ActivityTabProvider mActivityTabProvider; private ActivityTabTabObserver mActivityTabTabObserver; + private final Context mContext; + private final boolean mIsBrowserApp; /** * Creates and initializes the controller. Registers an {@link ActivityTabTabObserver} that * attempts to show the pdf download IPH when the download button is not in the omnibox. * + * @param activity The current activity. * @param windowAndroid The window associated with the activity. * @param activityTabProvider The provider of the current activity tab. + * @param profile The current {@link Profile}. + * @param toolbarMenuButton The toolbar menu button to which the IPH will be anchored. + * @param appMenuHandler The app menu handler. + * @param isBrowserApp Whether the current activity is ChromeTabbedActivity. */ public static PdfPageIphController create( - WindowAndroid windowAndroid, ActivityTabProvider activityTabProvider) { - return new PdfPageIphController(windowAndroid, activityTabProvider); + Activity activity, + WindowAndroid windowAndroid, + ActivityTabProvider activityTabProvider, + Profile profile, + View toolbarMenuButton, + AppMenuHandler appMenuHandler, + boolean isBrowserApp) { + if (!PdfUtils.shouldOpenPdfInline(profile.isOffTheRecord())) { + return null; + } + return new PdfPageIphController( + windowAndroid, + activityTabProvider, + profile, + toolbarMenuButton, + appMenuHandler, + new UserEducationHelper(activity, profile, new Handler(Looper.getMainLooper())), + isBrowserApp); } - PdfPageIphController(WindowAndroid windowAndroid, ActivityTabProvider activityTabProvider) { + PdfPageIphController( + WindowAndroid windowAndroid, + ActivityTabProvider activityTabProvider, + Profile profile, + View toolbarMenuButton, + AppMenuHandler appMenuHandler, + UserEducationHelper userEducationHelper, + boolean isBrowserApp) { mWindowAndroid = windowAndroid; + mToolbarMenuButton = toolbarMenuButton; + mContext = mToolbarMenuButton.getContext(); + mAppMenuHandler = appMenuHandler; + mUserEducationHelper = userEducationHelper; mActivityTabProvider = activityTabProvider; - createActivityTabTabObserver(); + mIsBrowserApp = isBrowserApp; + + createActivityTabTabObserver(profile); } public void destroy() { @@ -43,7 +95,7 @@ } } - private void createActivityTabTabObserver() { + private void createActivityTabTabObserver(Profile profile) { mActivityTabTabObserver = new ActivityTabTabObserver(mActivityTabProvider) { @Override @@ -51,21 +103,56 @@ if (tab == null || !tab.isNativePage() || !tab.getNativePage().isPdf()) { return; } - showDownloadIph(); + showDownloadIph(profile); } }; } - private void showDownloadIph() { - @SuppressWarnings("UnusedVariable") + ActivityTabTabObserver getActiveTabObserverForTesting() { + return mActivityTabTabObserver; + } + + private void showDownloadIph(Profile profile) { + boolean isTablet = DeviceFormFactor.isWindowOnTablet(mWindowAndroid); + // Do now show IPH if the download button is shown in the toolbar. + if (isTablet + && mIsBrowserApp + && !ChromeFeatureList.sHideTabletToolbarDownloadButton.isEnabled()) { + return; + } + Tracker tracker = TrackerFactory.getTrackerForProfile(profile); + String featureName = FeatureConstants.IPH_PDF_PAGE_DOWNLOAD; + if (!tracker.wouldTriggerHelpUi(featureName)) { + return; + } + int highlightMenuItemId; - if (DeviceFormFactor.isWindowOnTablet(mWindowAndroid)) { + if (isTablet && mIsBrowserApp) { highlightMenuItemId = R.id.download_page_id; } else { highlightMenuItemId = R.id.offline_page_id; } + requestShowDownloadIph(featureName, highlightMenuItemId); + } - @SuppressWarnings("UnusedVariable") - int textId = R.string.pdf_page_download_iph_text; + private void requestShowDownloadIph(String featureName, int highlightMenuItemId) { + mUserEducationHelper.requestShowIph( + new IphCommandBuilder( + mContext.getResources(), + featureName, + R.string.pdf_page_download_iph_text, + R.string.pdf_page_download_iph_text) + .setAnchorView(mToolbarMenuButton) + .setOnShowCallback(() -> turnOnHighlightForMenuItem(highlightMenuItemId)) + .setOnDismissCallback(this::turnOffHighlightForMenuItem) + .build()); + } + + private void turnOnHighlightForMenuItem(int highlightMenuItemId) { + mAppMenuHandler.setMenuHighlight(highlightMenuItemId); + } + + private void turnOffHighlightForMenuItem() { + mAppMenuHandler.clearMenuHighlight(); } }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/tabbed_mode/TabbedRootUiCoordinator.java b/chrome/android/java/src/org/chromium/chrome/browser/tabbed_mode/TabbedRootUiCoordinator.java index 0158196..364f123d 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/tabbed_mode/TabbedRootUiCoordinator.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/tabbed_mode/TabbedRootUiCoordinator.java
@@ -114,6 +114,7 @@ import org.chromium.chrome.browser.offlinepages.indicator.OfflineIndicatorControllerV2; import org.chromium.chrome.browser.offlinepages.indicator.OfflineIndicatorInProductHelpController; import org.chromium.chrome.browser.omnibox.UrlFocusChangeListener; +import org.chromium.chrome.browser.pdf.PdfPageIphController; import org.chromium.chrome.browser.preferences.ChromePreferenceKeys; import org.chromium.chrome.browser.preferences.ChromeSharedPreferences; import org.chromium.chrome.browser.privacy_sandbox.ActivityTypeMapper; @@ -224,6 +225,7 @@ private ReadAloudIphController mReadAloudIphController; private ReadLaterIphController mReadLaterIphController; private DesktopSiteSettingsIphController mDesktopSiteSettingsIphController; + private PdfPageIphController mPdfPageIphController; private RtlGestureNavIphController mRtlGestureNavIphController; private WebFeedFollowIntroController mWebFeedFollowIntroController; private UrlFocusChangeListener mUrlFocusChangeListener; @@ -609,6 +611,11 @@ mDesktopSiteSettingsIphController = null; } + if (mPdfPageIphController != null) { + mPdfPageIphController.destroy(); + mPdfPageIphController = null; + } + if (mRtlGestureNavIphController != null) { mRtlGestureNavIphController.destroy(); mRtlGestureNavIphController = null; @@ -1096,13 +1103,23 @@ mAppMenuCoordinator.getAppMenuHandler(), R.id.manage_all_windows_menu_id); } - DesktopSiteSettingsIphController.create( - mActivity, - mWindowAndroid, - mActivityTabProvider, - profile, - getToolbarManager().getMenuButtonView(), - mAppMenuCoordinator.getAppMenuHandler()); + mDesktopSiteSettingsIphController = + DesktopSiteSettingsIphController.create( + mActivity, + mWindowAndroid, + mActivityTabProvider, + profile, + getToolbarManager().getMenuButtonView(), + mAppMenuCoordinator.getAppMenuHandler()); + mPdfPageIphController = + PdfPageIphController.create( + mActivity, + mWindowAndroid, + mActivityTabProvider, + profile, + getToolbarManager().getMenuButtonView(), + mAppMenuCoordinator.getAppMenuHandler(), + /* isBrowserApp= */ true); } mPromoShownOneshotSupplier.set(didTriggerPromo);
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/settings/MainSettingsFragmentTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/settings/MainSettingsFragmentTest.java index 94f5491..b9598984 100644 --- a/chrome/android/javatests/src/org/chromium/chrome/browser/settings/MainSettingsFragmentTest.java +++ b/chrome/android/javatests/src/org/chromium/chrome/browser/settings/MainSettingsFragmentTest.java
@@ -158,6 +158,7 @@ @RunWith(ChromeJUnit4ClassRunner.class) @CommandLineFlags.Add({ChromeSwitches.DISABLE_FIRST_RUN_EXPERIENCE, "show-autofill-signatures"}) @DoNotBatch(reason = "Tests cannot run batched because they launch a Settings activity.") +@DisableFeatures(ChromeFeatureList.DATA_SHARING) public class MainSettingsFragmentTest { private static final String SEARCH_ENGINE_SHORT_NAME = "Google";
diff --git a/chrome/android/junit/BUILD.gn b/chrome/android/junit/BUILD.gn index f5c633f..b72181cb 100644 --- a/chrome/android/junit/BUILD.gn +++ b/chrome/android/junit/BUILD.gn
@@ -1284,6 +1284,7 @@ "src/org/chromium/chrome/browser/night_mode/NightModeUtilsTest.java", "src/org/chromium/chrome/browser/payments/AddressEditorTest.java", "src/org/chromium/chrome/browser/payments/ContactEditorTest.java", + "src/org/chromium/chrome/browser/pdf/PdfPageIphControllerUnitTest.java", "src/org/chromium/chrome/browser/privacy/settings/PrivacyPreferencesManagerImplTest.java", "src/org/chromium/chrome/browser/quickactionsearchwidget/QuickActionSearchWidgetProviderTest.java", "src/org/chromium/chrome/browser/read_later/ReadLaterIphControllerUnitTest.java",
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/pdf/OWNERS b/chrome/android/junit/src/org/chromium/chrome/browser/pdf/OWNERS new file mode 100644 index 0000000..ef38d56 --- /dev/null +++ b/chrome/android/junit/src/org/chromium/chrome/browser/pdf/OWNERS
@@ -0,0 +1 @@ +file://chrome/android/java/src/org/chromium/chrome/browser/pdf/OWNERS
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/pdf/PdfPageIphControllerUnitTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/pdf/PdfPageIphControllerUnitTest.java new file mode 100644 index 0000000..218e8bd --- /dev/null +++ b/chrome/android/junit/src/org/chromium/chrome/browser/pdf/PdfPageIphControllerUnitTest.java
@@ -0,0 +1,213 @@ +// 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.pdf; + +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import android.content.Context; +import android.view.View; + +import androidx.test.core.app.ApplicationProvider; + +import org.junit.After; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.ArgumentCaptor; +import org.mockito.Captor; +import org.mockito.Mock; +import org.mockito.junit.MockitoJUnit; +import org.mockito.junit.MockitoRule; +import org.robolectric.annotation.Config; + +import org.chromium.base.test.BaseRobolectricTestRunner; +import org.chromium.base.test.util.Features.DisableFeatures; +import org.chromium.chrome.R; +import org.chromium.chrome.browser.ActivityTabProvider; +import org.chromium.chrome.browser.ActivityTabProvider.ActivityTabTabObserver; +import org.chromium.chrome.browser.feature_engagement.TrackerFactory; +import org.chromium.chrome.browser.flags.ChromeFeatureList; +import org.chromium.chrome.browser.profiles.Profile; +import org.chromium.chrome.browser.tab.Tab; +import org.chromium.chrome.browser.ui.appmenu.AppMenuHandler; +import org.chromium.chrome.browser.ui.native_page.NativePage; +import org.chromium.chrome.browser.user_education.IphCommand; +import org.chromium.chrome.browser.user_education.UserEducationHelper; +import org.chromium.components.feature_engagement.FeatureConstants; +import org.chromium.components.feature_engagement.Tracker; +import org.chromium.ui.base.WindowAndroid; +import org.chromium.url.JUnitTestGURLs; + +import java.lang.ref.WeakReference; + +/** Unit tests for {@link PdfPageIphController}. */ +@RunWith(BaseRobolectricTestRunner.class) +public class PdfPageIphControllerUnitTest { + @Rule public final MockitoRule mMockitoRule = MockitoJUnit.rule(); + @Mock private WindowAndroid mWindowAndroid; + @Mock private ActivityTabProvider mActivityTabProvider; + @Mock private View mToolbarMenuButton; + @Mock private AppMenuHandler mAppMenuHandler; + @Mock private UserEducationHelper mUserEducationHelper; + @Mock private WeakReference<Context> mWeakReferenceContext; + @Mock private Tab mTab; + @Mock private NativePage mNativePage; + @Mock private Tracker mTracker; + @Mock private Profile mProfile; + + @Captor private ArgumentCaptor<IphCommand> mIphCommandCaptor; + + private PdfPageIphController mController; + + @Before + public void setUp() { + PdfUtils.setShouldOpenPdfInlineForTesting(true); + Context context = ApplicationProvider.getApplicationContext(); + doReturn(mWeakReferenceContext).when(mWindowAndroid).getContext(); + doReturn(context).when(mWeakReferenceContext).get(); + doReturn(context).when(mToolbarMenuButton).getContext(); + + TrackerFactory.setTrackerForTests(mTracker); + when(mTracker.wouldTriggerHelpUi(FeatureConstants.IPH_PDF_PAGE_DOWNLOAD)).thenReturn(true); + + when(mTab.isNativePage()).thenReturn(true); + when(mTab.getNativePage()).thenReturn(mNativePage); + when(mNativePage.isPdf()).thenReturn(true); + } + + @After + public void tearDown() { + TrackerFactory.setTrackerForTests(null); + } + + @Test + @Config(qualifiers = "sw320dp") + public void testDownloadIph() { + initializeController(true); + ActivityTabTabObserver activityTabTabObserver = + mController.getActiveTabObserverForTesting(); + activityTabTabObserver.onPageLoadFinished(mTab, JUnitTestGURLs.EXAMPLE_URL); + verify(mUserEducationHelper).requestShowIph(mIphCommandCaptor.capture()); + // The download button in CTA appears in the icon row for phone. + verifyIphCommand(true); + } + + @Test + @Config(qualifiers = "sw320dp") + public void testDownloadIph_CCT() { + initializeController(false); + ActivityTabTabObserver activityTabTabObserver = + mController.getActiveTabObserverForTesting(); + activityTabTabObserver.onPageLoadFinished(mTab, JUnitTestGURLs.EXAMPLE_URL); + verify(mUserEducationHelper).requestShowIph(mIphCommandCaptor.capture()); + // The download button in CCT appears in the icon row regardless of whether it is on a phone + // or tablet. + verifyIphCommand(true); + } + + @Test + @Config(qualifiers = "sw320dp") + public void testDownloadIph_NullTab() { + initializeController(true); + ActivityTabTabObserver activityTabTabObserver = + mController.getActiveTabObserverForTesting(); + activityTabTabObserver.onPageLoadFinished(null, JUnitTestGURLs.EXAMPLE_URL); + verify(mUserEducationHelper, never()).requestShowIph(mIphCommandCaptor.capture()); + } + + @Test + @Config(qualifiers = "sw320dp") + public void testDownloadIph_NotPdf() { + initializeController(true); + when(mNativePage.isPdf()).thenReturn(false); + ActivityTabTabObserver activityTabTabObserver = + mController.getActiveTabObserverForTesting(); + activityTabTabObserver.onPageLoadFinished(mTab, JUnitTestGURLs.EXAMPLE_URL); + verify(mUserEducationHelper, never()).requestShowIph(mIphCommandCaptor.capture()); + } + + @Test + @Config(qualifiers = "sw600dp") + public void testDownloadIph_Tablet() { + initializeController(true); + ActivityTabTabObserver activityTabTabObserver = + mController.getActiveTabObserverForTesting(); + activityTabTabObserver.onPageLoadFinished(mTab, JUnitTestGURLs.EXAMPLE_URL); + verify(mUserEducationHelper).requestShowIph(mIphCommandCaptor.capture()); + // The download button in CTA appears in the menu row for tablet. + verifyIphCommand(false); + } + + @Test + @Config(qualifiers = "sw600dp") + public void testDownloadIph_Tablet_CCT() { + initializeController(false); + ActivityTabTabObserver activityTabTabObserver = + mController.getActiveTabObserverForTesting(); + activityTabTabObserver.onPageLoadFinished(mTab, JUnitTestGURLs.EXAMPLE_URL); + verify(mUserEducationHelper).requestShowIph(mIphCommandCaptor.capture()); + // The download button in CCT appears in the icon row regardless of whether it is on a phone + // or tablet. + verifyIphCommand(true); + } + + @Test + @DisableFeatures(ChromeFeatureList.HIDE_TABLET_TOOLBAR_DOWNLOAD_BUTTON) + @Config(qualifiers = "sw600dp") + public void testDownloadIph_Tablet_DownloadButtonInToolbar() { + initializeController(true); + ActivityTabTabObserver activityTabTabObserver = + mController.getActiveTabObserverForTesting(); + activityTabTabObserver.onPageLoadFinished(mTab, JUnitTestGURLs.EXAMPLE_URL); + verify(mUserEducationHelper, never()).requestShowIph(mIphCommandCaptor.capture()); + } + + @Test + @Config(qualifiers = "sw320dp") + public void testDownloadIph_TrackerWouldNotTrigger() { + initializeController(true); + when(mTracker.wouldTriggerHelpUi(FeatureConstants.IPH_PDF_PAGE_DOWNLOAD)).thenReturn(false); + ActivityTabTabObserver activityTabTabObserver = + mController.getActiveTabObserverForTesting(); + activityTabTabObserver.onPageLoadFinished(mTab, JUnitTestGURLs.EXAMPLE_URL); + verify(mUserEducationHelper, never()).requestShowIph(mIphCommandCaptor.capture()); + } + + private void initializeController(boolean isBrowserApp) { + mController = + new PdfPageIphController( + mWindowAndroid, + mActivityTabProvider, + mProfile, + mToolbarMenuButton, + mAppMenuHandler, + mUserEducationHelper, + isBrowserApp); + } + + private void verifyIphCommand(boolean isIconRow) { + IphCommand command = mIphCommandCaptor.getValue(); + Assert.assertEquals( + "IphCommand feature should match.", + FeatureConstants.IPH_PDF_PAGE_DOWNLOAD, + command.featureName); + Assert.assertEquals( + "IphCommand stringId should match.", + R.string.pdf_page_download_iph_text, + command.stringId); + + command.onShowCallback.run(); + verify(mAppMenuHandler) + .setMenuHighlight(isIconRow ? R.id.offline_page_id : R.id.download_page_id); + + command.onDismissCallback.run(); + verify(mAppMenuHandler).clearMenuHighlight(); + } +}
diff --git a/chrome/browser/BUILD.gn b/chrome/browser/BUILD.gn index 7d2f5044..2eddbe5 100644 --- a/chrome/browser/BUILD.gn +++ b/chrome/browser/BUILD.gn
@@ -3745,6 +3745,8 @@ "enterprise/signals/context_info_fetcher.h", "enterprise/signals/device_info_fetcher.cc", "enterprise/signals/device_info_fetcher.h", + "enterprise/signals/profile_signals_collector.cc", + "enterprise/signals/profile_signals_collector.h", "first_run/first_run.cc", "first_run/first_run.h", "first_run/first_run_dialog.h",
diff --git a/chrome/browser/about_flags.cc b/chrome/browser/about_flags.cc index 3d9837a8..72f6d9c 100644 --- a/chrome/browser/about_flags.cc +++ b/chrome/browser/about_flags.cc
@@ -756,6 +756,17 @@ std::size(kOptimizationGuidePersonalizedFetchingAllowPageInsights), nullptr}}; +const FeatureEntry::FeatureParam kFeedHeaderRemovalParam1 = { + feed::kFeedHeaderRemovalTreatmentParam, + feed::kFeedHeaderRemovalTreatmentValue1}; +const FeatureEntry::FeatureParam kFeedHeaderRemovalParam2 = { + feed::kFeedHeaderRemovalTreatmentParam, + feed::kFeedHeaderRemovalTreatmentValue2}; +const FeatureEntry::FeatureVariation kFeedHeaderRemovalVariations[] = { + {"1", &kFeedHeaderRemovalParam1, 1, nullptr}, + {"2", &kFeedHeaderRemovalParam2, 1, nullptr}, +}; + #endif // BUILDFLAG(IS_ANDROID) #if !BUILDFLAG(IS_CHROMEOS) @@ -4746,6 +4757,9 @@ flag_descriptions::kAshEnableUnifiedDesktopName, flag_descriptions::kAshEnableUnifiedDesktopDescription, kOsCrOS, SINGLE_VALUE_TYPE(switches::kEnableUnifiedDesktop)}, + {"disable-system-blur", flag_descriptions::kDisableSystemBlur, + flag_descriptions::kDisableSystemBlurDescription, kOsCrOS, + FEATURE_VALUE_TYPE(chromeos::features::kDisableSystemBlur)}, {"rounded-windows", flag_descriptions::kRoundedWindows, flag_descriptions::kRoundedWindowsDescription, kOsCrOS, FEATURE_WITH_PARAMS_VALUE_TYPE(chromeos::features::kRoundedWindows, @@ -5809,6 +5823,11 @@ {"refresh-feed-on-start", flag_descriptions::kRefreshFeedOnRestartName, flag_descriptions::kRefreshFeedOnRestartDescription, kOsAndroid, FEATURE_VALUE_TYPE(feed::kRefreshFeedOnRestart)}, + {"feed-header-removal", flag_descriptions::kFeedHeaderRemovalName, + flag_descriptions::kFeedHeaderRemovalDescription, kOsAndroid, + FEATURE_WITH_PARAMS_VALUE_TYPE(feed::kFeedHeaderRemoval, + kFeedHeaderRemovalVariations, + "FeedHeaderRemoval")}, #endif // BUILDFLAG(IS_ANDROID) {"enable-force-dark", flag_descriptions::kAutoWebContentsDarkModeName, flag_descriptions::kAutoWebContentsDarkModeDescription, kOsAll,
diff --git a/chrome/browser/accessibility/tree_fixing/BUILD.gn b/chrome/browser/accessibility/tree_fixing/BUILD.gn index 3b81e16..1ef531d8 100644 --- a/chrome/browser/accessibility/tree_fixing/BUILD.gn +++ b/chrome/browser/accessibility/tree_fixing/BUILD.gn
@@ -53,6 +53,8 @@ sources = [ "internal/ax_tree_fixing_screen_ai_service.cc", "internal/ax_tree_fixing_screen_ai_service.h", + "internal/ax_tree_fixing_screenshotter.cc", + "internal/ax_tree_fixing_screenshotter.h", ] public_deps = [ "//ui/accessibility:ax_base" ] @@ -61,6 +63,9 @@ "//base", "//chrome/browser/profiles:profile", "//chrome/browser/screen_ai:screen_ai_service_router_factory", + "//components/paint_preview/browser", + "//components/paint_preview/common", + "//components/paint_preview/public", "//content/public/browser:browser", "//services/screen_ai/public/mojom:mojom", ]
diff --git a/chrome/browser/accessibility/tree_fixing/internal/DEPS b/chrome/browser/accessibility/tree_fixing/internal/DEPS new file mode 100644 index 0000000..20c72e0b --- /dev/null +++ b/chrome/browser/accessibility/tree_fixing/internal/DEPS
@@ -0,0 +1,6 @@ +specific_include_rules = { + 'ax_tree_fixing_screenshotter.*': [ + "+components/paint_preview/common/recording_map.h", + "+components/paint_preview/public/paint_preview_compositor_service.h", + ] +} \ No newline at end of file
diff --git a/chrome/browser/accessibility/tree_fixing/internal/ax_tree_fixing_screenshotter.cc b/chrome/browser/accessibility/tree_fixing/internal/ax_tree_fixing_screenshotter.cc new file mode 100644 index 0000000..d590863 --- /dev/null +++ b/chrome/browser/accessibility/tree_fixing/internal/ax_tree_fixing_screenshotter.cc
@@ -0,0 +1,120 @@ +// 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/accessibility/tree_fixing/internal/ax_tree_fixing_screenshotter.h" + +#include <string> + +#include "base/base_paths.h" +#include "base/files/file_path.h" +#include "base/functional/callback_helpers.h" +#include "base/path_service.h" +#include "base/strings/stringprintf.h" +#include "base/task/task_traits.h" +#include "base/task/thread_pool.h" +#include "components/paint_preview/browser/compositor_utils.h" +#include "components/paint_preview/browser/paint_preview_base_service.h" +#include "components/paint_preview/common/recording_map.h" +#include "third_party/skia/include/core/SkStream.h" +#include "third_party/skia/include/encode/SkPngEncoder.h" +#include "ui/gfx/geometry/rect.h" + +constexpr size_t kMaxPerCaptureSizeBytes = 50 * 1000L * 1000L; // 50 MB. + +AXTreeFixingScreenshotter::AXTreeFixingScreenshotter() + : paint_preview::PaintPreviewBaseService( + /*file_mixin=*/nullptr, // in-memory captures + /*policy=*/nullptr, // all content is deemed amenable + /*is_off_the_record=*/false), + compositor_service_(nullptr, base::OnTaskRunnerDeleter(nullptr)), + compositor_client_(nullptr, base::OnTaskRunnerDeleter(nullptr)) { + compositor_service_ = paint_preview::StartCompositorService(base::BindOnce( + &AXTreeFixingScreenshotter::OnCompositorServiceDisconnected, + weak_ptr_factory_.GetWeakPtr())); + CHECK(compositor_service_); +} + +AXTreeFixingScreenshotter::~AXTreeFixingScreenshotter() = default; + +void AXTreeFixingScreenshotter::RequestScreenshot( + const raw_ptr<content::WebContents> web_contents) { + if (!web_contents) { + return; + } + + CaptureParams capture_params; + capture_params.web_contents = web_contents; + capture_params.persistence = + paint_preview::RecordingPersistence::kMemoryBuffer; + capture_params.max_per_capture_size = kMaxPerCaptureSizeBytes; + CapturePaintPreview( + capture_params, + base::BindOnce(&AXTreeFixingScreenshotter::OnScreenshotCaptured, + weak_ptr_factory_.GetWeakPtr())); +} + +void AXTreeFixingScreenshotter::OnCompositorServiceDisconnected() { + compositor_client_.reset(); + compositor_service_.reset(); +} + +void AXTreeFixingScreenshotter::OnScreenshotCaptured( + paint_preview::PaintPreviewBaseService::CaptureStatus status, + std::unique_ptr<paint_preview::CaptureResult> result) { + if (status != PaintPreviewBaseService::CaptureStatus::kOk || + !result->capture_success) { + return; + } + if (!compositor_client_) { + compositor_client_ = compositor_service_->CreateCompositor( + base::BindOnce(&AXTreeFixingScreenshotter::SendCompositeRequest, + weak_ptr_factory_.GetWeakPtr(), std::move(result))); + return; + } + SendCompositeRequest(std::move(result)); +} + +void AXTreeFixingScreenshotter::SendCompositeRequest( + std::unique_ptr<paint_preview::CaptureResult> result) { + paint_preview::mojom::PaintPreviewBeginCompositeRequestPtr request = + paint_preview::mojom::PaintPreviewBeginCompositeRequest::New(); + std::pair<paint_preview::RecordingMap, paint_preview::PaintPreviewProto> + map_and_proto = + paint_preview::RecordingMapFromCaptureResult(std::move(*result)); + request->recording_map = std::move(map_and_proto.first); + request->preview = mojo_base::ProtoWrapper(std::move(map_and_proto.second)); + compositor_client_->BeginMainFrameComposite( + std::move(request), + base::BindOnce(&AXTreeFixingScreenshotter::OnCompositeFinished, + weak_ptr_factory_.GetWeakPtr())); +} + +void AXTreeFixingScreenshotter::OnCompositeFinished( + paint_preview::mojom::PaintPreviewCompositor::BeginCompositeStatus status, + paint_preview::mojom::PaintPreviewBeginCompositeResponsePtr response) { + if (status != paint_preview::mojom::PaintPreviewCompositor:: + BeginCompositeStatus::kSuccess && + status != paint_preview::mojom::PaintPreviewCompositor:: + BeginCompositeStatus::kPartialSuccess) { + return; + } + + // Pass an empty `gfx::Rect` allows us to get a bitmap for the full page. + compositor_client_->BitmapForMainFrame( + gfx::Rect(), /*scale_factor=*/1.0, + base::BindOnce(&AXTreeFixingScreenshotter::OnBitmapReceived, + weak_ptr_factory_.GetWeakPtr())); +} + +void AXTreeFixingScreenshotter::OnBitmapReceived( + paint_preview::mojom::PaintPreviewCompositor::BitmapStatus status, + const SkBitmap& bitmap) { + if (status != paint_preview::mojom::PaintPreviewCompositor::BitmapStatus:: + kSuccess || + bitmap.empty()) { + return; + } + + // TODO: Save bitmap. +}
diff --git a/chrome/browser/accessibility/tree_fixing/internal/ax_tree_fixing_screenshotter.h b/chrome/browser/accessibility/tree_fixing/internal/ax_tree_fixing_screenshotter.h new file mode 100644 index 0000000..c0cdf4a --- /dev/null +++ b/chrome/browser/accessibility/tree_fixing/internal/ax_tree_fixing_screenshotter.h
@@ -0,0 +1,75 @@ +// 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_ACCESSIBILITY_TREE_FIXING_INTERNAL_AX_TREE_FIXING_SCREENSHOTTER_H_ +#define CHROME_BROWSER_ACCESSIBILITY_TREE_FIXING_INTERNAL_AX_TREE_FIXING_SCREENSHOTTER_H_ + +#include <memory> + +#include "base/memory/raw_ptr.h" +#include "base/memory/weak_ptr.h" +#include "components/paint_preview/browser/paint_preview_base_service.h" +#include "components/paint_preview/public/paint_preview_compositor_service.h" +#include "content/public/browser/web_contents.h" + +// Used to capture a full-page screenshot of a WebContents. +class AXTreeFixingScreenshotter + : public paint_preview::PaintPreviewBaseService { + public: + AXTreeFixingScreenshotter(); + AXTreeFixingScreenshotter(const AXTreeFixingScreenshotter&) = delete; + AXTreeFixingScreenshotter& operator=(const AXTreeFixingScreenshotter&) = + delete; + ~AXTreeFixingScreenshotter() override; + + // Initiates the process of capturing a paint preview screenshot for the + // given |web_contents|. On completion, OnScreenshotCaptured() will be + // invoked. Does nothing if |web_contents| is null. + void RequestScreenshot(const raw_ptr<content::WebContents> web_contents); + + private: + // Called when the connection to the Paint Preview Compositor Service is lost. + // Resets the compositor client and service pointers to ensure they are not + // used in a disconnected state. + void OnCompositorServiceDisconnected(); + + // Callback invoked when the capture initiated by RequestScreenshot() + // completes. If the capture was successful, it proceeds to the compositing + // stage by calling SendCompositeRequest(). If this is the first successful + // capture, it will also establish the connection to the compositor service + // via CreateCompositor. + void OnScreenshotCaptured( + paint_preview::PaintPreviewBaseService::CaptureStatus status, + std::unique_ptr<paint_preview::CaptureResult> result); + + // Sends the captured paint preview data (|result|) to the compositor service + // to begin compositing into a bitmap. + // OnCompositeFinished() will be called upon completion of the compositing. + void SendCompositeRequest( + std::unique_ptr<paint_preview::CaptureResult> result); + + // Callback invoked when the compositor service finishes the request initiated + // by SendCompositeRequest(). If compositing was successful (or partially + // successful), it requests the final bitmap of the main frame. + void OnCompositeFinished( + paint_preview::mojom::PaintPreviewCompositor::BeginCompositeStatus status, + paint_preview::mojom::PaintPreviewBeginCompositeResponsePtr response); + + // Callback invoked when the compositor service provides the requested bitmap. + // This is the final step in the screenshot pipeline. + void OnBitmapReceived( + paint_preview::mojom::PaintPreviewCompositor::BitmapStatus status, + const SkBitmap& bitmap); + + std::unique_ptr<paint_preview::PaintPreviewCompositorService, + base::OnTaskRunnerDeleter> + compositor_service_; + std::unique_ptr<paint_preview::PaintPreviewCompositorClient, + base::OnTaskRunnerDeleter> + compositor_client_; + + base::WeakPtrFactory<AXTreeFixingScreenshotter> weak_ptr_factory_{this}; +}; + +#endif // CHROME_BROWSER_ACCESSIBILITY_TREE_FIXING_INTERNAL_AX_TREE_FIXING_SCREENSHOTTER_H_
diff --git a/chrome/browser/ai/ai_language_model.cc b/chrome/browser/ai/ai_language_model.cc index c00f524..1eb246cc 100644 --- a/chrome/browser/ai/ai_language_model.cc +++ b/chrome/browser/ai/ai_language_model.cc
@@ -410,6 +410,7 @@ void AILanguageModel::PromptGetInputSizeCompletion( mojo::RemoteSetElementId responder_id, Context::ContextItem current_item, + const std::optional<std::string>& response_json_schema, std::optional<uint32_t> result) { if (!session_) { // If the session is destroyed before this callback is invoked, we should @@ -447,8 +448,8 @@ MultimodalMessage request = context_->MakeRequest(); AddCurrentRequest(request, current_item); session_->SetInput(std::move(request)); - session_->ExecuteModel( - PromptApiRequest(), + session_->ExecuteModelWithResponseJsonSchema( + PromptApiRequest(), response_json_schema, base::BindRepeating(&AILanguageModel::ModelExecutionCallback, weak_ptr_factory_.GetWeakPtr(), std::move(current_item), responder_id)); @@ -514,6 +515,7 @@ void AILanguageModel::Prompt( std::vector<blink::mojom::AILanguageModelPromptPtr> prompts, + const std::optional<std::string>& response_json_schema, mojo::PendingRemote<blink::mojom::ModelStreamingResponder> pending_responder) { if (!session_) { @@ -562,7 +564,7 @@ request.read(), base::BindOnce(&AILanguageModel::PromptGetInputSizeCompletion, weak_ptr_factory_.GetWeakPtr(), responder_id, - std::move(item))); + std::move(item), response_json_schema)); } void AILanguageModel::Fork(
diff --git a/chrome/browser/ai/ai_language_model.h b/chrome/browser/ai/ai_language_model.h index 814b972..a1c2eca9 100644 --- a/chrome/browser/ai/ai_language_model.h +++ b/chrome/browser/ai/ai_language_model.h
@@ -158,6 +158,7 @@ // `blink::mojom::AILanguageModel` implementation. void Prompt(std::vector<blink::mojom::AILanguageModelPromptPtr> prompts, + const std::optional<std::string>& response_json_schema, mojo::PendingRemote<blink::mojom::ModelStreamingResponder> pending_responder) override; void Fork( @@ -179,9 +180,11 @@ mojo::PendingRemote<blink::mojom::AILanguageModel> TakePendingRemote(); private: - void PromptGetInputSizeCompletion(mojo::RemoteSetElementId responder_id, - Context::ContextItem current_item, - std::optional<uint32_t> result); + void PromptGetInputSizeCompletion( + mojo::RemoteSetElementId responder_id, + Context::ContextItem current_item, + const std::optional<std::string>& response_json_schema, + std::optional<uint32_t> result); void ModelExecutionCallback( const Context::ContextItem& current_item, mojo::RemoteSetElementId responder_id,
diff --git a/chrome/browser/ai/ai_language_model_unittest.cc b/chrome/browser/ai/ai_language_model_unittest.cc index d6c15c8..b058416 100644 --- a/chrome/browser/ai/ai_language_model_unittest.cc +++ b/chrome/browser/ai/ai_language_model_unittest.cc
@@ -335,9 +335,10 @@ } }); - EXPECT_CALL(*session, ExecuteModel(_, _)) + EXPECT_CALL(*session, ExecuteModelWithResponseJsonSchema(_, _, _)) .WillOnce( [&](const google::protobuf::MessageLite& request_metadata, + const std::optional<std::string>& response_json_schema, optimization_guide:: OptimizationGuideModelExecutionResultStreamingCallback callback) { @@ -361,9 +362,10 @@ options.expected_cloned_context + options.expected_prompt); }); - EXPECT_CALL(*session, ExecuteModel(_, _)) + EXPECT_CALL(*session, ExecuteModelWithResponseJsonSchema(_, _, _)) .WillOnce( [&](const google::protobuf::MessageLite& request_metadata, + const std::optional<std::string>& response_json_schema, optimization_guide:: OptimizationGuideModelExecutionResultStreamingCallback callback) { @@ -511,7 +513,8 @@ }); // The model should not be executed. - EXPECT_CALL(*session, ExecuteModel(_, _)).Times(0); + EXPECT_CALL(*session, ExecuteModelWithResponseJsonSchema(_, _, _)) + .Times(0); return session; }); @@ -586,10 +589,11 @@ : "U: A\nM: OK\nU: B\nM: "); }); - EXPECT_CALL(*session, ExecuteModel(_, _)) + EXPECT_CALL(*session, ExecuteModelWithResponseJsonSchema(_, _, _)) .Times(2) .WillRepeatedly( [&](const google::protobuf::MessageLite& request_metadata, + const std::optional<std::string>& response_json_schema, optimization_guide:: OptimizationGuideModelExecutionResultStreamingCallback callback) { @@ -631,10 +635,10 @@ responder_run_loop_2.Quit(); })); - mock_session->Prompt(MakeInput("A"), + mock_session->Prompt(MakeInput("A"), /*response_json_schema=*/std::nullopt, mock_responder_1.BindNewPipeAndPassRemote()); responder_run_loop_1.Run(); - mock_session->Prompt(MakeInput("B"), + mock_session->Prompt(MakeInput("B"), /*response_json_schema=*/std::nullopt, mock_responder_2.BindNewPipeAndPassRemote()); responder_run_loop_2.Run(); } @@ -716,6 +720,7 @@ })); mock_session->Prompt(MakeInput(prompt), + /*response_json_schema=*/std::nullopt, mock_responder.BindNewPipeAndPassRemote()); responder_run_loop.Run(); } @@ -861,6 +866,7 @@ AITestUtils::MockModelStreamingResponder& mock_responder) { mock_session->Destroy(); mock_session->Prompt(MakeInput(kTestPrompt), + /*response_json_schema=*/std::nullopt, mock_responder.BindNewPipeAndPassRemote()); })); } @@ -872,6 +878,7 @@ [](mojo::Remote<blink::mojom::AILanguageModel> mock_session, AITestUtils::MockModelStreamingResponder& mock_responder) { mock_session->Prompt(MakeInput(kTestPrompt), + /*response_json_schema=*/std::nullopt, mock_responder.BindNewPipeAndPassRemote()); mock_session->Destroy(); })); @@ -950,9 +957,10 @@ "U: <audio>\n" "M: "); }); - EXPECT_CALL(*session, ExecuteModel(_, _)) + EXPECT_CALL(*session, ExecuteModelWithResponseJsonSchema(_, _, _)) .WillOnce( [&](const google::protobuf::MessageLite& request_metadata, + const std::optional<std::string>& response_json_schema, optimization_guide:: OptimizationGuideModelExecutionResultStreamingCallback callback) { @@ -981,7 +989,7 @@ input.push_back(blink::mojom::AILanguageModelPrompt::New( Role::kUser, blink::mojom::AILanguageModelPromptContent::NewAudio(CreateTestAudio()))); - mock_session->Prompt(std::move(input), + mock_session->Prompt(std::move(input), /*response_json_schema=*/std::nullopt, mock_responder.BindNewPipeAndPassRemote()); run_loop.Run(); } @@ -1233,7 +1241,7 @@ input.push_back(blink::mojom::AILanguageModelPrompt::New( Role::kUser, blink::mojom::AILanguageModelPromptContent::NewBitmap( CreateTestBitmap(10, 10)))); - mock_session->Prompt(std::move(input), + mock_session->Prompt(std::move(input), /*response_json_schema=*/std::nullopt, mock_responder.BindNewPipeAndPassRemote()); run_loop.Run(); }
diff --git a/chrome/browser/ash/accessibility/spoken_feedback_browsertest.cc b/chrome/browser/ash/accessibility/spoken_feedback_browsertest.cc index 05aad8c..46a0d9d 100644 --- a/chrome/browser/ash/accessibility/spoken_feedback_browsertest.cc +++ b/chrome/browser/ash/accessibility/spoken_feedback_browsertest.cc
@@ -1162,8 +1162,7 @@ sm_.Replay(); } -// TODO(crbug.com/407459387): Fix and re-enable this test. -IN_PROC_BROWSER_TEST_P(SpokenFeedbackTest, DISABLED_NavigateChromeVoxMenu) { +IN_PROC_BROWSER_TEST_P(SpokenFeedbackTest, NavigateChromeVoxMenu) { EnableChromeVox(); sm_.Call([this]() { SendKeyPressWithSearch(ui::VKEY_OEM_PERIOD); }); sm_.ExpectSpeech("Search the menus"); @@ -2286,8 +2285,7 @@ } // Tests basic behavior of the tutorial when signed in. -// TODO(crbug.com/407459387): Fix and re-enable this test. -IN_PROC_BROWSER_TEST_P(SpokenFeedbackTest, DISABLED_Tutorial) { +IN_PROC_BROWSER_TEST_P(SpokenFeedbackTest, Tutorial) { EnableChromeVox(); sm_.Call([this]() { NavigateToUrl(GURL(R"(data:text/html;charset=utf-8,
diff --git a/chrome/browser/ash/system_web_apps/system_web_app_manager_unittest.cc b/chrome/browser/ash/system_web_apps/system_web_app_manager_unittest.cc index f124b38..c7ff7de 100644 --- a/chrome/browser/ash/system_web_apps/system_web_app_manager_unittest.cc +++ b/chrome/browser/ash/system_web_apps/system_web_app_manager_unittest.cc
@@ -255,8 +255,10 @@ EXPECT_THAT(externally_managed_app_manager().install_requests(), ElementsAre(options)); - EXPECT_THAT(externally_managed_app_manager().uninstall_requests(), - ElementsAre(AppUrl2())); + EXPECT_FALSE(provider() + .registrar_unsafe() + .LookUpAppIdByInstallUrl(AppUrl2()) + .has_value()); } TEST_F(SystemWebAppManagerTest, AlwaysUpdate) {
diff --git a/chrome/browser/chrome_web_platform_security_metrics_browsertest.cc b/chrome/browser/chrome_web_platform_security_metrics_browsertest.cc index ff4df49..438de83 100644 --- a/chrome/browser/chrome_web_platform_security_metrics_browsertest.cc +++ b/chrome/browser/chrome_web_platform_security_metrics_browsertest.cc
@@ -146,9 +146,6 @@ // SharedArrayBuffer is needed for these tests. features::kSharedArrayBuffer, // Some PNA worker feature relies on this. - // TODO(crbug.com/40263073): Remove this once PNA for workers - // metric logging doesn't rely on kPlzDedicatedWorker - blink::features::kPlzDedicatedWorker, blink::features::kPartitionedPopins, }; }
diff --git a/chrome/browser/data_sharing/DEPS b/chrome/browser/data_sharing/DEPS index e38944b8..6aa1a53 100644 --- a/chrome/browser/data_sharing/DEPS +++ b/chrome/browser/data_sharing/DEPS
@@ -1,4 +1,5 @@ include_rules = [ + "+components/collaboration/internal/metrics.h", "+components/data_sharing/public/data_sharing_sdk_delegate.h", # Minimize dependencies on internal code, but allow service construction. "+components/data_sharing/internal/data_sharing_service_impl.h",
diff --git a/chrome/browser/data_sharing/android/java/src/org/chromium/chrome/browser/data_sharing/DataSharingServiceFactoryTest.java b/chrome/browser/data_sharing/android/java/src/org/chromium/chrome/browser/data_sharing/DataSharingServiceFactoryTest.java index 7a2c504..d49ac240 100644 --- a/chrome/browser/data_sharing/android/java/src/org/chromium/chrome/browser/data_sharing/DataSharingServiceFactoryTest.java +++ b/chrome/browser/data_sharing/android/java/src/org/chromium/chrome/browser/data_sharing/DataSharingServiceFactoryTest.java
@@ -43,22 +43,20 @@ @Test @MediumTest public void testSettingTestFactory() throws TimeoutException { - DataSharingService testService = new TestDataSharingService(); - - DataSharingServiceFactory.setForTesting(testService); + ThreadUtils.runOnUiThreadBlocking( + () -> { + DataSharingService testService = new TestDataSharingService(); + DataSharingServiceFactory.setForTesting(testService); + }); LibraryLoader.getInstance().ensureInitialized(); mActivityTestRule.startMainActivityOnBlankPage(); ThreadUtils.runOnUiThreadBlocking( - new Runnable() { - @Override - public void run() { - DataSharingService dataSharingService = - DataSharingServiceFactory.getForProfile( - ProfileManager.getLastUsedRegularProfile()); - Assert.assertTrue(dataSharingService.isEmptyService()); - Assert.assertEquals(dataSharingService, testService); - } + () -> { + DataSharingService dataSharingService = + DataSharingServiceFactory.getForProfile( + ProfileManager.getLastUsedRegularProfile()); + Assert.assertTrue(dataSharingService.isEmptyService()); }); }
diff --git a/chrome/browser/data_sharing/data_sharing_navigation_throttle.cc b/chrome/browser/data_sharing/data_sharing_navigation_throttle.cc index b902155..59d66ca0 100644 --- a/chrome/browser/data_sharing/data_sharing_navigation_throttle.cc +++ b/chrome/browser/data_sharing/data_sharing_navigation_throttle.cc
@@ -7,6 +7,7 @@ #include "chrome/browser/data_sharing/data_sharing_navigation_utils.h" #include "chrome/browser/data_sharing/data_sharing_service_factory.h" #include "chrome/browser/profiles/profile.h" +#include "components/collaboration/internal/metrics.h" #include "components/data_sharing/public/features.h" #include "content/public/browser/navigation_handle.h" #include "content/public/browser/web_contents.h" @@ -98,6 +99,9 @@ const GURL& url = navigation_handle()->GetURL(); if (data_sharing_service && data_sharing_service->ShouldInterceptNavigationForShareURL(url)) { + collaboration::metrics::RecordJoinPageTransitionType( + data_sharing_service->GetLogger(), + navigation_handle()->GetPageTransition()); if (ShouldHandleShareURLNavigation(navigation_handle())) { data_sharing_service->HandleShareURLNavigationIntercepted( url, /* context = */ nullptr);
diff --git a/chrome/browser/download/chrome_download_manager_delegate.cc b/chrome/browser/download/chrome_download_manager_delegate.cc index f960dec..177076d 100644 --- a/chrome/browser/download/chrome_download_manager_delegate.cc +++ b/chrome/browser/download/chrome_download_manager_delegate.cc
@@ -43,6 +43,7 @@ #include "chrome/browser/download/download_request_limiter.h" #include "chrome/browser/download/download_stats.h" #include "chrome/browser/download/download_target_determiner.h" +#include "chrome/browser/download/download_ui_safe_browsing_util.h" #include "chrome/browser/download/insecure_download_blocking.h" #include "chrome/browser/download/save_package_file_picker.h" #include "chrome/browser/enterprise/connectors/common.h" @@ -183,12 +184,20 @@ namespace { -#if !BUILDFLAG(IS_ANDROID) // How long an ephemeral warning lasts before being automatically canceled (if // there is no user interaction). constexpr base::TimeDelta kEphemeralWarningLifetimeBeforeCancel = base::Hours(1); + +bool IsEphemeralWarningCancellationEnabled() { +#if BUILDFLAG(IS_ANDROID) + return ShouldShowSafeBrowsingAndroidDownloadWarnings(); #else + return download::IsDownloadBubbleEnabled(); +#endif +} + +#if BUILDFLAG(IS_ANDROID) const char kPdfDirName[] = "pdfs"; #endif @@ -480,7 +489,6 @@ } #endif // BUILDFLAG(SAFE_BROWSING_DOWNLOAD_PROTECTION) -#if !BUILDFLAG(IS_ANDROID) // Events related to ephemeral warning cancellation. // These values are persisted to logs. Entries should not be renumbered and // numeric values should never be reused. @@ -503,7 +511,6 @@ base::UmaHistogramEnumeration("SBClientDownload.CancelEphemeralWarning", event); } -#endif // !BUILDFLAG(IS_ANDROID) void OnCheckDownloadAllowedFailed( content::CheckDownloadAllowedCallback check_download_allowed_cb) { @@ -2204,15 +2211,14 @@ download::GetDownloadTaskRunner()->PostTask( FROM_HERE, base::BindOnce([]() { base::DeleteFile(GetTempPdfDir()); })); } -#else - CancelAllEphemeralWarnings(); #endif + + CancelAllEphemeralWarnings(); } -#if !BUILDFLAG(IS_ANDROID) void ChromeDownloadManagerDelegate::ScheduleCancelForEphemeralWarning( const std::string& guid) { - if (!download::IsDownloadBubbleEnabled()) { + if (!IsEphemeralWarningCancellationEnabled()) { return; } LogCancelEphemeralWarningEvent( @@ -2226,6 +2232,7 @@ void ChromeDownloadManagerDelegate::CancelForEphemeralWarning( const std::string& guid) { + CHECK(IsEphemeralWarningCancellationEnabled()); LogCancelEphemeralWarningEvent( CancelEphemeralWarningEvent::kCancellationTriggered); download::DownloadItem* download = download_manager_->GetDownloadByGuid(guid); @@ -2250,7 +2257,7 @@ } void ChromeDownloadManagerDelegate::CancelAllEphemeralWarnings() { - if (!download::IsDownloadBubbleEnabled()) { + if (!IsEphemeralWarningCancellationEnabled()) { return; } content::DownloadManager::DownloadVector downloads; @@ -2263,4 +2270,3 @@ } } } -#endif // !BUILDFLAG(IS_ANDROID)
diff --git a/chrome/browser/download/chrome_download_manager_delegate.h b/chrome/browser/download/chrome_download_manager_delegate.h index 41db535e..b3a0d69 100644 --- a/chrome/browser/download/chrome_download_manager_delegate.h +++ b/chrome/browser/download/chrome_download_manager_delegate.h
@@ -206,12 +206,15 @@ bool ShouldBlockFile(download::DownloadItem* item, download::DownloadDangerType danger_type) const; -#if !BUILDFLAG(IS_ANDROID) - // Schedules the ephemeral warning download to be canceled. It will only be + // Schedules an ephemeral warning download to be canceled. It will only be // canceled if it continues to be an ephemeral warning that hasn't been acted - // on when the scheduled time arrives. + // on when the scheduled time arrives. This should be scheduled after the user + // sees the warning in the UI, i.e. should not be scheduled before the user + // has a chance to see the warning. + // Note: Any scheduled cancellations will be abandoned at browser shutdown, + // but the cancellation should occur when the browser next starts up, in + // CancelAllEphemeralWarnings(). void ScheduleCancelForEphemeralWarning(const std::string& guid); -#endif // Returns true if |path| should open in the browser. virtual bool IsOpenInBrowserPreferredForFile(const base::FilePath& path); @@ -346,9 +349,6 @@ // multiple downloads are associated with the same file path. bool IsMostRecentDownloadItemAtFilePath(download::DownloadItem* download); - // TODO(crbug.com/397407934): Enable ephemeral warning cancellation on - // Android. -#if !BUILDFLAG(IS_ANDROID) // Cancels a download if it's still an ephemeral warning (and has not been // acted on by the user). void CancelForEphemeralWarning(const std::string& guid); @@ -356,7 +356,6 @@ // that were not cleaned up. This function cleans them up on startup, when the // download manager is initialized. void CancelAllEphemeralWarnings(); -#endif // content::DownloadManager::Observer void OnManagerInitialized() override;
diff --git a/chrome/browser/download/chrome_download_manager_delegate_unittest.cc b/chrome/browser/download/chrome_download_manager_delegate_unittest.cc index d3c7fe6..03267ac0 100644 --- a/chrome/browser/download/chrome_download_manager_delegate_unittest.cc +++ b/chrome/browser/download/chrome_download_manager_delegate_unittest.cc
@@ -1846,9 +1846,15 @@ } #endif // BUILDFLAG(IS_ANDROID) -#if !BUILDFLAG(IS_ANDROID) #if !BUILDFLAG(IS_CHROMEOS) TEST_F(ChromeDownloadManagerDelegateTest, ScheduleCancelForEphemeralWarning) { +#if BUILDFLAG(IS_ANDROID) + // Enable the feature on Android to activate warnings, and thus ephemeral + // warning cancellation. + base::test::ScopedFeatureList feature_list; + feature_list.InitAndEnableFeature(safe_browsing::kMaliciousApkDownloadCheck); +#endif + std::unique_ptr<download::MockDownloadItem> download_item = CreateActiveDownloadItem(0); EXPECT_CALL(*download_item, GetDangerType()) @@ -1865,6 +1871,13 @@ TEST_F(ChromeDownloadManagerDelegateTest, ScheduleCancelForEphemeralWarning_DownloadKept) { +#if BUILDFLAG(IS_ANDROID) + // Enable the feature on Android to activate warnings, and thus ephemeral + // warning cancellation. + base::test::ScopedFeatureList feature_list; + feature_list.InitAndEnableFeature(safe_browsing::kMaliciousApkDownloadCheck); +#endif + std::unique_ptr<download::MockDownloadItem> download_item = CreateActiveDownloadItem(0); EXPECT_CALL(*download_item, GetDangerType()) @@ -1879,6 +1892,13 @@ #endif // !BUILDFLAG(IS_CHROMEOS) TEST_F(ChromeDownloadManagerDelegateTest, CancelAllEphemeralWarnings) { +#if BUILDFLAG(IS_ANDROID) + // Enable the feature on Android to activate warnings, and thus ephemeral + // warning cancellation. + base::test::ScopedFeatureList feature_list; + feature_list.InitAndEnableFeature(safe_browsing::kMaliciousApkDownloadCheck); +#endif + std::vector<raw_ptr<download::DownloadItem, VectorExperimental>> items; auto safe_item = CreateActiveDownloadItem(0); EXPECT_CALL(*safe_item, GetDangerType()) @@ -1910,7 +1930,6 @@ delegate()->CancelAllEphemeralWarnings(); } -#endif // !BUILDFLAG(IS_ANDROID) #if BUILDFLAG(SAFE_BROWSING_DOWNLOAD_PROTECTION) namespace { @@ -2324,6 +2343,7 @@ } // Auto cancel is only available on platforms with download bubble. +// TODO(crbug.com/397407934): Support auto cancel reports on Android. #if !BUILDFLAG(IS_CHROMEOS) && !BUILDFLAG(IS_ANDROID) TEST_F(ChromeDownloadManagerDelegateTestWithSafeBrowsing, AutoCanceledReport_Sent) {
diff --git a/chrome/browser/download/download_item_model.cc b/chrome/browser/download/download_item_model.cc index 38081a59..deb3f64 100644 --- a/chrome/browser/download/download_item_model.cc +++ b/chrome/browser/download/download_item_model.cc
@@ -1066,8 +1066,13 @@ return DownloadUIModel::ShouldShowInBubble(); } +#endif // !BUILDFLAG(IS_ANDROID) bool DownloadItemModel::IsEphemeralWarning() const { + // On Android, insecure downloads display a InsecureDownloadDialog prior to + // the download and do not display any warning in the UI, so there is no + // associated warning message to hide/cancel. +#if !BUILDFLAG(IS_ANDROID) switch (GetInsecureDownloadStatus()) { case download::DownloadItem::InsecureDownloadStatus::BLOCK: case download::DownloadItem::InsecureDownloadStatus::WARN: @@ -1078,6 +1083,7 @@ case download::DownloadItem::InsecureDownloadStatus::SILENT_BLOCK: break; } +#endif switch (GetDangerType()) { case download::DOWNLOAD_DANGER_TYPE_DANGEROUS_FILE: @@ -1109,8 +1115,6 @@ } } -#endif // !BUILDFLAG(IS_ANDROID) - offline_items_collection::FailState DownloadItemModel::GetLastFailState() const { return OfflineItemUtils::ConvertDownloadInterruptReasonToFailState(
diff --git a/chrome/browser/download/download_item_model.h b/chrome/browser/download/download_item_model.h index 61cff67..d7d554b 100644 --- a/chrome/browser/download/download_item_model.h +++ b/chrome/browser/download/download_item_model.h
@@ -28,7 +28,8 @@ public download::DownloadItem::Observer { public: #if !BUILDFLAG(IS_ANDROID) - // How long an ephemeral warning is displayed on the download bubble. + // How long an ephemeral warning is displayed on the download bubble on + // Desktop. static constexpr base::TimeDelta kEphemeralWarningLifetimeOnBubble = base::Minutes(5); #endif @@ -130,9 +131,10 @@ TailoredWarningType GetTailoredWarningType() const override; DangerUiPattern GetDangerUiPattern() const override; bool ShouldShowInBubble() const override; - bool IsEphemeralWarning() const override; #endif + bool IsEphemeralWarning() const override; + #if BUILDFLAG(SAFE_BROWSING_DOWNLOAD_PROTECTION) void CompleteSafeBrowsingScan() override; void ReviewScanningVerdict(content::WebContents* web_contents) override;
diff --git a/chrome/browser/download/download_ui_model.cc b/chrome/browser/download/download_ui_model.cc index b31f4dc..13308380 100644 --- a/chrome/browser/download/download_ui_model.cc +++ b/chrome/browser/download/download_ui_model.cc
@@ -799,13 +799,12 @@ bool DownloadUIModel::ShouldShowInBubble() const { return ShouldShowInShelf(); } +#endif // !BUILDFLAG(IS_ANDROID) bool DownloadUIModel::IsEphemeralWarning() const { return false; } -#endif // !BUILDFLAG(IS_ANDROID) - std::string DownloadUIModel::GetMimeType() const { return "text/html"; }
diff --git a/chrome/browser/download/download_ui_model.h b/chrome/browser/download/download_ui_model.h index cf15fb1..05775c7 100644 --- a/chrome/browser/download/download_ui_model.h +++ b/chrome/browser/download/download_ui_model.h
@@ -489,12 +489,12 @@ // Returns the UI pattern to be used for the download, e.g. dangerous or // suspicious. Returns kNoWarning if the download has no warning. virtual DangerUiPattern GetDangerUiPattern() const; +#endif - // Ephemeral warnings are ones that are quickly removed from the bubble if the + // Ephemeral warnings are ones that are quickly removed from the UI if the // user has not acted on them, and later deleted altogether. Is this that kind // of warning? virtual bool IsEphemeralWarning() const; -#endif #if BUILDFLAG(SAFE_BROWSING_DOWNLOAD_PROTECTION) // Complete the Safe Browsing scan early.
diff --git a/chrome/browser/download/download_ui_safe_browsing_util.cc b/chrome/browser/download/download_ui_safe_browsing_util.cc index dc88d10..04266cbf 100644 --- a/chrome/browser/download/download_ui_safe_browsing_util.cc +++ b/chrome/browser/download/download_ui_safe_browsing_util.cc
@@ -94,3 +94,11 @@ } } #endif // BUILDFLAG(SAFE_BROWSING_DOWNLOAD_PROTECTION) + +#if BUILDFLAG(IS_ANDROID) +bool ShouldShowSafeBrowsingAndroidDownloadWarnings() { + return base::FeatureList::IsEnabled( + safe_browsing::kMaliciousApkDownloadCheck) && + !safe_browsing::kMaliciousApkDownloadCheckTelemetryOnly.Get(); +} +#endif
diff --git a/chrome/browser/download/download_ui_safe_browsing_util.h b/chrome/browser/download/download_ui_safe_browsing_util.h index 26cfa3f..f743419 100644 --- a/chrome/browser/download/download_ui_safe_browsing_util.h +++ b/chrome/browser/download/download_ui_safe_browsing_util.h
@@ -7,6 +7,7 @@ #include <string> +#include "build/build_config.h" #include "components/download/public/common/download_danger_type.h" #include "components/safe_browsing/buildflags.h" @@ -58,4 +59,11 @@ download::DownloadItem* item); #endif // BUILDFLAG(SAFE_BROWSING_DOWNLOAD_PROTECTION) +#if BUILDFLAG(IS_ANDROID) +// Whether Safe Browsing Android Download Protection warnings should be shown +// in the UI (for malicious APK downloads). This checks the feature state only; +// Safe Browsing state is checked elsewhere. +bool ShouldShowSafeBrowsingAndroidDownloadWarnings(); +#endif + #endif // CHROME_BROWSER_DOWNLOAD_DOWNLOAD_UI_SAFE_BROWSING_UTIL_H_
diff --git a/chrome/browser/enterprise/signals/profile_signals_collector.cc b/chrome/browser/enterprise/signals/profile_signals_collector.cc new file mode 100644 index 0000000..4bfd36a --- /dev/null +++ b/chrome/browser/enterprise/signals/profile_signals_collector.cc
@@ -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. + +#include "chrome/browser/enterprise/signals/profile_signals_collector.h" + +#include <utility> + +#include "base/check.h" +#include "base/functional/bind.h" +#include "chrome/browser/browser_process.h" +#include "chrome/browser/profiles/profile.h" +#include "chrome/common/pref_names.h" +#include "components/device_signals/core/browser/browser_utils.h" +#include "components/device_signals/core/browser/signals_types.h" +#include "components/device_signals/core/browser/user_permission_service.h" +#include "components/device_signals/core/common/platform_utils.h" +#include "components/enterprise/buildflags/buildflags.h" +#include "components/policy/content/policy_blocklist_service.h" +#include "components/policy/core/common/cloud/cloud_policy_manager.h" +#include "components/prefs/pref_service.h" +#include "components/version_info/version_info.h" + +#if BUILDFLAG(ENTERPRISE_CLOUD_CONTENT_ANALYSIS) +#include "chrome/browser/enterprise/connectors/connectors_service.h" +#endif + +namespace device_signals { + +namespace { + +bool GetBuiltInDnsClientEnabled(PrefService* local_state) { + DCHECK(local_state); + return local_state->GetBoolean(prefs::kBuiltInDnsClientEnabled); +} + +} // namespace + +ProfileSignalsCollector::ProfileSignalsCollector(Profile* profile) + : BaseSignalsCollector({ + {SignalName::kBrowserContextSignals, + base::BindRepeating(&ProfileSignalsCollector::GetProfileSignals, + base::Unretained(this))}, + }), + policy_blocklist_service_( + PolicyBlocklistFactory::GetForBrowserContext(profile)), + profile_prefs_(profile->GetPrefs()), + policy_manager_(profile->GetCloudPolicyManager()), + connectors_service_( + enterprise_connectors::ConnectorsServiceFactory::GetForBrowserContext( + profile)) { +#if BUILDFLAG(ENTERPRISE_CLOUD_CONTENT_ANALYSIS) + DCHECK(connectors_service_); +#endif // BUILDFLAG(ENTERPRISE_CLOUD_CONTENT_ANALYSIS) + DCHECK(policy_blocklist_service_); +} + +ProfileSignalsCollector::~ProfileSignalsCollector() = default; + +void ProfileSignalsCollector::GetProfileSignals( + UserPermission permission, + const SignalsAggregationRequest& request, + SignalsAggregationResponse& response, + base::OnceClosure done_closure) { + ProfileSignalsResponse signal_response; + signal_response.built_in_dns_client_enabled = + GetBuiltInDnsClientEnabled(g_browser_process->local_state()); + signal_response.chrome_remote_desktop_app_blocked = + device_signals::GetChromeRemoteDesktopAppBlocked( + policy_blocklist_service_); + signal_response.password_protection_warning_trigger = + device_signals::GetPasswordProtectionWarningTrigger(profile_prefs_); + signal_response.profile_enrollment_domain = + device_signals::TryGetEnrollmentDomain(policy_manager_); + signal_response.safe_browsing_protection_level = + device_signals::GetSafeBrowsingProtectionLevel(profile_prefs_); + signal_response.site_isolation_enabled = + device_signals::GetSiteIsolationEnabled(); +#if BUILDFLAG(ENTERPRISE_CLOUD_CONTENT_ANALYSIS) + signal_response.realtime_url_check_mode = + connectors_service_->GetAppliedRealTimeUrlCheck(); +#endif // BUILDFLAG(ENTERPRISE_CLOUD_CONTENT_ANALYSIS) + + response.profile_signals_response = std::move(signal_response); + + // All signals are fetched synchronously for now, so we can run the closure + // immediately. Once async signals are added, `done_closure` should be moved + // to be run in the callback. + std::move(done_closure).Run(); +} + +} // namespace device_signals
diff --git a/chrome/browser/enterprise/signals/profile_signals_collector.h b/chrome/browser/enterprise/signals/profile_signals_collector.h new file mode 100644 index 0000000..5d96e0893 --- /dev/null +++ b/chrome/browser/enterprise/signals/profile_signals_collector.h
@@ -0,0 +1,50 @@ +// 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_ENTERPRISE_SIGNALS_PROFILE_SIGNALS_COLLECTOR_H_ +#define CHROME_BROWSER_ENTERPRISE_SIGNALS_PROFILE_SIGNALS_COLLECTOR_H_ + +#include "base/memory/raw_ptr.h" +#include "base/memory/weak_ptr.h" +#include "components/device_signals/core/browser/base_signals_collector.h" + +class PrefService; +class Profile; +class PolicyBlocklistService; + +namespace policy { +class CloudPolicyManager; +} // namespace policy + +namespace enterprise_connectors { +class ConnectorsService; +} // namespace enterprise_connectors + +namespace device_signals { + +class ProfileSignalsCollector : public BaseSignalsCollector { + public: + explicit ProfileSignalsCollector(Profile* profile); + + ProfileSignalsCollector(const ProfileSignalsCollector&) = delete; + ProfileSignalsCollector& operator=(const ProfileSignalsCollector&) = delete; + + ~ProfileSignalsCollector() override; + + private: + void GetProfileSignals(UserPermission permission, + const SignalsAggregationRequest& request, + SignalsAggregationResponse& response, + base::OnceClosure done_closure); + + const raw_ptr<PolicyBlocklistService> policy_blocklist_service_; + const raw_ptr<PrefService> profile_prefs_; + const raw_ptr<policy::CloudPolicyManager> policy_manager_; + const raw_ptr<enterprise_connectors::ConnectorsService> connectors_service_; + base::WeakPtrFactory<ProfileSignalsCollector> weak_factory_{this}; +}; + +} // namespace device_signals + +#endif // CHROME_BROWSER_ENTERPRISE_SIGNALS_PROFILE_SIGNALS_COLLECTOR_H_
diff --git a/chrome/browser/enterprise/signals/profile_signals_collector_browsertest.cc b/chrome/browser/enterprise/signals/profile_signals_collector_browsertest.cc new file mode 100644 index 0000000..23b6ce9 --- /dev/null +++ b/chrome/browser/enterprise/signals/profile_signals_collector_browsertest.cc
@@ -0,0 +1,134 @@ +// 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/enterprise/signals/profile_signals_collector.h" + +#include <array> +#include <utility> + +#include "base/memory/raw_ptr.h" +#include "base/test/task_environment.h" +#include "base/values.h" +#include "chrome/browser/browser_process.h" +#include "chrome/browser/profiles/profile.h" +#include "chrome/browser/ui/browser.h" +#include "chrome/common/pref_names.h" +#include "chrome/test/base/in_process_browser_test.h" +#include "components/device_signals/core/browser/signals_types.h" +#include "components/device_signals/core/browser/user_permission_service.h" +#include "components/device_signals/core/common/signals_constants.h" +#include "components/policy/core/common/cloud/cloud_policy_manager.h" +#include "components/policy/core/common/cloud/cloud_policy_util.h" +#include "components/policy/proto/device_management_backend.pb.h" +#include "components/prefs/pref_service.h" +#include "components/safe_browsing/core/common/safe_browsing_prefs.h" +#include "content/public/test/browser_test.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace { + +constexpr char kFakeUserEnrollmentDomain[] = "fake.domain.google.com"; + +} // namespace + +namespace device_signals { + +class ProfileSignalsCollectorTest : public InProcessBrowserTest { + protected: + std::unique_ptr<ProfileSignalsCollector> CreateProfileSignalsCollector() { + return std::make_unique<ProfileSignalsCollector>(browser()->profile()); + } + + void SetFakePolicyAndPrefData() { + auto policy_data = std::make_unique<enterprise_management::PolicyData>(); + policy_data->set_managed_by(kFakeUserEnrollmentDomain); + browser() + ->profile() + ->GetCloudPolicyManager() + ->core() + ->store() + ->set_policy_data_for_testing(std::move(policy_data)); + + g_browser_process->local_state()->SetBoolean( + prefs::kBuiltInDnsClientEnabled, true); + + // Give the testing profile a safe browsing level of "STANDARD_PROTECTION" + browser()->profile()->GetPrefs()->SetBoolean(prefs::kSafeBrowsingEnabled, + true); + browser()->profile()->GetPrefs()->SetBoolean(prefs::kSafeBrowsingEnhanced, + false); + } + + // Helper function to check the profile level signals are collected correctly. + void CheckSignalsCollected(ProfileSignalsResponse& response) { + EXPECT_EQ(response.profile_enrollment_domain, kFakeUserEnrollmentDomain); + EXPECT_EQ(response.safe_browsing_protection_level, + safe_browsing::SafeBrowsingState::STANDARD_PROTECTION); + EXPECT_TRUE(response.built_in_dns_client_enabled); + } + + std::unique_ptr<ProfileSignalsCollector> signal_collector_ = nullptr; +}; + +// Test that runs a sanity check on the set of signals supported by this +// collector. Will need to be updated if new signals become supported. +IN_PROC_BROWSER_TEST_F(ProfileSignalsCollectorTest, + SupportedBrowserContextSignalNames) { + auto signals_collector = CreateProfileSignalsCollector(); + const std::array<SignalName, 1> supported_signals{ + {SignalName::kBrowserContextSignals}}; + + const auto names_set = signals_collector->GetSupportedSignalNames(); + + EXPECT_EQ(names_set.size(), supported_signals.size()); + for (const auto& signal_name : supported_signals) { + EXPECT_TRUE(names_set.find(signal_name) != names_set.end()); + } +} + +// Happy path test case for OS signals collection with full permission. +IN_PROC_BROWSER_TEST_F(ProfileSignalsCollectorTest, GetSignal_Success) { + auto signals_collector = CreateProfileSignalsCollector(); + SetFakePolicyAndPrefData(); + + SignalName signal_name = SignalName::kBrowserContextSignals; + SignalsAggregationRequest empty_request; + SignalsAggregationResponse response; + signals_collector->GetSignal(signal_name, UserPermission::kGranted, + empty_request, response, base::DoNothing()); + + ASSERT_FALSE(response.top_level_error.has_value()); + ASSERT_TRUE(response.profile_signals_response); + CheckSignalsCollected(response.profile_signals_response.value()); +} + +// Tests that an unsupported signal is marked as unsupported. +IN_PROC_BROWSER_TEST_F(ProfileSignalsCollectorTest, + GetBrowserContextSignal_Unsupported) { + auto signals_collector = CreateProfileSignalsCollector(); + SignalName signal_name = SignalName::kAntiVirus; + SignalsAggregationRequest empty_request; + SignalsAggregationResponse response; + signals_collector->GetSignal(signal_name, UserPermission::kGranted, + empty_request, response, base::DoNothing()); + + ASSERT_TRUE(response.top_level_error.has_value()); + EXPECT_EQ(response.top_level_error.value(), + SignalCollectionError::kUnsupported); +} + +// Tests that signal collection is halted if permission is not sufficient. +IN_PROC_BROWSER_TEST_F(ProfileSignalsCollectorTest, GetSignal_MissingUser) { + auto signals_collector = CreateProfileSignalsCollector(); + SignalName signal_name = SignalName::kBrowserContextSignals; + SignalsAggregationRequest empty_request; + SignalsAggregationResponse response; + signals_collector->GetSignal(signal_name, UserPermission::kMissingUser, + empty_request, response, base::DoNothing()); + + ASSERT_FALSE(response.top_level_error.has_value()); + ASSERT_FALSE(response.profile_signals_response); +} + +} // namespace device_signals
diff --git a/chrome/browser/extensions/BUILD.gn b/chrome/browser/extensions/BUILD.gn index e0e1eab..5ffdb47f9 100644 --- a/chrome/browser/extensions/BUILD.gn +++ b/chrome/browser/extensions/BUILD.gn
@@ -204,6 +204,8 @@ "extension_management_constants.h", "extension_management_internal.cc", "extension_management_internal.h", + "extension_migrator.cc", + "extension_migrator.h", "extension_special_storage_policy.cc", "extension_special_storage_policy.h", "extension_tab_util.cc", @@ -218,11 +220,19 @@ "extension_web_ui_override_registrar.h", "extension_webkit_preferences.cc", "extension_webkit_preferences.h", + "external_component_loader.cc", + "external_component_loader.h", "external_install_error.h", "external_install_manager.cc", "external_install_manager.h", "external_install_manager_factory.cc", "external_install_manager_factory.h", + "external_loader.cc", + "external_loader.h", + "external_pref_loader.cc", + "external_pref_loader.h", + "external_provider_impl.cc", + "external_provider_impl.h", "external_provider_manager.cc", "external_provider_manager.h", "external_provider_manager_factory.cc", @@ -322,6 +332,7 @@ "//base", "//chrome/app:command_ids", "//chrome/browser:browser_process", + "//chrome/browser/apps:user_type_filter", "//chrome/browser/bitmap_fetcher", "//chrome/browser/content_settings:content_settings_factory", "//chrome/browser/extensions:cws_item_service_proto", @@ -330,6 +341,7 @@ "//chrome/browser/google", "//chrome/browser/prefetch", "//chrome/browser/prefs", + "//chrome/browser/prefs:util", "//chrome/browser/preloading:prefs", "//chrome/browser/profiles:profile", "//chrome/browser/safe_browsing", @@ -651,8 +663,6 @@ "extension_management.h", "extension_menu_icon_loader.cc", "extension_menu_icon_loader.h", - "extension_migrator.cc", - "extension_migrator.h", "extension_safety_check_utils.cc", "extension_safety_check_utils.h", "extension_service.cc", @@ -676,18 +686,10 @@ "extension_view_host.h", "extension_view_host_factory.cc", "extension_view_host_factory.h", - "external_component_loader.cc", - "external_component_loader.h", "external_install_error_desktop.cc", "external_install_error_desktop.h", - "external_loader.cc", - "external_loader.h", "external_policy_loader.cc", "external_policy_loader.h", - "external_pref_loader.cc", - "external_pref_loader.h", - "external_provider_impl.cc", - "external_provider_impl.h", "file_handlers/file_handling_launch_utils.cc", "file_handlers/file_handling_launch_utils.h", "forced_extensions/force_installed_metrics.cc",
diff --git a/chrome/browser/extensions/extension_protocols_unittest.cc b/chrome/browser/extensions/extension_protocols_unittest.cc index 54e59ebf..7d59dec 100644 --- a/chrome/browser/extensions/extension_protocols_unittest.cc +++ b/chrome/browser/extensions/extension_protocols_unittest.cc
@@ -52,7 +52,6 @@ #include "services/network/test/test_url_loader_client.h" #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" -#include "third_party/blink/public/common/features.h" #include "third_party/blink/public/common/loader/referrer_utils.h" using extensions::ExtensionRegistry; @@ -767,9 +766,6 @@ network::mojom::RequestDestination::kStyle, network::mojom::RequestDestination::kVideo, }; - if (!base::FeatureList::IsEnabled(blink::features::kPlzDedicatedWorker)) { - destinations.push_back(network::mojom::RequestDestination::kWorker); - } for (network::mojom::RequestDestination destination : destinations) { auto get_result = RequestOrLoad(extension->GetResourceURL("background.js"), destination);
diff --git a/chrome/browser/extensions/external_provider_impl.cc b/chrome/browser/extensions/external_provider_impl.cc index c299e9717..9bed348 100644 --- a/chrome/browser/extensions/external_provider_impl.cc +++ b/chrome/browser/extensions/external_provider_impl.cc
@@ -29,10 +29,8 @@ #include "chrome/browser/app_mode/app_mode_utils.h" #include "chrome/browser/browser_process.h" #include "chrome/browser/browser_process_platform_part.h" -#include "chrome/browser/extensions/extension_management.h" #include "chrome/browser/extensions/extension_migrator.h" #include "chrome/browser/extensions/external_component_loader.h" -#include "chrome/browser/extensions/external_policy_loader.h" #include "chrome/browser/extensions/external_pref_loader.h" #include "chrome/browser/extensions/forced_extensions/install_stage_tracker.h" #include "chrome/browser/policy/profile_policy_connector.h" @@ -55,6 +53,11 @@ #include "extensions/common/manifest.h" #include "ui/base/l10n/l10n_util.h" +#if !BUILDFLAG(IS_ANDROID) +#include "chrome/browser/extensions/extension_management.h" +#include "chrome/browser/extensions/external_policy_loader.h" +#endif + #if BUILDFLAG(IS_CHROMEOS) #include "ash/constants/ash_paths.h" #include "ash/constants/ash_switches.h" @@ -654,7 +657,8 @@ "ExternalProviderImpl::CreateExternalProviders"); scoped_refptr<ExternalLoader> external_loader; scoped_refptr<ExternalLoader> external_recommended_loader; - ManifestLocation crx_location = ManifestLocation::kInvalidLocation; + [[maybe_unused]] ManifestLocation crx_location = + ManifestLocation::kInvalidLocation; #if BUILDFLAG(IS_CHROMEOS) if (ash::ProfileHelper::IsSigninProfile(profile)) { @@ -700,6 +704,10 @@ } #endif +#if BUILDFLAG(IS_ANDROID) + // TODO(crbug.com/394876083): Port ExtensionManagement to Android. + NOTIMPLEMENTED() << "Policy loaded extensions not yet supported on Android"; +#else if (!external_loader.get()) { external_loader = base::MakeRefCounted<ExternalPolicyLoader>( profile, ExtensionManagementFactory::GetForBrowserContext(profile), @@ -715,6 +723,7 @@ ManifestLocation::kExternalPolicyDownload, Extension::NO_FLAGS); policy_provider->set_allow_updates(true); provider_list->push_back(std::move(policy_provider)); +#endif // BUILDFLAG(IS_ANDROID) // Load the KioskAppExternalProvider when running in the Chrome App kiosk // mode. @@ -758,7 +767,10 @@ return; } +#if !BUILDFLAG(IS_ANDROID) // Extensions provided by recommended policies. + // TODO(crbug.com/394876083): Port ExtensionManagement to Android. + // No NOTIMPLEMENTED() here because we already logged above. if (external_recommended_loader.get()) { auto recommended_provider = std::make_unique<ExternalProviderImpl>( service, external_recommended_loader, profile, crx_location, @@ -766,6 +778,7 @@ recommended_provider->set_auto_acknowledge(true); provider_list->push_back(std::move(recommended_provider)); } +#endif // !BUILDFLAG(IS_ANDROID) // In tests don't install pre-installed apps. // It would only slowdown tests and make them flaky.
diff --git a/chrome/browser/extensions/external_provider_manager.cc b/chrome/browser/extensions/external_provider_manager.cc index e249edb..73f7b3c1 100644 --- a/chrome/browser/extensions/external_provider_manager.cc +++ b/chrome/browser/extensions/external_provider_manager.cc
@@ -20,6 +20,7 @@ #include "chrome/browser/extensions/crx_installer.h" #include "chrome/browser/extensions/extension_error_controller.h" #include "chrome/browser/extensions/external_install_manager.h" +#include "chrome/browser/extensions/external_provider_impl.h" #include "chrome/browser/extensions/external_provider_manager_factory.h" #include "chrome/browser/extensions/forced_extensions/install_stage_tracker.h" #include "chrome/browser/extensions/installed_loader.h" @@ -44,10 +45,6 @@ #include "extensions/common/verifier_formats.h" #include "url/gurl.h" -#if !BUILDFLAG(IS_ANDROID) -#include "chrome/browser/extensions/external_provider_impl.h" -#endif - #if BUILDFLAG(IS_CHROMEOS) #include "chrome/browser/ash/extensions/install_limiter.h" #endif @@ -92,14 +89,9 @@ } void ExternalProviderManager::CreateExternalProviders() { -#if BUILDFLAG(IS_ANDROID) - // TODO(crbug.com/407824044): Port ExternalProviderImpl to desktop Android. - NOTIMPLEMENTED() << "External providers not yet supported on Android"; -#else ExternalProviderImpl::CreateExternalProviders( this, Profile::FromBrowserContext(context_.get()), &external_extension_providers_); -#endif } // Some extensions will autoupdate themselves externally from Chrome. These
diff --git a/chrome/browser/extensions/installed_loader.cc b/chrome/browser/extensions/installed_loader.cc index 61d59e4..fe69fdb3 100644 --- a/chrome/browser/extensions/installed_loader.cc +++ b/chrome/browser/extensions/installed_loader.cc
@@ -1050,7 +1050,6 @@ extension_user_count + extension_external_count); base::UmaHistogramCounts100("Extensions.LoadExtensionExternal", extension_external_count); - base::UmaHistogramCounts100("Extensions.LoadUserScript", user_script_count); base::UmaHistogramCounts100("Extensions.LoadTheme", theme_count); // Histogram name different for legacy reasons. base::UmaHistogramCounts100("PageActionController.ExtensionsWithPageActions",
diff --git a/chrome/browser/file_system_access/chrome_file_system_access_permission_context.cc b/chrome/browser/file_system_access/chrome_file_system_access_permission_context.cc index f08ff19..cc84089 100644 --- a/chrome/browser/file_system_access/chrome_file_system_access_permission_context.cc +++ b/chrome/browser/file_system_access/chrome_file_system_access_permission_context.cc
@@ -551,6 +551,8 @@ case Result::UNKNOWN: case Result::SAFE: case Result::ALLOWLISTED_BY_POLICY: + case Result::SENSITIVE_CONTENT_WARNING: + case Result::DEEP_SCANNED_SAFE: return ChromeFileSystemAccessPermissionContext::AfterWriteCheckResult:: kAllow; @@ -562,15 +564,13 @@ case Result::BLOCKED_TOO_LARGE: case Result::DANGEROUS_ACCOUNT_COMPROMISE: case Result::BLOCKED_SCAN_FAILED: + case Result::SENSITIVE_CONTENT_BLOCK: return ChromeFileSystemAccessPermissionContext::AfterWriteCheckResult:: kBlock; // This shouldn't be returned for File System Access write checks. case Result::ASYNC_SCANNING: case Result::ASYNC_LOCAL_PASSWORD_SCANNING: - case Result::SENSITIVE_CONTENT_WARNING: - case Result::SENSITIVE_CONTENT_BLOCK: - case Result::DEEP_SCANNED_SAFE: case Result::PROMPT_FOR_SCANNING: case Result::PROMPT_FOR_LOCAL_PASSWORD_SCANNING: case Result::DEEP_SCANNED_FAILED:
diff --git a/chrome/browser/flag-metadata.json b/chrome/browser/flag-metadata.json index 97279b2..3351cfa 100644 --- a/chrome/browser/flag-metadata.json +++ b/chrome/browser/flag-metadata.json
@@ -2115,6 +2115,14 @@ "expiry_milestone": 100 }, { + "name": "disable-system-blur", + "owners": [ + "zoraiznaeem@chromium.org", + "chromeos-foundations@google.com" + ], + "expiry_milestone": 140 + }, + { "name": "disable-virtual-keyboard", "owners": [ "dvallet@chromium.org", "essential-inputs-team@google.com" ], "expiry_milestone": 116 @@ -4722,6 +4730,11 @@ "expiry_milestone": 140 }, { + "name": "feed-header-removal", + "owners": [ "//chrome/android/feed/OWNERS", "jianli@chromium.org" ], + "expiry_milestone": 142 + }, + { "name": "feed-header-stick-to-top", "owners": [ "//chrome/android/feed/OWNERS", "chili@chromium.org" ], "expiry_milestone": 115
diff --git a/chrome/browser/flag_descriptions.cc b/chrome/browser/flag_descriptions.cc index 6d45b52..e797bb6 100644 --- a/chrome/browser/flag_descriptions.cc +++ b/chrome/browser/flag_descriptions.cc
@@ -1165,6 +1165,10 @@ "unrelated tabs. This is an experimental mode that will result in more " "processes being created."; +const char kDisableSystemBlur[] = "Disable system blur"; +const char kDisableSystemBlurDescription[] = + "Removes background blur from system UI"; + const char kDisallowDocWrittenScriptsUiName[] = "Block scripts loaded via document.write"; const char kDisallowDocWrittenScriptsUiDescription[] = @@ -4673,6 +4677,9 @@ const char kFeedDynamicColorsDescription[] = "Allows feed to fully respect dynamic colors if supported by the client."; +const char kFeedHeaderRemovalName[] = "Removing feed header"; +const char kFeedHeaderRemovalDescription[] = "Stops showing the feed header."; + const char kFloatingSnackbarName[] = "FloatingSnackbar"; const char kFloatingSnackbarDescription[] = "Enables the snackbar to float on top of the web content.";
diff --git a/chrome/browser/flag_descriptions.h b/chrome/browser/flag_descriptions.h index d722672..43991f28 100644 --- a/chrome/browser/flag_descriptions.h +++ b/chrome/browser/flag_descriptions.h
@@ -611,6 +611,9 @@ extern const char kDisableProcessReuse[]; extern const char kDisableProcessReuseDescription[]; +extern const char kDisableSystemBlur[]; +extern const char kDisableSystemBlurDescription[]; + extern const char kDoubleBufferCompositingName[]; extern const char kDoubleBufferCompositingDescription[]; @@ -2748,6 +2751,9 @@ extern const char kFeedDiscoFeedEndpointName[]; extern const char kFeedDiscoFeedEndpointDescription[]; +extern const char kFeedHeaderRemovalName[]; +extern const char kFeedHeaderRemovalDescription[]; + extern const char kFloatingSnackbarName[]; extern const char kFloatingSnackbarDescription[];
diff --git a/chrome/browser/flags/android/chrome_feature_list.cc b/chrome/browser/flags/android/chrome_feature_list.cc index c7e3e8d4..6eeb1d01 100644 --- a/chrome/browser/flags/android/chrome_feature_list.cc +++ b/chrome/browser/flags/android/chrome_feature_list.cc
@@ -157,6 +157,7 @@ &feed::kFeedContainment, &feed::kFeedDynamicColors, &feed::kFeedFollowUiUpdate, + &feed::kFeedHeaderRemoval, &feed::kFeedImageMemoryCacheSizePercentage, &feed::kFeedLoadingPlaceholder, &feed::kFeedNoViewCache,
diff --git a/chrome/browser/flags/android/java/src/org/chromium/chrome/browser/flags/ChromeFeatureList.java b/chrome/browser/flags/android/java/src/org/chromium/chrome/browser/flags/ChromeFeatureList.java index dcebb54..e860690c 100644 --- a/chrome/browser/flags/android/java/src/org/chromium/chrome/browser/flags/ChromeFeatureList.java +++ b/chrome/browser/flags/android/java/src/org/chromium/chrome/browser/flags/ChromeFeatureList.java
@@ -353,6 +353,7 @@ public static final String ESB_AI_STRING_UPDATE = "EsbAiStringUpdate"; public static final String FEED_CONTAINMENT = "FeedContainment"; public static final String FEED_FOLLOW_UI_UPDATE = "FeedFollowUiUpdate"; + public static final String FEED_HEADER_REMOVAL = "FeedHeaderRemoval"; public static final String FEED_IMAGE_MEMORY_CACHE_SIZE_PERCENTAGE = "FeedImageMemoryCacheSizePercentage"; public static final String FEED_LOADING_PLACEHOLDER = "FeedLoadingPlaceholder";
diff --git a/chrome/browser/glic/widget/glic_window_controller.cc b/chrome/browser/glic/widget/glic_window_controller.cc index 26a0c5f2..e158447b 100644 --- a/chrome/browser/glic/widget/glic_window_controller.cc +++ b/chrome/browser/glic/widget/glic_window_controller.cc
@@ -775,6 +775,9 @@ // There is no open detached animation so wait for glic to load to continue // opening. SetWindowState(State::kWaitingForGlicToLoad); + + // This is needed in case of theme difference between OS and chrome. + GetGlicWidget()->ThemeChanged(); } // This happens after the web client is initialized. It signals the web client
diff --git a/chrome/browser/net/private_network_access_browsertest.cc b/chrome/browser/net/private_network_access_browsertest.cc index 736f29f..6c91626 100644 --- a/chrome/browser/net/private_network_access_browsertest.cc +++ b/chrome/browser/net/private_network_access_browsertest.cc
@@ -43,7 +43,6 @@ #include "services/network/public/cpp/private_network_access_check_result.h" #include "services/network/public/cpp/url_loader_completion_status.h" #include "testing/gmock/include/gmock/gmock.h" -#include "third_party/blink/public/common/features.h" #include "third_party/blink/public/mojom/use_counter/metrics/web_feature.mojom.h" namespace { @@ -263,7 +262,6 @@ bool is_warning_only = false) : PrivateNetworkAccessBrowserTestBase( { - blink::features::kPlzDedicatedWorker, features::kBlockInsecurePrivateNetworkRequests, features::kBlockInsecurePrivateNetworkRequestsFromPrivate, features::kBlockInsecurePrivateNetworkRequestsDeprecationTrial, @@ -297,7 +295,6 @@ PrivateNetworkAccessRespectPreflightResultsBrowserTest() : PrivateNetworkAccessBrowserTestBase( { - blink::features::kPlzDedicatedWorker, features::kBlockInsecurePrivateNetworkRequests, features::kPrivateNetworkAccessSendPreflights, features::kPrivateNetworkAccessRespectPreflightResults, @@ -1474,7 +1471,6 @@ PrivateNetworkAccessWithNullIPKillswitchTest() : PrivateNetworkAccessBrowserTestBase( { - blink::features::kPlzDedicatedWorker, features::kBlockInsecurePrivateNetworkRequests, features::kBlockInsecurePrivateNetworkRequestsFromPrivate, features::kBlockInsecurePrivateNetworkRequestsDeprecationTrial,
diff --git a/chrome/browser/pdf/pdf_extension_js_test.cc b/chrome/browser/pdf/pdf_extension_js_test.cc index 0fc110c..0f2713a 100644 --- a/chrome/browser/pdf/pdf_extension_js_test.cc +++ b/chrome/browser/pdf/pdf_extension_js_test.cc
@@ -590,6 +590,10 @@ RunTestsInJsModule("ink2_text_side_panel_test.js", "test.pdf"); } +IN_PROC_BROWSER_TEST_P(PDFExtensionJSInk2Test, Ink2TextStylesSelector) { + RunTestsInJsModule("ink2_text_styles_selector_test.js", "test.pdf"); +} + IN_PROC_BROWSER_TEST_P(PDFExtensionJSInk2Test, Ink2BrushSelector) { RunTestsInJsModule("ink2_brush_selector_test.js", "test.pdf"); }
diff --git a/chrome/browser/privacy_sandbox/browsing_topics_settings_interactive_uitest.cc b/chrome/browser/privacy_sandbox/browsing_topics_settings_interactive_uitest.cc index 041cb01..ada6566 100644 --- a/chrome/browser/privacy_sandbox/browsing_topics_settings_interactive_uitest.cc +++ b/chrome/browser/privacy_sandbox/browsing_topics_settings_interactive_uitest.cc
@@ -17,6 +17,10 @@ #include "components/privacy_sandbox/privacy_sandbox_prefs.h" #include "content/public/test/browser_test.h" +#if BUILDFLAG(IS_MAC) +#include "base/mac/mac_util.h" +#endif + namespace { using DeepQuery = WebContentsInteractionTestUtil::DeepQuery; @@ -139,6 +143,13 @@ // topic toggle is ON (checked == true). IN_PROC_BROWSER_TEST_F(PrivacySandboxSettingsTopicsInteractiveTest, UnblockOneTopicOnAdTopicsPage) { +#if BUILDFLAG(IS_MAC) + // https://crbug.com/407801060 + if (base::mac::MacOSMajorVersion() == 15) { + GTEST_SKIP() << "Disabled on macOS Sequoia."; + } +#endif + BlockTopic(1); RunTestSequence( InstrumentTab(kPrivacySandboxTopicsElementId), @@ -151,7 +162,7 @@ ExecuteJsAt(kPrivacySandboxTopicsElementId, firstBlockedItemButton, "(el) => el.click()"), CheckResult([this]() { return GetBlockedTopicsSize(); }, 0u, - "Checking that there is 0 blocked topics"), + "Checking that there are 0 blocked topics"), NavigateWebContents(kPrivacySandboxTopicsElementId, GURL(chrome::kPrivacySandboxManageTopicsURL)), CheckJsResultAt(kPrivacySandboxTopicsElementId, firstToggle,
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 98facd6..64100a4 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
@@ -364,16 +364,6 @@ } @Test - public void testDontHidePlayer_nonTabSwitcherUi() { - requestAndStartPlayback(); - mLayoutStateObserver.getValue().onStartedShowing(LayoutType.START_SURFACE); - verify(mPlayerCoordinator, never()).hidePlayers(); - - mLayoutStateObserver.getValue().onFinishedHiding(LayoutType.START_SURFACE); - verify(mPlayerCoordinator, never()).restorePlayers(); - } - - @Test public void testHidePlayer_FullScreen() { requestAndStartPlayback(); mFullscreenObserver.getValue().onEnterFullscreen(mTab, new FullscreenOptions(true, true));
diff --git a/chrome/browser/resources/ash/settings/common/deep_linking_mixin.ts b/chrome/browser/resources/ash/settings/common/deep_linking_mixin.ts index d94c149..78a036e 100644 --- a/chrome/browser/resources/ash/settings/common/deep_linking_mixin.ts +++ b/chrome/browser/resources/ash/settings/common/deep_linking_mixin.ts
@@ -45,15 +45,6 @@ type: Object, value: Setting, }, - - /** - * Set of setting IDs that could be deep linked to. Initialized as - * an empty set, should be overridden with applicable setting IDs. - */ - supportedSettingIds: { - type: Object, - value: () => new Set<Setting>(), - }, }; } @@ -61,7 +52,12 @@ // the exact value of the Setting enum. // eslint-disable-next-line @typescript-eslint/naming-convention Setting: Setting; - supportedSettingIds: Set<Setting>; + + /** + * Set of setting IDs that could be deep linked to. Initialized as an + * empty set, should be overridden with applicable setting IDs. + */ + supportedSettingIds = new Set<Setting>(); /** * Retrieves the settingId saved in the url's query parameter. Returns
diff --git a/chrome/browser/resources/ash/settings/crostini_page/bruschetta_subpage.ts b/chrome/browser/resources/ash/settings/crostini_page/bruschetta_subpage.ts index 84f15c17..e75d599d 100644 --- a/chrome/browser/resources/ash/settings/crostini_page/bruschetta_subpage.ts +++ b/chrome/browser/resources/ash/settings/crostini_page/bruschetta_subpage.ts
@@ -43,16 +43,6 @@ type: Boolean, value: false, }, - - /** - * Used by DeepLinkingMixin to focus this page's deep links. - */ - supportedSettingIds: { - type: Object, - value: () => new Set<Setting>([ - Setting.kBruschettaMicAccess, - ]), - }, }; } @@ -62,6 +52,11 @@ ]; } + // DeepLinkingMixin override + override supportedSettingIds = new Set<Setting>([ + Setting.kBruschettaMicAccess, + ]); + private browserProxy_: CrostiniBrowserProxy; private showBruschettaMicPermissionDialog_: boolean;
diff --git a/chrome/browser/resources/ash/settings/crostini_page/crostini_arc_adb.ts b/chrome/browser/resources/ash/settings/crostini_page/crostini_arc_adb.ts index d3e6ad27..0a454e2 100644 --- a/chrome/browser/resources/ash/settings/crostini_page/crostini_arc_adb.ts +++ b/chrome/browser/resources/ash/settings/crostini_page/crostini_arc_adb.ts
@@ -94,18 +94,16 @@ type: Boolean, value: false, }, - - /** - * Used by DeepLinkingMixin to focus this page's deep links. - */ - supportedSettingIds: { - type: Object, - value: () => new Set<Setting>([Setting.kCrostiniAdbDebugging]), - }, }; } prefs: PrefsState; + + // DeepLinkingMixin override + override supportedSettingIds = new Set<Setting>([ + Setting.kCrostiniAdbDebugging, + ]); + private arcAdbEnabled_: boolean; private arcAdbNeedPowerwash_: boolean; private browserProxy_: CrostiniBrowserProxy;
diff --git a/chrome/browser/resources/ash/settings/crostini_page/crostini_export_import.ts b/chrome/browser/resources/ash/settings/crostini_page/crostini_export_import.ts index 5314803..d416212 100644 --- a/chrome/browser/resources/ash/settings/crostini_page/crostini_export_import.ts +++ b/chrome/browser/resources/ash/settings/crostini_page/crostini_export_import.ts
@@ -117,21 +117,17 @@ type: String, value: DEFAULT_CROSTINI_VM, }, - - /** - * Used by DeepLinkingMixin to focus this page's deep links. - */ - supportedSettingIds: { - type: Object, - value: () => new Set<Setting>([ - Setting.kBackupLinuxAppsAndFiles, - Setting.kRestoreLinuxAppsAndFiles, - ]), - }, }; } prefs: PrefsState; + + // DeepLinkingMixin override + override supportedSettingIds = new Set<Setting>([ + Setting.kBackupLinuxAppsAndFiles, + Setting.kRestoreLinuxAppsAndFiles, + ]); + private allContainers_: ContainerInfo[]; private browserProxy_: CrostiniBrowserProxy; private defaultVmName_: string;
diff --git a/chrome/browser/resources/ash/settings/crostini_page/crostini_settings_card.ts b/chrome/browser/resources/ash/settings/crostini_page/crostini_settings_card.ts index 15763bfd..bfda4e4 100644 --- a/chrome/browser/resources/ash/settings/crostini_page/crostini_settings_card.ts +++ b/chrome/browser/resources/ash/settings/crostini_page/crostini_settings_card.ts
@@ -66,14 +66,6 @@ }, }, - /** - * Used by DeepLinkingMixin to focus this page's deep links. - */ - supportedSettingIds: { - type: Object, - value: () => new Set<Setting>([Setting.kSetUpCrostini]), - }, - showBruschetta_: { type: Boolean, value() { @@ -83,6 +75,11 @@ }; } + // DeepLinkingMixin override + override supportedSettingIds = new Set<Setting>([ + Setting.kSetUpCrostini, + ]); + private browserProxy_: CrostiniBrowserProxy; private disableCrostiniInstall_: boolean; private isCrostiniAllowed_: boolean;
diff --git a/chrome/browser/resources/ash/settings/crostini_page/crostini_subpage.ts b/chrome/browser/resources/ash/settings/crostini_page/crostini_subpage.ts index a6944a5..df1012d2 100644 --- a/chrome/browser/resources/ash/settings/crostini_page/crostini_subpage.ts +++ b/chrome/browser/resources/ash/settings/crostini_page/crostini_subpage.ts
@@ -169,19 +169,6 @@ type: Boolean, value: false, }, - - /** - * Used by DeepLinkingMixin to focus this page's deep links. - */ - supportedSettingIds: { - type: Object, - value: () => new Set<Setting>([ - Setting.kUninstallCrostini, - Setting.kCrostiniDiskResize, - Setting.kCrostiniMicAccess, - Setting.kCrostiniContainerUpgrade, - ]), - }, }; } @@ -192,6 +179,14 @@ ]; } + // DeepLinkingMixin override + override supportedSettingIds = new Set<Setting>([ + Setting.kUninstallCrostini, + Setting.kCrostiniDiskResize, + Setting.kCrostiniMicAccess, + Setting.kCrostiniContainerUpgrade, + ]); + private browserProxy_: CrostiniBrowserProxy; private canDiskResize_: boolean; private diskResizeButtonAriaLabel_: string;
diff --git a/chrome/browser/resources/ash/settings/date_time_page/date_time_settings_card.ts b/chrome/browser/resources/ash/settings/date_time_page/date_time_settings_card.ts index 328fb4d1..6839796 100644 --- a/chrome/browser/resources/ash/settings/date_time_page/date_time_settings_card.ts +++ b/chrome/browser/resources/ash/settings/date_time_page/date_time_settings_card.ts
@@ -49,17 +49,6 @@ }, /** - * Used by DeepLinkingMixin to focus this page's deep links. - */ - supportedSettingIds: { - type: Object, - value: () => new Set<Setting>([ - Setting.k24HourClock, - Setting.kChangeTimeZone, - ]), - }, - - /** * Whether date and time are settable. Normally the date and time are * forced by network time, so default to false to initially hide the * button. @@ -91,6 +80,13 @@ } activeTimeZoneDisplayName: string; + + // DeepLinkingMixin override + override supportedSettingIds = new Set<Setting>([ + Setting.k24HourClock, + Setting.kChangeTimeZone, + ]); + private canSetDateTime_: boolean; private shouldShowManagedByParentIcon_: boolean; private timeZoneSettingSublabel_: string;
diff --git a/chrome/browser/resources/ash/settings/date_time_page/timezone_subpage.ts b/chrome/browser/resources/ash/settings/date_time_page/timezone_subpage.ts index 957794b..760cf707 100644 --- a/chrome/browser/resources/ash/settings/date_time_page/timezone_subpage.ts +++ b/chrome/browser/resources/ash/settings/date_time_page/timezone_subpage.ts
@@ -68,14 +68,6 @@ }, }, - /** - * Used by DeepLinkingMixin to focus this page's deep links. - */ - supportedSettingIds: { - type: Object, - value: () => new Set<Setting>([Setting.kChangeTimeZone]), - }, - geolocationWarningText_: { type: String, computed: 'computedGeolocationWarningText(activeTimeZoneDisplayName,' + @@ -97,6 +89,12 @@ } activeTimeZoneDisplayName: string; + + // DeepLinkingMixin override + override supportedSettingIds = new Set<Setting>([ + Setting.kChangeTimeZone, + ]); + private canSetSystemTimezone_: boolean; private browserProxy_: DateTimeBrowserProxy; private showEnableSystemGeolocationDialog_: boolean;
diff --git a/chrome/browser/resources/ash/settings/device_page/audio.ts b/chrome/browser/resources/ash/settings/device_page/audio.ts index c6b8fa4..f73898dc2 100644 --- a/chrome/browser/resources/ash/settings/device_page/audio.ts +++ b/chrome/browser/resources/ash/settings/device_page/audio.ts
@@ -116,17 +116,6 @@ value: false, }, - /** - * Used by DeepLinkingMixin to focus this page's deep links. - */ - supportedSettingIds: { - type: Object, - value: () => new Set<Setting>([ - Setting.kChargingSounds, - Setting.kLowBatterySound, - ]), - }, - showAllowAGC: { type: Boolean, value: loadTimeData.getBoolean('enableForceRespectUiGainsToggle'), @@ -153,6 +142,12 @@ }; } + // DeepLinkingMixin override + override supportedSettingIds = new Set<Setting>([ + Setting.kChargingSounds, + Setting.kLowBatterySound, + ]); + protected isAllowAGCEnabled: boolean; protected showAllowAGC: boolean; protected isHfpMicSrEnabled: boolean;
diff --git a/chrome/browser/resources/ash/settings/device_page/display.ts b/chrome/browser/resources/ash/settings/device_page/display.ts index 88a7ecb0..88ed85326e 100644 --- a/chrome/browser/resources/ash/settings/device_page/display.ts +++ b/chrome/browser/resources/ash/settings/device_page/display.ts
@@ -281,25 +281,6 @@ type: Number, value: null, }, - - /** - * Used by DeepLinkingMixin to focus this page's deep links. - */ - supportedSettingIds: { - type: Object, - value: () => new Set<Setting>([ - Setting.kDisplaySize, - Setting.kDisplayOrientation, - Setting.kDisplayArrangement, - Setting.kDisplayResolution, - Setting.kDisplayRefreshRate, - Setting.kDisplayMirroring, - Setting.kAllowWindowsToSpanDisplays, - Setting.kAmbientColors, - Setting.kTouchscreenCalibration, - Setting.kDisplayOverscan, - ]), - }, }; } @@ -320,6 +301,21 @@ overscanDisplayId: string; primaryDisplayId: string; selectedDisplay?: DisplayUnitInfo; + + // DeepLinkingMixin override + override supportedSettingIds = new Set<Setting>([ + Setting.kDisplaySize, + Setting.kDisplayOrientation, + Setting.kDisplayArrangement, + Setting.kDisplayResolution, + Setting.kDisplayRefreshRate, + Setting.kDisplayMirroring, + Setting.kAllowWindowsToSpanDisplays, + Setting.kAmbientColors, + Setting.kTouchscreenCalibration, + Setting.kDisplayOverscan, + ]); + private browserProxy_: DevicePageBrowserProxy; private brightnessSliderMax_: number; private brightnessSliderMin_: number;
diff --git a/chrome/browser/resources/ash/settings/device_page/display_night_light.ts b/chrome/browser/resources/ash/settings/device_page/display_night_light.ts index a110244..887aa73d 100644 --- a/chrome/browser/resources/ash/settings/device_page/display_night_light.ts +++ b/chrome/browser/resources/ash/settings/device_page/display_night_light.ts
@@ -95,17 +95,6 @@ nightLightScheduleSubLabel_: String, - /** - * Used by DeepLinkingMixin to focus this page's deep links. - */ - supportedSettingIds: { - type: Object, - value: () => new Set<Setting>([ - Setting.kNightLight, - Setting.kNightLightColorTemperature, - ]), - }, - shouldShowGeolocationWarningText_: { type: Boolean, computed: 'computeShouldShowGeolocationWarningText_(' + @@ -165,6 +154,13 @@ } isInternalDisplay: boolean; + + // DeepLinkingMixin override + override supportedSettingIds = new Set<Setting>([ + Setting.kNightLight, + Setting.kNightLightColorTemperature, + ]); + private displaySettingsProvider: DisplaySettingsProviderInterface = getDisplaySettingsProvider(); private nightLightScheduleSubLabel_: string;
diff --git a/chrome/browser/resources/ash/settings/device_page/keyboard.ts b/chrome/browser/resources/ash/settings/device_page/keyboard.ts index 8855697..c5b1d4a3 100644 --- a/chrome/browser/resources/ash/settings/device_page/keyboard.ts +++ b/chrome/browser/resources/ash/settings/device_page/keyboard.ts
@@ -117,23 +117,6 @@ }, /** - * Whether the setting for long press diacritics should be shown - */ - - /** - * Used by DeepLinkingMixin to focus this page's deep links. - */ - supportedSettingIds: { - type: Object, - value: () => new Set<Setting>([ - Setting.kKeyboardFunctionKeys, - Setting.kKeyboardAutoRepeat, - Setting.kKeyboardShortcuts, - Setting.kShowDiacritic, - ]), - }, - - /** * Whether settings should be split per device. */ isDeviceSettingsSplitEnabled_: { @@ -147,6 +130,15 @@ } prefs: PrefsState; + + // DeepLinkingMixin override + override supportedSettingIds = new Set<Setting>([ + Setting.kKeyboardFunctionKeys, + Setting.kKeyboardAutoRepeat, + Setting.kKeyboardShortcuts, + Setting.kShowDiacritic, + ]); + private browserProxy_: DevicePageBrowserProxy; private hasAssistantKey_: boolean; private hasLauncherKey_: boolean;
diff --git a/chrome/browser/resources/ash/settings/device_page/per_device_keyboard.ts b/chrome/browser/resources/ash/settings/device_page/per_device_keyboard.ts index e16e9dc..66c3376 100644 --- a/chrome/browser/resources/ash/settings/device_page/per_device_keyboard.ts +++ b/chrome/browser/resources/ash/settings/device_page/per_device_keyboard.ts
@@ -90,21 +90,17 @@ value: [2000, 1000, 500, 300, 200, 100, 50, 30, 20], readOnly: true, }, - - /** - * Used by DeepLinkingMixin to focus this page's deep links. - */ - supportedSettingIds: { - type: Object, - value: () => new Set<Setting>([ - Setting.kKeyboardAutoRepeat, - Setting.kKeyboardShortcuts, - ]), - }, }; } prefs: PrefsState; + + // DeepLinkingMixin override + override supportedSettingIds = new Set<Setting>([ + Setting.kKeyboardAutoRepeat, + Setting.kKeyboardShortcuts, + ]); + protected keyboards: Keyboard[]; protected keyboardPolicies: KeyboardPolicies; private autoRepeatDelays: number[];
diff --git a/chrome/browser/resources/ash/settings/device_page/per_device_keyboard_subsection.ts b/chrome/browser/resources/ash/settings/device_page/per_device_keyboard_subsection.ts index e35f03d1..79f4c2f 100644 --- a/chrome/browser/resources/ash/settings/device_page/per_device_keyboard_subsection.ts +++ b/chrome/browser/resources/ash/settings/device_page/per_device_keyboard_subsection.ts
@@ -130,17 +130,6 @@ value: '', }, - /** - * Used by DeepLinkingMixin to focus this page's deep links. - */ - supportedSettingIds: { - type: Object, - value: () => new Set<Setting>([ - Setting.kKeyboardFunctionKeys, - Setting.kKeyboardRemapKeys, - ]), - }, - keyboardIndex: { type: Number, }, @@ -201,6 +190,12 @@ } } + // DeepLinkingMixin override + override supportedSettingIds = new Set<Setting>([ + Setting.kKeyboardFunctionKeys, + Setting.kKeyboardRemapKeys, + ]); + protected keyboard: Keyboard; protected keyboardPolicies: KeyboardPolicies; private topRowAreFunctionKeysPref: chrome.settingsPrivate.PrefObject;
diff --git a/chrome/browser/resources/ash/settings/device_page/per_device_mouse_subsection.ts b/chrome/browser/resources/ash/settings/device_page/per_device_mouse_subsection.ts index 1b6e791..12070c4f 100644 --- a/chrome/browser/resources/ash/settings/device_page/per_device_mouse_subsection.ts +++ b/chrome/browser/resources/ash/settings/device_page/per_device_mouse_subsection.ts
@@ -165,20 +165,6 @@ type: Object, }, - /** - * Used by DeepLinkingMixin to focus this page's deep links. - */ - supportedSettingIds: { - type: Object, - value: () => new Set<Setting>([ - Setting.kMouseSwapPrimaryButtons, - Setting.kMouseReverseScrolling, - Setting.kMouseAcceleration, - Setting.kMouseScrollAcceleration, - Setting.kMouseSpeed, - ]), - }, - mouseIndex: { type: Number, }, @@ -261,6 +247,16 @@ } isWelcomeExperienceEnabled: boolean; + + // DeepLinkingMixin override + override supportedSettingIds = new Set<Setting>([ + Setting.kMouseSwapPrimaryButtons, + Setting.kMouseReverseScrolling, + Setting.kMouseAcceleration, + Setting.kMouseScrollAcceleration, + Setting.kMouseSpeed, + ]); + private mouse: Mouse; protected mousePolicies: MousePolicies; private primaryRightPref: chrome.settingsPrivate.PrefObject;
diff --git a/chrome/browser/resources/ash/settings/device_page/per_device_pointing_stick_subsection.ts b/chrome/browser/resources/ash/settings/device_page/per_device_pointing_stick_subsection.ts index 4c1c6ed..f504402f 100644 --- a/chrome/browser/resources/ash/settings/device_page/per_device_pointing_stick_subsection.ts +++ b/chrome/browser/resources/ash/settings/device_page/per_device_pointing_stick_subsection.ts
@@ -112,18 +112,6 @@ pointingStick: {type: Object}, - /** - * Used by DeepLinkingMixin to focus this page's deep links. - */ - supportedSettingIds: { - type: Object, - value: () => new Set<Setting>([ - Setting.kPointingStickAcceleration, - Setting.kPointingStickSpeed, - Setting.kPointingStickSwapPrimaryButtons, - ]), - }, - pointingStickIndex: { type: Number, }, @@ -156,6 +144,13 @@ } } + // DeepLinkingMixin override + override supportedSettingIds = new Set<Setting>([ + Setting.kPointingStickAcceleration, + Setting.kPointingStickSpeed, + Setting.kPointingStickSwapPrimaryButtons, + ]); + private pointingStick: PointingStick; private sensitivityValues: number[]; private swapPrimaryOptions: number[];
diff --git a/chrome/browser/resources/ash/settings/device_page/per_device_touchpad_subsection.ts b/chrome/browser/resources/ash/settings/device_page/per_device_touchpad_subsection.ts index e449791..040d0c59 100644 --- a/chrome/browser/resources/ash/settings/device_page/per_device_touchpad_subsection.ts +++ b/chrome/browser/resources/ash/settings/device_page/per_device_touchpad_subsection.ts
@@ -191,24 +191,6 @@ touchpad: {type: Object}, - /** - * Used by DeepLinkingMixin to focus this page's deep links. - */ - supportedSettingIds: { - type: Object, - value: () => new Set<Setting>([ - Setting.kTouchpadTapToClick, - Setting.kTouchpadTapDragging, - Setting.kTouchpadReverseScrolling, - Setting.kTouchpadAcceleration, - Setting.kTouchpadScrollAcceleration, - Setting.kTouchpadSpeed, - Setting.kTouchpadHapticFeedback, - Setting.kTouchpadHapticClickSensitivity, - Setting.kTouchpadSimulateRightClick, - ]), - }, - touchpadIndex: { type: Number, }, @@ -259,6 +241,19 @@ } } + // DeepLinkingMixin override + override supportedSettingIds = new Set<Setting>([ + Setting.kTouchpadTapToClick, + Setting.kTouchpadTapDragging, + Setting.kTouchpadReverseScrolling, + Setting.kTouchpadAcceleration, + Setting.kTouchpadScrollAcceleration, + Setting.kTouchpadSpeed, + Setting.kTouchpadHapticFeedback, + Setting.kTouchpadHapticClickSensitivity, + Setting.kTouchpadSimulateRightClick, + ]); + private touchpad: Touchpad; private enableTapToClickPref: chrome.settingsPrivate.PrefObject; private enableTapDraggingPref: chrome.settingsPrivate.PrefObject;
diff --git a/chrome/browser/resources/ash/settings/device_page/pointers.ts b/chrome/browser/resources/ash/settings/device_page/pointers.ts index 1739bf8..390d93e 100644 --- a/chrome/browser/resources/ash/settings/device_page/pointers.ts +++ b/chrome/browser/resources/ash/settings/device_page/pointers.ts
@@ -109,29 +109,6 @@ }, /** - * Used by DeepLinkingMixin to focus this page's deep links. - */ - supportedSettingIds: { - type: Object, - value: () => new Set<Setting>([ - Setting.kTouchpadTapToClick, - Setting.kTouchpadTapDragging, - Setting.kTouchpadReverseScrolling, - Setting.kTouchpadAcceleration, - Setting.kTouchpadSpeed, - Setting.kTouchpadHapticFeedback, - Setting.kTouchpadHapticClickSensitivity, - Setting.kPointingStickAcceleration, - Setting.kPointingStickSpeed, - Setting.kPointingStickSwapPrimaryButtons, - Setting.kMouseSwapPrimaryButtons, - Setting.kMouseReverseScrolling, - Setting.kMouseAcceleration, - Setting.kMouseSpeed, - ]), - }, - - /** * Whether settings should be split per device. */ isDeviceSettingsSplitEnabled_: { @@ -148,6 +125,25 @@ hasPointingStick: boolean; hasTouchpad: boolean; hasHapticTouchpad: boolean; + + // DeepLinkingMixin override + override supportedSettingIds = new Set<Setting>([ + Setting.kTouchpadTapToClick, + Setting.kTouchpadTapDragging, + Setting.kTouchpadReverseScrolling, + Setting.kTouchpadAcceleration, + Setting.kTouchpadSpeed, + Setting.kTouchpadHapticFeedback, + Setting.kTouchpadHapticClickSensitivity, + Setting.kPointingStickAcceleration, + Setting.kPointingStickSpeed, + Setting.kPointingStickSwapPrimaryButtons, + Setting.kMouseSwapPrimaryButtons, + Setting.kMouseReverseScrolling, + Setting.kMouseAcceleration, + Setting.kMouseSpeed, + ]); + private isDeviceSettingsSplitEnabled_: boolean; /**
diff --git a/chrome/browser/resources/ash/settings/device_page/power.ts b/chrome/browser/resources/ash/settings/device_page/power.ts index 87d1d96f..568b7c7 100644 --- a/chrome/browser/resources/ash/settings/device_page/power.ts +++ b/chrome/browser/resources/ash/settings/device_page/power.ts
@@ -179,25 +179,19 @@ computed: 'computeBatterySaverHidden_(batteryStatus_, batterySaverFeatureEnabled_)', }, - - /** - * Used by DeepLinkingMixin to focus this page's deep links. - */ - supportedSettingIds: { - type: Object, - value: () => new Set<Setting>([ - Setting.kPowerIdleBehaviorWhileCharging, - Setting.kPowerSource, - Setting.kSleepWhenLaptopLidClosed, - Setting.kPowerIdleBehaviorWhileOnBattery, - Setting.kAdaptiveCharging, - Setting.kBatterySaver, - ]), - }, - }; } + // DeepLinkingMixin override + override supportedSettingIds = new Set<Setting>([ + Setting.kPowerIdleBehaviorWhileCharging, + Setting.kPowerSource, + Setting.kSleepWhenLaptopLidClosed, + Setting.kPowerIdleBehaviorWhileOnBattery, + Setting.kAdaptiveCharging, + Setting.kBatterySaver, + ]); + private acIdleManaged_: boolean; private acIdleOptions_: IdleOption[]; private adaptiveChargingEnabled_: boolean;
diff --git a/chrome/browser/resources/ash/settings/device_page/stylus.ts b/chrome/browser/resources/ash/settings/device_page/stylus.ts index 73ccc15..0f9a9a7 100644 --- a/chrome/browser/resources/ash/settings/device_page/stylus.ts +++ b/chrome/browser/resources/ash/settings/device_page/stylus.ts
@@ -94,22 +94,17 @@ type: Boolean, value: false, }, - - /** - * Used by DeepLinkingMixin to focus this page's deep links. - */ - supportedSettingIds: { - type: Object, - value: () => new Set<Setting>([ - Setting.kStylusToolsInShelf, - Setting.kStylusNoteTakingApp, - ]), - }, - }; } prefs: PrefsState; + + // DeepLinkingMixin override + override supportedSettingIds = new Set<Setting>([ + Setting.kStylusToolsInShelf, + Setting.kStylusNoteTakingApp, + ]); + private appChoices_: NoteAppInfo[]; private browserProxy_: DevicePageBrowserProxy; private selectedApp_: NoteAppInfo|null;
diff --git a/chrome/browser/resources/ash/settings/guest_os/guest_os_shared_usb_devices.ts b/chrome/browser/resources/ash/settings/guest_os/guest_os_shared_usb_devices.ts index e6f5e20..a78d2127 100644 --- a/chrome/browser/resources/ash/settings/guest_os/guest_os_shared_usb_devices.ts +++ b/chrome/browser/resources/ash/settings/guest_os/guest_os_shared_usb_devices.ts
@@ -123,23 +123,19 @@ return []; }, }, - - /** - * Used by DeepLinkingMixin to focus this page's deep links. - */ - supportedSettingIds: { - type: Object, - value: () => new Set<Setting>([ - Setting.kGuestUsbNotification, - Setting.kGuestUsbPersistentPassthrough, - ]), - }, }; } defaultGuestId: GuestId; guestOsType: GuestOsType; hasContainers: boolean; + + // DeepLinkingMixin override + override supportedSettingIds = new Set<Setting>([ + Setting.kGuestUsbNotification, + Setting.kGuestUsbPersistentPassthrough, + ]); + private allContainers_: ContainerInfo[]; private browserProxy_: GuestOsBrowserProxy; private reassignDevice_: GuestOsSharedUsbDevice|null;
diff --git a/chrome/browser/resources/ash/settings/internet_page/hotspot_subpage.ts b/chrome/browser/resources/ash/settings/internet_page/hotspot_subpage.ts index 779c105..ef7a07d 100644 --- a/chrome/browser/resources/ash/settings/internet_page/hotspot_subpage.ts +++ b/chrome/browser/resources/ash/settings/internet_page/hotspot_subpage.ts
@@ -69,19 +69,17 @@ }; }, }, - - /** - * Used by DeepLinkingMixin to focus this page's deep links. - */ - supportedSettingIds: { - type: Object, - value: () => new Set<Setting>( - [Setting.kHotspotOnOff, Setting.kHotspotAutoDisabled]), - }, }; } hotspotInfo: HotspotInfo|undefined; + + // DeepLinkingMixin override + override supportedSettingIds = new Set<Setting>([ + Setting.kHotspotOnOff, + Setting.kHotspotAutoDisabled, + ]); + private isHotspotToggleOn_: boolean; private autoDisableVirtualPref_: chrome.settingsPrivate.PrefObject<boolean>;
diff --git a/chrome/browser/resources/ash/settings/internet_page/internet_detail_menu.ts b/chrome/browser/resources/ash/settings/internet_page/internet_detail_menu.ts index 21d4be19..c1092ad 100644 --- a/chrome/browser/resources/ash/settings/internet_page/internet_detail_menu.ts +++ b/chrome/browser/resources/ash/settings/internet_page/internet_detail_menu.ts
@@ -75,21 +75,17 @@ type: String, value: '', }, - - /** - * Used by DeepLinkingMixin to focus this page's deep links. - */ - supportedSettingIds: { - type: Object, - value: () => new Set<Setting>([ - Setting.kCellularRenameESimNetwork, - Setting.kCellularRemoveESimNetwork, - ]), - }, }; } deviceState: OncMojo.DeviceStateProperties|undefined; + + // DeepLinkingMixin override + override supportedSettingIds = new Set<Setting>([ + Setting.kCellularRenameESimNetwork, + Setting.kCellularRemoveESimNetwork, + ]); + private eSimNetworkState_: OncMojo.NetworkStateProperties|null; private isGuest_: boolean; private guid_: string;
diff --git a/chrome/browser/resources/ash/settings/internet_page/internet_detail_subpage.ts b/chrome/browser/resources/ash/settings/internet_page/internet_detail_subpage.ts index 31e81b7..8d40ed4 100644 --- a/chrome/browser/resources/ash/settings/internet_page/internet_detail_subpage.ts +++ b/chrome/browser/resources/ash/settings/internet_page/internet_detail_subpage.ts
@@ -335,38 +335,6 @@ proxyExpanded_: Boolean, dataUsageExpanded_: Boolean, - - /** - * Used by DeepLinkingMixin to focus this page's deep links. - */ - supportedSettingIds: { - type: Object, - value: () => new Set<Setting>([ - Setting.kConfigureEthernet, - Setting.kEthernetAutoConfigureIp, - Setting.kEthernetDns, - Setting.kEthernetProxy, - Setting.kDisconnectWifiNetwork, - Setting.kPreferWifiNetwork, - Setting.kForgetWifiNetwork, - Setting.kWifiAutoConfigureIp, - Setting.kWifiDns, - Setting.kWifiHidden, - Setting.kWifiProxy, - Setting.kWifiAutoConnectToNetwork, - Setting.kCellularSimLock, - Setting.kCellularRoaming, - Setting.kCellularApn, - Setting.kDisconnectCellularNetwork, - Setting.kCellularAutoConfigureIp, - Setting.kCellularDns, - Setting.kCellularProxy, - Setting.kCellularAutoConnectToNetwork, - Setting.kDisconnectTetherNetwork, - Setting.kWifiMetered, - Setting.kCellularMetered, - ]), - }, }; } @@ -388,6 +356,34 @@ globalPolicy?: GlobalPolicy; guid: string; managedNetworkAvailable: boolean; + + // DeepLinkingMixin override + override supportedSettingIds = new Set<Setting>([ + Setting.kConfigureEthernet, + Setting.kEthernetAutoConfigureIp, + Setting.kEthernetDns, + Setting.kEthernetProxy, + Setting.kDisconnectWifiNetwork, + Setting.kPreferWifiNetwork, + Setting.kForgetWifiNetwork, + Setting.kWifiAutoConfigureIp, + Setting.kWifiDns, + Setting.kWifiHidden, + Setting.kWifiProxy, + Setting.kWifiAutoConnectToNetwork, + Setting.kCellularSimLock, + Setting.kCellularRoaming, + Setting.kCellularApn, + Setting.kDisconnectCellularNetwork, + Setting.kCellularAutoConfigureIp, + Setting.kCellularDns, + Setting.kCellularProxy, + Setting.kCellularAutoConnectToNetwork, + Setting.kDisconnectTetherNetwork, + Setting.kWifiMetered, + Setting.kCellularMetered, + ]); + private advancedExpanded_: boolean; private alwaysOnVpn_: chrome.settingsPrivate.PrefObject<boolean>; private applyingChanges_: boolean;
diff --git a/chrome/browser/resources/ash/settings/internet_page/internet_known_networks_subpage.ts b/chrome/browser/resources/ash/settings/internet_page/internet_known_networks_subpage.ts index b0b170e..6fa435e 100644 --- a/chrome/browser/resources/ash/settings/internet_page/internet_known_networks_subpage.ts +++ b/chrome/browser/resources/ash/settings/internet_page/internet_known_networks_subpage.ts
@@ -127,21 +127,17 @@ type: Number, value: null, }, - - /** - * Used by DeepLinkingMixin to focus this page's deep links. - */ - supportedSettingIds: { - type: Object, - value: () => new Set<Setting>([ - Setting.kPreferWifiNetwork, - Setting.kForgetWifiNetwork, - ]), - }, }; } networkType: NetworkType|undefined; + + // DeepLinkingMixin override + override supportedSettingIds = new Set<Setting>([ + Setting.kPreferWifiNetwork, + Setting.kForgetWifiNetwork, + ]); + private enableForget_: boolean; private networkConfig_: CrosNetworkConfigInterface; private networkStateList_: OncMojo.NetworkStateProperties[];
diff --git a/chrome/browser/resources/ash/settings/internet_page/internet_page.ts b/chrome/browser/resources/ash/settings/internet_page/internet_page.ts index 1998358..a944a571 100644 --- a/chrome/browser/resources/ash/settings/internet_page/internet_page.ts +++ b/chrome/browser/resources/ash/settings/internet_page/internet_page.ts
@@ -329,18 +329,6 @@ value: '', }, - /** - * Used by DeepLinkingMixin to focus this page's deep links. - */ - supportedSettingIds: { - type: Object, - value: () => new Set<Setting>([ - Setting.kWifiOnOff, - Setting.kMobileOnOff, - Setting.kCellularAddApn, - ]), - }, - errorToastMessage_: { type: String, value: '', @@ -387,6 +375,14 @@ deviceStates: Record<string, OncMojo.DeviceStateProperties>|undefined; hotspotInfo: HotspotInfo|undefined; managedNetworkAvailable: boolean; + + // DeepLinkingMixin override + override supportedSettingIds = new Set<Setting>([ + Setting.kWifiOnOff, + Setting.kMobileOnOff, + Setting.kCellularAddApn, + ]); + private addConnectionExpanded_: boolean; private browserProxy_: InternetPageBrowserProxy; private cellularSetupDialogPageName_: CellularSetupPageName|null;
diff --git a/chrome/browser/resources/ash/settings/internet_page/internet_subpage.ts b/chrome/browser/resources/ash/settings/internet_page/internet_subpage.ts index 10c5ec5..4e8f4f71 100644 --- a/chrome/browser/resources/ash/settings/internet_page/internet_subpage.ts +++ b/chrome/browser/resources/ash/settings/internet_page/internet_subpage.ts
@@ -234,20 +234,6 @@ type: Number, value: null, }, - - /** - * Used by DeepLinkingMixin to focus this page's deep links. - */ - supportedSettingIds: { - type: Object, - value: () => new Set<Setting>([ - Setting.kWifiOnOff, - Setting.kWifiAddNetwork, - Setting.kMobileOnOff, - Setting.kInstantTetheringOnOff, - Setting.kAddESimNetwork, - ]), - }, }; } @@ -267,6 +253,16 @@ showSpinner: boolean; tetherDeviceState: OncMojo.DeviceStateProperties|undefined; vpnProviders: VpnProvider[]; + + // DeepLinkingMixin override + override supportedSettingIds = new Set<Setting>([ + Setting.kWifiOnOff, + Setting.kWifiAddNetwork, + Setting.kMobileOnOff, + Setting.kInstantTetheringOnOff, + Setting.kAddESimNetwork, + ]); + private alwaysOnVpnMode_: AlwaysOnVpnMode|undefined; private alwaysOnVpnService_: string|undefined; private browserProxy_: InternetPageBrowserProxy;
diff --git a/chrome/browser/resources/ash/settings/kerberos_page/kerberos_accounts_subpage.ts b/chrome/browser/resources/ash/settings/kerberos_page/kerberos_accounts_subpage.ts index f54ce5cc..0884d908 100644 --- a/chrome/browser/resources/ash/settings/kerberos_page/kerberos_accounts_subpage.ts +++ b/chrome/browser/resources/ash/settings/kerberos_page/kerberos_accounts_subpage.ts
@@ -89,21 +89,16 @@ type: String, value: '', }, - - /** - * Used by DeepLinkingMixin to focus this page's deep links. - */ - supportedSettingIds: { - type: Object, - value: () => new Set<Setting>([ - Setting.kAddKerberosTicketV2, - Setting.kRemoveKerberosTicketV2, - Setting.kSetActiveKerberosTicketV2, - ]), - }, }; } + // DeepLinkingMixin override + override supportedSettingIds = new Set<Setting>([ + Setting.kAddKerberosTicketV2, + Setting.kRemoveKerberosTicketV2, + Setting.kSetActiveKerberosTicketV2, + ]); + private accountToastText_: string; private accounts_: KerberosAccount[]; private addAccountsAllowed_: boolean;
diff --git a/chrome/browser/resources/ash/settings/multidevice_page/multidevice_page.ts b/chrome/browser/resources/ash/settings/multidevice_page/multidevice_page.ts index 5fc605a..7204596 100644 --- a/chrome/browser/resources/ash/settings/multidevice_page/multidevice_page.ts +++ b/chrome/browser/resources/ash/settings/multidevice_page/multidevice_page.ts
@@ -164,20 +164,6 @@ }, /** - * Used by DeepLinkingMixin to focus this page's deep links. - */ - supportedSettingIds: { - type: Object, - value: () => new Set<Setting>([ - Setting.kSetUpMultiDevice, - Setting.kVerifyMultiDeviceSetup, - Setting.kMultiDeviceOnOff, - Setting.kNearbyShareDeviceVisibility, - Setting.kNearbyShareOnOff, - ]), - }, - - /** * Reflects the password sub-dialog property. */ isPasswordDialogShowing_: { @@ -239,6 +225,16 @@ } isSettingsRetreived: boolean; + + // DeepLinkingMixin override + override supportedSettingIds = new Set<Setting>([ + Setting.kSetUpMultiDevice, + Setting.kVerifyMultiDeviceSetup, + Setting.kMultiDeviceOnOff, + Setting.kNearbyShareDeviceVisibility, + Setting.kNearbyShareOnOff, + ]); + private authToken_: TokenInfo|undefined; private authTokenReply_: RequestTokenReply|undefined|null; private browserProxy_: MultiDeviceBrowserProxy;
diff --git a/chrome/browser/resources/ash/settings/multidevice_page/multidevice_subpage.ts b/chrome/browser/resources/ash/settings/multidevice_page/multidevice_subpage.ts index dbfd765..9218dc3 100644 --- a/chrome/browser/resources/ash/settings/multidevice_page/multidevice_subpage.ts +++ b/chrome/browser/resources/ash/settings/multidevice_page/multidevice_subpage.ts
@@ -48,28 +48,19 @@ return getTemplate(); } - static get properties() { - return { - /** - * Used by DeepLinkingMixin to focus this page's deep links. - */ - supportedSettingIds: { - type: Object, - value: () => new Set<Setting>([ - Setting.kInstantTetheringOnOff, - Setting.kMultiDeviceOnOff, - Setting.kSmartLockOnOff, - Setting.kForgetPhone, - Setting.kPhoneHubOnOff, - Setting.kPhoneHubCameraRollOnOff, - Setting.kPhoneHubNotificationsOnOff, - Setting.kPhoneHubTaskContinuationOnOff, - Setting.kWifiSyncOnOff, - Setting.kPhoneHubAppsOnOff, - ]), - }, - }; - } + // DeepLinkingMixin override + override supportedSettingIds = new Set<Setting>([ + Setting.kInstantTetheringOnOff, + Setting.kMultiDeviceOnOff, + Setting.kSmartLockOnOff, + Setting.kForgetPhone, + Setting.kPhoneHubOnOff, + Setting.kPhoneHubCameraRollOnOff, + Setting.kPhoneHubNotificationsOnOff, + Setting.kPhoneHubTaskContinuationOnOff, + Setting.kWifiSyncOnOff, + Setting.kPhoneHubAppsOnOff, + ]); private browserProxy_: MultiDeviceBrowserProxy;
diff --git a/chrome/browser/resources/ash/settings/nearby_share_page/nearby_share_subpage.ts b/chrome/browser/resources/ash/settings/nearby_share_page/nearby_share_subpage.ts index 0533b80..09b21e14 100644 --- a/chrome/browser/resources/ash/settings/nearby_share_page/nearby_share_subpage.ts +++ b/chrome/browser/resources/ash/settings/nearby_share_page/nearby_share_subpage.ts
@@ -117,21 +117,6 @@ value: () => loadTimeData.getBoolean('isQuickShareV2Enabled'), }, - /** - * Used by DeepLinkingMixin to focus this page's deep links. - */ - supportedSettingIds: { - type: Object, - value: () => new Set<Setting>([ - Setting.kNearbyShareOnOff, - Setting.kNearbyShareDeviceName, - Setting.kNearbyShareDeviceVisibility, - Setting.kNearbyShareContacts, - Setting.kNearbyShareDataUsage, - Setting.kDevicesNearbyAreSharingNotificationOnOff, - ]), - }, - shouldShowFastInititationNotificationToggle_: { type: Boolean, computed: `computeShouldShowFastInititationNotificationToggle_( @@ -158,6 +143,17 @@ isSettingsRetreived: boolean; settings: NearbySettings; + + // DeepLinkingMixin override + override supportedSettingIds = new Set<Setting>([ + Setting.kNearbyShareOnOff, + Setting.kNearbyShareDeviceName, + Setting.kNearbyShareDeviceVisibility, + Setting.kNearbyShareContacts, + Setting.kNearbyShareDataUsage, + Setting.kDevicesNearbyAreSharingNotificationOnOff, + ]); + private inHighVisibility_: boolean; private isQuickShareV2Enabled_: boolean; private manageContactsUrl_: string;
diff --git a/chrome/browser/resources/ash/settings/os_a11y_page/audio_and_captions_page.ts b/chrome/browser/resources/ash/settings/os_a11y_page/audio_and_captions_page.ts index 682005fe..ae42f21 100644 --- a/chrome/browser/resources/ash/settings/os_a11y_page/audio_and_captions_page.ts +++ b/chrome/browser/resources/ash/settings/os_a11y_page/audio_and_captions_page.ts
@@ -134,24 +134,20 @@ ]; }, }, - - /** - * Used by DeepLinkingMixin to focus this page's deep links. - */ - supportedSettingIds: { - type: Object, - value: () => new Set<Setting>([ - Setting.kFlashNotifications, - Setting.kLiveCaption, - Setting.kMonoAudio, - Setting.kStartupSound, - ]), - }, }; } languages: LanguagesModel; languageHelper: LanguageHelper; + + // DeepLinkingMixin override + override supportedSettingIds = new Set<Setting>([ + Setting.kFlashNotifications, + Setting.kLiveCaption, + Setting.kMonoAudio, + Setting.kStartupSound, + ]); + private audioAndCaptionsBrowserProxy_: AudioAndCaptionsPageBrowserProxy; private isKioskModeActive_: boolean;
diff --git a/chrome/browser/resources/ash/settings/os_a11y_page/cursor_and_touchpad_page.ts b/chrome/browser/resources/ash/settings/os_a11y_page/cursor_and_touchpad_page.ts index cf5fde5..a88ad61 100644 --- a/chrome/browser/resources/ash/settings/os_a11y_page/cursor_and_touchpad_page.ts +++ b/chrome/browser/resources/ash/settings/os_a11y_page/cursor_and_touchpad_page.ts
@@ -267,23 +267,6 @@ }, /** - * Used by DeepLinkingMixin to focus this page's deep links. - */ - supportedSettingIds: { - type: Object, - value: () => new Set<Setting>([ - Setting.kAutoClickWhenCursorStops, - Setting.kDisableTouchpad, - Setting.kEnableCursorColor, - Setting.kHighlightCursorWhileMoving, - Setting.kLargeCursor, - Setting.kMouseKeysEnabled, - Setting.kOverscrollEnabled, - Setting.kTabletNavigationButtons, - ]), - }, - - /** * Check if at least one mouse is connected. */ hasMouse_: { @@ -313,6 +296,18 @@ ]; } + // DeepLinkingMixin override + override supportedSettingIds = new Set<Setting>([ + Setting.kAutoClickWhenCursorStops, + Setting.kDisableTouchpad, + Setting.kEnableCursorColor, + Setting.kHighlightCursorWhileMoving, + Setting.kLargeCursor, + Setting.kMouseKeysEnabled, + Setting.kOverscrollEnabled, + Setting.kTabletNavigationButtons, + ]); + private autoClickDelayOptions_: Option[]; private autoClickMovementThresholdOptions_: Option[]; private cursorAndTouchpadBrowserProxy_: CursorAndTouchpadPageBrowserProxy;
diff --git a/chrome/browser/resources/ash/settings/os_a11y_page/display_and_magnification_subpage.ts b/chrome/browser/resources/ash/settings/os_a11y_page/display_and_magnification_subpage.ts index 04990582..bf9722b 100644 --- a/chrome/browser/resources/ash/settings/os_a11y_page/display_and_magnification_subpage.ts +++ b/chrome/browser/resources/ash/settings/os_a11y_page/display_and_magnification_subpage.ts
@@ -134,29 +134,24 @@ return loadTimeData.getBoolean('isKioskModeActive'); }, }, - - /** - * Used by DeepLinkingMixin to focus this page's deep links. - */ - supportedSettingIds: { - type: Object, - value: () => new Set<Setting>([ - Setting.kAccessibilityMagnifierFollowsSts, - Setting.kColorCorrectionEnabled, - Setting.kColorCorrectionFilterType, - Setting.kColorCorrectionFilterAmount, - Setting.kDockedMagnifier, - Setting.kFullscreenMagnifier, - Setting.kFullscreenMagnifierMouseFollowingMode, - Setting.kFullscreenMagnifierFocusFollowing, - Setting.kMagnifierFollowsChromeVox, - Setting.kReducedAnimationsEnabled, - Setting.kAlwaysShowScrollbarsEnabled, - ]), - }, }; } + // DeepLinkingMixin override + override supportedSettingIds = new Set<Setting>([ + Setting.kAccessibilityMagnifierFollowsSts, + Setting.kColorCorrectionEnabled, + Setting.kColorCorrectionFilterType, + Setting.kColorCorrectionFilterAmount, + Setting.kDockedMagnifier, + Setting.kFullscreenMagnifier, + Setting.kFullscreenMagnifierMouseFollowingMode, + Setting.kFullscreenMagnifierFocusFollowing, + Setting.kMagnifierFollowsChromeVox, + Setting.kReducedAnimationsEnabled, + Setting.kAlwaysShowScrollbarsEnabled, + ]); + private isKioskModeActive_: boolean; private screenMagnifierMouseFollowingModePrefValues_: {[key: string]: number}; private screenMagnifierZoomOptions_: Array<{value: number, name: string}>;
diff --git a/chrome/browser/resources/ash/settings/os_a11y_page/facegaze_subpage.ts b/chrome/browser/resources/ash/settings/os_a11y_page/facegaze_subpage.ts index fb8c7ef..3eb711a 100644 --- a/chrome/browser/resources/ash/settings/os_a11y_page/facegaze_subpage.ts +++ b/chrome/browser/resources/ash/settings/os_a11y_page/facegaze_subpage.ts
@@ -48,26 +48,20 @@ computed: 'getToggleLabel_(prefs.settings.a11y.face_gaze.enabled_sentinel.value)', }, - - supportedSettingIds: { - type: Object, - value: () => new Set<Setting>([ - Setting.kFaceGaze, - ]), - }, }; } + // DeepLinkingMixin override + override supportedSettingIds = new Set<Setting>([ + Setting.kFaceGaze, + ]); + private getToggleLabel_(): string { return this.getPref('settings.a11y.face_gaze.enabled_sentinel').value ? this.i18n('deviceOn') : this.i18n('deviceOff'); } - static get observers() { - return []; - } - override currentRouteChanged(route: Route): void { // Does not apply to this page. if (route !== routes.MANAGE_FACEGAZE_SETTINGS) {
diff --git a/chrome/browser/resources/ash/settings/os_a11y_page/keyboard_and_text_input_page.ts b/chrome/browser/resources/ash/settings/os_a11y_page/keyboard_and_text_input_page.ts index 4edcd81d..fea7d0a6 100644 --- a/chrome/browser/resources/ash/settings/os_a11y_page/keyboard_and_text_input_page.ts +++ b/chrome/browser/resources/ash/settings/os_a11y_page/keyboard_and_text_input_page.ts
@@ -114,25 +114,6 @@ }, }, - /** - * Used by DeepLinkingMixin to focus this page's deep links. - */ - supportedSettingIds: { - type: Object, - value: () => new Set<Setting>([ - Setting.kBounceKeys, - Setting.kCaretBlinkInterval, - Setting.kCaretBrowsing, - Setting.kDictation, - Setting.kEnableSwitchAccess, - Setting.kHighlightKeyboardFocus, - Setting.kHighlightTextCaret, - Setting.kOnScreenKeyboard, - Setting.kSlowKeys, - Setting.kStickyKeys, - ]), - }, - focusHighlightEnabledVirtualPref_: { type: Object, computed: 'computeEnabledWithConflictingFeature_(' + @@ -186,6 +167,20 @@ ]; } + // DeepLinkingMixin override + override supportedSettingIds = new Set<Setting>([ + Setting.kBounceKeys, + Setting.kCaretBlinkInterval, + Setting.kCaretBrowsing, + Setting.kDictation, + Setting.kEnableSwitchAccess, + Setting.kHighlightKeyboardFocus, + Setting.kHighlightTextCaret, + Setting.kOnScreenKeyboard, + Setting.kSlowKeys, + Setting.kStickyKeys, + ]); + private dictationLearnMoreUrl_: string; private dictationLocaleMenuSubtitle_: string; private dictationLocaleOptions_: LocaleInfo[];
diff --git a/chrome/browser/resources/ash/settings/os_a11y_page/mouse_keys_subpage.ts b/chrome/browser/resources/ash/settings/os_a11y_page/mouse_keys_subpage.ts index 8d9e216..3feedf4 100644 --- a/chrome/browser/resources/ash/settings/os_a11y_page/mouse_keys_subpage.ts +++ b/chrome/browser/resources/ash/settings/os_a11y_page/mouse_keys_subpage.ts
@@ -71,13 +71,6 @@ 'getToggleLabel_(prefs.settings.a11y.mouse_keys.enabled.value)', }, - supportedSettingIds: { - type: Object, - value: () => new Set<Setting>([ - Setting.kMouseKeysEnabled, - ]), - }, - mouseKeysDominantHandOptions_: { readOnly: true, type: Array, @@ -208,6 +201,11 @@ }; } + // DeepLinkingMixin override + override supportedSettingIds = new Set<Setting>([ + Setting.kMouseKeysEnabled, + ]); + private primaryKeyboardRightHandPreviewOptions_: KeyboardPreviewOption[]; private primaryKeyboardLeftHandPreviewOptions_: KeyboardPreviewOption[];
diff --git a/chrome/browser/resources/ash/settings/os_a11y_page/os_a11y_page.ts b/chrome/browser/resources/ash/settings/os_a11y_page/os_a11y_page.ts index dd8cb70..16ef717 100644 --- a/chrome/browser/resources/ash/settings/os_a11y_page/os_a11y_page.ts +++ b/chrome/browser/resources/ash/settings/os_a11y_page/os_a11y_page.ts
@@ -101,18 +101,6 @@ }, languageHelper: Object, - - /** - * Used by DeepLinkingMixin to focus this page's deep links. - */ - supportedSettingIds: { - type: Object, - value: () => new Set<Setting>([ - Setting.kA11yQuickSettings, - Setting.kGetImageDescriptionsFromGoogle, - Setting.kLiveCaption, - ]), - }, }; } @@ -120,6 +108,13 @@ languages: LanguagesModel; languageHelper: LanguageHelper; + // DeepLinkingMixin override + override supportedSettingIds = new Set<Setting>([ + Setting.kA11yQuickSettings, + Setting.kGetImageDescriptionsFromGoogle, + Setting.kLiveCaption, + ]); + private browserProxy_: OsA11yPageBrowserProxy; private hasScreenReader_: boolean; private isGuest_: boolean;
diff --git a/chrome/browser/resources/ash/settings/os_a11y_page/select_to_speak_subpage.ts b/chrome/browser/resources/ash/settings/os_a11y_page/select_to_speak_subpage.ts index edd94ed..f1db633 100644 --- a/chrome/browser/resources/ash/settings/os_a11y_page/select_to_speak_subpage.ts +++ b/chrome/browser/resources/ash/settings/os_a11y_page/select_to_speak_subpage.ts
@@ -215,18 +215,6 @@ loadTimeData.getString('selectToSpeakLearnMoreUrl'); }, }, - - /** - * Used by DeepLinkingMixin to focus this page's deep links. - */ - supportedSettingIds: { - type: Object, - value: () => new Set<Setting>([ - Setting.kSelectToSpeakWordHighlight, - Setting.kSelectToSpeakBackgroundShading, - Setting.kSelectToSpeakNavigationControls, - ]), - }, }; } @@ -242,6 +230,13 @@ ]; } + // DeepLinkingMixin override + override supportedSettingIds = new Set<Setting>([ + Setting.kSelectToSpeakWordHighlight, + Setting.kSelectToSpeakBackgroundShading, + Setting.kSelectToSpeakNavigationControls, + ]); + private langBrowserProxy_: LanguagesBrowserProxy; private enhancedNetworkVoicesVirtualPref_: chrome.settingsPrivate.PrefObject<boolean>;
diff --git a/chrome/browser/resources/ash/settings/os_a11y_page/switch_access_subpage.ts b/chrome/browser/resources/ash/settings/os_a11y_page/switch_access_subpage.ts index d0592bd..374134f586 100644 --- a/chrome/browser/resources/ash/settings/os_a11y_page/switch_access_subpage.ts +++ b/chrome/browser/resources/ash/settings/os_a11y_page/switch_access_subpage.ts
@@ -152,18 +152,6 @@ value: 1, }, - /** - * Used by DeepLinkingMixin to focus this page's deep links. - */ - supportedSettingIds: { - type: Object, - value: () => new Set<Setting>([ - Setting.kSwitchActionAssignment, - Setting.kSwitchActionAutoScan, - Setting.kSwitchActionAutoScanKeyboard, - ]), - }, - showSwitchAccessActionAssignmentDialog_: { type: Boolean, value: false, @@ -187,6 +175,13 @@ }; } + // DeepLinkingMixin override + override supportedSettingIds = new Set<Setting>([ + Setting.kSwitchActionAssignment, + Setting.kSwitchActionAutoScan, + Setting.kSwitchActionAutoScanKeyboard, + ]); + private action_: SwitchAccessCommand|null; private autoScanSpeedRangeMs_: number[]; private focusAfterDialogClose_: HTMLElement|null;
diff --git a/chrome/browser/resources/ash/settings/os_a11y_page/text_to_speech_subpage.ts b/chrome/browser/resources/ash/settings/os_a11y_page/text_to_speech_subpage.ts index 6954fad..7911d898 100644 --- a/chrome/browser/resources/ash/settings/os_a11y_page/text_to_speech_subpage.ts +++ b/chrome/browser/resources/ash/settings/os_a11y_page/text_to_speech_subpage.ts
@@ -67,21 +67,17 @@ * Indicate whether a screen reader is enabled. */ hasScreenReader: Boolean, - - /** - * Used by DeepLinkingMixin to focus this page's deep links. - */ - supportedSettingIds: { - type: Object, - value: () => new Set<Setting>([ - Setting.kChromeVox, - Setting.kSelectToSpeak, - ]), - }, }; } hasScreenReader: boolean; + + // DeepLinkingMixin override + override supportedSettingIds = new Set<Setting>([ + Setting.kChromeVox, + Setting.kSelectToSpeak, + ]); + private deviceBrowserProxy_: DevicePageBrowserProxy; private hasKeyboard_: boolean; private textToSpeechBrowserProxy_: TextToSpeechSubpageBrowserProxy;
diff --git a/chrome/browser/resources/ash/settings/os_a11y_page/tts_voice_subpage.ts b/chrome/browser/resources/ash/settings/os_a11y_page/tts_voice_subpage.ts index 2d7cb77..36c1abf 100644 --- a/chrome/browser/resources/ash/settings/os_a11y_page/tts_voice_subpage.ts +++ b/chrome/browser/resources/ash/settings/os_a11y_page/tts_voice_subpage.ts
@@ -149,20 +149,6 @@ type: Boolean, value: false, }, - - /** - * Used by DeepLinkingMixin to focus this page's deep links. - */ - supportedSettingIds: { - type: Object, - value: () => new Set<Setting>([ - Setting.kTextToSpeechRate, - Setting.kTextToSpeechPitch, - Setting.kTextToSpeechVolume, - Setting.kTextToSpeechVoice, - Setting.kTextToSpeechEngines, - ]), - }, }; } @@ -173,6 +159,16 @@ languagesOpened: boolean; languagesToVoices: TtsLanguage[]; prefs: PrefsState; + + // DeepLinkingMixin override + override supportedSettingIds = new Set<Setting>([ + Setting.kTextToSpeechRate, + Setting.kTextToSpeechPitch, + Setting.kTextToSpeechVolume, + Setting.kTextToSpeechVoice, + Setting.kTextToSpeechEngines, + ]); + private isPreviewing_: boolean; private langBrowserProxy_: LanguagesBrowserProxy; private previewText_: string;
diff --git a/chrome/browser/resources/ash/settings/os_about_page/detailed_build_info_subpage.ts b/chrome/browser/resources/ash/settings/os_about_page/detailed_build_info_subpage.ts index 5aedd812..c0af283 100644 --- a/chrome/browser/resources/ash/settings/os_about_page/detailed_build_info_subpage.ts +++ b/chrome/browser/resources/ash/settings/os_about_page/detailed_build_info_subpage.ts
@@ -84,18 +84,6 @@ value: '', }, - /** - * Used by DeepLinkingMixin to focus this page's deep links. - */ - supportedSettingIds: { - type: Object, - value: () => new Set<Setting>([ - Setting.kChangeChromeChannel, - Setting.kChangeDeviceName, - Setting.kCopyDetailedBuildInfo, - ]), - }, - shouldHideEolInfo_: { type: Boolean, computed: 'computeShouldHideEolInfo_(eolMessageWithMonthAndYear)', @@ -158,6 +146,13 @@ }; } + // DeepLinkingMixin override + override supportedSettingIds = new Set<Setting>([ + Setting.kChangeChromeChannel, + Setting.kChangeDeviceName, + Setting.kCopyDetailedBuildInfo, + ]); + private versionInfo_: VersionInfo; private channelInfo_: ChannelInfo; private deviceNameMetadata_: DeviceNameMetadata;
diff --git a/chrome/browser/resources/ash/settings/os_about_page/os_about_page.ts b/chrome/browser/resources/ash/settings/os_about_page/os_about_page.ts index 8a53799..9aefa3583 100644 --- a/chrome/browser/resources/ash/settings/os_about_page/os_about_page.ts +++ b/chrome/browser/resources/ash/settings/os_about_page/os_about_page.ts
@@ -224,22 +224,6 @@ }, /** - * Used by DeepLinkingMixin to focus this page's deep links. - */ - supportedSettingIds: { - type: Object, - value: () => new Set<Setting>([ - Setting.kCheckForOsUpdate, - Setting.kSeeWhatsNew, - Setting.kGetHelpWithChromeOs, - Setting.kReportAnIssue, - Setting.kTermsOfService, - Setting.kDiagnostics, - Setting.kFirmwareUpdates, - ]), - }, - - /** * Controls whether the extended updates opt-in option is shown. */ showExtendedUpdatesOption_: { @@ -292,6 +276,17 @@ ]; } + // DeepLinkingMixin override + override supportedSettingIds = new Set<Setting>([ + Setting.kCheckForOsUpdate, + Setting.kSeeWhatsNew, + Setting.kGetHelpWithChromeOs, + Setting.kReportAnIssue, + Setting.kTermsOfService, + Setting.kDiagnostics, + Setting.kFirmwareUpdates, + ]); + private isDarkModeActive_: boolean; private currentUpdateStatusEvent_: UpdateStatusChangedEvent; private isManaged_: boolean;
diff --git a/chrome/browser/resources/ash/settings/os_apps_page/android_apps_subpage.ts b/chrome/browser/resources/ash/settings/os_apps_page/android_apps_subpage.ts index f32f0178..0c71bd41 100644 --- a/chrome/browser/resources/ash/settings/os_apps_page/android_apps_subpage.ts +++ b/chrome/browser/resources/ash/settings/os_apps_page/android_apps_subpage.ts
@@ -73,22 +73,18 @@ /** Whether Arc VM manage usb subpage should be shown. */ isArcVmManageUsbAvailable: Boolean, - - /** - * Used by DeepLinkingMixin to focus this page's deep links. - */ - supportedSettingIds: { - type: Object, - value: () => new Set<Setting>([ - Setting.kManageAndroidPreferences, - Setting.kRemovePlayStore, - ]), - }, }; } androidAppsInfo: AndroidAppsInfo; isArcVmManageUsbAvailable: boolean; + + // DeepLinkingMixin override + override supportedSettingIds = new Set<Setting>([ + Setting.kManageAndroidPreferences, + Setting.kRemovePlayStore, + ]); + private dialogBody_: string; private playStoreEnabled_: boolean;
diff --git a/chrome/browser/resources/ash/settings/os_apps_page/app_notifications_page/app_notifications_subpage.ts b/chrome/browser/resources/ash/settings/os_apps_page/app_notifications_page/app_notifications_subpage.ts index 167cc3ba..9694a10 100644 --- a/chrome/browser/resources/ash/settings/os_apps_page/app_notifications_page/app_notifications_subpage.ts +++ b/chrome/browser/resources/ash/settings/os_apps_page/app_notifications_page/app_notifications_subpage.ts
@@ -73,21 +73,17 @@ type: Array, value: [], }, - - /** - * Used by DeepLinkingMixin to focus this page's deep links. - */ - supportedSettingIds: { - type: Object, - value: () => new Set<Setting>([ - Setting.kDoNotDisturbOnOff, - Setting.kAppBadgingOnOff, - ]), - }, }; } prefs: PrefsState; + + // DeepLinkingMixin override + override supportedSettingIds = new Set<Setting>([ + Setting.kDoNotDisturbOnOff, + Setting.kAppBadgingOnOff, + ]); + private appList_: App[]; private appNotificationsObserverReceiver_: AppNotificationsObserverReceiver| null;
diff --git a/chrome/browser/resources/ash/settings/os_apps_page/os_apps_page.ts b/chrome/browser/resources/ash/settings/os_apps_page/os_apps_page.ts index a668e64..5a1329d 100644 --- a/chrome/browser/resources/ash/settings/os_apps_page/os_apps_page.ts +++ b/chrome/browser/resources/ash/settings/os_apps_page/os_apps_page.ts
@@ -205,23 +205,19 @@ type: Boolean, value: false, }, - - /** - * Used by DeepLinkingMixin to focus this page's deep links. - */ - supportedSettingIds: { - type: Object, - value: () => new Set<Setting>([ - Setting.kManageAndroidPreferences, - Setting.kTurnOnPlayStore, - Setting.kAppParentalControls, - ]), - }, }; } androidAppsInfo: AndroidAppsInfo; searchTerm: string; + + // DeepLinkingMixin override + override supportedSettingIds = new Set<Setting>([ + Setting.kManageAndroidPreferences, + Setting.kTurnOnPlayStore, + Setting.kAppParentalControls, + ]); + private app_: App; private appNotificationsObserverReceiver_: AppNotificationsObserverReceiver; private appsWithNotifications_: AppWithNotifications[];
diff --git a/chrome/browser/resources/ash/settings/os_bluetooth_page/os_bluetooth_devices_subpage.ts b/chrome/browser/resources/ash/settings/os_bluetooth_page/os_bluetooth_devices_subpage.ts index 248665f0..f985185 100644 --- a/chrome/browser/resources/ash/settings/os_bluetooth_page/os_bluetooth_devices_subpage.ts +++ b/chrome/browser/resources/ash/settings/os_bluetooth_page/os_bluetooth_devices_subpage.ts
@@ -54,15 +54,6 @@ }, /** - * Used by DeepLinkingMixin to focus this page's deep links. - */ - supportedSettingIds: { - type: Object, - value: () => - new Set<Setting>([Setting.kBluetoothOnOff, Setting.kFastPairOnOff]), - }, - - /** * Reflects the current state of the toggle button. This will be set when * the |systemProperties| state changes or when the user presses the * toggle. @@ -100,6 +91,12 @@ }; } + // DeepLinkingMixin override + override supportedSettingIds = new Set<Setting>([ + Setting.kBluetoothOnOff, + Setting.kFastPairOnOff, + ]); + systemProperties: BluetoothSystemProperties; private browserProxy_: OsBluetoothDevicesSubpageBrowserProxy; private connectedDevices_: PairedBluetoothDeviceProperties[];
diff --git a/chrome/browser/resources/ash/settings/os_files_page/files_settings_card.ts b/chrome/browser/resources/ash/settings/os_files_page/files_settings_card.ts index 47037f4b..2e5cd23 100644 --- a/chrome/browser/resources/ash/settings/os_files_page/files_settings_card.ts +++ b/chrome/browser/resources/ash/settings/os_files_page/files_settings_card.ts
@@ -26,7 +26,6 @@ import {assertExhaustive} from '../assert_extras.js'; import {DeepLinkingMixin} from '../common/deep_linking_mixin.js'; import {RouteOriginMixin} from '../common/route_origin_mixin.js'; -import type {Setting} from '../mojom-webui/setting.mojom-webui.js'; import type {Route} from '../router.js'; import {Router, routes} from '../router.js'; @@ -48,14 +47,6 @@ static get properties() { return { - /** - * Used by DeepLinkingMixin to focus this page's deep links. - */ - supportedSettingIds: { - type: Object, - value: () => new Set<Setting>([]), - }, - bulkPinningPrefEnabled_: Boolean, mirrorSyncPrefEnabled_: Boolean,
diff --git a/chrome/browser/resources/ash/settings/os_files_page/google_drive_subpage.ts b/chrome/browser/resources/ash/settings/os_files_page/google_drive_subpage.ts index f0144128..b86d733 100644 --- a/chrome/browser/resources/ash/settings/os_files_page/google_drive_subpage.ts +++ b/chrome/browser/resources/ash/settings/os_files_page/google_drive_subpage.ts
@@ -82,15 +82,6 @@ static get properties() { return { /** - * Used by DeepLinkingMixin to focus this page's deep links. - */ - supportedSettingIds: { - type: Object, - value: () => new Set<Setting>( - [Setting.kGoogleDriveRemoveAccess, Setting.kGoogleDriveFileSync]), - }, - - /** * Ensures the data binding is updated on the UI when * `contentCacheSize_` is updated. */ @@ -137,6 +128,12 @@ ]; } + // DeepLinkingMixin override + override supportedSettingIds = new Set<Setting>([ + Setting.kGoogleDriveRemoveAccess, + Setting.kGoogleDriveFileSync, + ]); + /** * Reflects the state of `prefs.gdata.disabled` pref. */
diff --git a/chrome/browser/resources/ash/settings/os_people_page/additional_accounts_settings_card.ts b/chrome/browser/resources/ash/settings/os_people_page/additional_accounts_settings_card.ts index 7087ff9..55c9a477 100644 --- a/chrome/browser/resources/ash/settings/os_people_page/additional_accounts_settings_card.ts +++ b/chrome/browser/resources/ash/settings/os_people_page/additional_accounts_settings_card.ts
@@ -93,21 +93,17 @@ }, readOnly: true, }, - - /** - * Used by DeepLinkingMixin to focus this page's deep links. - */ - supportedSettingIds: { - type: Object, - value: () => new Set<Setting>([ - Setting.kAddAccount, - Setting.kRemoveAccount, - ]), - }, }; } accounts: Account[]; + + // DeepLinkingMixin override + override supportedSettingIds = new Set<Setting>([ + Setting.kAddAccount, + Setting.kRemoveAccount, + ]); + private actionMenuAccount_: Account|null; private browserProxy_: AccountManagerBrowserProxy; private isChildUser_: boolean;
diff --git a/chrome/browser/resources/ash/settings/os_people_page/fingerprint_list_subpage.ts b/chrome/browser/resources/ash/settings/os_people_page/fingerprint_list_subpage.ts index 80fd9b89..a579c96 100644 --- a/chrome/browser/resources/ash/settings/os_people_page/fingerprint_list_subpage.ts +++ b/chrome/browser/resources/ash/settings/os_people_page/fingerprint_list_subpage.ts
@@ -70,21 +70,17 @@ type: Boolean, value: true, }, - - /** - * Used by DeepLinkingMixin to focus this page's deep links. - */ - supportedSettingIds: { - type: Object, - value: () => new Set<Setting>([ - Setting.kAddFingerprintV2, - Setting.kRemoveFingerprintV2, - ]), - }, }; } authToken: string|undefined; + + // DeepLinkingMixin override + override supportedSettingIds = new Set<Setting>([ + Setting.kAddFingerprintV2, + Setting.kRemoveFingerprintV2, + ]); + private fingerprints_: string[]; private showSetupFingerprintDialog_: boolean; private allowAddAnotherFinger_: boolean;
diff --git a/chrome/browser/resources/ash/settings/os_people_page/graduation/graduation_settings_card.ts b/chrome/browser/resources/ash/settings/os_people_page/graduation/graduation_settings_card.ts index eb1b3c91..63861474 100644 --- a/chrome/browser/resources/ash/settings/os_people_page/graduation/graduation_settings_card.ts +++ b/chrome/browser/resources/ash/settings/os_people_page/graduation/graduation_settings_card.ts
@@ -36,19 +36,10 @@ return getTemplate(); } - static get properties() { - return { - /** - * Used by DeepLinkingMixin to focus this page's deep links. - */ - supportedSettingIds: { - type: Object, - value: () => new Set<Setting>([ - Setting.kGraduation, - ]), - }, - }; - } + // DeepLinkingMixin override + override supportedSettingIds = new Set<Setting>([ + Setting.kGraduation, + ]); override currentRouteChanged(newRoute: Route): void { if (newRoute !== routes.OS_PEOPLE) {
diff --git a/chrome/browser/resources/ash/settings/os_people_page/lock_screen_subpage.ts b/chrome/browser/resources/ash/settings/os_people_page/lock_screen_subpage.ts index 8508640..a5fcfef4 100644 --- a/chrome/browser/resources/ash/settings/os_people_page/lock_screen_subpage.ts +++ b/chrome/browser/resources/ash/settings/os_people_page/lock_screen_subpage.ts
@@ -156,19 +156,6 @@ showDisableRecoveryDialog_: Boolean, /** - * Used by DeepLinkingMixin to focus this page's deep links. - */ - supportedSettingIds: { - type: Object, - value: () => new Set<Setting>([ - Setting.kLockScreenV2, - Setting.kChangeAuthPinV2, - Setting.kLockScreenNotification, - Setting.kDataRecovery, - ]), - }, - - /** * Whether the device account is managed. */ deviceAccountManaged_: { @@ -183,6 +170,15 @@ prefs: PrefsState; authToken: string|undefined; + + // DeepLinkingMixin override + override supportedSettingIds = new Set<Setting>([ + Setting.kLockScreenV2, + Setting.kChangeAuthPinV2, + Setting.kLockScreenNotification, + Setting.kDataRecovery, + ]); + private fingerprintUnlockEnabled_: boolean; private numFingerprints_: number; private numFingerprintDescription_: string;
diff --git a/chrome/browser/resources/ash/settings/os_people_page/os_sync_controls_subpage.ts b/chrome/browser/resources/ash/settings/os_people_page/os_sync_controls_subpage.ts index 35f87d5..28e2909 100644 --- a/chrome/browser/resources/ash/settings/os_people_page/os_sync_controls_subpage.ts +++ b/chrome/browser/resources/ash/settings/os_people_page/os_sync_controls_subpage.ts
@@ -85,17 +85,14 @@ value: true, computed: `computeDataTypeTogglesDisabled_(osSyncPrefs.syncAllOsTypes)`, }, - - /** - * Used by DeepLinkingMixin to focus this page's deep links. - */ - supportedSettingIds: { - type: Object, - value: () => new Set<Setting>([Setting.kSplitSyncOnOff]), - }, }; } + // DeepLinkingMixin override + override supportedSettingIds = new Set<Setting>([ + Setting.kSplitSyncOnOff, + ]); + private areDataTypeTogglesDisabled_: boolean; private supportedSettingsIds: Set<Setting>; private browserProxy_: OsSyncBrowserProxy;
diff --git a/chrome/browser/resources/ash/settings/os_printing_page/cups_printers.ts b/chrome/browser/resources/ash/settings/os_printing_page/cups_printers.ts index c144772..9a26ef33 100644 --- a/chrome/browser/resources/ash/settings/os_printing_page/cups_printers.ts +++ b/chrome/browser/resources/ash/settings/os_printing_page/cups_printers.ts
@@ -203,18 +203,6 @@ }, /** - * Used by DeepLinkingMixin to focus this page's deep links. - */ - supportedSettingIds: { - type: Object, - value: () => new Set<Setting>([ - Setting.kAddPrinter, - Setting.kSavedPrinters, - Setting.kPrintJobs, - ]), - }, - - /** * Indicates whether the nearby printers section is expanded. * @private {boolean} */ @@ -240,6 +228,13 @@ printers: CupsPrinterInfo[]; searchTerm: string; + // DeepLinkingMixin override + override supportedSettingIds = new Set<Setting>([ + Setting.kAddPrinter, + Setting.kSavedPrinters, + Setting.kPrintJobs, + ]); + private addPrintServerResultText_: string; private addPrinterResultText_: string; private attemptedLoadingPrinters_: boolean;
diff --git a/chrome/browser/resources/ash/settings/os_printing_page/printing_settings_card.ts b/chrome/browser/resources/ash/settings/os_printing_page/printing_settings_card.ts index c3dffbc1..5bc681f 100644 --- a/chrome/browser/resources/ash/settings/os_printing_page/printing_settings_card.ts +++ b/chrome/browser/resources/ash/settings/os_printing_page/printing_settings_card.ts
@@ -38,18 +38,11 @@ return getTemplate(); } - static get properties() { - return { - /** - * Used by DeepLinkingMixin to focus this page's deep links. - */ - supportedSettingIds: { - type: Object, - value: () => - new Set<Setting>([Setting.kPrintJobs, Setting.kScanningApp]), - }, - }; - } + // DeepLinkingMixin override + override supportedSettingIds = new Set<Setting>([ + Setting.kPrintJobs, + Setting.kScanningApp, + ]); private browserProxy_: CupsPrintersBrowserProxy;
diff --git a/chrome/browser/resources/ash/settings/os_privacy_page/manage_users_subpage.ts b/chrome/browser/resources/ash/settings/os_privacy_page/manage_users_subpage.ts index 022d0a5..2d7760c 100644 --- a/chrome/browser/resources/ash/settings/os_privacy_page/manage_users_subpage.ts +++ b/chrome/browser/resources/ash/settings/os_privacy_page/manage_users_subpage.ts
@@ -73,24 +73,20 @@ return isChild(); }, }, - - /** - * Used by DeepLinkingMixin to focus this page's deep links. - */ - supportedSettingIds: { - type: Object, - value: () => new Set<Setting>([ - Setting.kGuestBrowsingV2, - Setting.kShowUsernamesAndPhotosAtSignInV2, - Setting.kRestrictSignInV2, - Setting.kAddToUserAllowlistV2, - Setting.kRemoveFromUserAllowlistV2, - ]), - }, }; } prefs: PrefsState; + + // DeepLinkingMixin override + override supportedSettingIds = new Set<Setting>([ + Setting.kGuestBrowsingV2, + Setting.kShowUsernamesAndPhotosAtSignInV2, + Setting.kRestrictSignInV2, + Setting.kAddToUserAllowlistV2, + Setting.kRemoveFromUserAllowlistV2, + ]); + private isOwner_: boolean; private isUserListManaged_: boolean; private isChild_: boolean;
diff --git a/chrome/browser/resources/ash/settings/os_privacy_page/os_privacy_page.ts b/chrome/browser/resources/ash/settings/os_privacy_page/os_privacy_page.ts index 7189d068..dce5899 100644 --- a/chrome/browser/resources/ash/settings/os_privacy_page/os_privacy_page.ts +++ b/chrome/browser/resources/ash/settings/os_privacy_page/os_privacy_page.ts
@@ -104,19 +104,6 @@ syncStatus: Object, /** - * Used by DeepLinkingMixin to focus this page's deep links. - */ - supportedSettingIds: { - type: Object, - value: () => new Set<Setting>([ - Setting.kVerifiedAccess, - Setting.kNonSplitSyncEncryptionOptions, - Setting.kImproveSearchSuggestions, - Setting.kMakeSearchesAndBrowsingBetter, - ]), - }, - - /** * True if fingerprint settings should be displayed on this machine. */ fingerprintUnlockEnabled_: { @@ -232,6 +219,15 @@ } syncStatus: SyncStatus; + + // DeepLinkingMixin override + override supportedSettingIds = new Set<Setting>([ + Setting.kVerifiedAccess, + Setting.kNonSplitSyncEncryptionOptions, + Setting.kImproveSearchSuggestions, + Setting.kMakeSearchesAndBrowsingBetter, + ]); + private authTokenInfo_: chrome.quickUnlockPrivate.TokenInfo|undefined; private browserProxy_: PeripheralDataAccessBrowserProxy; private authTokenReply_: RequestTokenReply|undefined|null;
diff --git a/chrome/browser/resources/ash/settings/os_privacy_page/privacy_hub_subpage.ts b/chrome/browser/resources/ash/settings/os_privacy_page/privacy_hub_subpage.ts index bf52acf..4822c80 100644 --- a/chrome/browser/resources/ash/settings/os_privacy_page/privacy_hub_subpage.ts +++ b/chrome/browser/resources/ash/settings/os_privacy_page/privacy_hub_subpage.ts
@@ -143,23 +143,18 @@ computed: 'computeMicrophoneToggleTooltipText_(isMicListEmpty_, ' + 'microphoneHardwareToggleActive_)', }, - - /** - * Used by DeepLinkingMixin to focus this page's deep links. - */ - supportedSettingIds: { - type: Object, - value: () => new Set<Setting>([ - Setting.kCameraOnOff, - Setting.kMicrophoneOnOff, - Setting.kSpeakOnMuteDetectionOnOff, - Setting.kGeolocationOnOff, - Setting.kUsageStatsAndCrashReports, - ]), - }, }; } + // DeepLinkingMixin override + override supportedSettingIds = new Set<Setting>([ + Setting.kCameraOnOff, + Setting.kMicrophoneOnOff, + Setting.kSpeakOnMuteDetectionOnOff, + Setting.kGeolocationOnOff, + Setting.kUsageStatsAndCrashReports, + ]); + private browserProxy_: PrivacyHubBrowserProxy; private showPrivacyHubLocationControl_: boolean; private locationSublabel_: string;
diff --git a/chrome/browser/resources/ash/settings/os_privacy_page/smart_privacy_subpage.ts b/chrome/browser/resources/ash/settings/os_privacy_page/smart_privacy_subpage.ts index 00059e6..ebe236f 100644 --- a/chrome/browser/resources/ash/settings/os_privacy_page/smart_privacy_subpage.ts +++ b/chrome/browser/resources/ash/settings/os_privacy_page/smart_privacy_subpage.ts
@@ -93,20 +93,15 @@ return loadTimeData.getBoolean('isSnoopingProtectionEnabled'); }, }, - - /** - * Used by DeepLinkingMixin to focus this page's deep links. - */ - supportedSettingIds: { - type: Object, - value: () => new Set<Setting>([ - Setting.kQuickDim, - Setting.kSnoopingProtection, - ]), - }, }; } + // DeepLinkingMixin override + override supportedSettingIds = new Set<Setting>([ + Setting.kQuickDim, + Setting.kSnoopingProtection, + ]); + private isQuickDimEnabled_: boolean; private isSnoopingProtectionEnabled_: boolean; private smartPrivacyQuickLockRangeMs_: SliderTick[];
diff --git a/chrome/browser/resources/ash/settings/os_reset_page/reset_settings_card.ts b/chrome/browser/resources/ash/settings/os_reset_page/reset_settings_card.ts index 49ca90e0..460a34bb 100644 --- a/chrome/browser/resources/ash/settings/os_reset_page/reset_settings_card.ts +++ b/chrome/browser/resources/ash/settings/os_reset_page/reset_settings_card.ts
@@ -67,20 +67,15 @@ }, readOnly: true, }, - - /** - * Used by DeepLinkingMixin to focus this page's deep links. - */ - supportedSettingIds: { - type: Object, - value: () => new Set<Setting>([ - Setting.kPowerwash, - Setting.kSanitizeCrosSettings, - ]), - }, }; } + // DeepLinkingMixin override + override supportedSettingIds = new Set<Setting>([ + Setting.kPowerwash, + Setting.kSanitizeCrosSettings, + ]); + private osResetBrowserProxy_: OsResetBrowserProxy; private installedESimProfiles_: ESimProfileRemote[]; private readonly isSanitizeAllowed_: boolean;
diff --git a/chrome/browser/resources/ash/settings/os_search_page/google_assistant_subpage.ts b/chrome/browser/resources/ash/settings/os_search_page/google_assistant_subpage.ts index 7f8f7671..a1980d7 100644 --- a/chrome/browser/resources/ash/settings/os_search_page/google_assistant_subpage.ts +++ b/chrome/browser/resources/ash/settings/os_search_page/google_assistant_subpage.ts
@@ -125,21 +125,6 @@ }, dspHotwordState_: Number, - - /** - * Used by DeepLinkingMixin to focus this page's deep links. - */ - supportedSettingIds: { - type: Object, - value: () => new Set<Setting>([ - Setting.kAssistantOnOff, - Setting.kAssistantRelatedInfo, - Setting.kAssistantOkGoogle, - Setting.kAssistantNotifications, - Setting.kAssistantVoiceInput, - Setting.kTrainAssistantVoiceModel, - ]), - }, }; } @@ -154,6 +139,16 @@ ]; } + // DeepLinkingMixin override + override supportedSettingIds = new Set<Setting>([ + Setting.kAssistantOnOff, + Setting.kAssistantRelatedInfo, + Setting.kAssistantOkGoogle, + Setting.kAssistantNotifications, + Setting.kAssistantVoiceInput, + Setting.kTrainAssistantVoiceModel, + ]); + private browserProxy_: GoogleAssistantBrowserProxy; private dspHotwordState_: DspHotwordState; private hotwordDspAvailable_: boolean;
diff --git a/chrome/browser/resources/ash/settings/os_search_page/search_and_assistant_settings_card.ts b/chrome/browser/resources/ash/settings/os_search_page/search_and_assistant_settings_card.ts index 3ea739b..a47f3bd 100644 --- a/chrome/browser/resources/ash/settings/os_search_page/search_and_assistant_settings_card.ts +++ b/chrome/browser/resources/ash/settings/os_search_page/search_and_assistant_settings_card.ts
@@ -127,26 +127,22 @@ return isAssistantAllowed(); }, }, - - /** - * Used by DeepLinkingMixin to focus this page's deep links. - */ - supportedSettingIds: { - type: Object, - value: () => new Set<Setting>([ - Setting.kPreferredSearchEngine, - Setting.kMagicBoostOnOff, - Setting.kMahiOnOff, - Setting.kShowOrca, - Setting.kLobsterOnOff, - Setting.kSunfishOnOff, - Setting.kScannerOnOff, - ]), - }, }; } prefs: PrefsState; + + // DeepLinkingMixin override + override supportedSettingIds = new Set<Setting>([ + Setting.kPreferredSearchEngine, + Setting.kMagicBoostOnOff, + Setting.kMahiOnOff, + Setting.kShowOrca, + Setting.kLobsterOnOff, + Setting.kSunfishOnOff, + Setting.kScannerOnOff, + ]); + private isAssistantAllowed_: boolean; private isQuickAnswersSupported_: boolean; private isMagicBoostFeatureEnabled_: boolean;
diff --git a/chrome/browser/resources/ash/settings/os_search_page/search_subpage.ts b/chrome/browser/resources/ash/settings/os_search_page/search_subpage.ts index d22daad..7937586 100644 --- a/chrome/browser/resources/ash/settings/os_search_page/search_subpage.ts +++ b/chrome/browser/resources/ash/settings/os_search_page/search_subpage.ts
@@ -48,20 +48,6 @@ static get properties() { return { - /** - * Used by DeepLinkingMixin to focus this page's deep links. - */ - supportedSettingIds: { - type: Object, - value: () => new Set<Setting>([ - Setting.kPreferredSearchEngine, - Setting.kQuickAnswersOnOff, - Setting.kQuickAnswersDefinition, - Setting.kQuickAnswersTranslation, - Setting.kQuickAnswersUnitConversion, - ]), - }, - quickAnswersTranslationDisabled_: { type: Boolean, value() { @@ -86,6 +72,15 @@ }; } + // DeepLinkingMixin override + override supportedSettingIds = new Set<Setting>([ + Setting.kPreferredSearchEngine, + Setting.kQuickAnswersOnOff, + Setting.kQuickAnswersDefinition, + Setting.kQuickAnswersTranslation, + Setting.kQuickAnswersUnitConversion, + ]); + private quickAnswersSubLabel_: string; private quickAnswersSubToggleEnabled_: boolean; private quickAnswersTranslationDisabled_: boolean;
diff --git a/chrome/browser/resources/ash/settings/parental_controls_page/parental_controls_settings_card.ts b/chrome/browser/resources/ash/settings/parental_controls_page/parental_controls_settings_card.ts index 9b3cf1c..149da67 100644 --- a/chrome/browser/resources/ash/settings/parental_controls_page/parental_controls_settings_card.ts +++ b/chrome/browser/resources/ash/settings/parental_controls_page/parental_controls_settings_card.ts
@@ -43,14 +43,6 @@ static get properties() { return { - /** - * Used by DeepLinkingMixin to focus this page's deep links. - */ - supportedSettingIds: { - type: Object, - value: () => new Set<Setting>([Setting.kSetUpParentalControls]), - }, - isChild_: { type: Boolean, value() { @@ -68,6 +60,11 @@ }; } + // DeepLinkingMixin override + override supportedSettingIds = new Set<Setting>([ + Setting.kSetUpParentalControls, + ]); + private online_: boolean; private browserProxy_: ParentalControlsBrowserProxy;
diff --git a/chrome/browser/resources/ash/settings/personalization_page/personalization_page.ts b/chrome/browser/resources/ash/settings/personalization_page/personalization_page.ts index 46407d1..2296d6e 100644 --- a/chrome/browser/resources/ash/settings/personalization_page/personalization_page.ts +++ b/chrome/browser/resources/ash/settings/personalization_page/personalization_page.ts
@@ -21,7 +21,6 @@ import {DeepLinkingMixin} from '../common/deep_linking_mixin.js'; import {RouteObserverMixin} from '../common/route_observer_mixin.js'; import {Section} from '../mojom-webui/routes.mojom-webui.js'; -import {Setting} from '../mojom-webui/setting.mojom-webui.js'; import type {Route} from '../router.js'; import {routes} from '../router.js'; @@ -49,16 +48,6 @@ value: Section.kPersonalization, readOnly: true, }, - - /** - * Used by DeepLinkingMixin to focus this page's deep links. - */ - supportedSettingIds: { - type: Object, - value: () => new Set<Setting>([ - Setting.kSnapWindowSuggestions, - ]), - }, }; }
diff --git a/chrome/browser/resources/ash/settings/system_preferences_page/multitasking_settings_card.ts b/chrome/browser/resources/ash/settings/system_preferences_page/multitasking_settings_card.ts index 88fbe3ec..23b61f0 100644 --- a/chrome/browser/resources/ash/settings/system_preferences_page/multitasking_settings_card.ts +++ b/chrome/browser/resources/ash/settings/system_preferences_page/multitasking_settings_card.ts
@@ -44,19 +44,16 @@ type: Object, notify: true, }, - - /** - * Used by DeepLinkingMixin to focus this element's deep links. - */ - supportedSettingIds: { - type: Object, - value: () => new Set<Setting>([Setting.kSnapWindowSuggestions]), - }, }; } prefs: PrefsState; + // DeepLinkingMixin override + override supportedSettingIds = new Set<Setting>([ + Setting.kSnapWindowSuggestions, + ]); + override currentRouteChanged(newRoute: Route): void { if (newRoute !== routes.SYSTEM_PREFERENCES) { return;
diff --git a/chrome/browser/resources/ash/settings/system_preferences_page/startup_settings_card.ts b/chrome/browser/resources/ash/settings/system_preferences_page/startup_settings_card.ts index ec8667d..eea62141 100644 --- a/chrome/browser/resources/ash/settings/system_preferences_page/startup_settings_card.ts +++ b/chrome/browser/resources/ash/settings/system_preferences_page/startup_settings_card.ts
@@ -44,14 +44,6 @@ }, /** - * Used by DeepLinkingMixin to focus this element's deep links. - */ - supportedSettingIds: { - type: Object, - value: () => new Set<Setting>([Setting.kRestoreAppsAndPages]), - }, - - /** * List of options for the on startup dropdown menu. */ onStartupDropdownOptions_: { @@ -69,6 +61,12 @@ } prefs: PrefsState; + + // DeepLinkingMixin override + override supportedSettingIds = new Set<Setting>([ + Setting.kRestoreAppsAndPages, + ]); + private readonly onStartupDropdownOptions_: Array<{value: number, name: string}>;
diff --git a/chrome/browser/resources/chromeos/accessibility/BUILD.gn b/chrome/browser/resources/chromeos/accessibility/BUILD.gn index 0b18344..113b5504 100644 --- a/chrome/browser/resources/chromeos/accessibility/BUILD.gn +++ b/chrome/browser/resources/chromeos/accessibility/BUILD.gn
@@ -29,6 +29,7 @@ "common:build", "enhanced_network_tts:build", "select_to_speak/mv2:build", + "select_to_speak/mv3:build", "switch_access/mv2:build", "switch_access/mv3:build", ] @@ -92,6 +93,7 @@ "common:browser_tests", "enhanced_network_tts:browser_tests", "select_to_speak/mv2:browser_tests", + "select_to_speak/mv3:browser_tests", "switch_access/mv2:browser_tests", "switch_access/mv3:browser_tests", ]
diff --git a/chrome/browser/resources/chromeos/accessibility/chromevox/mv2/panel/tutorial_test.js b/chrome/browser/resources/chromeos/accessibility/chromevox/mv2/panel/tutorial_test.js index 94639e3..d02efcd 100644 --- a/chrome/browser/resources/chromeos/accessibility/chromevox/mv2/panel/tutorial_test.js +++ b/chrome/browser/resources/chromeos/accessibility/chromevox/mv2/panel/tutorial_test.js
@@ -205,9 +205,8 @@ // The first three nudges should read the current item with full context. // Afterward, general hints will be given about using ChromeVox. Lastly, // we will give a hint for exiting the tutorial. -// TODO(crbug.com/407459387): Fix and re-enable this test. AX_TEST_F( - 'ChromeVoxTutorialTest', 'DISABLED_GeneralNudgesTest', async function() { + 'ChromeVoxTutorialTest', 'GeneralNudgesTest', async function() { const mockFeedback = this.createMockFeedback(); const root = await this.runWithLoadedTree(this.simpleDoc); await this.launchAndWaitForTutorial(); @@ -399,8 +398,7 @@ }); // Tests that the title of an interactive lesson is read when shown. -// TODO(crbug.com/407459387): Fix and re-enable this test. -AX_TEST_F('ChromeVoxTutorialTest', 'DISABLED_AutoReadTitle', async function() { +AX_TEST_F('ChromeVoxTutorialTest', 'AutoReadTitle', async function() { const mockFeedback = this.createMockFeedback(); const root = await this.runWithLoadedTree(this.simpleDoc); await this.launchAndWaitForTutorial(); @@ -449,8 +447,7 @@ }); // Tests for correct speech and earcons on the earcons lesson. -// TODO(crbug.com/407459387): Fix and re-enable this test. -AX_TEST_F('ChromeVoxTutorialTest', 'DISABLED_EarconLesson', async function() { +AX_TEST_F('ChromeVoxTutorialTest', 'EarconLesson', async function() { const mockFeedback = this.createMockFeedback(); const root = await this.runWithLoadedTree(this.simpleDoc); await this.launchAndWaitForTutorial(); @@ -550,8 +547,7 @@ }); // Tests that tutorial nudges are restarted whenever the current range changes. -// TODO(crbug.com/407459387): Fix and re-enable this test. -AX_TEST_F('ChromeVoxTutorialTest', 'DISABLED_RestartNudges', async function() { +AX_TEST_F('ChromeVoxTutorialTest', 'RestartNudges', async function() { const root = await this.runWithLoadedTree(this.simpleDoc); await this.launchAndWaitForTutorial(); const tutorial = this.getTutorial(); @@ -575,8 +571,7 @@ }); // Tests that the tutorial closes and ChromeVox navigates to a resource link. -// TODO(crbug.com/407459387): Fix and re-enable this test. -AX_TEST_F('ChromeVoxTutorialTest', 'DISABLED_ResourcesTest', async function() { +AX_TEST_F('ChromeVoxTutorialTest', 'ResourcesTest', async function() { const mockFeedback = this.createMockFeedback(); const root = await this.runWithLoadedTree(this.simpleDoc); await this.launchAndWaitForTutorial(); @@ -600,8 +595,7 @@ // Tests that choosing a curriculum with only 1 lesson automatically opens the // lesson. -// TODO(crbug.com/407459387): Fix and re-enable this test. -AX_TEST_F('ChromeVoxTutorialTest', 'DISABLED_OnlyLessonTest', async function() { +AX_TEST_F('ChromeVoxTutorialTest', 'OnlyLessonTest', async function() { const mockFeedback = this.createMockFeedback(); const root = await this.runWithLoadedTree(this.simpleDoc); await this.launchAndWaitForTutorial(); @@ -635,10 +629,8 @@ // Tests that interactive mode and ForcedActionPath are properly set when // showing different screens in the tutorial. -// TODO(crbug.com/407459387): Fix and re-enable this test. AX_TEST_F( - 'ChromeVoxTutorialTest', 'DISABLED_StartStopInteractiveMode', - async function() { + 'ChromeVoxTutorialTest', 'StartStopInteractiveMode', async function() { const root = await this.runWithLoadedTree(this.simpleDoc); await this.launchAndWaitForTutorial(); const tutorial = this.getTutorial(); @@ -749,9 +741,8 @@ await mockFeedback.replay(); }); -// TODO(crbug.com/407459387): Fix and re-enable this test. AX_TEST_F( - 'ChromeVoxTutorialTest', 'DISABLED_GeneralTouchNudges', async function() { + 'ChromeVoxTutorialTest', 'GeneralTouchNudges', async function() { const mockFeedback = this.createMockFeedback(); const root = await this.runWithLoadedTree(this.simpleDoc); await this.launchAndWaitForTutorial();
diff --git a/chrome/browser/resources/chromeos/accessibility/select_to_speak/mv2/BUILD.gn b/chrome/browser/resources/chromeos/accessibility/select_to_speak/mv2/BUILD.gn index fec8bef..f855d19 100644 --- a/chrome/browser/resources/chromeos/accessibility/select_to_speak/mv2/BUILD.gn +++ b/chrome/browser/resources/chromeos/accessibility/select_to_speak/mv2/BUILD.gn
@@ -18,7 +18,7 @@ } select_to_speak_out_dir = - "$root_out_dir/resources/chromeos/accessibility/select_to_speak" + "$root_out_dir/resources/chromeos/accessibility/select_to_speak/mv2" # Directory where typescript build will occur. ts_build_staging_dir = "$target_gen_dir/ts_build_staging"
diff --git a/chrome/browser/resources/chromeos/accessibility/select_to_speak/mv2/background.html b/chrome/browser/resources/chromeos/accessibility/select_to_speak/mv2/background.html index 8409c11..f20e69e 100644 --- a/chrome/browser/resources/chromeos/accessibility/select_to_speak/mv2/background.html +++ b/chrome/browser/resources/chromeos/accessibility/select_to_speak/mv2/background.html
@@ -1,2 +1,3 @@ <!-- Module entrypoint. --> -<script type="module" src="/select_to_speak/select_to_speak_main.js"></script> +<script type="module" src="/select_to_speak/mv2/select_to_speak_main.js"> +</script>
diff --git a/chrome/browser/resources/chromeos/accessibility/select_to_speak/mv3/BUILD.gn b/chrome/browser/resources/chromeos/accessibility/select_to_speak/mv3/BUILD.gn index 474f1e9..5c39eca 100644 --- a/chrome/browser/resources/chromeos/accessibility/select_to_speak/mv3/BUILD.gn +++ b/chrome/browser/resources/chromeos/accessibility/select_to_speak/mv3/BUILD.gn
@@ -18,7 +18,7 @@ } select_to_speak_out_dir = - "$root_out_dir/resources/chromeos/accessibility/select_to_speak" + "$root_out_dir/resources/chromeos/accessibility/select_to_speak/mv3" # Directory where typescript build will occur. ts_build_staging_dir = "$target_gen_dir/ts_build_staging" @@ -91,7 +91,6 @@ "../../common:copied_files", ] sources = [ - "background.html", "checked.png", "earcons/null_selection.ogg", "gdocs_script.js",
diff --git a/chrome/browser/resources/chromeos/accessibility/select_to_speak/mv3/background.html b/chrome/browser/resources/chromeos/accessibility/select_to_speak/mv3/background.html deleted file mode 100644 index 8409c11..0000000 --- a/chrome/browser/resources/chromeos/accessibility/select_to_speak/mv3/background.html +++ /dev/null
@@ -1,2 +0,0 @@ -<!-- Module entrypoint. --> -<script type="module" src="/select_to_speak/select_to_speak_main.js"></script>
diff --git a/chrome/browser/resources/chromeos/accessibility/select_to_speak/mv3/select_to_speak.ts b/chrome/browser/resources/chromeos/accessibility/select_to_speak/mv3/select_to_speak.ts index b7b104a..1574579 100644 --- a/chrome/browser/resources/chromeos/accessibility/select_to_speak/mv3/select_to_speak.ts +++ b/chrome/browser/resources/chromeos/accessibility/select_to_speak/mv3/select_to_speak.ts
@@ -1830,7 +1830,7 @@ */ async maybeCreateOffscreenDocument_() { const offscreenUrl = - chrome.runtime.getURL('select_to_speak/offscreen.html'); + chrome.runtime.getURL('select_to_speak/mv3/offscreen.html'); const existingContexts = await chrome.runtime.getContexts({ contextTypes: [chrome.runtime.ContextType.OFFSCREEN_DOCUMENT],
diff --git a/chrome/browser/resources/chromeos/accessibility/select_to_speak_manifest.json.jinja2 b/chrome/browser/resources/chromeos/accessibility/select_to_speak_manifest.json.jinja2 index 377a6df..addb6b87b2 100644 --- a/chrome/browser/resources/chromeos/accessibility/select_to_speak_manifest.json.jinja2 +++ b/chrome/browser/resources/chromeos/accessibility/select_to_speak_manifest.json.jinja2
@@ -6,13 +6,13 @@ "manifest_version": 3, "minimum_chrome_version": "93", "background": { - "service_worker": "select_to_speak/select_to_speak_main.js", + "service_worker": "select_to_speak/mv3/select_to_speak_main.js", "type": "module" }, {% else %} "manifest_version": 2, "background": { - "page": "select_to_speak/background.html" + "page": "select_to_speak/mv2/background.html" }, {% endif %} "name": "__MSG_SELECT_TO_SPEAK_NAME__", @@ -47,9 +47,9 @@ ], {% endif %} "icons": { - "128": "select_to_speak/sts-icon-128.png", - "16": "select_to_speak/sts-icon-16.png", - "48": "select_to_speak/sts-icon-48.png" + "128": "select_to_speak/mv3/sts-icon-128.png", + "16": "select_to_speak/mv3/sts-icon-16.png", + "48": "select_to_speak/mv3/sts-icon-48.png" }, "automation": { "desktop": true @@ -62,7 +62,7 @@ "all_frames": true, {% if is_manifest_v3 == '1' %} "js": [ - "select_to_speak/gdocs_script.js" + "select_to_speak/mv3/gdocs_script.js" ], "world": "MAIN", {% else %}
diff --git a/chrome/browser/resources/on_device_internals/tools.ts b/chrome/browser/resources/on_device_internals/tools.ts index 53b8bd2..9072fcf5 100644 --- a/chrome/browser/resources/on_device_internals/tools.ts +++ b/chrome/browser/resources/on_device_internals/tools.ts
@@ -416,6 +416,7 @@ maxOutputTokens: 0, topK: null, temperature: null, + responseJsonSchema: '', }, this.responseRouter_.$.bindNewPipeAndPassRemote()); const onResponseId =
diff --git a/chrome/browser/resources/pdf/BUILD.gn b/chrome/browser/resources/pdf/BUILD.gn index 83a832a..d2ec296a 100644 --- a/chrome/browser/resources/pdf/BUILD.gn +++ b/chrome/browser/resources/pdf/BUILD.gn
@@ -118,6 +118,8 @@ "elements/ink_size_selector.ts", "elements/selectable_icon_button.html.ts", "elements/selectable_icon_button.ts", + "elements/text_styles_selector.html.ts", + "elements/text_styles_selector.ts", "elements/viewer_bottom_toolbar.html.ts", "elements/viewer_bottom_toolbar.ts", "elements/viewer_bottom_toolbar_dropdown.html.ts", @@ -162,6 +164,7 @@ "elements/ink_size_selector.css", "elements/selectable_icon_button.css", "elements/side_panel_shared.css", + "elements/text_styles_selector.css", "elements/viewer_bottom_toolbar.css", "elements/viewer_bottom_toolbar_dropdown.css", "elements/viewer_side_panel.css",
diff --git a/chrome/browser/resources/pdf/elements/text_styles_selector.css b/chrome/browser/resources/pdf/elements/text_styles_selector.css new file mode 100644 index 0000000..2ddab22 --- /dev/null +++ b/chrome/browser/resources/pdf/elements/text_styles_selector.css
@@ -0,0 +1,25 @@ +/* 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=./shared_vars.css.js + * #css_wrapper_metadata_end */ + +:host { + display: block; +} + +cr-icon-button { + --cr-icon-button-fill-color: var(--viewer-icon-ink-fill-color); + --cr-icon-button-focus-outline-color: var(--viewer-icon-focus-outline-color); + --cr-icon-button-margin-end: 6px; + --cr-icon-button-margin-start: 6px; +} + +cr-icon-button.active { + --cr-icon-button-fill-color: var(--viewer-icon-ink-selected-fill-color); + background-color: var(--viewer-icon-ink-selected-background-color); + border-radius: 50%; +}
diff --git a/chrome/browser/resources/pdf/elements/text_styles_selector.html.ts b/chrome/browser/resources/pdf/elements/text_styles_selector.html.ts new file mode 100644 index 0000000..fb9d4ba --- /dev/null +++ b/chrome/browser/resources/pdf/elements/text_styles_selector.html.ts
@@ -0,0 +1,26 @@ +// 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_icon_button/cr_icon_button.js'; +import './icons.html.js'; + +import {html} from 'chrome://resources/lit/v3_0/lit.rollup.js'; + +import type {TextStylesSelectorElement} from './text_styles_selector.js'; + +export function getHtml(this: TextStylesSelectorElement) { + // clang-format off + return html`<!--_html_template_start_--> + ${this.getTextStyles_().map(style => html` + <cr-icon-button class="${this.getActiveClass_(style)}" + @click="${this.onStyleButtonClick_}" + data-style="${style}" + iron-icon="pdf:text-format-${style}" + aria-pressed="${this.getAriaPressed_(style)}" + aria-label="${style}" + title="${style}"> + </cr-icon-button>`)} + <!--_html_template_end_-->`; + // clang-format on +}
diff --git a/chrome/browser/resources/pdf/elements/text_styles_selector.ts b/chrome/browser/resources/pdf/elements/text_styles_selector.ts new file mode 100644 index 0000000..bf41599 --- /dev/null +++ b/chrome/browser/resources/pdf/elements/text_styles_selector.ts
@@ -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 {EventTracker} from 'chrome://resources/js/event_tracker.js'; +import {CrLitElement} from 'chrome://resources/lit/v3_0/lit.rollup.js'; + +import type {AnnotationText, TextStyles} from '../constants.js'; +import {TextStyle} from '../constants.js'; +import {Ink2Manager} from '../ink2_manager.js'; + +import {getCss} from './text_styles_selector.css.js'; +import {getHtml} from './text_styles_selector.html.js'; + + +export class TextStylesSelectorElement extends CrLitElement { + static get is() { + return 'text-styles-selector'; + } + + static override get styles() { + return getCss(); + } + + override render() { + return getHtml.bind(this)(); + } + + static override get properties() { + return { + currentStyles_: {type: Object}, + }; + } + + protected currentStyles_: TextStyles = { + [TextStyle.BOLD]: false, + [TextStyle.ITALIC]: false, + [TextStyle.UNDERLINE]: false, + [TextStyle.STRIKETHROUGH]: false, + }; + + private tracker_: EventTracker = new EventTracker(); + + constructor() { + super(); + this.onTextChanged_(Ink2Manager.getInstance().getCurrentText()); + } + + override connectedCallback() { + super.connectedCallback(); + this.tracker_.add( + Ink2Manager.getInstance(), 'text-changed', + (e: Event) => + this.onTextChanged_((e as CustomEvent<AnnotationText>).detail)); + } + + override disconnectedCallback() { + super.disconnectedCallback(); + this.tracker_.removeAll(); + } + + protected getTextStyles_(): TextStyle[] { + return Object.values(TextStyle); + } + + protected onStyleButtonClick_(e: Event) { + const style = (e.target as HTMLElement).dataset['style'] as TextStyle; + const newStyles = Object.assign({}, this.currentStyles_); + newStyles[style] = !newStyles[style]; + Ink2Manager.getInstance().setTextStyles(newStyles); + } + + protected getActiveClass_(style: TextStyle) { + return this.currentStyles_[style] ? 'active' : ''; + } + + protected getAriaPressed_(style: TextStyle) { + return this.currentStyles_[style] ? 'true' : 'false'; + } + + private onTextChanged_(text: AnnotationText) { + this.currentStyles_ = text.styles; + } +} + +declare global { + interface HTMLElementTagNameMap { + 'text-styles-selector': TextStylesSelectorElement; + } +} + +customElements.define(TextStylesSelectorElement.is, TextStylesSelectorElement);
diff --git a/chrome/browser/resources/pdf/elements/viewer_text_side_panel.css b/chrome/browser/resources/pdf/elements/viewer_text_side_panel.css index 4e9dda2..8910959a 100644 --- a/chrome/browser/resources/pdf/elements/viewer_text_side_panel.css +++ b/chrome/browser/resources/pdf/elements/viewer_text_side_panel.css
@@ -12,8 +12,6 @@ * #css_wrapper_metadata_end */ :host { - --cr-icon-button-margin-end: 6px; - --cr-icon-button-margin-start: 6px; --cr-radio-group-item-padding: 6px; } @@ -43,7 +41,7 @@ border-radius: 50%; } -cr-radio-group, .style-buttons { +cr-radio-group, text-styles-selector { padding-top: 16px; }
diff --git a/chrome/browser/resources/pdf/elements/viewer_text_side_panel.html.ts b/chrome/browser/resources/pdf/elements/viewer_text_side_panel.html.ts index a3e1a3d..3ffc243bd 100644 --- a/chrome/browser/resources/pdf/elements/viewer_text_side_panel.html.ts +++ b/chrome/browser/resources/pdf/elements/viewer_text_side_panel.html.ts
@@ -2,11 +2,11 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -import 'chrome://resources/cr_elements/cr_icon_button/cr_icon_button.js'; import 'chrome://resources/cr_elements/cr_radio_group/cr_radio_group.js'; import './icons.html.js'; import './ink_color_selector.js'; import './selectable_icon_button.js'; +import './text_styles_selector.js'; import {html} from 'chrome://resources/lit/v3_0/lit.rollup.js'; @@ -35,17 +35,7 @@ </div> <div class="side-panel-content"> <h2>Styles</h2> - <div class="style-buttons"> - ${this.getTextStyles_().map(style => html` - <cr-icon-button class="${this.getActiveClass_(style)}" - @click="${this.onStyleButtonClick_}" - data-style="${style}" - iron-icon="pdf:text-format-${style}" - aria-pressed="${this.getAriaPressed_(style)}" - aria-label="${style}" - title="${style}"> - </cr-icon-button>`)} - </div> + <text-styles-selector></text-styles-selector> <cr-radio-group selectable-elements="selectable-icon-button" .selected="${this.currentAlignment_}" @selected-changed="${this.onSelectedAlignmentChanged_}">
diff --git a/chrome/browser/resources/pdf/elements/viewer_text_side_panel.ts b/chrome/browser/resources/pdf/elements/viewer_text_side_panel.ts index cd4e3e8..9232feea 100644 --- a/chrome/browser/resources/pdf/elements/viewer_text_side_panel.ts +++ b/chrome/browser/resources/pdf/elements/viewer_text_side_panel.ts
@@ -5,8 +5,8 @@ import {EventTracker} from 'chrome://resources/js/event_tracker.js'; import {CrLitElement} from 'chrome://resources/lit/v3_0/lit.rollup.js'; -import type {AnnotationText, Color, TextStyles} from '../constants.js'; -import {TextAlignment, TextStyle} from '../constants.js'; +import type {AnnotationText, Color} from '../constants.js'; +import {TextAlignment} from '../constants.js'; import {Ink2Manager} from '../ink2_manager.js'; import {hexToColor} from '../pdf_viewer_utils.js'; @@ -67,7 +67,6 @@ currentColor_: {type: Object}, currentFont_: {type: String}, currentSize_: {type: Number}, - currentStyles_: {type: Object}, colors_: {type: Array}, fonts_: {type: Array}, sizes_: {type: Array}, @@ -78,12 +77,6 @@ protected currentColor_: Color = hexToColor(TEXT_COLORS[0]!.color); protected currentFont_: string = ''; protected currentSize_: number = TEXT_SIZES[0]!; - protected currentStyles_: TextStyles = { - [TextStyle.BOLD]: false, - [TextStyle.ITALIC]: false, - [TextStyle.UNDERLINE]: false, - [TextStyle.STRIKETHROUGH]: false, - }; protected colors_ = TEXT_COLORS; protected fonts_ = [ @@ -114,10 +107,6 @@ this.tracker_.removeAll(); } - protected getTextStyles_(): TextStyle[] { - return Object.values(TextStyle); - } - protected isSelectedFont_(font: string) { return font === this.currentFont_; } @@ -136,21 +125,6 @@ Ink2Manager.getInstance().setTextSize(newValue); } - protected onStyleButtonClick_(e: Event) { - const style = (e.target as HTMLElement).dataset['style'] as TextStyle; - const newStyles = Object.assign({}, this.currentStyles_); - newStyles[style] = !newStyles[style]; - Ink2Manager.getInstance().setTextStyles(newStyles); - } - - protected getActiveClass_(style: TextStyle) { - return this.currentStyles_[style] ? 'active' : ''; - } - - protected getAriaPressed_(style: TextStyle) { - return this.currentStyles_[style] ? 'true' : 'false'; - } - protected onSelectedAlignmentChanged_(e: CustomEvent<{value: string}>) { const newAlignment = e.detail.value as TextAlignment; Ink2Manager.getInstance().setTextAlignment(newAlignment); @@ -170,7 +144,6 @@ this.currentColor_ = text.color; this.currentFont_ = text.font; this.currentSize_ = text.size; - this.currentStyles_ = text.styles; this.currentAlignment_ = text.alignment; } }
diff --git a/chrome/browser/resources/pdf/pdf_viewer_wrapper.ts b/chrome/browser/resources/pdf/pdf_viewer_wrapper.ts index 252e006..252b42c0 100644 --- a/chrome/browser/resources/pdf/pdf_viewer_wrapper.ts +++ b/chrome/browser/resources/pdf/pdf_viewer_wrapper.ts
@@ -28,6 +28,7 @@ export {InkColorSelectorElement} from './elements/ink_color_selector.js'; export {InkSizeSelectorElement, HIGHLIGHTER_SIZES, PEN_SIZES} from './elements/ink_size_selector.js'; export {SelectableIconButtonElement} from './elements/selectable_icon_button.js'; +export {TextStylesSelectorElement} from './elements/text_styles_selector.js'; // </if> export {ViewerAttachmentElement} from './elements/viewer_attachment.js'; export {ViewerAttachmentBarElement} from './elements/viewer_attachment_bar.js';
diff --git a/chrome/browser/resources/settings/autofill_page/credit_card_list_entry.html b/chrome/browser/resources/settings/autofill_page/credit_card_list_entry.html index 1e6c9ee..1762a35 100644 --- a/chrome/browser/resources/settings/autofill_page/credit_card_list_entry.html +++ b/chrome/browser/resources/settings/autofill_page/credit_card_list_entry.html
@@ -35,18 +35,46 @@ [[getSummaryAriaLabel_(creditCard)]], [[getSummaryAriaSublabel_(creditCard)]] </div> - <div id="summaryLabel" class="ellipses" aria-hidden="true"> - [[creditCard.metadata.summaryLabel]] - </div> + <template is="dom-if" if="[[!shouldShowNewFopDisplay_()]]"> + <div id="summaryLabel" class="ellipses" aria-hidden="true"> + [[creditCard.metadata.summaryLabel]] + </div> + </template> + <template is="dom-if" if="[[shouldShowNewFopDisplay_()]]"> + <div> + <span id="label" class="ellipses" aria-hidden="true"> + [[creditCard.metadata.summaryLabel]] + </span> + <template is="dom-if" if="[[hasCardIdentifier_(creditCard)]]"> + <div class="sub-label" aria-hidden="true"> + <span id="summaryLabel_1"> + [[creditCard.metadata.summarySublabel]] + </span> + <span id="summaryLabel_2"> + [[getExpirationlabel_(creditCard)]] + </span> + </div> + </template> + <template is="dom-if" if="[[!hasCardIdentifier_(creditCard)]]"> + <span id="expirationLabel" class="sub-label"> + [[getExpirationlabel_(creditCard)]] + </span> + </template> + </div> + </template> <div id="summarySublabel" class="sub-label"> <span aria-hidden="true">[[getSummarySublabel_(creditCard)]]</span> + <template is="dom-if" + if="[[hasSummaryAndBenefitSublabel_(creditCard)]]"> + | + </template> <template is="dom-if" if="[[isCardBenefitsProductUrlAvailable_(creditCard)]]"> - | <a id="summaryTermsLink" - aria-label="[[getBenefitsTermsAriaLabel_(creditCard)]]" - aria-description="$i18n{opensInNewTab}" - on-click="onSummarySublabelTermsLinkClick_" - href="[[getCardBenefitsProductUrl_(creditCard)]]" - target="_blank">$i18n{benefitsTermsTagForCreditCardListEntry}</a> + <a id="summaryTermsLink" + aria-label="[[getBenefitsTermsAriaLabel_(creditCard)]]" + aria-description="$i18n{opensInNewTab}" + on-click="onSummarySublabelTermsLinkClick_" + href="[[getCardBenefitsProductUrl_(creditCard)]]" + target="_blank">$i18n{benefitsTermsTagForCreditCardListEntry}</a> </template> </div> </div>
diff --git a/chrome/browser/resources/settings/autofill_page/credit_card_list_entry.ts b/chrome/browser/resources/settings/autofill_page/credit_card_list_entry.ts index 44be1c9..4636e4c 100644 --- a/chrome/browser/resources/settings/autofill_page/credit_card_list_entry.ts +++ b/chrome/browser/resources/settings/autofill_page/credit_card_list_entry.ts
@@ -49,11 +49,21 @@ return { /** A saved credit card. */ creditCard: Object, + + showNewFopDisplayEnabled_: { + type: Boolean, + value() { + return loadTimeData.getBoolean('enableNewFopDisplay'); + }, + readOnly: true, + }, }; } creditCard: chrome.autofillPrivate.CreditCardEntry; + private showNewFopDisplayEnabled_: boolean; + get dotsMenu(): HTMLElement|null { return this.shadowRoot!.getElementById('creditCardMenu'); } @@ -120,6 +130,20 @@ } /** + * Returns true if the new FOP display should be shown. + */ + private shouldShowNewFopDisplay_(): boolean { + return this.showNewFopDisplayEnabled_; + } + + /** + * The card has a product description or a nickname. + */ + private hasCardIdentifier_(): boolean { + return (this.creditCard.metadata!.summarySublabel || '').length > 0; + } + + /** * The 3-dot menu should be shown if the card is not a masked server card or * if the card is eligible for virtual card enrollment. */ @@ -190,17 +214,23 @@ } /** + * Returns expiration date if applicable. + */ + private getExpirationlabel_(): string { + return this.isVirtualCardEnrolled_() ? '' : + ' · ' + this.getCardExpiryDate_(); + } + + /** * Returns one of the following sublabels, based on the card's status: * Virtual card enrollment tag - * Expiration date tag (MM/YY) * 'CVC saved' tag * Benefit tag (Place the benefit tag last because it includes a link to * product terms.) * e.g., one of the following: - * 11/23 - * 11/23 | CVC saved - * 11/23 | Card benefits available (terms apply) - * 11/23 | CVC saved | Card benefits available (terms apply) + * CVC saved + * Card benefits available (terms apply) + * CVC saved | Card benefits available (terms apply) * Virtual card turned on * Virtual card turned on | CVC saved * Virtual card turned on | Card benefits available (terms apply) @@ -211,13 +241,21 @@ const separator = ' | '; let summarySublabel = this.isVirtualCardEnrolled_() ? this.i18n('virtualCardTurnedOn') : - this.getCardExpiryDate_(); + (this.shouldShowNewFopDisplay_() ? '' : this.getCardExpiryDate_()); if (this.isCardCvcAvailable_()) { - summarySublabel += separator + this.i18n('cvcTagForCreditCardListEntry'); + if (summarySublabel.length > 0) { + summarySublabel += separator; + } + summarySublabel += this.i18n('cvcTagForCreditCardListEntry'); } return summarySublabel; } + private hasSummaryAndBenefitSublabel_(): boolean { + return this.getSummarySublabel_().length > 0 && + this.isCardBenefitsProductUrlAvailable_(); + } + private getSummaryAriaSublabel_(): string { switch (this.getCardSublabelType()) { case CardSummarySublabelType.VIRTUAL_CARD_WITH_CVC_AND_BENEFITS_TAG:
diff --git a/chrome/browser/resources/settings/people_page/sync_page.html b/chrome/browser/resources/settings/people_page/sync_page.html index be8abd1..ede8a7e 100644 --- a/chrome/browser/resources/settings/people_page/sync_page.html +++ b/chrome/browser/resources/settings/people_page/sync_page.html
@@ -225,7 +225,7 @@ <if expr="not chromeos_ash"> <template is="dom-if" if="[[showSetupCancelDialog_]]" restamp> <cr-dialog id="setupCancelDialog" on-close="onSetupCancelDialogClose_" - ignore-popstate> + ignore-popstate show-on-attach> <div slot="title">$i18n{syncSetupCancelDialogTitle}</div> <div slot="body">$i18n{syncSetupCancelDialogBody}</div> <div slot="button-container">
diff --git a/chrome/browser/resources/settings/people_page/sync_page.ts b/chrome/browser/resources/settings/people_page/sync_page.ts index d9fe4d9..d292ce94 100644 --- a/chrome/browser/resources/settings/people_page/sync_page.ts +++ b/chrome/browser/resources/settings/people_page/sync_page.ts
@@ -32,7 +32,7 @@ import {WebUiListenerMixin} from '//resources/cr_elements/web_ui_listener_mixin.js'; import {assert, assertNotReached} from '//resources/js/assert.js'; import {focusWithoutInk} from '//resources/js/focus_without_ink.js'; -import {flush, PolymerElement} from '//resources/polymer/v3_0/polymer/polymer_bundled.min.js'; +import {PolymerElement} from '//resources/polymer/v3_0/polymer/polymer_bundled.min.js'; import type {SyncBrowserProxy, SyncPrefs, SyncStatus} from '/shared/settings/people_page/sync_browser_proxy.js'; import {PageStatus, SignedInState, StatusAction, SyncBrowserProxyImpl} from '/shared/settings/people_page/sync_browser_proxy.js'; import {I18nMixin} from 'chrome://resources/cr_elements/i18n_mixin.js'; @@ -427,10 +427,6 @@ requestAnimationFrame(() => { router.navigateTo(router.getRoutes().SYNC); this.showSetupCancelDialog_ = true; - // Flush to make sure that the setup cancel dialog is attached. - flush(); - this.shadowRoot!.querySelector<CrDialogElement>( - '#setupCancelDialog')!.showModal(); }); return; }
diff --git a/chrome/browser/safe_browsing/download_protection/deep_scanning_browsertest.cc b/chrome/browser/safe_browsing/download_protection/deep_scanning_browsertest.cc index 9b8d9f8..0e43eb0 100644 --- a/chrome/browser/safe_browsing/download_protection/deep_scanning_browsertest.cc +++ b/chrome/browser/safe_browsing/download_protection/deep_scanning_browsertest.cc
@@ -39,6 +39,7 @@ #include "chrome/browser/safe_browsing/download_protection/ppapi_download_request.h" #include "chrome/browser/safe_browsing/test_safe_browsing_service.h" #include "chrome/browser/ui/browser.h" +#include "chrome/browser/ui/views/file_system_access/file_system_access_test_utils.h" #include "chrome/common/chrome_paths.h" #include "chrome/common/pref_names.h" #include "chrome/test/base/in_process_browser_test.h" @@ -63,6 +64,7 @@ #include "content/public/browser/download_manager.h" #include "content/public/browser/save_page_type.h" #include "content/public/test/browser_test.h" +#include "content/public/test/browser_test_utils.h" #include "content/public/test/download_test_observer.h" #include "content/public/test/test_utils.h" #include "mojo/public/cpp/bindings/remote.h" @@ -83,6 +85,8 @@ enterprise_obfuscation::kKeySize + enterprise_obfuscation::kNonceSize + enterprise_obfuscation::kAuthTagSize; +constexpr char kTestContent[] = "test content"; + // Extract the metadata proto from the raw request string based on multipart // upload protocol. Returns true on success. bool GetMultipartUploadMetadata( @@ -1573,4 +1577,306 @@ EXPECT_FALSE(base::PathExists(extra_files_dir)); } +class FileSystemAccessDeepScanningBrowserTest + : public DownloadDeepScanningBrowserTestBase { + public: + FileSystemAccessDeepScanningBrowserTest() + : DownloadDeepScanningBrowserTestBase(/*connectors_machine_scope=*/true, + /*is_consumer=*/false, + /*is_obfuscated=*/false) { + scoped_feature_list_.InitAndEnableFeature( + safe_browsing::kEnterpriseFileSystemAccessDeepScan); + } + + void SetUpOnMainThread() override { + DownloadDeepScanningBrowserTestBase::SetUpOnMainThread(); + + // Setup a temporary directory for the file save path. + ASSERT_TRUE(temp_dir_.CreateUniqueTempDir()); + + // Have file save path automatically selected by picker. + ui::SelectFileDialog::SetFactory( + std::make_unique<SelectPredeterminedFileDialogFactory>( + std::vector<base::FilePath>{GetTestFilePath()})); + + // Navigate to a simple page for FS API calls to run from. + ASSERT_TRUE(ui_test_utils::NavigateToURL( + browser(), embedded_test_server()->GetURL("/title1.html"))); + } + + void TearDownOnMainThread() override { + ui::SelectFileDialog::SetFactory(nullptr); + DownloadDeepScanningBrowserTestBase::TearDownOnMainThread(); + } + + base::FilePath GetTestFilePath() { + return temp_dir_.GetPath().AppendASCII("test_fsa_file.txt"); + } + + void InitiateWriteViaFSA(content::WebContents* web_contents, + const std::string& content) { + // Script that triggers FSA API write. Executes async and reports results + // via messaging. + const std::string js_script = R"( + (async (content) => { + try { + const handle = await window.showSaveFilePicker(); + const writable = await handle.createWritable(); + await writable.write(content); + await writable.close(); + window.domAutomationController.send("Success"); + } catch (e) { + window.domAutomationController.send("Error: " + e.name + + " - " + e.message); + } + })(')" + content + R"('); + )"; + + content::ExecuteScriptAsync(web_contents, js_script); + } + + protected: + base::ScopedTempDir temp_dir_; + + private: + base::test::ScopedFeatureList scoped_feature_list_; +}; + +IN_PROC_BROWSER_TEST_F(FileSystemAccessDeepScanningBrowserTest, BlockedWrite) { + SetUpReporting(); + + // Prepare the scan response with DLP block and successful malware scan. + enterprise_connectors::ContentAnalysisResponse response; + auto* dlp_result = response.add_results(); + dlp_result->set_tag("dlp"); + dlp_result->set_status( + enterprise_connectors::ContentAnalysisResponse::Result::SUCCESS); + auto* dlp_rule = dlp_result->add_triggered_rules(); + dlp_rule->set_action(enterprise_connectors::TriggeredRule::BLOCK); + + auto* malware_result = response.add_results(); + malware_result->set_tag("malware"); + malware_result->set_status( + enterprise_connectors::ContentAnalysisResponse::Result::SUCCESS); + + ExpectContentAnalysisResumableMetadataResponse({"dlp", "malware"}); + ExpectContentAnalysisResumableContentResponse(response); + + // Setup message queue for JS responses. + content::WebContents* web_contents = + browser()->tab_strip_model()->GetActiveWebContents(); + ASSERT_TRUE(web_contents); + content::DOMMessageQueue message_queue(web_contents); + + base::RunLoop validator_run_loop; + enterprise_connectors::test::EventReportValidator validator(client()); + validator.SetDoneClosure(validator_run_loop.QuitClosure()); + + InitiateWriteViaFSA(web_contents, kTestContent); + + WaitForDeepScanRequest(); + + GURL url = embedded_test_server()->GetURL("/title1.html"); + std::set<std::string> mimetypes = {"text/plain"}; + validator.ExpectSensitiveDataEvent( + /*url*/ url.spec(), + /*tab_url*/ url.spec(), + /*source*/ "", + /*destination*/ "", + /*filename*/ GetTestFilePath().AsUTF8Unsafe(), + // echo -n [kTestContent] | sha256sum | tr a-f A-F + /*sha*/ + "6AE8A75555209FD6C44157C0AED8016E763FF435A19CF186F76863140143FF72", + /*trigger*/ + extensions::SafeBrowsingPrivateEventRouter::kTriggerFileDownload, + /*dlp_verdict*/ response.results(0), + /*mimetypes*/ &mimetypes, + /*size*/ sizeof(kTestContent) - 1, + enterprise_connectors::EventResultToString( + enterprise_connectors::EventResult::BLOCKED), + /*username*/ kUserName, + /*profile_identifier*/ GetProfileIdentifier(), + /*scan_id*/ last_request().request_token(), + /*content_transfer_method*/ std::nullopt, + /*user_justification*/ std::nullopt); + + EXPECT_EQ(last_request().reason(), + enterprise_connectors::ContentAnalysisRequest::NORMAL_DOWNLOAD); + + validator_run_loop.Run(); + + std::string js_completion_result; + ASSERT_TRUE(message_queue.WaitForMessage(&js_completion_result)); + // TODO(crbug.com/407065784): Improve error message for SB checks. + EXPECT_EQ(js_completion_result, + "\"Error: AbortError - Blocked by Safe Browsing.\""); + + // File is created but remains empty due to block. + base::ScopedAllowBlockingForTesting allow_blocking; + EXPECT_TRUE(base::PathExists(GetTestFilePath())); + EXPECT_EQ(0, base::GetFileSize(GetTestFilePath())); +} + +IN_PROC_BROWSER_TEST_F(FileSystemAccessDeepScanningBrowserTest, AllowedWrite) { + SetUpReporting(); + + // Prepare the scan response with no DLP or malware rules triggered. + enterprise_connectors::ContentAnalysisResponse response; + auto* dlp_result = response.add_results(); + dlp_result->set_tag("dlp"); + dlp_result->set_status( + enterprise_connectors::ContentAnalysisResponse::Result::SUCCESS); + + auto* malware_result = response.add_results(); + malware_result->set_tag("malware"); + malware_result->set_status( + enterprise_connectors::ContentAnalysisResponse::Result::SUCCESS); + + ExpectContentAnalysisResumableMetadataResponse({"dlp", "malware"}); + ExpectContentAnalysisResumableContentResponse(response); + + // Setup message queue for JS responses. + content::WebContents* web_contents = + browser()->tab_strip_model()->GetActiveWebContents(); + ASSERT_TRUE(web_contents); + content::DOMMessageQueue message_queue(web_contents); + + InitiateWriteViaFSA(web_contents, kTestContent); + + WaitForDeepScanRequest(); + + EXPECT_EQ(last_request().reason(), + enterprise_connectors::ContentAnalysisRequest::NORMAL_DOWNLOAD); + + std::string js_completion_result; + ASSERT_TRUE(message_queue.WaitForMessage(&js_completion_result)); + EXPECT_EQ(js_completion_result, "\"Success\""); + + // Checks that file is written successfully. + base::ScopedAllowBlockingForTesting allow_blocking; + EXPECT_TRUE(base::PathExists(GetTestFilePath())); + + std::string file_content; + EXPECT_TRUE(base::ReadFileToString(GetTestFilePath(), &file_content)); + EXPECT_EQ(file_content, kTestContent); +} + +IN_PROC_BROWSER_TEST_F(FileSystemAccessDeepScanningBrowserTest, WarnedWrite) { + SetUpReporting(); + + // Prepare the scan response with warn DLP rule triggered. + enterprise_connectors::ContentAnalysisResponse response; + auto* dlp_result = response.add_results(); + dlp_result->set_tag("dlp"); + dlp_result->set_status( + enterprise_connectors::ContentAnalysisResponse::Result::SUCCESS); + auto* dlp_rule = dlp_result->add_triggered_rules(); + dlp_rule->set_action(enterprise_connectors::TriggeredRule::WARN); + + auto* malware_result = response.add_results(); + malware_result->set_tag("malware"); + malware_result->set_status( + enterprise_connectors::ContentAnalysisResponse::Result::SUCCESS); + + ExpectContentAnalysisResumableMetadataResponse({"dlp", "malware"}); + ExpectContentAnalysisResumableContentResponse(response); + + // Setup message queue for JS responses. + content::WebContents* web_contents = + browser()->tab_strip_model()->GetActiveWebContents(); + ASSERT_TRUE(web_contents); + content::DOMMessageQueue message_queue(web_contents); + + base::RunLoop validator_run_loop; + enterprise_connectors::test::EventReportValidator validator(client()); + validator.SetDoneClosure(validator_run_loop.QuitClosure()); + + InitiateWriteViaFSA(web_contents, kTestContent); + + WaitForDeepScanRequest(); + + GURL url = embedded_test_server()->GetURL("/title1.html"); + std::set<std::string> mimetypes = {"text/plain"}; + validator.ExpectSensitiveDataEvent( + /*url*/ url.spec(), + /*tab_url*/ url.spec(), + /*source*/ "", + /*destination*/ "", + /*filename*/ GetTestFilePath().AsUTF8Unsafe(), + // echo -n [kTestContent] | sha256sum | tr a-f A-F + /*sha*/ + "6AE8A75555209FD6C44157C0AED8016E763FF435A19CF186F76863140143FF72", + /*trigger*/ + extensions::SafeBrowsingPrivateEventRouter::kTriggerFileDownload, + /*dlp_verdict*/ response.results(0), + /*mimetypes*/ &mimetypes, + /*size*/ sizeof(kTestContent) - 1, + enterprise_connectors::EventResultToString( + enterprise_connectors::EventResult::WARNED), + /*username*/ kUserName, + /*profile_identifier*/ GetProfileIdentifier(), + /*scan_id*/ last_request().request_token(), + /*content_transfer_method*/ std::nullopt, + /*user_justification*/ std::nullopt); + + validator_run_loop.Run(); + + // For warn verdicts, we allow the write to happen as there is currently no + // dialog that allows the user to bypass warnings. + std::string js_completion_result; + ASSERT_TRUE(message_queue.WaitForMessage(&js_completion_result)); + EXPECT_EQ(js_completion_result, "\"Success\""); + + // Checks that file is written successfully. + base::ScopedAllowBlockingForTesting allow_blocking; + EXPECT_TRUE(base::PathExists(GetTestFilePath())); + + std::string file_content; + EXPECT_TRUE(base::ReadFileToString(GetTestFilePath(), &file_content)); + EXPECT_EQ(file_content, kTestContent); +} + +IN_PROC_BROWSER_TEST_F(FileSystemAccessDeepScanningBrowserTest, + DeepScanFailure) { + SetUpReporting(); + + // Configure scan response with failed status. + enterprise_connectors::ContentAnalysisResponse response; + auto* dlp_result = response.add_results(); + dlp_result->set_tag("dlp"); + dlp_result->set_status( + enterprise_connectors::ContentAnalysisResponse::Result::FAILURE); + + auto* malware_result = response.add_results(); + malware_result->set_tag("malware"); + malware_result->set_status( + enterprise_connectors::ContentAnalysisResponse::Result::FAILURE); + + ExpectContentAnalysisResumableMetadataResponse({"dlp", "malware"}); + ExpectContentAnalysisResumableContentResponse(response); + + // Setup message queue for JS responses. + content::WebContents* web_contents = + browser()->tab_strip_model()->GetActiveWebContents(); + ASSERT_TRUE(web_contents); + content::DOMMessageQueue message_queue(web_contents); + + InitiateWriteViaFSA(web_contents, kTestContent); + + WaitForDeepScanRequest(); + + std::string js_completion_result; + ASSERT_TRUE(message_queue.WaitForMessage(&js_completion_result)); + + // When scan fails, write should still succeed (fail-open behavior). + EXPECT_EQ(js_completion_result, "\"Success\""); + + base::ScopedAllowBlockingForTesting allow_blocking; + EXPECT_TRUE(base::PathExists(GetTestFilePath())); + + std::string file_content; + EXPECT_TRUE(base::ReadFileToString(GetTestFilePath(), &file_content)); + EXPECT_EQ(file_content, kTestContent); +} + } // namespace safe_browsing
diff --git a/chrome/browser/safe_browsing/download_protection/file_system_access_metadata.cc b/chrome/browser/safe_browsing/download_protection/file_system_access_metadata.cc index 0ac92df2..d0ca964 100644 --- a/chrome/browser/safe_browsing/download_protection/file_system_access_metadata.cc +++ b/chrome/browser/safe_browsing/download_protection/file_system_access_metadata.cc
@@ -49,7 +49,12 @@ ext = ext.substr(1); } - if (net::GetMimeTypeFromExtension(ext, &mime_type)) { + // Searches within chrome's built-in list of filetype/extension + // associations, as using `GetMimeTypeFromExtension` includes + // platform-defined mime type mappings that can potentially block. + // TODO(crbug.com/407598185): Add platform MIME types lookup for better + // protection. + if (net::GetWellKnownMimeTypeFromExtension(ext, &mime_type)) { mime_type_ = mime_type; } else { // Default to octet-stream for unknown MIME type.
diff --git a/chrome/browser/send_tab_to_self/send_tab_to_self_client_service.cc b/chrome/browser/send_tab_to_self/send_tab_to_self_client_service.cc index 8512732..6e79d3b7 100644 --- a/chrome/browser/send_tab_to_self/send_tab_to_self_client_service.cc +++ b/chrome/browser/send_tab_to_self/send_tab_to_self_client_service.cc
@@ -19,15 +19,13 @@ SendTabToSelfClientService::SendTabToSelfClientService( std::unique_ptr<ReceivingUiHandler> receiving_ui_handler, SendTabToSelfModel* model) - : model_(model), receiving_ui_handler_(std::move(receiving_ui_handler)) { - model_->AddObserver(this); + : receiving_ui_handler_(std::move(receiving_ui_handler)) { + model_observation_.Observe(model); } SendTabToSelfClientService::~SendTabToSelfClientService() = default; void SendTabToSelfClientService::Shutdown() { - model_->RemoveObserver(this); - model_ = nullptr; receiving_ui_handler_.reset(); }
diff --git a/chrome/browser/send_tab_to_self/send_tab_to_self_client_service.h b/chrome/browser/send_tab_to_self/send_tab_to_self_client_service.h index 29c1ece..969cfa1 100644 --- a/chrome/browser/send_tab_to_self/send_tab_to_self_client_service.h +++ b/chrome/browser/send_tab_to_self/send_tab_to_self_client_service.h
@@ -9,6 +9,7 @@ #include <vector> #include "base/memory/raw_ptr.h" +#include "base/scoped_observation.h" #include "chrome/browser/send_tab_to_self/receiving_ui_handler.h" #include "components/keyed_service/core/keyed_service.h" #include "components/send_tab_to_self/send_tab_to_self_model.h" @@ -25,6 +26,9 @@ class SendTabToSelfClientService : public KeyedService, public SendTabToSelfModelObserver { public: + // `model` must outlive this object. `receiving_ui_handler` must be usable + // until this keyed service is Shutdown() (in particular it cannot depend on + // any services that are instantiated after this one). SendTabToSelfClientService( std::unique_ptr<ReceivingUiHandler> receiving_ui_handler, SendTabToSelfModel* model); @@ -51,8 +55,10 @@ ReceivingUiHandler* GetReceivingUiHandler() const; private: - // Owned by the SendTabToSelfSyncService which should outlive this class - raw_ptr<SendTabToSelfModel> model_; + // The model outlives this object, so this is fine. + base::ScopedObservation<SendTabToSelfModel, SendTabToSelfModelObserver> + model_observation_{this}; + // Reset on Shutdown(). std::unique_ptr<ReceivingUiHandler> receiving_ui_handler_; };
diff --git a/chrome/browser/speech/on_device_speech_recognition_impl.cc b/chrome/browser/speech/on_device_speech_recognition_impl.cc index 6bd429ec..f6e91404 100644 --- a/chrome/browser/speech/on_device_speech_recognition_impl.cc +++ b/chrome/browser/speech/on_device_speech_recognition_impl.cc
@@ -28,23 +28,9 @@ #include "components/soda/soda_util.h" namespace { -// Returns a boolean indicating whether the language is both enabled and not -// already installed. +// Returns a boolean indicating whether the language is enabled. bool IsLanguageInstallable(const std::string& language_code, bool is_soda_binary_installed) { - if (is_soda_binary_installed) { - // Check if the language pack is already installed if the SODA binary was - // already installed. If the SODA binary has not been installed, - // `prefs::kSodaRegisteredLanguagePacks` will contain the default language - // pack which may not exist on the device. - for (const auto& language : g_browser_process->local_state()->GetList( - prefs::kSodaRegisteredLanguagePacks)) { - if (language.GetString() == language_code) { - return false; - } - } - } - return base::Contains( speech::SodaInstaller::GetInstance()->GetLiveCaptionEnabledLanguages(), language_code); @@ -55,7 +41,17 @@ namespace speech { -OnDeviceSpeechRecognitionImpl::~OnDeviceSpeechRecognitionImpl() = default; +OnDeviceSpeechRecognitionImpl::~OnDeviceSpeechRecognitionImpl() { +#if !BUILDFLAG(IS_ANDROID) + speech::SodaInstaller* soda_installer = speech::SodaInstaller::GetInstance(); + // `soda_installer` is not guaranteed to be valid, since it's possible for + // this class to out-live it. This means that this class cannot use + // ScopedObservation and needs to manage removing the observer itself. + if (soda_installer) { + soda_installer->RemoveObserver(this); + } +#endif // !BUILDFLAG(IS_ANDROID) +} void OnDeviceSpeechRecognitionImpl::Bind( mojo::PendingReceiver<media::mojom::OnDeviceSpeechRecognition> receiver) { @@ -79,6 +75,7 @@ callback) { #if BUILDFLAG(IS_ANDROID) std::move(callback).Run(false); +} #else std::optional<speech::SodaLanguagePackComponentConfig> language_config = speech::GetLanguageComponentConfigMatchingLanguageSubtag(language); @@ -99,6 +96,7 @@ return; } + language_installation_callbacks_[language].push_back(std::move(callback)); // `InstallSoda` will only install the SODA binary if it is not already // installed. speech::SodaInstaller::GetInstance()->InstallSoda( @@ -108,10 +106,22 @@ // installed. speech::SodaInstaller::GetInstance()->InstallLanguage( language_config.value().language_name, g_browser_process->local_state()); - std::move(callback).Run(true); -#endif // BUILDFLAG(IS_ANDROID) } +void OnDeviceSpeechRecognitionImpl::OnSodaInstalled( + speech::LanguageCode language_code) { + RunAndRemoveInstallationCallbacks(GetLanguageName(language_code), + /*installation_success=*/true); +} + +void OnDeviceSpeechRecognitionImpl::OnSodaInstallError( + speech::LanguageCode language_code, + speech::SodaInstaller::ErrorCode error_code) { + RunAndRemoveInstallationCallbacks(GetLanguageName(language_code), + /*installation_success=*/false); +} +#endif // !BUILDFLAG(IS_ANDROID) + OnDeviceSpeechRecognitionImpl::OnDeviceSpeechRecognitionImpl( content::RenderFrameHost* frame_host) : content::DocumentUserData<OnDeviceSpeechRecognitionImpl>(frame_host), @@ -119,7 +129,14 @@ frame_host->GetProcess()->GetBrowserContext()) ->GetPrefs()), language_prefs_( - std::make_unique<language::LanguagePrefs>(pref_service_)) {} + std::make_unique<language::LanguagePrefs>(pref_service_)) { +#if !BUILDFLAG(IS_ANDROID) + speech::SodaInstaller* soda_installer = speech::SodaInstaller::GetInstance(); + if (soda_installer) { + soda_installer->AddObserver(this); + } +#endif // !BUILDFLAG(IS_ANDROID) +} bool OnDeviceSpeechRecognitionImpl::CanInstallWithoutUserConsent( const std::string& language) { @@ -142,6 +159,22 @@ return false; } +#if !BUILDFLAG(IS_ANDROID) +void OnDeviceSpeechRecognitionImpl::RunAndRemoveInstallationCallbacks( + const std::string& language, + bool installation_success) { + auto it = language_installation_callbacks_.find(language); + if (it != language_installation_callbacks_.end()) { + std::list<InstallOnDeviceSpeechRecognitionCallback>& callbacks = it->second; + for (auto callback_iterator = callbacks.begin(); + callback_iterator != callbacks.end();) { + std::move(*callback_iterator).Run(installation_success); + callback_iterator = callbacks.erase(callback_iterator); + } + language_installation_callbacks_.erase(it); + } +} +#endif // !BUILDFLAG(IS_ANDROID) DOCUMENT_USER_DATA_KEY_IMPL(OnDeviceSpeechRecognitionImpl); } // namespace speech
diff --git a/chrome/browser/speech/on_device_speech_recognition_impl.h b/chrome/browser/speech/on_device_speech_recognition_impl.h index 80355bb..6b0a612 100644 --- a/chrome/browser/speech/on_device_speech_recognition_impl.h +++ b/chrome/browser/speech/on_device_speech_recognition_impl.h
@@ -11,6 +11,13 @@ #include "media/mojo/mojom/speech_recognizer.mojom.h" #include "mojo/public/cpp/bindings/receiver_set.h" +#if !BUILDFLAG(IS_ANDROID) +#include <list> + +#include "base/containers/flat_map.h" +#include "components/soda/soda_installer.h" +#endif // !BUILDFLAG(IS_ANDROID) + class PrefService; namespace content { @@ -25,6 +32,9 @@ class OnDeviceSpeechRecognitionImpl : public content::DocumentUserData<OnDeviceSpeechRecognitionImpl>, +#if !BUILDFLAG(IS_ANDROID) + public speech::SodaInstaller::Observer, +#endif // !BUILDFLAG(IS_ANDROID) public media::mojom::OnDeviceSpeechRecognition { public: OnDeviceSpeechRecognitionImpl(const OnDeviceSpeechRecognitionImpl&) = delete; @@ -46,6 +56,15 @@ OnDeviceSpeechRecognitionImpl::InstallOnDeviceSpeechRecognitionCallback callback) override; +#if !BUILDFLAG(IS_ANDROID) + // SodaInstaller::Observer: + void OnSodaInstalled(speech::LanguageCode language_code) override; + void OnSodaInstallError(speech::LanguageCode language_code, + speech::SodaInstaller::ErrorCode error_code) override; + void OnSodaProgress(speech::LanguageCode language_code, + int combined_progress) override {} +#endif // !BUILDFLAG(IS_ANDROID) + private: friend class content::DocumentUserData<OnDeviceSpeechRecognitionImpl>; explicit OnDeviceSpeechRecognitionImpl(content::RenderFrameHost* frame_host); @@ -54,9 +73,20 @@ // explicit user consent. bool CanInstallWithoutUserConsent(const std::string& language); +#if !BUILDFLAG(IS_ANDROID) + void RunAndRemoveInstallationCallbacks(const std::string& language, + bool installation_success); +#endif // !BUILDFLAG(IS_ANDROID) + raw_ptr<PrefService> pref_service_; std::unique_ptr<language::LanguagePrefs> language_prefs_; +#if !BUILDFLAG(IS_ANDROID) + base::flat_map<std::string, + std::list<InstallOnDeviceSpeechRecognitionCallback>> + language_installation_callbacks_; +#endif // !BUILDFLAG(IS_ANDROID) + mojo::Receiver<media::mojom::OnDeviceSpeechRecognition> receiver_{this}; DOCUMENT_USER_DATA_KEY_DECL();
diff --git a/chrome/browser/tab_group_suggestion/android/java/src/org/chromium/chrome/browser/tab_group_suggestion/SuggestionEventObserver.java b/chrome/browser/tab_group_suggestion/android/java/src/org/chromium/chrome/browser/tab_group_suggestion/SuggestionEventObserver.java index 76763f33..f5b31fd 100644 --- a/chrome/browser/tab_group_suggestion/android/java/src/org/chromium/chrome/browser/tab_group_suggestion/SuggestionEventObserver.java +++ b/chrome/browser/tab_group_suggestion/android/java/src/org/chromium/chrome/browser/tab_group_suggestion/SuggestionEventObserver.java
@@ -47,8 +47,7 @@ @TabLaunchType int type, @TabCreationState int creationState, boolean markedForSelection) { - if (markedForSelection - && creationState == TabCreationState.LIVE_IN_FOREGROUND) { + if (creationState != TabCreationState.FROZEN_ON_RESTORE) { mGroupSuggestionsService.didAddTab(tab.getId(), type); } }
diff --git a/chrome/browser/tab_group_suggestion/android/junit/src/org/chromium/chrome/browser/tab_group_suggestion/SuggestionEventObserverUnitTest.java b/chrome/browser/tab_group_suggestion/android/junit/src/org/chromium/chrome/browser/tab_group_suggestion/SuggestionEventObserverUnitTest.java index afade1d..4e79580 100644 --- a/chrome/browser/tab_group_suggestion/android/junit/src/org/chromium/chrome/browser/tab_group_suggestion/SuggestionEventObserverUnitTest.java +++ b/chrome/browser/tab_group_suggestion/android/junit/src/org/chromium/chrome/browser/tab_group_suggestion/SuggestionEventObserverUnitTest.java
@@ -98,11 +98,24 @@ .getValue() .didAddTab( mTab, - TabLaunchType.FROM_RESTORE, + TabLaunchType.FROM_CHROME_UI, TabCreationState.LIVE_IN_FOREGROUND, /* markedForSelection= */ true); - verify(mGroupSuggestionsService).didAddTab(eq(TAB_ID), eq(TabLaunchType.FROM_RESTORE)); + verify(mGroupSuggestionsService).didAddTab(eq(TAB_ID), eq(TabLaunchType.FROM_CHROME_UI)); + } + + @Test + public void testDidAddTab_IgnoreRestore() { + mTabModelObserverCaptor + .getValue() + .didAddTab( + mTab, + TabLaunchType.FROM_RESTORE, + TabCreationState.FROZEN_ON_RESTORE, + /* markedForSelection= */ true); + + verify(mGroupSuggestionsService, never()).didAddTab(anyInt(), anyInt()); } @Test
diff --git a/chrome/browser/touch_to_fill/autofill/android/touch_to_fill_delegate_android_impl_unittest.cc b/chrome/browser/touch_to_fill/autofill/android/touch_to_fill_delegate_android_impl_unittest.cc index 9977abb..242d3aa 100644 --- a/chrome/browser/touch_to_fill/autofill/android/touch_to_fill_delegate_android_impl_unittest.cc +++ b/chrome/browser/touch_to_fill/autofill/android/touch_to_fill_delegate_android_impl_unittest.cc
@@ -272,7 +272,6 @@ std::unique_ptr<TestAutofillDriver> autofill_driver_; std::unique_ptr<MockBrowserAutofillManager> browser_autofill_manager_; raw_ptr<TouchToFillDelegateAndroidImpl> touch_to_fill_delegate_; - base::test::ScopedFeatureList scoped_feature_list_; base::HistogramTester histogram_tester_; }; @@ -467,12 +466,6 @@ class TouchToFillDelegateAndroidImplCreditCardUnitTest : public TouchToFillDelegateAndroidImplUnitTest { public: - TouchToFillDelegateAndroidImplCreditCardUnitTest() { - scoped_feature_list_.InitWithFeatureState( - features::kAutofillEnableVcnGrayOutForMerchantOptOut, - /*enabled=*/false); - } - static std::vector<CreditCard> GetCardsToSuggest( std::vector<const CreditCard*> credit_cards) { std::vector<CreditCard> cards_to_suggest; @@ -488,7 +481,6 @@ TouchToFillDelegateAndroidImplUnitTest::SetUp(); ConfigureForCreditCards(test::GetCreditCard()); } - base::test::ScopedFeatureList scoped_feature_list_; }; TEST_F(TouchToFillDelegateAndroidImplCreditCardUnitTest, @@ -771,39 +763,6 @@ TryToShowTouchToFill(/*expected_success=*/true); } -TEST_F( - TouchToFillDelegateAndroidImplCreditCardUnitTest, - TryToShowTouchToFillDoesNotShowVirtualCardSuggestionsForOptedOutMerchants) { - autofill_client_.GetPersonalDataManager() - .test_payments_data_manager() - .ClearCreditCards(); - CreditCard credit_card = - test::GetMaskedServerCardEnrolledIntoVirtualCardNumber(); - autofill_client_.GetPersonalDataManager() - .payments_data_manager() - .AddCreditCard(credit_card); - - ASSERT_FALSE(touch_to_fill_delegate_->IsShowingTouchToFill()); - - ON_CALL(*static_cast<MockAutofillOptimizationGuide*>( - autofill_client_.GetAutofillOptimizationGuide()), - ShouldBlockFormFieldSuggestion) - .WillByDefault(testing::Return(true)); - - // Since merchant has opted out of virtual cards and gray-out feature is - // disabled, `HasDeactivatedStyle()` should return false for the virtual card - // suggestion. - EXPECT_CALL( - payments_autofill_client(), - ShowTouchToFillCreditCard( - _, ElementsAre(credit_card), - ElementsAre(EqualsSuggestionFields( - credit_card.CardNameForAutofillDisplay(credit_card.nickname()), - credit_card.ObfuscatedNumberWithVisibleLastFourDigits(), - /*has_deactivated_style=*/false)))); - TryToShowTouchToFill(/*expected_success=*/true); -} - TEST_F(TouchToFillDelegateAndroidImplCreditCardUnitTest, TryToShowTouchToFillShowsVirtualCardSuggestionsForEnrolledCards) { autofill_client_.GetPersonalDataManager() @@ -1102,17 +1061,11 @@ : public TouchToFillDelegateAndroidImplCreditCardUnitTest { public: TouchToFillDelegateAndroidImplVcnGrayOutForMerchantOptOutUnitTest() { - scoped_feature_list_.InitWithFeatureState( - features::kAutofillEnableVcnGrayOutForMerchantOptOut, - /*enabled=*/true); ON_CALL(*static_cast<MockAutofillOptimizationGuide*>( autofill_client_.GetAutofillOptimizationGuide()), ShouldBlockFormFieldSuggestion) .WillByDefault(testing::Return(true)); } - - protected: - base::test::ScopedFeatureList scoped_feature_list_; }; TEST_F(TouchToFillDelegateAndroidImplVcnGrayOutForMerchantOptOutUnitTest,
diff --git a/chrome/browser/ui/android/layouts/java/src/org/chromium/chrome/browser/layouts/LayoutType.java b/chrome/browser/ui/android/layouts/java/src/org/chromium/chrome/browser/layouts/LayoutType.java index e50e399..0f7a9f40 100644 --- a/chrome/browser/ui/android/layouts/java/src/org/chromium/chrome/browser/layouts/LayoutType.java +++ b/chrome/browser/ui/android/layouts/java/src/org/chromium/chrome/browser/layouts/LayoutType.java
@@ -21,7 +21,6 @@ LayoutType.TAB_SWITCHER, LayoutType.TOOLBAR_SWIPE, LayoutType.SIMPLE_ANIMATION, - LayoutType.START_SURFACE }) @Retention(RetentionPolicy.SOURCE) @NullMarked @@ -31,6 +30,5 @@ int TAB_SWITCHER = 2; int TOOLBAR_SWIPE = 4; int SIMPLE_ANIMATION = 8; - @Deprecated int START_SURFACE = 16; - // Next layout type should be 32. + // Next layout type should be 16. }
diff --git a/chrome/browser/ui/android/omnibox/BUILD.gn b/chrome/browser/ui/android/omnibox/BUILD.gn index 9f7acfc..bb27f16 100644 --- a/chrome/browser/ui/android/omnibox/BUILD.gn +++ b/chrome/browser/ui/android/omnibox/BUILD.gn
@@ -274,7 +274,6 @@ "java/res/color-night/omnibox_suggestion_bg.xml", "java/res/color-night/omnibox_suggestion_dropdown_bg.xml", "java/res/color/omnibox_suggestion_bg.xml", - "java/res/color/omnibox_suggestion_bg_hover.xml", "java/res/color/omnibox_suggestion_dropdown_bg.xml", "java/res/drawable-hdpi/bookmark_edit_active.png", "java/res/drawable-hdpi/btn_suggestion_refine.png", @@ -317,7 +316,6 @@ "java/res/drawable/ic_wb_sunny_round.xml", "java/res/drawable/logo_partly_cloudy.xml", "java/res/drawable/logo_translate_round.xml", - "java/res/drawable/omnibox_suggestion_bg_hover_layers.xml", "java/res/drawable/switch_to_tab.xml", "java/res/drawable/trending_up_black_24dp.xml", "java/res/layout-sw600dp/location_bar.xml",
diff --git a/chrome/browser/ui/android/omnibox/java/res/color/omnibox_suggestion_bg_hover.xml b/chrome/browser/ui/android/omnibox/java/res/color/omnibox_suggestion_bg_hover.xml deleted file mode 100644 index e5b8b59a..0000000 --- a/chrome/browser/ui/android/omnibox/java/res/color/omnibox_suggestion_bg_hover.xml +++ /dev/null
@@ -1,23 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- -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. ---> - -<selector xmlns:android="http://schemas.android.com/apk/res/android"> - <!-- selected --> - <item - android:alpha="@dimen/omnibox_suggestion_bg_hover_alpha" - android:color="?attr/colorOnSurface" - android:state_selected="true"/> - - <!-- hovered --> - <item - android:alpha="@dimen/omnibox_suggestion_bg_hover_alpha" - android:color="?attr/colorOnSurface" - android:state_hovered="true"/> - - <!-- default state --> - <item android:color="@android:color/transparent"/> -</selector> \ No newline at end of file
diff --git a/chrome/browser/ui/android/omnibox/java/res/drawable/omnibox_suggestion_bg_hover_layers.xml b/chrome/browser/ui/android/omnibox/java/res/drawable/omnibox_suggestion_bg_hover_layers.xml deleted file mode 100644 index fc2de20..0000000 --- a/chrome/browser/ui/android/omnibox/java/res/drawable/omnibox_suggestion_bg_hover_layers.xml +++ /dev/null
@@ -1,22 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- -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. ---> - -<layer-list xmlns:android="http://schemas.android.com/apk/res/android"> - <!-- base surface color--> - <item> - <shape android:shape="rectangle"> - <solid android:color="@color/omnibox_suggestion_bg"/> - </shape> - </item> - - <!-- hover layer --> - <item> - <shape android:shape="rectangle"> - <solid android:color="@color/omnibox_suggestion_bg_hover"/> - </shape> - </item> -</layer-list> \ No newline at end of file
diff --git a/chrome/browser/ui/android/omnibox/java/res/values/dimens.xml b/chrome/browser/ui/android/omnibox/java/res/values/dimens.xml index 555e642e..5d46000 100644 --- a/chrome/browser/ui/android/omnibox/java/res/values/dimens.xml +++ b/chrome/browser/ui/android/omnibox/java/res/values/dimens.xml
@@ -111,7 +111,7 @@ <dimen name="omnibox_suggestion_dropdown_round_corner_radius">0dp</dimen> <dimen name="omnibox_min_space_above_window_bottom">0dp</dimen> - <item name="omnibox_suggestion_bg_hover_alpha" format="float" type="dimen">0.08</item> + <item name="omnibox_suggestion_bg_hover_overlay_fraction" type="fraction">8%</item> <!-- Adding search engine logo to the omnibox. --> <!-- Max size which will fit completely in the composed/rounded bg. -->
diff --git a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/styles/OmniboxResourceProvider.java b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/styles/OmniboxResourceProvider.java index 93e32bd8..9a8257a3 100644 --- a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/styles/OmniboxResourceProvider.java +++ b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/styles/OmniboxResourceProvider.java
@@ -10,6 +10,7 @@ import android.graphics.drawable.Drawable; import android.graphics.drawable.Drawable.ConstantState; import android.graphics.drawable.LayerDrawable; +import android.graphics.drawable.StateListDrawable; import android.util.SparseArray; import android.util.TypedValue; @@ -389,30 +390,42 @@ : ContextCompat.getColor(context, R.color.omnibox_suggestion_bg); } - /** - * Returns the background hover drawable for suggestions in a "standard" (non-incognito) model - * with the given context. - */ - private static Drawable getHoverSuggestionBackgroundDrawable( + /** Returns the background hover color for suggestions in a model with the given context. */ + private static @ColorInt int getHoverSuggestionBackgroundColor( Context context, @BrandedColorScheme int colorScheme) { - return colorScheme == BrandedColorScheme.INCOGNITO - ? new ColorDrawable(context.getColor(R.color.omnibox_suggestion_bg_hover_incognito)) - : AppCompatResources.getDrawable( - context, R.drawable.omnibox_suggestion_bg_hover_layers); + + if (colorScheme == BrandedColorScheme.INCOGNITO) { + return context.getColor(R.color.omnibox_suggestion_bg_hover_incognito); + } + + // omnibox_suggestion_bg + 8% colorOnSurface + @ColorInt int baseColor = ContextCompat.getColor(context, R.color.omnibox_suggestion_bg); + @ColorInt int hoverColor = MaterialColors.getColor(context, R.attr.colorOnSurface, TAG); + float fraction = + context.getResources() + .getFraction(R.fraction.omnibox_suggestion_bg_hover_overlay_fraction, 1, 1); + + return ColorUtils.overlayColor(baseColor, hoverColor, fraction); } /** Returns a stateful suggestion background with the select default state. */ public static Drawable getStatefulSuggestionBackground( Context context, @ColorInt int defaultColor, @BrandedColorScheme int colorScheme) { var background = new ColorDrawable(defaultColor); - // Hover, selected, hover and selected are defined in the drawable here. - var hover = getHoverSuggestionBackgroundDrawable(context, colorScheme); + var hover = new ColorDrawable(getHoverSuggestionBackgroundColor(context, colorScheme)); // Ripple effect to use when the user interacts with the suggestion. var ripple = resolveAttributeToDrawable(context, colorScheme, R.attr.selectableItemBackground); - return new LayerDrawable(new Drawable[] {background, hover, ripple}); + var statefulBackground = new StateListDrawable(); + statefulBackground.addState(new int[] {android.R.attr.state_selected}, hover); + statefulBackground.addState(new int[] {android.R.attr.state_hovered}, hover); + statefulBackground.addState( + new int[] {android.R.attr.state_selected, android.R.attr.state_hovered}, hover); + statefulBackground.addState(new int[] {}, background); + + return new LayerDrawable(new Drawable[] {statefulBackground, ripple}); } /**
diff --git a/chrome/browser/ui/autofill/payments/desktop_payments_window_manager.cc b/chrome/browser/ui/autofill/payments/desktop_payments_window_manager.cc index 1264898..2f7b944b 100644 --- a/chrome/browser/ui/autofill/payments/desktop_payments_window_manager.cc +++ b/chrome/browser/ui/autofill/payments/desktop_payments_window_manager.cc
@@ -17,6 +17,7 @@ #include "chrome/browser/ui/tabs/tab_strip_model.h" #include "components/autofill/content/browser/content_autofill_client.h" #include "components/autofill/core/browser/autofill_progress_dialog_type.h" +#include "components/autofill/core/browser/metrics/payments/bnpl_metrics.h" #include "components/autofill/core/browser/metrics/payments/payments_window_metrics.h" #include "components/autofill/core/browser/payments/card_unmask_challenge_option.h" #include "components/autofill/core/browser/payments/payments_autofill_client.h" @@ -110,6 +111,7 @@ flow_type_ = FlowType::kBnpl; bnpl_context_ = std::move(context); CreatePopup(bnpl_context_->initial_url, GetPopupSizeForBnpl()); + autofill_metrics::LogBnplPopupWindowShown(bnpl_context_->issuer_id); } void DesktopPaymentsWindowManager::DidFinishNavigation( @@ -302,6 +304,7 @@ } std::move(bnpl_context_->completion_callback) .Run(result, std::move(most_recent_url_navigation_)); + autofill_metrics::LogBnplPopupWindowResult(bnpl_context_->issuer_id, result); Reset(); }
diff --git a/chrome/browser/ui/autofill/payments/desktop_payments_window_manager_interactive_uitest.cc b/chrome/browser/ui/autofill/payments/desktop_payments_window_manager_interactive_uitest.cc index 126d03e3..89c101a3 100644 --- a/chrome/browser/ui/autofill/payments/desktop_payments_window_manager_interactive_uitest.cc +++ b/chrome/browser/ui/autofill/payments/desktop_payments_window_manager_interactive_uitest.cc
@@ -22,8 +22,10 @@ #include "chrome/test/interaction/interactive_browser_test.h" #include "components/autofill/content/browser/test_autofill_client_injector.h" #include "components/autofill/content/browser/test_content_autofill_client.h" +#include "components/autofill/core/browser/metrics/payments/bnpl_metrics.h" #include "components/autofill/core/browser/metrics/payments/payments_window_metrics.h" #include "components/autofill/core/browser/payments/card_unmask_challenge_option.h" +#include "components/autofill/core/browser/payments/constants.h" #include "components/autofill/core/browser/payments/payments_autofill_client.h" #include "components/autofill/core/browser/payments/payments_window_manager.h" #include "components/autofill/core/browser/payments/test_payments_autofill_client.h" @@ -101,6 +103,7 @@ window_manager().InitVcn3dsAuthentication(std::move(context)); } else if (name.find("Bnpl") != std::string::npos) { PaymentsWindowManager::BnplContext context; + context.issuer_id = kBnplAffirmIssuerId; context.success_url_prefix = GURL(kBnplSuccessUrlPrefix); context.failure_url_prefix = GURL(kBnplFailureUrlPrefix); context.initial_url = GURL(kBnplInitialUrl); @@ -821,6 +824,41 @@ EXPECT_FALSE(test_api(window_manager()).GetBnplContext().has_value()); } +// Test that the PopupWindowShown histogram is logged if the BNPL pop-up is +// shown. +IN_PROC_BROWSER_TEST_F(DesktopPaymentsWindowManagerInteractiveUiTest, + InvokeUi_Bnpl_PopupWindowShownLogged) { + ShowUi("Bnpl"); + + histogram_tester_.ExpectUniqueSample( + /*name=*/"Autofill.Bnpl.PopupWindowShown.Affirm", + /*sample=*/true, /*expected_bucket_count=*/1); +} + +// Test that the BNPL PopupWindowResult histogram logs a success result if the +// pop-up flow was successful. +IN_PROC_BROWSER_TEST_F(DesktopPaymentsWindowManagerInteractiveUiTest, + InvokeUi_Bnpl_Success_PopupWindowResultLogged) { + ShowUi("Bnpl"); + + // Navigate to the URL that denotes success inside of the BNPL pop-up. + GetPopupWebContents()->OpenURL( + content::OpenURLParams(GURL(std::string(kBnplSuccessUrlPrefix) + + "testqueryparam?param1=true"), + content::Referrer(), + WindowOpenDisposition::CURRENT_TAB, + ui::PageTransition::PAGE_TRANSITION_AUTO_TOPLEVEL, + /*is_renderer_initiated=*/false), + /*navigation_handle_callback=*/{}); + + WaitForPopupClose(); + + histogram_tester_.ExpectUniqueSample( + /*name=*/"Autofill.Bnpl.PopupWindowResult.Affirm", + /*sample=*/PaymentsWindowManager::BnplFlowResult::kSuccess, + /*expected_bucket_count=*/1); +} + // Test that the BNPL pop-up is shown correctly, and on close the completion // callback is triggered with a failure result if the flow was a failure. IN_PROC_BROWSER_TEST_F(DesktopPaymentsWindowManagerInteractiveUiTest, @@ -850,6 +888,30 @@ EXPECT_FALSE(test_api(window_manager()).GetBnplContext().has_value()); } +// Test that the BNPL PopupWindowResult histogram logs a failure result if the +// pop-up flow was a failure. +IN_PROC_BROWSER_TEST_F(DesktopPaymentsWindowManagerInteractiveUiTest, + InvokeUi_Bnpl_Failure_PopupWindowResultLogged) { + ShowUi("Bnpl"); + + // Navigate to the URL that denotes failure inside of the BNPL pop-up. + GetPopupWebContents()->OpenURL( + content::OpenURLParams(GURL(std::string(kBnplFailureUrlPrefix) + + "testqueryparam?param1=true"), + content::Referrer(), + WindowOpenDisposition::CURRENT_TAB, + ui::PageTransition::PAGE_TRANSITION_AUTO_TOPLEVEL, + /*is_renderer_initiated=*/false), + /*navigation_handle_callback=*/{}); + + WaitForPopupClose(); + + histogram_tester_.ExpectUniqueSample( + /*name=*/"Autofill.Bnpl.PopupWindowResult.Affirm", + /*sample=*/PaymentsWindowManager::BnplFlowResult::kFailure, + /*expected_bucket_count=*/1); +} + // Test that the BNPL pop-up is shown correctly, and on close the completion // callback is triggered with a "user closed" result if the flow was closed by // the user. @@ -870,6 +932,19 @@ EXPECT_FALSE(test_api(window_manager()).GetBnplContext().has_value()); } +// Test that the PopupWindowResult histogram logs a "user closed" result if the +// pop-up flow was closed by the user. +IN_PROC_BROWSER_TEST_F(DesktopPaymentsWindowManagerInteractiveUiTest, + InvokeUi_Bnpl_UserClosedPopup_PopupWindowResultLogged) { + ShowUi("Bnpl"); + ClosePopupAndWait(); + + histogram_tester_.ExpectUniqueSample( + /*name=*/"Autofill.Bnpl.PopupWindowResult.Affirm", + /*sample=*/PaymentsWindowManager::BnplFlowResult::kUserClosed, + /*expected_bucket_count=*/1); +} + // Test that the BNPL pop-up is shown correctly, and on close the completion // callback is triggered with a "user closed" result if the flow was closed and // the URL contained the success URL prefix, but the success URL prefix was not
diff --git a/chrome/browser/ui/views/frame/browser_view.cc b/chrome/browser/ui/views/frame/browser_view.cc index c336b6d..1686198 100644 --- a/chrome/browser/ui/views/frame/browser_view.cc +++ b/chrome/browser/ui/views/frame/browser_view.cc
@@ -197,8 +197,6 @@ #include "chrome/grit/branded_strings.h" #include "chrome/grit/generated_resources.h" #include "chrome/grit/theme_resources.h" -#include "chromeos/components/mgs/managed_guest_session_utils.h" -#include "chromeos/ui/frame/caption_buttons/frame_caption_button_container_view.h" #include "components/autofill/core/common/autofill_payments_features.h" #include "components/content_settings/core/common/features.h" #include "components/enterprise/buildflags/buildflags.h" @@ -310,6 +308,8 @@ #include "chrome/browser/ui/views/frame/browser_non_client_frame_view_chromeos.h" #include "chrome/browser/ui/views/frame/top_controls_slide_controller_chromeos.h" #include "chrome/grit/chrome_unscaled_resources.h" +#include "chromeos/components/mgs/managed_guest_session_utils.h" +#include "chromeos/ui/frame/caption_buttons/frame_caption_button_container_view.h" #include "chromeos/ui/frame/caption_buttons/frame_size_button.h" #include "chromeos/ui/wm/desks/desks_helper.h" #include "ui/compositor/compositor_metrics_tracker.h"
diff --git a/chrome/browser/ui/views/frame/picture_in_picture_browser_frame_view_interactive_uitest.cc b/chrome/browser/ui/views/frame/picture_in_picture_browser_frame_view_interactive_uitest.cc index ceb3e878..3efc317 100644 --- a/chrome/browser/ui/views/frame/picture_in_picture_browser_frame_view_interactive_uitest.cc +++ b/chrome/browser/ui/views/frame/picture_in_picture_browser_frame_view_interactive_uitest.cc
@@ -28,6 +28,7 @@ #include "media/base/media_switches.h" #include "net/dns/mock_host_resolver.h" #include "third_party/blink/public/common/features.h" +#include "ui/compositor/layer.h" #include "ui/events/test/event_generator.h" #include "ui/gfx/animation/animation_test_api.h" #include "ui/views/widget/widget_utils.h"
diff --git a/chrome/browser/ui/views/location_bar/icon_label_bubble_view.cc b/chrome/browser/ui/views/location_bar/icon_label_bubble_view.cc index 083e136..7863d06 100644 --- a/chrome/browser/ui/views/location_bar/icon_label_bubble_view.cc +++ b/chrome/browser/ui/views/location_bar/icon_label_bubble_view.cc
@@ -24,6 +24,7 @@ #include "ui/gfx/geometry/rect.h" #include "ui/gfx/geometry/skia_conversions.h" #include "ui/gfx/scoped_canvas.h" +#include "ui/gfx/text_constants.h" #include "ui/views/accessibility/ax_virtual_view.h" #include "ui/views/accessibility/view_accessibility.h" #include "ui/views/animation/flood_fill_ink_drop_ripple.h" @@ -219,13 +220,33 @@ layout.child_layouts.emplace_back(ink_drop_container(), true, GetLocalBounds()); + // First calculate the preferred size of this view, according to the + // preferred size of the label (i.e. with an expanded label). + // If that won't fit in the available bounds, then size this view + // based on a collapsed label. + const int preferred_label_width = label()->GetPreferredSize().width(); + gfx::Size preferred_size = GetSizeForLabelWidth(preferred_label_width); + if (size_bounds.width().is_bounded() && + preferred_size.width() > size_bounds.width()) { + if (label()->GetElideBehavior() == gfx::NO_ELIDE) { + preferred_size = GetSizeForLabelWidth(0); + } else { + // If the label supports eliding, whittle away from its fully expanded + // size. + preferred_size.set_width(size_bounds.width().value()); + } + } + layout.host_size = preferred_size; + const int height = preferred_size.height(); + // We may not have horizontal room for both the image and the trailing // padding. When the view is expanding (or showing-label steady state), the // image. When the view is contracting (or hidden-label steady state), whittle // away at the trailing padding instead. int bubble_trailing_padding = GetEndPaddingWithSeparator(); int image_width = image_container_view()->GetPreferredSize().width(); - const int space_shortage = image_width + bubble_trailing_padding - width(); + const int space_shortage = + image_width + bubble_trailing_padding - preferred_size.width(); if (space_shortage > 0) { if (ShouldShowLabel()) { image_width -= space_shortage; @@ -233,29 +254,44 @@ bubble_trailing_padding -= space_shortage; } } - gfx::Rect image_bounds(GetInsets().left(), 0, image_width, height()); - layout.child_layouts.emplace_back( - const_cast<views::View*>(this->image_container_view()), true, - image_bounds); + + const int image_x = GetInsets().left(); + const gfx::Rect image_bounds(image_x, 0, image_width, height); + + // There may be extra padding added if the label is shown. + // The "_with_label" image values are used to compute the label's bounds, + // which is then compared to the available bounds for the label. If the + // label would fit in the bounds (i.e. not collapsed), then the "_with_label" + // values will accepted as the image's bounds too. + const int image_x_with_label = + image_x + GetWidthBetween(0, expanded_label_additional_insets_.leading()); + const gfx::Rect image_bounds_with_label(image_x_with_label, 0, image_width, + height); // Compute the label bounds. The label gets whatever size is left over after // accounting for the preferred image width and padding amounts. Note that if // the label has zero size it doesn't actually matter what we compute its X // value to be, since it won't be visible. - const int label_x = image_bounds.right() + GetInternalSpacing(); - int label_width = label()->GetPreferredSize(size_bounds).width(); - if (size_bounds.width().is_bounded() && - label_width > size_bounds.width() - label_x - bubble_trailing_padding - - GetWidthBetweenIconAndSeparator()) { - label_width = 0; - } - gfx::Rect label_bounds(label_x, 0, label_width, height()); + const int label_x = image_bounds_with_label.right() + GetInternalSpacing(); + const int available_label_width = + std::max(0, layout.host_size.width() - label_x - bubble_trailing_padding - + GetWidthBetweenIconAndSeparator()); + gfx::Rect label_bounds(label_x, 0, available_label_width, height); layout.child_layouts.emplace_back( label(), static_cast<views::LayoutManagerBase*>(GetLayoutManager()) ->CanBeVisible(label()), label_bounds); + // If the fully expanded label fits, or it is mid-animation, then we + // accept the "_with_label" image bounds. + const bool can_label_expand = available_label_width >= preferred_label_width; + layout.child_layouts.emplace_back( + const_cast<views::View*>(this->image_container_view()), true, + (can_label_expand || slide_animation_.is_animating()) + ? image_bounds_with_label + : image_bounds); + // The separator should be the same height as the icons. const int separator_height = GetLayoutConstant(LOCATION_BAR_ICON_SIZE); gfx::Rect separator_bounds(label_bounds); @@ -271,7 +307,6 @@ gfx::Rect(separator_x, separator_bounds.y(), separator_width, separator_height)); - layout.host_size = GetSizeForLabelWidth(label_width); return layout; } @@ -302,6 +337,12 @@ label()->SetFontList(font_list); } +void IconLabelBubbleView::SetExpandedLabelAdditionalInsets( + const views::Inset1D& insets) { + expanded_label_additional_insets_ = insets; + InvalidateLayout(); +} + SkColor IconLabelBubbleView::GetBackgroundColor() const { ui::ColorId background_color_id = ui::kUiColorsStart; if (background_color_id_.has_value()) { @@ -597,7 +638,8 @@ const int min_width = shrinking ? image_size.width() : grow_animation_starting_width_; - const int max_width = image_size.width() + GetInternalSpacing() + label_width; + const int max_width = image_size.width() + GetInternalSpacing() + + label_width + expanded_label_additional_insets_.size(); return gfx::Size(GetWidthBetween(min_width, max_width), image_size.height()); }
diff --git a/chrome/browser/ui/views/location_bar/icon_label_bubble_view.h b/chrome/browser/ui/views/location_bar/icon_label_bubble_view.h index 3ce698e2..9762422 100644 --- a/chrome/browser/ui/views/location_bar/icon_label_bubble_view.h +++ b/chrome/browser/ui/views/location_bar/icon_label_bubble_view.h
@@ -23,6 +23,7 @@ #include "ui/views/animation/ink_drop_observer.h" #include "ui/views/controls/button/label_button.h" #include "ui/views/controls/label.h" +#include "ui/views/layout/flex_layout_types.h" #include "ui/views/widget/widget_observer.h" namespace gfx { @@ -119,6 +120,10 @@ void SetLabel(std::u16string_view label, std::u16string_view accessible_name); void SetFontList(const gfx::FontList& font_list); + // Applies additional padding around the icon and label whenever the label + // is visible and not collapsed. + void SetExpandedLabelAdditionalInsets(const views::Inset1D& insets); + gfx::RoundedCornersF GetCornerRadii() const; void SetCornerRadii(const gfx::RoundedCornersF& radii); @@ -335,6 +340,9 @@ std::optional<ui::ColorId> foreground_color_id_; std::optional<gfx::RoundedCornersF> radii_; + + // Padding that should be applied when the label is expanded. + views::Inset1D expanded_label_additional_insets_; }; #endif // CHROME_BROWSER_UI_VIEWS_LOCATION_BAR_ICON_LABEL_BUBBLE_VIEW_H_
diff --git a/chrome/browser/ui/views/location_bar/icon_label_bubble_view_unittest.cc b/chrome/browser/ui/views/location_bar/icon_label_bubble_view_unittest.cc index 6a1c892..5dd974b2 100644 --- a/chrome/browser/ui/views/location_bar/icon_label_bubble_view_unittest.cc +++ b/chrome/browser/ui/views/location_bar/icon_label_bubble_view_unittest.cc
@@ -21,11 +21,14 @@ #include "ui/events/base_event_utils.h" #include "ui/events/gesture_detection/gesture_configuration.h" #include "ui/events/test/event_generator.h" +#include "ui/gfx/geometry/rect.h" #include "ui/gfx/image/image_unittest_util.h" #include "ui/views/animation/ink_drop.h" #include "ui/views/animation/test/ink_drop_host_test_api.h" #include "ui/views/animation/test/test_ink_drop.h" +#include "ui/views/border.h" #include "ui/views/controls/image_view.h" +#include "ui/views/layout/flex_layout_types.h" #include "ui/views/widget/widget_utils.h" #if defined(USE_AURA) @@ -622,6 +625,81 @@ EXPECT_EQ(nullptr, view()->GetBackground()); } +// Tests that the client-provided additional padding for labels is correctly +// applied when the label is expanded. +TEST_F(IconLabelBubbleViewTest, AdditionalPaddingForLabelAppliedWhenExpanded) { + view()->SetUpAnimation(); + view()->ResetSlideAnimation(true); + ASSERT_TRUE(view()->IsLabelVisible()); + view()->SizeToPreferredSize(); + EXPECT_EQ(view()->GetInsets().left(), view()->GetImageContainerView()->x()); + + const int initial_width = view()->width(); + const int initial_image_x = view()->GetImageContainerView()->x(); + const int initial_label_x = view()->GetLabelBounds().x(); + const int initial_label_right = view()->GetLabelBounds().right(); + + // Set the additional insets for labels. + constexpr int kExpandedLabelLeftPadding = 1; + constexpr int kExpandedLabelRightPadding = 2; + view()->SetExpandedLabelAdditionalInsets( + views::Inset1D(kExpandedLabelLeftPadding, kExpandedLabelRightPadding)); + view()->SizeToPreferredSize(); + + // The container and label should be shifted to the right by the additional + // left padding. + EXPECT_EQ(initial_image_x + kExpandedLabelLeftPadding, + view()->GetImageContainerView()->x()); + EXPECT_EQ(initial_label_x + kExpandedLabelLeftPadding, + view()->GetLabelBounds().x()); + + // The total width should also include the left and right values of the + // additional padding. + EXPECT_EQ(initial_label_right + kExpandedLabelLeftPadding + + kExpandedLabelRightPadding, + view()->GetLabelBounds().right()); + EXPECT_EQ( + initial_width + kExpandedLabelLeftPadding + kExpandedLabelRightPadding, + view()->width()); + + // Collapse the label. The additional padding should not be applied anymore. + view()->SetBounds(0, 0, initial_width - view()->GetLabelBounds().width(), + view()->height()); + EXPECT_EQ(0, view()->GetLabelBounds().width()); + EXPECT_EQ(initial_image_x, view()->GetImageContainerView()->x()); +} + +// Tests that the client-provided additional padding for labels is not +// applied when the label is not shown. +TEST_F(IconLabelBubbleViewTest, AdditionalPaddingRespectsLabelVisibility) { + view()->SetUpAnimation(); + view()->ResetSlideAnimation(false); + view()->SizeToPreferredSize(); + ASSERT_FALSE(view()->IsLabelVisible()); + + const int initial_width = view()->width(); + const int initial_image_x = view()->GetImageContainerView()->x(); + + // Set the additional insets for labels. These shouldn't be applied until + // the label is shown. + constexpr int kExpandedLabelLeftPadding = 1; + constexpr int kExpandedLabelRightPadding = 2; + view()->SetExpandedLabelAdditionalInsets( + views::Inset1D(kExpandedLabelLeftPadding, kExpandedLabelRightPadding)); + view()->SizeToPreferredSize(); + + // Nothing should have changed. + EXPECT_EQ(initial_image_x, view()->GetImageContainerView()->x()); + EXPECT_EQ(initial_width, view()->width()); + + // Show the label. The additional padding should be applied. + view()->ResetSlideAnimation(true); + view()->SizeToPreferredSize(); + ASSERT_TRUE(view()->IsLabelVisible()); + EXPECT_EQ(initial_image_x + kExpandedLabelLeftPadding, + view()->GetImageContainerView()->x()); +} + #if defined(USE_AURA) // Verifies IconLabelBubbleView::CalculatePreferredSize() doesn't crash when // there is a widget but no compositor. @@ -645,29 +723,28 @@ } #endif -// This view facilitates checking each of its calculated widths, used +// This view facilitates checking view state during animation steps, used // for regression testing crbug.com/401231035. -class TestIconLabelBubbleViewWidthChecker : public TestIconLabelBubbleView { +class TestIconLabelBubbleViewAnimationChecker : public TestIconLabelBubbleView { public: using TestIconLabelBubbleView::TestIconLabelBubbleView; - void SetWidthCheckCallback(base::RepeatingCallback<void(int)> cb) { - width_check_cb_ = std::move(cb); + void SetAnimationProgressedCallback( + base::RepeatingCallback<void(views::View*)> callback) { + animation_progress_callback_ = std::move(callback); } private: - int GetWidthBetween(int min, int max) const override { - int result = IconLabelBubbleView::GetWidthBetween(min, max); - if (width_check_cb_) { - width_check_cb_.Run(result); + void AnimationProgressed(const gfx::Animation* animation) override { + IconLabelBubbleView::AnimationProgressed(animation); + if (animation_progress_callback_) { + animation_progress_callback_.Run(this); } - return result; } - - base::RepeatingCallback<void(int)> width_check_cb_; + base::RepeatingCallback<void(views::View*)> animation_progress_callback_; }; -class IconLabelBubbleViewWidthTest : public IconLabelBubbleViewTestBase { +class IconLabelBubbleViewAnimationTest : public IconLabelBubbleViewTestBase { public: using IconLabelBubbleViewTestBase::IconLabelBubbleViewTestBase; @@ -678,7 +755,8 @@ widget_ = CreateTestWidget(views::Widget::InitParams::CLIENT_OWNS_WIDGET); view_ = widget_->SetContentsView( - std::make_unique<TestIconLabelBubbleViewWidthChecker>(font_list, this)); + std::make_unique<TestIconLabelBubbleViewAnimationChecker>(font_list, + this)); widget_->Show(); } @@ -688,16 +766,16 @@ ChromeViewsTestBase::TearDown(); } - TestIconLabelBubbleViewWidthChecker* view() { return view_; } + TestIconLabelBubbleViewAnimationChecker* view() { return view_; } private: std::unique_ptr<views::Widget> widget_; - raw_ptr<TestIconLabelBubbleViewWidthChecker> view_; + raw_ptr<TestIconLabelBubbleViewAnimationChecker> view_; }; // Regression test for crbug.com/401231035, where AnimateOut would flicker at // the beginning of the animation. -TEST_F(IconLabelBubbleViewWidthTest, WidthDecreasesDuringAnimateOut) { +TEST_F(IconLabelBubbleViewAnimationTest, WidthDecreasesDuringAnimateOut) { gfx::Animation::SetPrefersReducedMotionForTesting(false); ASSERT_FALSE(gfx::Animation::PrefersReducedMotion()); @@ -709,9 +787,10 @@ int last_width = view()->GetPreferredSize().width(); int animation_step_count = 0; - view()->SetWidthCheckCallback(base::BindRepeating( - [](int& last_width, int& animation_step_count, int width) { - EXPECT_LE(width, last_width) + view()->SetAnimationProgressedCallback(base::BindRepeating( + [](int& last_width, int& animation_step_count, views::View* view) { + const int width = view->GetPreferredSize().width(); + EXPECT_LT(width, last_width) << "Failed on animation step #" << animation_step_count; last_width = width; ++animation_step_count;
diff --git a/chrome/browser/ui/views/omnibox/omnibox_match_cell_view.cc b/chrome/browser/ui/views/omnibox/omnibox_match_cell_view.cc index cd657b87..439f1880 100644 --- a/chrome/browser/ui/views/omnibox/omnibox_match_cell_view.cc +++ b/chrome/browser/ui/views/omnibox/omnibox_match_cell_view.cc
@@ -249,9 +249,6 @@ const AutocompleteMatch& match) { if (ShouldDisplayImage(match)) { // Enterprise search aggregator people suggestions may display an image. - CHECK(AutocompleteMatch::IsSearchType(match.type) || - match.provider->type() == - AutocompleteProvider::TYPE_ENTERPRISE_SEARCH_AGGREGATOR); layout_style_ = LayoutStyle::SEARCH_SUGGESTION_WITH_IMAGE; } else if (AutocompleteMatch::IsSearchType(match.type)) { layout_style_ = LayoutStyle::SEARCH_SUGGESTION;
diff --git a/chrome/browser/ui/views/overlay/video_overlay_window_views.h b/chrome/browser/ui/views/overlay/video_overlay_window_views.h index 2bd1a297..0034e5bc 100644 --- a/chrome/browser/ui/views/overlay/video_overlay_window_views.h +++ b/chrome/browser/ui/views/overlay/video_overlay_window_views.h
@@ -11,7 +11,6 @@ #include "base/memory/weak_ptr.h" #include "base/timer/timer.h" #include "chrome/browser/picture_in_picture/auto_pip_setting_overlay_view.h" -#include "chromeos/ui/frame/highlight_border_overlay.h" #include "components/global_media_controls/public/views/media_progress_view.h" #include "content/public/browser/overlay_window.h" #include "content/public/browser/video_picture_in_picture_window_controller.h" @@ -21,6 +20,10 @@ #include "ui/views/view_observer.h" #include "ui/views/widget/widget.h" +#if BUILDFLAG(IS_CHROMEOS) +#include "chromeos/ui/frame/highlight_border_overlay.h" +#endif + namespace views { class ImageView; class Label;
diff --git a/chrome/browser/ui/webui/omnibox/omnibox_page_handler.cc b/chrome/browser/ui/webui/omnibox/omnibox_page_handler.cc index bbed140..781b4237 100644 --- a/chrome/browser/ui/webui/omnibox/omnibox_page_handler.cc +++ b/chrome/browser/ui/webui/omnibox/omnibox_page_handler.cc
@@ -270,7 +270,12 @@ base::UTF16ToUTF8(input.inline_autocompletion); result->destination_url = input.destination_url.spec(); result->stripped_destination_url = input.stripped_destination_url.spec(); - result->image = input.ImageUrl().spec().c_str(); + + GURL image = input.ImageUrl(); + if (image.is_empty()) + image = input.icon_url; + result->image = image.spec().c_str(); + result->contents = base::UTF16ToUTF8(input.contents); result->contents_class = mojo::ConvertTo<std::vector<mojom::ACMatchClassificationPtr>>(
diff --git a/chrome/browser/web_applications/policy/web_app_policy_manager_unittest.cc b/chrome/browser/web_applications/policy/web_app_policy_manager_unittest.cc index d378d4c6..eb14a415 100644 --- a/chrome/browser/web_applications/policy/web_app_policy_manager_unittest.cc +++ b/chrome/browser/web_applications/policy/web_app_policy_manager_unittest.cc
@@ -30,6 +30,7 @@ #include "chrome/browser/web_applications/mojom/user_display_mode.mojom.h" #include "chrome/browser/web_applications/os_integration/os_integration_manager.h" #include "chrome/browser/web_applications/policy/web_app_policy_constants.h" +#include "chrome/browser/web_applications/proto/web_app_install_state.pb.h" #include "chrome/browser/web_applications/test/fake_externally_managed_app_manager.h" #include "chrome/browser/web_applications/test/fake_web_app_provider.h" #include "chrome/browser/web_applications/test/os_integration_test_override_impl.h" @@ -124,116 +125,32 @@ .Set(kDefaultLaunchContainerKey, kDefaultLaunchContainerWindowValue); } -ExternalInstallOptions GetWindowedInstallOptions( - PlaceholderResolutionBehavior placeholder_resolution_behavior = - PlaceholderResolutionBehavior::kClose) { - ExternalInstallOptions options(GURL(kWindowedUrl), - mojom::UserDisplayMode::kStandalone, - ExternalInstallSource::kExternalPolicy); - options.add_to_applications_menu = true; - options.add_to_desktop = false; - options.add_to_quick_launch_bar = false; - options.install_placeholder = true; - options.placeholder_resolution_behavior = placeholder_resolution_behavior; - return options; -} - base::Value::Dict GetTabbedItem() { return base::Value::Dict() .Set(kUrlKey, kTabbedUrl) .Set(kDefaultLaunchContainerKey, kDefaultLaunchContainerTabValue); } -ExternalInstallOptions GetTabbedInstallOptions( - PlaceholderResolutionBehavior placeholder_resolution_behavior = - PlaceholderResolutionBehavior::kClose) { - ExternalInstallOptions options(GURL(kTabbedUrl), - mojom::UserDisplayMode::kBrowser, - ExternalInstallSource::kExternalPolicy); - options.add_to_applications_menu = true; - options.add_to_desktop = false; - options.add_to_quick_launch_bar = false; - options.install_placeholder = true; - options.placeholder_resolution_behavior = placeholder_resolution_behavior; - return options; -} - base::Value::Dict GetNoContainerItem() { return base::Value::Dict().Set(kUrlKey, kNoContainerUrl); } -ExternalInstallOptions GetNoContainerInstallOptions( - PlaceholderResolutionBehavior placeholder_resolution_behavior = - PlaceholderResolutionBehavior::kClose) { - ExternalInstallOptions options(GURL(kNoContainerUrl), - mojom::UserDisplayMode::kBrowser, - ExternalInstallSource::kExternalPolicy); - options.add_to_applications_menu = true; - options.add_to_desktop = false; - options.add_to_quick_launch_bar = false; - options.install_placeholder = true; - options.placeholder_resolution_behavior = placeholder_resolution_behavior; - return options; -} - base::Value::Dict GetCreateDesktopShortcutDefaultItem() { return base::Value::Dict().Set(kUrlKey, kNoContainerUrl); } -ExternalInstallOptions GetCreateDesktopShortcutDefaultInstallOptions( - PlaceholderResolutionBehavior placeholder_resolution_behavior = - PlaceholderResolutionBehavior::kClose) { - ExternalInstallOptions options(GURL(kNoContainerUrl), - mojom::UserDisplayMode::kBrowser, - ExternalInstallSource::kExternalPolicy); - options.add_to_applications_menu = true; - options.add_to_desktop = false; - options.add_to_quick_launch_bar = false; - options.install_placeholder = true; - options.placeholder_resolution_behavior = placeholder_resolution_behavior; - return options; -} - base::Value::Dict GetCreateDesktopShortcutFalseItem() { return base::Value::Dict() .Set(kUrlKey, kNoContainerUrl) .Set(kCreateDesktopShortcutKey, false); } -ExternalInstallOptions GetCreateDesktopShortcutFalseInstallOptions( - PlaceholderResolutionBehavior placeholder_resolution_behavior = - PlaceholderResolutionBehavior::kClose) { - ExternalInstallOptions options(GURL(kNoContainerUrl), - mojom::UserDisplayMode::kBrowser, - ExternalInstallSource::kExternalPolicy); - options.add_to_applications_menu = true; - options.add_to_desktop = false; - options.add_to_quick_launch_bar = false; - options.install_placeholder = true; - options.placeholder_resolution_behavior = placeholder_resolution_behavior; - return options; -} - base::Value::Dict GetCreateDesktopShortcutTrueItem() { return base::Value::Dict() .Set(kUrlKey, kNoContainerUrl) .Set(kCreateDesktopShortcutKey, true); } -ExternalInstallOptions GetCreateDesktopShortcutTrueInstallOptions( - PlaceholderResolutionBehavior placeholder_resolution_behavior = - PlaceholderResolutionBehavior::kClose) { - ExternalInstallOptions options(GURL(kNoContainerUrl), - mojom::UserDisplayMode::kBrowser, - ExternalInstallSource::kExternalPolicy); - options.add_to_applications_menu = true; - options.add_to_desktop = true; - options.add_to_quick_launch_bar = false; - options.install_placeholder = true; - options.placeholder_resolution_behavior = placeholder_resolution_behavior; - return options; -} - class MockAppRegistrarObserver : public WebAppRegistrarObserver { public: void OnWebAppSettingsPolicyChanged() override { @@ -258,21 +175,6 @@ .Set(kFallbackAppNameKey, kDefaultFallbackAppName); } -ExternalInstallOptions GetFallbackAppNameInstallOptions( - PlaceholderResolutionBehavior placeholder_resolution_behavior = - PlaceholderResolutionBehavior::kClose) { - ExternalInstallOptions options(GURL(kWindowedUrl), - mojom::UserDisplayMode::kStandalone, - ExternalInstallSource::kExternalPolicy); - options.add_to_applications_menu = true; - options.add_to_desktop = false; - options.add_to_quick_launch_bar = false; - options.install_placeholder = true; - options.placeholder_resolution_behavior = placeholder_resolution_behavior; - options.fallback_app_name = kDefaultFallbackAppName; - return options; -} - base::Value::Dict GetCustomAppNameItem(std::string name) { return base::Value::Dict() .Set(kUrlKey, kWindowedUrl) @@ -280,22 +182,6 @@ .Set(kCustomNameKey, std::move(name)); } -ExternalInstallOptions GetCustomAppNameInstallOptions( - std::string name, - PlaceholderResolutionBehavior placeholder_resolution_behavior = - PlaceholderResolutionBehavior::kClose) { - ExternalInstallOptions options(GURL(kWindowedUrl), - mojom::UserDisplayMode::kStandalone, - ExternalInstallSource::kExternalPolicy); - options.add_to_applications_menu = true; - options.add_to_desktop = false; - options.add_to_quick_launch_bar = false; - options.install_placeholder = true; - options.placeholder_resolution_behavior = placeholder_resolution_behavior; - options.override_name = std::move(name); - return options; -} - base::Value::Dict GetCustomAppIconItem(bool secure = true) { return base::Value::Dict() .Set(kUrlKey, kWindowedUrl) @@ -307,21 +193,6 @@ .Set(kCustomIconHashKey, kDefaultCustomIconHash)); } -ExternalInstallOptions GetCustomAppIconInstallOptions( - PlaceholderResolutionBehavior placeholder_resolution_behavior = - PlaceholderResolutionBehavior::kClose) { - ExternalInstallOptions options(GURL(kWindowedUrl), - mojom::UserDisplayMode::kStandalone, - ExternalInstallSource::kExternalPolicy); - options.add_to_applications_menu = true; - options.add_to_desktop = false; - options.add_to_quick_launch_bar = false; - options.install_placeholder = true; - options.placeholder_resolution_behavior = placeholder_resolution_behavior; - options.override_icon_url = GURL(kDefaultCustomIconUrl); - return options; -} - void SetWebAppSettingsListPref(Profile* profile, std::string_view pref) { ASSERT_OK_AND_ASSIGN( auto result, @@ -403,6 +274,11 @@ std::unique_ptr<WebApp> web_app = test::CreateWebApp( install_url, ConvertExternalInstallSourceToSource(install_source)); + if (install_options.fallback_app_name) { + web_app->SetName(install_options.fallback_app_name.value()); + } + // Since `override_name` gets precendence over + // `fallback_app_name`, this will override the `fallback`. if (install_options.override_name) { web_app->SetName(install_options.override_name.value()); } @@ -522,6 +398,21 @@ loop.Run(); } + const WebApp* GetPolicyInstalledWindowedApp() { + return app_registrar().LookUpAppByInstallSourceInstallUrl( + WebAppManagement::kPolicy, GURL(kWindowedUrl)); + } + + const WebApp* GetPolicyInstalledTabbedApp() { + return app_registrar().LookUpAppByInstallSourceInstallUrl( + WebAppManagement::kPolicy, GURL(kTabbedUrl)); + } + + const WebApp* GetPolicyInstalledNoContainerApp() { + return app_registrar().LookUpAppByInstallSourceInstallUrl( + WebAppManagement::kPolicy, GURL(kNoContainerUrl)); + } + private: webapps::InstallResultCode install_result_code_ = webapps::InstallResultCode::kSuccessNewInstall; @@ -553,9 +444,6 @@ }; TEST_F(WebAppPolicyManagerTest, NoPrefValues) { - const auto& install_requests = - externally_managed_app_manager().install_requests(); - EXPECT_TRUE(install_requests.empty()); ValidateEmptyWebAppSettingsPolicy(); } @@ -564,10 +452,7 @@ base::Value::List()); WaitForAppsToSynchronize(); - - const auto& install_requests = - externally_managed_app_manager().install_requests(); - EXPECT_TRUE(install_requests.empty()); + ASSERT_TRUE(app_registrar().is_empty()); } TEST_F(WebAppPolicyManagerTest, NoWebAppSettings) { @@ -688,14 +573,8 @@ WaitForAppsToSynchronize(); - const auto& install_requests = - externally_managed_app_manager().install_requests(); - - std::vector<ExternalInstallOptions> expected_install_options_list; - expected_install_options_list.push_back(GetWindowedInstallOptions()); - expected_install_options_list.push_back(GetTabbedInstallOptions()); - - EXPECT_EQ(install_requests, expected_install_options_list); + EXPECT_NE(GetPolicyInstalledWindowedApp(), nullptr); + EXPECT_NE(GetPolicyInstalledTabbedApp(), nullptr); } TEST_F(WebAppPolicyManagerTest, ForceInstallAppWithNoDefaultLaunchContainer) { @@ -706,13 +585,7 @@ WaitForAppsToSynchronize(); - const auto& install_requests = - externally_managed_app_manager().install_requests(); - - std::vector<ExternalInstallOptions> expected_install_options_list; - expected_install_options_list.push_back(GetNoContainerInstallOptions()); - - EXPECT_EQ(install_requests, expected_install_options_list); + EXPECT_NE(GetPolicyInstalledNoContainerApp(), nullptr); } TEST_F(WebAppPolicyManagerTest, @@ -724,14 +597,10 @@ WaitForAppsToSynchronize(); - const auto& install_requests = - externally_managed_app_manager().install_requests(); - - std::vector<ExternalInstallOptions> expected_install_options_list; - expected_install_options_list.push_back( - GetCreateDesktopShortcutDefaultInstallOptions()); - - EXPECT_EQ(install_requests, expected_install_options_list); + EXPECT_NE(GetPolicyInstalledNoContainerApp(), nullptr); + EXPECT_EQ(proto::INSTALLED_WITHOUT_OS_INTEGRATION, + app_registrar().GetInstallState( + GetPolicyInstalledNoContainerApp()->app_id())); } TEST_F(WebAppPolicyManagerTest, ForceInstallAppWithCreateDesktopShortcut) { @@ -743,16 +612,10 @@ WaitForAppsToSynchronize(); - const auto& install_requests = - externally_managed_app_manager().install_requests(); - - std::vector<ExternalInstallOptions> expected_install_options_list; - expected_install_options_list.push_back( - GetCreateDesktopShortcutFalseInstallOptions()); - expected_install_options_list.push_back( - GetCreateDesktopShortcutTrueInstallOptions()); - - EXPECT_EQ(install_requests, expected_install_options_list); + EXPECT_NE(GetPolicyInstalledNoContainerApp(), nullptr); + EXPECT_EQ(proto::INSTALLED_WITHOUT_OS_INTEGRATION, + app_registrar().GetInstallState( + GetPolicyInstalledNoContainerApp()->app_id())); } TEST_F(WebAppPolicyManagerTest, ForceInstallAppWithFallbackAppName) { @@ -763,13 +626,10 @@ WaitForAppsToSynchronize(); - const auto& install_requests = - externally_managed_app_manager().install_requests(); - - std::vector<ExternalInstallOptions> expected_install_options_list; - expected_install_options_list.push_back(GetFallbackAppNameInstallOptions()); - - EXPECT_EQ(install_requests, expected_install_options_list); + EXPECT_NE(GetPolicyInstalledWindowedApp(), nullptr); + EXPECT_EQ(kDefaultFallbackAppName, + app_registrar().GetAppShortName( + GetPolicyInstalledWindowedApp()->app_id())); } TEST_F(WebAppPolicyManagerTest, ForceInstallAppWithCustomAppIcon) { @@ -779,14 +639,7 @@ std::move(list)); WaitForAppsToSynchronize(); - - const auto& install_requests = - externally_managed_app_manager().install_requests(); - - std::vector<ExternalInstallOptions> expected_install_options_list; - expected_install_options_list.push_back(GetCustomAppIconInstallOptions()); - - EXPECT_EQ(install_requests, expected_install_options_list); + EXPECT_NE(GetPolicyInstalledWindowedApp(), nullptr); } // If the custom icon URL is not https, the icon should be ignored. @@ -798,14 +651,10 @@ WaitForAppsToSynchronize(); - const auto& install_requests = - externally_managed_app_manager().install_requests(); - - std::vector<ExternalInstallOptions> expected_install_options_list; - expected_install_options_list.push_back(GetCustomAppIconInstallOptions()); - - EXPECT_EQ(1u, install_requests.size()); - EXPECT_FALSE(install_requests[0].override_icon_url); + EXPECT_NE(GetPolicyInstalledWindowedApp(), nullptr); + const webapps::AppId& app_id = GetPolicyInstalledWindowedApp()->app_id(); + auto icon_infos = app_registrar().GetAppIconInfos(app_id); + EXPECT_EQ(0u, icon_infos.size()); } TEST_F(WebAppPolicyManagerTest, ForceInstallAppWithCustomAppName) { @@ -816,14 +665,10 @@ WaitForAppsToSynchronize(); - const auto& install_requests = - externally_managed_app_manager().install_requests(); - - std::vector<ExternalInstallOptions> expected_install_options_list; - expected_install_options_list.push_back( - GetCustomAppNameInstallOptions(kDefaultCustomAppName)); - - EXPECT_EQ(install_requests, expected_install_options_list); + EXPECT_NE(GetPolicyInstalledWindowedApp(), nullptr); + EXPECT_EQ(kDefaultCustomAppName, + app_registrar().GetAppShortName( + GetPolicyInstalledWindowedApp()->app_id())); } TEST_F(WebAppPolicyManagerTest, ForceInstallAppWithCustomAppNameRefresh) { @@ -837,6 +682,12 @@ std::move(list)); } WaitForAppsToSynchronize(); + + EXPECT_NE(GetPolicyInstalledWindowedApp(), nullptr); + EXPECT_EQ(kDefaultCustomAppName, + app_registrar().GetAppShortName( + GetPolicyInstalledWindowedApp()->app_id())); + // Change custom name { base::Value::List list; @@ -846,20 +697,6 @@ } WaitForAppsToSynchronize(); - const auto& install_requests = - externally_managed_app_manager().install_requests(); - - // App should have been installed twice, with a force-install the second time. - std::vector<ExternalInstallOptions> expected_install_options_list; - expected_install_options_list.push_back( - GetCustomAppNameInstallOptions(kDefaultCustomAppName)); - auto options = - GetCustomAppNameInstallOptions(kPrefix + kDefaultCustomAppName); - options.force_reinstall = true; - expected_install_options_list.push_back(options); - - EXPECT_EQ(install_requests, expected_install_options_list); - base::flat_map<webapps::AppId, base::flat_set<GURL>> apps = app_registrar().GetExternallyInstalledApps( ExternalInstallSource::kExternalPolicy); @@ -875,14 +712,7 @@ std::move(first_list)); WaitForAppsToSynchronize(); - - const auto& install_requests = - externally_managed_app_manager().install_requests(); - - std::vector<ExternalInstallOptions> expected_install_options_list; - expected_install_options_list.push_back(GetWindowedInstallOptions()); - - EXPECT_EQ(install_requests, expected_install_options_list); + EXPECT_NE(GetPolicyInstalledWindowedApp(), nullptr); base::Value::List second_list; second_list.Append(GetTabbedItem()); @@ -891,9 +721,7 @@ WaitForAppsToSynchronize(); - expected_install_options_list.push_back(GetTabbedInstallOptions()); - - EXPECT_EQ(install_requests, expected_install_options_list); + EXPECT_NE(GetPolicyInstalledTabbedApp(), nullptr); } TEST_F(WebAppPolicyManagerTest, UninstallAppInstalledInPreviousSession) { @@ -914,15 +742,11 @@ WaitForAppsToSynchronize(); - // We should only try to install the app in the policy. - std::vector<ExternalInstallOptions> expected_install_options_list; - expected_install_options_list.push_back(GetWindowedInstallOptions()); - EXPECT_EQ(externally_managed_app_manager().install_requests(), - expected_install_options_list); - - // We should try to uninstall the app that is no longer in the policy. - EXPECT_EQ(std::vector<GURL>({GURL(kTabbedUrl)}), - externally_managed_app_manager().uninstall_requests()); + // Verify only the app corresponding to `kWindowedUrl` is installed, + // everything else is removed. + EXPECT_NE(GetPolicyInstalledWindowedApp(), nullptr); + EXPECT_EQ(GetPolicyInstalledNoContainerApp(), nullptr); + EXPECT_EQ(GetPolicyInstalledTabbedApp(), nullptr); } // Tests that we correctly uninstall an app that we installed in the same @@ -936,14 +760,8 @@ std::move(first_list)); WaitForAppsToSynchronize(); - const auto& install_requests = - externally_managed_app_manager().install_requests(); - - std::vector<ExternalInstallOptions> expected_install_options_list; - expected_install_options_list.push_back(GetWindowedInstallOptions()); - expected_install_options_list.push_back(GetTabbedInstallOptions()); - - EXPECT_EQ(install_requests, expected_install_options_list); + EXPECT_NE(GetPolicyInstalledWindowedApp(), nullptr); + EXPECT_NE(GetPolicyInstalledTabbedApp(), nullptr); // Push a new policy without the tabbed site. base::Value::List second_list; @@ -952,14 +770,9 @@ std::move(second_list)); WaitForAppsToSynchronize(); - // We'll try to install the app again but ExternallyManagedAppManager will - // handle not re-installing the app. - expected_install_options_list.push_back(GetWindowedInstallOptions()); - - EXPECT_EQ(install_requests, expected_install_options_list); - - EXPECT_EQ(std::vector<GURL>({GURL(kTabbedUrl)}), - externally_managed_app_manager().uninstall_requests()); + // GetPolicyInstalledTabbedApp() will be deleted. + EXPECT_NE(GetPolicyInstalledWindowedApp(), nullptr); + EXPECT_EQ(GetPolicyInstalledTabbedApp(), nullptr); } // Tests that we correctly reinstall a placeholder app. @@ -970,13 +783,10 @@ std::move(list)); WaitForAppsToSynchronize(); - - std::vector<ExternalInstallOptions> expected_options_list; - expected_options_list.push_back(GetWindowedInstallOptions()); - - const auto& install_options_list = - externally_managed_app_manager().install_requests(); - EXPECT_EQ(expected_options_list, install_options_list); + ASSERT_NE(GetPolicyInstalledWindowedApp(), nullptr); + const webapps::AppId& app_id = GetPolicyInstalledWindowedApp()->app_id(); + EXPECT_FALSE( + app_registrar().IsPlaceholderApp(app_id, WebAppManagement::kPolicy)); MakeInstalledAppPlaceholder(GURL(kWindowedUrl)); base::test::TestFuture<const GURL&, @@ -987,13 +797,9 @@ EXPECT_EQ(future.Get<1>().code, webapps::InstallResultCode::kSuccessNewInstall); - auto reinstall_options = GetWindowedInstallOptions( - /*placeholder_resolution_behavior=*/PlaceholderResolutionBehavior:: - kWaitForAppWindowsClosed); - reinstall_options.install_placeholder = false; - expected_options_list.push_back(std::move(reinstall_options)); - - EXPECT_EQ(expected_options_list, install_options_list); + EXPECT_NE(GetPolicyInstalledWindowedApp(), nullptr); + EXPECT_TRUE( + app_registrar().IsPlaceholderApp(app_id, WebAppManagement::kPolicy)); } TEST_F(WebAppPolicyManagerTest, DoNotReinstallIfNotPlaceholder) { @@ -1004,12 +810,10 @@ WaitForAppsToSynchronize(); - std::vector<ExternalInstallOptions> expected_options_list; - expected_options_list.push_back(GetWindowedInstallOptions()); - - const auto& install_options_list = - externally_managed_app_manager().install_requests(); - EXPECT_EQ(expected_options_list, install_options_list); + ASSERT_NE(GetPolicyInstalledWindowedApp(), nullptr); + const webapps::AppId& app_id = GetPolicyInstalledWindowedApp()->app_id(); + EXPECT_FALSE( + app_registrar().IsPlaceholderApp(app_id, WebAppManagement::kPolicy)); // By default, the app being installed is not a placeholder app. base::test::TestFuture<const GURL&, @@ -1020,9 +824,10 @@ EXPECT_EQ(future.Get<1>().code, webapps::InstallResultCode::kFailedPlaceholderUninstall); - // No other options are added to list as the app is currently not - // installed as a placeholder app. - EXPECT_EQ(expected_options_list, install_options_list); + // App is still currently not installed as a placeholder app. + EXPECT_NE(GetPolicyInstalledWindowedApp(), nullptr); + EXPECT_FALSE( + app_registrar().IsPlaceholderApp(app_id, WebAppManagement::kPolicy)); } // Tests that we correctly reinstall a placeholder app when the placeholder @@ -1035,12 +840,11 @@ WaitForAppsToSynchronize(); - std::vector<ExternalInstallOptions> expected_options_list; - expected_options_list.push_back(GetFallbackAppNameInstallOptions()); - - const auto& install_options_list = - externally_managed_app_manager().install_requests(); - EXPECT_EQ(expected_options_list, install_options_list); + ASSERT_NE(GetPolicyInstalledWindowedApp(), nullptr); + const webapps::AppId& app_id = GetPolicyInstalledWindowedApp()->app_id(); + EXPECT_FALSE( + app_registrar().IsPlaceholderApp(app_id, WebAppManagement::kPolicy)); + EXPECT_EQ(kDefaultFallbackAppName, app_registrar().GetAppShortName(app_id)); MakeInstalledAppPlaceholder(GURL(kWindowedUrl)); base::test::TestFuture<const GURL&, @@ -1051,12 +855,9 @@ EXPECT_EQ(future.Get<1>().code, webapps::InstallResultCode::kSuccessNewInstall); - auto reinstall_options = GetFallbackAppNameInstallOptions( - PlaceholderResolutionBehavior::kWaitForAppWindowsClosed); - reinstall_options.install_placeholder = false; - expected_options_list.push_back(std::move(reinstall_options)); - - EXPECT_EQ(expected_options_list, install_options_list); + EXPECT_TRUE( + app_registrar().IsPlaceholderApp(app_id, WebAppManagement::kPolicy)); + EXPECT_EQ(kDefaultFallbackAppName, app_registrar().GetAppShortName(app_id)); } TEST_F(WebAppPolicyManagerTest, TryToInexistentPlaceholderApp) { @@ -1067,12 +868,9 @@ WaitForAppsToSynchronize(); - std::vector<ExternalInstallOptions> expected_options_list; - expected_options_list.push_back(GetWindowedInstallOptions()); - - const auto& install_options_list = - externally_managed_app_manager().install_requests(); - EXPECT_EQ(expected_options_list, install_options_list); + ASSERT_NE(GetPolicyInstalledWindowedApp(), nullptr); + EXPECT_FALSE(app_registrar().IsPlaceholderApp( + GetPolicyInstalledWindowedApp()->app_id(), WebAppManagement::kPolicy)); base::test::TestFuture<const GURL&, ExternallyManagedAppManager::InstallResult> @@ -1083,7 +881,7 @@ EXPECT_EQ(future.Get<1>().code, webapps::InstallResultCode::kFailedPlaceholderUninstall); - EXPECT_EQ(expected_options_list, install_options_list); + ASSERT_EQ(GetPolicyInstalledTabbedApp(), nullptr); } TEST_F(WebAppPolicyManagerTest, SayRefreshTwoTimesQuickly) { @@ -1106,18 +904,7 @@ WaitForAppsToSynchronize(); WaitForAppsToSynchronize(); - // Both apps should have been installed. - std::vector<ExternalInstallOptions> expected_options_list; - expected_options_list.push_back(GetWindowedInstallOptions()); - expected_options_list.push_back(GetTabbedInstallOptions()); - - const auto& install_options_list = - externally_managed_app_manager().install_requests(); - EXPECT_EQ(expected_options_list, install_options_list); - EXPECT_EQ(std::vector<GURL>({GURL(kWindowedUrl)}), - externally_managed_app_manager().uninstall_requests()); - - // There should be exactly 1 app remaining. + // There should be exactly 1 app remaining at the end for the `tabbed` item. base::flat_map<webapps::AppId, base::flat_set<GURL>> apps = app_registrar().GetExternallyInstalledApps( ExternalInstallSource::kExternalPolicy); @@ -1178,9 +965,8 @@ WaitForAppsToSynchronize(); - const auto& install_requests = - externally_managed_app_manager().install_requests(); - EXPECT_EQ(0u, install_requests.size()); + // No apps are installed. + ASSERT_TRUE(app_registrar().is_empty()); } #if BUILDFLAG(IS_CHROMEOS) @@ -1287,14 +1073,8 @@ std::move(list)); WaitForAppsToSynchronize(); - const auto& install_requests = - externally_managed_app_manager().install_requests(); - - std::vector<ExternalInstallOptions> expected_install_options_list; - expected_install_options_list.push_back(GetWindowedInstallOptions()); - expected_install_options_list.push_back(GetTabbedInstallOptions()); - - EXPECT_EQ(install_requests, expected_install_options_list); + EXPECT_NE(GetPolicyInstalledWindowedApp(), nullptr); + EXPECT_NE(GetPolicyInstalledTabbedApp(), nullptr); EXPECT_EQ(GetUrlRunOnOsLoginPolicy(kWindowedUrl), RunOnOsLoginPolicy::kAllowed); @@ -1347,18 +1127,11 @@ base::Value::List().Append(GetWindowedItem()).Append(GetTabbedItem())); loop.Run(); } - // WaitForAppsToSynchronize(); provider()->command_manager().AwaitAllCommandsCompleteForTesting(); - const auto& install_requests = - externally_managed_app_manager().install_requests(); - - std::vector<ExternalInstallOptions> expected_install_options_list; - expected_install_options_list.push_back(GetWindowedInstallOptions()); - expected_install_options_list.push_back(GetTabbedInstallOptions()); - - EXPECT_EQ(install_requests, expected_install_options_list); + EXPECT_NE(GetPolicyInstalledWindowedApp(), nullptr); + EXPECT_NE(GetPolicyInstalledTabbedApp(), nullptr); EXPECT_EQ(2, mock_observer.GetOnWebAppSettingsPolicyChangedCalledCount()); app_registrar().RemoveObserver(&mock_observer); }
diff --git a/chrome/browser/web_applications/test/fake_externally_managed_app_manager.cc b/chrome/browser/web_applications/test/fake_externally_managed_app_manager.cc index 9c34d4c3..6e92550 100644 --- a/chrome/browser/web_applications/test/fake_externally_managed_app_manager.cc +++ b/chrome/browser/web_applications/test/fake_externally_managed_app_manager.cc
@@ -56,7 +56,6 @@ std::vector<GURL> uninstall_urls, ExternalInstallSource install_source, const UninstallCallback& callback) { - std::ranges::copy(uninstall_urls, std::back_inserter(uninstall_requests_)); if (handle_uninstall_request_callback_) { for (auto& app_url : uninstall_urls) { base::SequencedTaskRunner::GetCurrentDefault()
diff --git a/chrome/browser/web_applications/test/fake_externally_managed_app_manager.h b/chrome/browser/web_applications/test/fake_externally_managed_app_manager.h index 075e8a8..dbd654eb 100644 --- a/chrome/browser/web_applications/test/fake_externally_managed_app_manager.h +++ b/chrome/browser/web_applications/test/fake_externally_managed_app_manager.h
@@ -30,9 +30,6 @@ const std::vector<ExternalInstallOptions>& install_requests() const { return install_requests_; } - const std::vector<GURL>& uninstall_requests() const { - return uninstall_requests_; - } void SetDropRequestsForTesting(bool drop_requests_for_testing) { drop_requests_for_testing_ = drop_requests_for_testing; @@ -58,7 +55,6 @@ private: std::vector<ExternalInstallOptions> install_requests_; - std::vector<GURL> uninstall_requests_; bool drop_requests_for_testing_ = false; HandleInstallRequestCallback handle_install_request_callback_; HandleUninstallRequestCallback handle_uninstall_request_callback_;
diff --git a/chrome/build/android-arm64.pgo.txt b/chrome/build/android-arm64.pgo.txt index fa08221..7c965598 100644 --- a/chrome/build/android-arm64.pgo.txt +++ b/chrome/build/android-arm64.pgo.txt
@@ -1 +1 @@ -chrome-android64-main-1743612696-a6233e2c17a9191a0908cca5b88020b34767c1fa-03db902005f7450fd40b6935eb5034e6291926d3.profdata +chrome-android64-main-1743630746-edd340869be9643cbbcd15b49db23717448ca94e-262b719445890ba9222827f07a1815dd4559d4f3.profdata
diff --git a/chrome/build/mac-arm.pgo.txt b/chrome/build/mac-arm.pgo.txt index 2b546f4..1826d90 100644 --- a/chrome/build/mac-arm.pgo.txt +++ b/chrome/build/mac-arm.pgo.txt
@@ -1 +1 @@ -chrome-mac-arm-main-1743616772-33794e3c7e056c516f71432241e1fbf3c1737f03-352a1f4e325c40d92576d3c712839d7d4b68364a.profdata +chrome-mac-arm-main-1743631082-84fe0935cb4e1078ded9f1f7c64e7f7ac726111e-512affd6b7a308ed019ba89dcdac95f92dd57f00.profdata
diff --git a/chrome/build/mac.pgo.txt b/chrome/build/mac.pgo.txt index 5664b45..8099781 100644 --- a/chrome/build/mac.pgo.txt +++ b/chrome/build/mac.pgo.txt
@@ -1 +1 @@ -chrome-mac-main-1743595152-026471aea975f940172aedde0a9c063517ec2c78-02a1e3553feb7b0b624e4a65be2e64d3b2df651e.profdata +chrome-mac-main-1743616772-1503d4a6683266afb5b58ed4ae9fa1927943e323-352a1f4e325c40d92576d3c712839d7d4b68364a.profdata
diff --git a/chrome/build/win-arm64.pgo.txt b/chrome/build/win-arm64.pgo.txt index a956511..71a9612 100644 --- a/chrome/build/win-arm64.pgo.txt +++ b/chrome/build/win-arm64.pgo.txt
@@ -1 +1 @@ -chrome-win-arm64-main-1743595152-f2e151e1f2e71e803704a33f7a10ab5679af8349-02a1e3553feb7b0b624e4a65be2e64d3b2df651e.profdata +chrome-win-arm64-main-1743616772-fb01b97b315bc9c55aa01c36b614d92412b953c0-352a1f4e325c40d92576d3c712839d7d4b68364a.profdata
diff --git a/chrome/build/win32.pgo.txt b/chrome/build/win32.pgo.txt index 13b23274d..96d08818 100644 --- a/chrome/build/win32.pgo.txt +++ b/chrome/build/win32.pgo.txt
@@ -1 +1 @@ -chrome-win32-main-1743595152-52c531b88da1a8df21a6e2078f89905dfc910382-02a1e3553feb7b0b624e4a65be2e64d3b2df651e.profdata +chrome-win32-main-1743605912-6c264f91807c8b76131f7a79820a84de5e6a019d-f1a583c6e39d26bbb02710ce3ccfd253cf812415.profdata
diff --git a/chrome/common/extensions/manifest_handlers/background_scripts_manifest_unittest.cc b/chrome/common/extensions/manifest_handlers/background_scripts_manifest_unittest.cc new file mode 100644 index 0000000..661b979 --- /dev/null +++ b/chrome/common/extensions/manifest_handlers/background_scripts_manifest_unittest.cc
@@ -0,0 +1,28 @@ +// 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/memory/scoped_refptr.h" +#include "base/strings/string_number_conversions.h" +#include "chrome/common/extensions/manifest_tests/chrome_manifest_test.h" +#include "extensions/common/error_utils.h" +#include "extensions/common/extension.h" +#include "extensions/common/manifest_constants.h" +#include "extensions/common/manifest_handlers/background_info.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace extensions { + +namespace errors = manifest_errors; + +using BackgroundScriptsManifestTest = ChromeManifestTest; + +TEST_F(BackgroundScriptsManifestTest, FailLoadingNonJsScripts) { + scoped_refptr<Extension> extension = LoadAndExpectWarning( + "background_bad_mime_type_manifest.json", + ErrorUtils::FormatErrorMessage(errors::kInvalidBackgroundScriptMimeType, + base::NumberToString(0))); + EXPECT_FALSE(BackgroundInfo::HasPersistentBackgroundPage(extension.get())); +} + +} // namespace extensions
diff --git a/chrome/installer/mac/BUILD.gn b/chrome/installer/mac/BUILD.gn index f78a5985..3deac9b 100644 --- a/chrome/installer/mac/BUILD.gn +++ b/chrome/installer/mac/BUILD.gn
@@ -120,13 +120,9 @@ "//chrome/app/theme/google_chrome/mac/document_canary.icns", "//chrome/app/theme/google_chrome/mac/document_dev.icns", "internal/Google_Chrome.765bb3620a0f7a33500da39b20122b1cec41140f.provisionprofile", - "internal/Google_Chrome.e809e0e54407d4f3914b7883b7ed28650316ac2e.provisionprofile", "internal/Google_Chrome_Beta.765bb3620a0f7a33500da39b20122b1cec41140f.provisionprofile", - "internal/Google_Chrome_Beta.e809e0e54407d4f3914b7883b7ed28650316ac2e.provisionprofile", "internal/Google_Chrome_Canary.765bb3620a0f7a33500da39b20122b1cec41140f.provisionprofile", - "internal/Google_Chrome_Canary.e809e0e54407d4f3914b7883b7ed28650316ac2e.provisionprofile", "internal/Google_Chrome_Dev.765bb3620a0f7a33500da39b20122b1cec41140f.provisionprofile", - "internal/Google_Chrome_Dev.e809e0e54407d4f3914b7883b7ed28650316ac2e.provisionprofile", "internal/chrome_beta_dmg_dsstore", "internal/chrome_beta_dmg_icon.icns", "internal/chrome_canary_dmg_dsstore",
diff --git a/chrome/installer/mac/signing/driver.py b/chrome/installer/mac/signing/driver.py index 7ad8c88..2fd03817 100644 --- a/chrome/installer/mac/signing/driver.py +++ b/chrome/installer/mac/signing/driver.py
@@ -42,6 +42,10 @@ return '' @property + def provisioning_profile_basename(self): + return None + + @property def run_spctl_assess(self): # Self-signed or ad-hoc signed signing identities won't pass # spctl assessment so don't do it.
diff --git a/chrome/installer/mac/signing/model.py b/chrome/installer/mac/signing/model.py index cdae0e7f..b65876c 100644 --- a/chrome/installer/mac/signing/model.py +++ b/chrome/installer/mac/signing/model.py
@@ -14,35 +14,6 @@ from signing import commands -def _get_unexpired_identities(): - """Returns a set of the SHA-1 hashes of unexpired code signing identities - - Raises: - ValueError: If no unexpired code signing identities are found. - """ - # Avoid -v because it filters out self-signed certificates. - command = ['security', 'find-identity', '-p', 'codesigning'] - output = commands.run_command_output(command) - - matches = re.finditer( - rb'\d+\) (?P<id>[0-9A-Fa-f]{40}) "[^"]+"( \((?P<error>[^\)]+)\))?', - output, - flags=re.MULTILINE) - - identities = set() - for match in matches: - # Exclude expired certificates. Other errors are ignored. - if match.group('error') == b'CSSMERR_TP_CERT_EXPIRED': - continue - - identities.add(match.group('id')) - - if not identities: - raise ValueError('No code signing identities found') - - return identities - - def _get_identity_hash(identity): """Returns a string of the SHA-1 hash of a specified keychain identity. @@ -58,21 +29,15 @@ if len(identity) == 40 and all(ch in string.hexdigits for ch in identity): return identity.lower() - unexpired_identities = _get_unexpired_identities() - command = ['security', 'find-certificate', '-a', '-c', identity, '-Z'] output = commands.run_command_output(command) - hashes = re.findall( + hash_match = re.search( b'^SHA-1 hash: ([0-9A-Fa-f]{40})$', output, flags=re.MULTILINE) - if not hashes: + if not hash_match: raise ValueError('Cannot find identity', identity) - valid_hashes = [h for h in hashes if h in unexpired_identities] - if not valid_hashes: - raise ValueError('Identity found, but expired', identity) - - return valid_hashes[0].decode('utf-8').lower() + return hash_match.group(1).decode('utf-8').lower() class CodeSignedProduct(object):
diff --git a/chrome/release_scripts b/chrome/release_scripts index dd1c8ec..4f61c19 160000 --- a/chrome/release_scripts +++ b/chrome/release_scripts
@@ -1 +1 @@ -Subproject commit dd1c8ec0edb90fc7d5860a50b690f3bd8026ce14 +Subproject commit 4f61c195e5c4e456130a2de1ad5362a125ea7a61
diff --git a/chrome/test/BUILD.gn b/chrome/test/BUILD.gn index c0da1bb0..a4f2f6d 100644 --- a/chrome/test/BUILD.gn +++ b/chrome/test/BUILD.gn
@@ -2432,6 +2432,7 @@ "//components/plus_addresses:test_support", "//components/plus_addresses/resources/strings", "//components/policy:chrome_settings_proto_generated_compile", + "//components/policy/content", "//components/policy/core/browser:pref_mapping_test_support", "//components/policy/core/browser:test_support", "//components/policy/test_support", @@ -2955,6 +2956,7 @@ "../browser/enterprise/browser_management/management_service_browsertest.cc", "../browser/enterprise/connectors/reporting/crash_reporting_context_browsertest.cc", "../browser/enterprise/incognito/incognito_navigation_throttle_browsertest.cc", + "../browser/enterprise/signals/profile_signals_collector_browsertest.cc", "../browser/enterprise/util/managed_browser_utils_browsertest.cc", "../browser/enterprise/watermark/watermark_browsertest.cc", "../browser/error_reporting/crash_client_upload_info_browsertest.cc", @@ -8810,6 +8812,7 @@ "../browser/extensions/installed_loader_unittest.cc", "../common/extensions/chrome_manifest_url_handlers_unittest.cc", "../common/extensions/manifest_handlers/automation_unittest.cc", + "../common/extensions/manifest_handlers/background_scripts_manifest_unittest.cc", "../common/extensions/manifest_handlers/content_scripts_manifest_unittest.cc", "../common/extensions/manifest_handlers/exclude_matches_manifest_unittest.cc", "../common/extensions/manifest_handlers/natively_connectable_handler_unittest.cc",
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 80d2da5..835038dd 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
@@ -15,7 +15,6 @@ import static org.hamcrest.CoreMatchers.allOf; import static org.hamcrest.CoreMatchers.containsString; -import static org.chromium.base.test.transit.Condition.whether; import static org.chromium.base.test.transit.ViewSpec.viewSpec; import androidx.annotation.Nullable; @@ -24,19 +23,16 @@ import org.chromium.base.supplier.Supplier; import org.chromium.base.test.transit.Condition; -import org.chromium.base.test.transit.ConditionStatus; import org.chromium.base.test.transit.Elements; 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.UiThreadCondition; 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.flags.ChromeFeatureList; import org.chromium.chrome.browser.hub.PaneId; import org.chromium.chrome.browser.hub.R; -import org.chromium.chrome.browser.layouts.LayoutManager; import org.chromium.chrome.browser.layouts.LayoutType; import org.chromium.chrome.browser.tabmodel.TabModelSelector; import org.chromium.chrome.test.transit.layouts.LayoutTypeVisibleCondition; @@ -98,7 +94,6 @@ } } - elements.declareEnterCondition(new HubLayoutNotInTransition()); elements.declareEnterCondition( new LayoutTypeVisibleCondition(mActivityElement, LayoutType.TAB_SWITCHER)); } @@ -170,27 +165,4 @@ withContentDescription(containsString(contentDescription)))) .perform(click()); } - - private class HubLayoutNotInTransition extends UiThreadCondition { - private HubLayoutNotInTransition() { - dependOnSupplier(mActivityElement, "ChromeTabbedActivity"); - } - - @Override - protected ConditionStatus checkWithSuppliers() { - LayoutManager layoutManager = mActivityElement.get().getLayoutManager(); - boolean startingToShow = layoutManager.isLayoutStartingToShow(LayoutType.TAB_SWITCHER); - boolean startingToHide = layoutManager.isLayoutStartingToHide(LayoutType.TAB_SWITCHER); - return whether( - !startingToShow && !startingToHide, - "startingToShow=%b, startingToHide=%b", - startingToShow, - startingToHide); - } - - @Override - public String buildDescription() { - return "LayoutManager is not in transition to or from TAB_SWITCHER (Hub)"; - } - } }
diff --git a/chrome/test/android/javatests/src/org/chromium/chrome/test/transit/layouts/LayoutTypeVisibleCondition.java b/chrome/test/android/javatests/src/org/chromium/chrome/test/transit/layouts/LayoutTypeVisibleCondition.java index ede5bee..b0b9e22 100644 --- a/chrome/test/android/javatests/src/org/chromium/chrome/test/transit/layouts/LayoutTypeVisibleCondition.java +++ b/chrome/test/android/javatests/src/org/chromium/chrome/test/transit/layouts/LayoutTypeVisibleCondition.java
@@ -8,6 +8,7 @@ import org.chromium.base.test.transit.Condition; import org.chromium.base.test.transit.ConditionStatus; import org.chromium.chrome.browser.ChromeTabbedActivity; +import org.chromium.chrome.browser.compositor.layouts.Layout; import org.chromium.chrome.browser.layouts.LayoutType; /** Check that the current LayoutType is the expected one. */ @@ -24,12 +25,30 @@ @Override protected ConditionStatus checkWithSuppliers() throws Exception { - // TODO(crbug.com/394600771): Also check that the layout is not in transition, like - // {@link HubLayoutNotInTransition} does. - @LayoutType - int layoutType = mActivitySupplier.get().getLayoutManager().getActiveLayoutType(); - return whetherEquals( - mExpectedLayoutType, layoutType, LayoutTypeVisibleCondition::getLayoutTypeName); + Layout activeLayout = mActivitySupplier.get().getLayoutManager().getActiveLayout(); + if (activeLayout == null) { + return whetherEquals( + mExpectedLayoutType, + LayoutType.NONE, + LayoutTypeVisibleCondition::getLayoutTypeName); + } + + @LayoutType int layoutType = activeLayout.getLayoutType(); + boolean isStartingToHide = activeLayout.isStartingToHide(); + boolean isStartingToShow = activeLayout.isStartingToShow(); + String expectedDescription = getLayoutTypeName(mExpectedLayoutType); + String actualDescription = getLayoutTypeName(layoutType); + if (isStartingToHide) { + actualDescription += " (starting to hide)"; + } + if (isStartingToShow) { + actualDescription += " (starting to show)"; + } + return whether( + mExpectedLayoutType == layoutType && !isStartingToHide && !isStartingToShow, + "Expected: %s; Actual: %s", + expectedDescription, + actualDescription); } @Override
diff --git a/chrome/test/android/javatests/src/org/chromium/chrome/test/transit/page/PageStation.java b/chrome/test/android/javatests/src/org/chromium/chrome/test/transit/page/PageStation.java index 920535d8..6e655c3 100644 --- a/chrome/test/android/javatests/src/org/chromium/chrome/test/transit/page/PageStation.java +++ b/chrome/test/android/javatests/src/org/chromium/chrome/test/transit/page/PageStation.java
@@ -274,9 +274,6 @@ elements.declareEnterCondition( new PageUrlContainsCondition(mExpectedUrlSubstring, mPageLoadedSupplier)); } - - elements.declareEnterCondition( - new LayoutTypeVisibleCondition(mActivityElement, LayoutType.BROWSING)); } public boolean isIncognito() {
diff --git a/chrome/test/data/extensions/manifest_tests/background_bad_mime_type_manifest.json b/chrome/test/data/extensions/manifest_tests/background_bad_mime_type_manifest.json new file mode 100644 index 0000000..7ca36162 --- /dev/null +++ b/chrome/test/data/extensions/manifest_tests/background_bad_mime_type_manifest.json
@@ -0,0 +1,8 @@ +{ + "name": "background script with bad mime type", + "manifest_version": 2, + "version": "1", + "background": { + "scripts": ["background_bad_mime_type_script.json"] + } +}
diff --git a/chrome/test/data/extensions/manifest_tests/background_bad_mime_type_script.json b/chrome/test/data/extensions/manifest_tests/background_bad_mime_type_script.json new file mode 100644 index 0000000..0670b51a --- /dev/null +++ b/chrome/test/data/extensions/manifest_tests/background_bad_mime_type_script.json
@@ -0,0 +1 @@ +console.log("This is not a valid background script file type");
diff --git a/chrome/test/data/pdf/BUILD.gn b/chrome/test/data/pdf/BUILD.gn index 44deba3..1b82097 100644 --- a/chrome/test/data/pdf/BUILD.gn +++ b/chrome/test/data/pdf/BUILD.gn
@@ -75,6 +75,7 @@ "ink2_size_selector_test.ts", "ink2_test.ts", "ink2_text_side_panel_test.ts", + "ink2_text_styles_selector_test.ts", "ink2_viewer_toolbar_test.ts", "selectable_icon_button_test.ts", ]
diff --git a/chrome/test/data/pdf/ink2_text_side_panel_test.ts b/chrome/test/data/pdf/ink2_text_side_panel_test.ts index d114f9f..ddcdaeb 100644 --- a/chrome/test/data/pdf/ink2_text_side_panel_test.ts +++ b/chrome/test/data/pdf/ink2_text_side_panel_test.ts
@@ -2,8 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -import {AnnotationMode, hexToColor, Ink2Manager, TEXT_COLORS, TextAlignment, TextStyle, UserAction} from 'chrome-extension://mhjfbmdgcfjbbpaeojofohoefgiehjai/pdf_viewer_wrapper.js'; -import type {Color, CrIconButtonElement, SelectableIconButtonElement} from 'chrome-extension://mhjfbmdgcfjbbpaeojofohoefgiehjai/pdf_viewer_wrapper.js'; +import {AnnotationMode, hexToColor, Ink2Manager, TEXT_COLORS, TextAlignment, UserAction} from 'chrome-extension://mhjfbmdgcfjbbpaeojofohoefgiehjai/pdf_viewer_wrapper.js'; +import type {Color, SelectableIconButtonElement} from 'chrome-extension://mhjfbmdgcfjbbpaeojofohoefgiehjai/pdf_viewer_wrapper.js'; import {assert} from 'chrome://resources/js/assert.js'; import {loadTimeData} from 'chrome://resources/js/load_time_data.js'; import {eventToPromise, isVisible, microtasksFinished} from 'chrome://webui-test/test_util.js'; @@ -88,45 +88,13 @@ chrome.test.succeed(); }, - // Test that the styles can be toggled. - async function testSelectStyles() { + // Test that the side panel has a style select element. + function testHasStyleSelect() { chrome.test.assertEq(AnnotationMode.TEXT, viewer.$.toolbar.annotationMode); const sidePanel = viewer.shadowRoot.querySelector('viewer-text-side-panel'); assert(sidePanel); - - const initialStyles = Ink2Manager.getInstance().getCurrentText().styles; - - // Check that the button toggles its style and aria-pressed state and - // triggers a text-changed event when clicked. - async function testButton( - button: CrIconButtonElement, style: TextStyle, icon: string) { - chrome.test.assertEq(icon, button.ironIcon); - const initialValue = initialStyles[style]; - chrome.test.assertEq(initialValue, button.classList.contains('active')); - chrome.test.assertEq( - initialValue.toString(), button.getAttribute('aria-pressed')); - - const whenChanged = - eventToPromise('text-changed', Ink2Manager.getInstance()); - button.click(); - const changedEvent = await whenChanged; - chrome.test.assertEq(!initialValue, changedEvent.detail.styles[style]); - await microtasksFinished(); - chrome.test.assertEq(!initialValue, button.classList.contains('active')); - chrome.test.assertEq( - (!initialValue).toString(), button.getAttribute('aria-pressed')); - } - - // For each button, check that it can be toggled and change the styles. - const buttons = sidePanel.shadowRoot.querySelectorAll('cr-icon-button'); - chrome.test.assertEq(4, buttons.length); - await testButton(buttons[0]!, TextStyle.BOLD, 'pdf:text-format-bold'); - await testButton(buttons[1]!, TextStyle.ITALIC, 'pdf:text-format-italic'); - await testButton( - buttons[2]!, TextStyle.UNDERLINE, 'pdf:text-format-underline'); - await testButton( - buttons[3]!, TextStyle.STRIKETHROUGH, 'pdf:text-format-strikethrough'); - + chrome.test.assertTrue( + !!sidePanel.shadowRoot.querySelector('text-styles-selector')); chrome.test.succeed(); },
diff --git a/chrome/test/data/pdf/ink2_text_styles_selector_test.ts b/chrome/test/data/pdf/ink2_text_styles_selector_test.ts new file mode 100644 index 0000000..caa9c91 --- /dev/null +++ b/chrome/test/data/pdf/ink2_text_styles_selector_test.ts
@@ -0,0 +1,54 @@ +// 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 {Ink2Manager, TextStyle} from 'chrome-extension://mhjfbmdgcfjbbpaeojofohoefgiehjai/pdf_viewer_wrapper.js'; +import type {CrIconButtonElement} from 'chrome-extension://mhjfbmdgcfjbbpaeojofohoefgiehjai/pdf_viewer_wrapper.js'; +import {eventToPromise, microtasksFinished} from 'chrome://webui-test/test_util.js'; + +import {setupTestMockPluginForInk} from './test_util.js'; + +setupTestMockPluginForInk(); +const manager = Ink2Manager.getInstance(); +const styleSelector = document.createElement('text-styles-selector'); +document.body.appendChild(styleSelector); + +chrome.test.runTests([ + // Test that the styles can be toggled. + async function testSelectStyles() { + const initialStyles = manager.getCurrentText().styles; + + // Check that the button toggles its style and aria-pressed state and + // triggers a text-changed event when clicked. + async function testButton( + button: CrIconButtonElement, style: TextStyle, icon: string) { + chrome.test.assertEq(icon, button.ironIcon); + const initialValue = initialStyles[style]; + chrome.test.assertEq(initialValue, button.classList.contains('active')); + chrome.test.assertEq( + initialValue.toString(), button.getAttribute('aria-pressed')); + + const whenChanged = eventToPromise('text-changed', manager); + button.click(); + const changedEvent = await whenChanged; + chrome.test.assertEq(!initialValue, changedEvent.detail.styles[style]); + await microtasksFinished(); + chrome.test.assertEq(!initialValue, button.classList.contains('active')); + chrome.test.assertEq( + (!initialValue).toString(), button.getAttribute('aria-pressed')); + } + + // For each button, check that it can be toggled and confirm it is + // displaying the expected icon. + const buttons = styleSelector.shadowRoot.querySelectorAll('cr-icon-button'); + chrome.test.assertEq(4, buttons.length); + await testButton(buttons[0]!, TextStyle.BOLD, 'pdf:text-format-bold'); + await testButton(buttons[1]!, TextStyle.ITALIC, 'pdf:text-format-italic'); + await testButton( + buttons[2]!, TextStyle.UNDERLINE, 'pdf:text-format-underline'); + await testButton( + buttons[3]!, TextStyle.STRIKETHROUGH, 'pdf:text-format-strikethrough'); + + chrome.test.succeed(); + }, +]);
diff --git a/chrome/test/data/webui/cr_elements/cr_lit_element_test.ts b/chrome/test/data/webui/cr_elements/cr_lit_element_test.ts index 32ea30d..2ce804f 100644 --- a/chrome/test/data/webui/cr_elements/cr_lit_element_test.ts +++ b/chrome/test/data/webui/cr_elements/cr_lit_element_test.ts
@@ -309,7 +309,13 @@ assertThrows(function() { element.$.foo; assertNotReached('Previous statement should have thrown an exception'); - }, 'CrLitElement CR-DUMMY-LIT $ dictionary accessed before element is connected at least once.'); + }, 'CrLitElement CR-DUMMY-LIT accessed \'$.foo\' before connected at least once.'); + + assertThrows(function() { + element.id = 'dummyId'; + element.$.foo; + assertNotReached('Previous statement should have thrown an exception'); + }, 'CrLitElement CR-DUMMY-LIT#dummyId accessed \'$.foo\' before connected at least once.'); assertDeepEquals([], element.lifecycleCallbacks); });
diff --git a/chrome/test/data/webui/settings/autofill_fake_data.ts b/chrome/test/data/webui/settings/autofill_fake_data.ts index 1b6e5c2b8..9a2d70a 100644 --- a/chrome/test/data/webui/settings/autofill_fake_data.ts +++ b/chrome/test/data/webui/settings/autofill_fake_data.ts
@@ -84,12 +84,15 @@ /** * Creates a new random credit card entry for testing. */ -export function createCreditCardEntry(): - chrome.autofillPrivate.CreditCardEntry { +export function createCreditCardEntry( + isNewFopDisplay: boolean = true, + hasIdentifier: boolean = false): chrome.autofillPrivate.CreditCardEntry { const cards = ['Visa', 'Mastercard', 'Discover', 'Card']; const card = cards[Math.floor(Math.random() * cards.length)]; const cardNumber = appendLuhnCheckBit(patternMaker('xxxxxxxxxxxxxxx', 10)); const now = new Date(); + const networkAndLastFour = card + ' ' + + '****' + cardNumber.substr(-4); return { guid: makeGuid(), name: 'Jane Doe', @@ -101,9 +104,10 @@ imageSrc: 'chrome://theme/IDR_AUTOFILL_CC_GENERIC', metadata: { isLocal: true, - summaryLabel: card + ' ' + - '****' + cardNumber.substr(-4), - summarySublabel: 'Jane Doe', + summaryLabel: hasIdentifier ? `My Credit Card` : networkAndLastFour, + summarySublabel: isNewFopDisplay ? + (hasIdentifier ? networkAndLastFour : '') : + 'Jane Doe', }, }; }
diff --git a/chrome/test/data/webui/settings/payments_section_card_rows_test.ts b/chrome/test/data/webui/settings/payments_section_card_rows_test.ts index a9b09ff..454cd90c 100644 --- a/chrome/test/data/webui/settings/payments_section_card_rows_test.ts +++ b/chrome/test/data/webui/settings/payments_section_card_rows_test.ts
@@ -35,21 +35,33 @@ loadTimeData.overrideValues({ migrationEnabled: true, showIbansSettings: true, + enableNewFopDisplay: true, }); metricsBrowserProxy = new TestMetricsBrowserProxy(); MetricsBrowserProxyImpl.setInstance(metricsBrowserProxy); }); test('verifyCreditCardFields', async function() { + loadTimeData.overrideValues({ + enableNewFopDisplay: true, + }); const creditCard = createCreditCardEntry(); const section = await createPaymentsSection( [creditCard], /*ibans=*/[], /*payOverTimeIssuers=*/[], /*prefValues=*/ {}); const rowShadowRoot = getCardRowShadowRoot(section.$.paymentsList); + assertTrue(isVisible(rowShadowRoot.querySelector<HTMLElement>('#label'))); + assertTrue(isVisible( + rowShadowRoot.querySelector<HTMLElement>('#expirationLabel'))); assertEquals( creditCard.metadata!.summaryLabel, rowShadowRoot.querySelector<HTMLElement>( - '#summaryLabel')!.textContent!.trim()); + '#label')!.textContent!.trim()); + assertEquals( + '· ' + parseInt(creditCard.expirationMonth!, 10) + '/' + + creditCard.expirationYear!.substring(2), + rowShadowRoot.querySelector<HTMLElement>( + '#expirationLabel')!.textContent!.trim()); }); test('verifyCreditCardRowButtonIsDropdownWhenLocal', async function() { @@ -368,27 +380,39 @@ assertFalse(!!menu); }); - test('verifyCreditCardSummarySublabelWithExpirationDate', async function() { - const creditCard = createCreditCardEntry(); + test( + 'verifyCreditCardSummarySublabelWithNetworkLastFourAndExpirationDate', + async function() { + loadTimeData.overrideValues({ + enableNewFopDisplay: true, + }); + const creditCard = createCreditCardEntry(); - const section = await createPaymentsSection( - [creditCard], /*ibans=*/[], /*payOverTimeIssuers=*/[], - /*prefValues=*/ {}); + const section = await createPaymentsSection( + [creditCard], /*ibans=*/[], /*payOverTimeIssuers=*/[], + /*prefValues=*/ {}); - const creditCardList = section.$.paymentsList; - assertTrue(!!creditCardList); - assertEquals(1, getLocalAndServerCreditCardListItems().length); - assertFalse(getCardRowShadowRoot(section.$.paymentsList) - .querySelector<HTMLElement>('#summarySublabel')!.hidden); - assertTrue(!!creditCard.expirationMonth); - assertTrue(!!creditCard.expirationYear); - assertEquals( - parseInt(creditCard.expirationMonth, 10) + '/' + - creditCard.expirationYear.substring(2), - getCardRowShadowRoot(section.$.paymentsList) - .querySelector<HTMLElement>( - '#summarySublabel')!.textContent!.trim()); - }); + const creditCardList = section.$.paymentsList; + assertTrue(!!creditCardList); + assertEquals(1, getLocalAndServerCreditCardListItems().length); + assertTrue(isVisible(getCardRowShadowRoot(section.$.paymentsList) + .querySelector<HTMLElement>('#label'))); + assertEquals( + creditCard.metadata!.summaryLabel, + getCardRowShadowRoot(section.$.paymentsList) + .querySelector<HTMLElement>('#label')!.textContent!.trim()); + assertTrue( + isVisible(getCardRowShadowRoot(section.$.paymentsList) + .querySelector<HTMLElement>('#expirationLabel'))); + assertTrue(!!creditCard.expirationMonth); + assertTrue(!!creditCard.expirationYear); + assertEquals( + '· ' + parseInt(creditCard.expirationMonth, 10) + '/' + + creditCard.expirationYear.substring(2), + getCardRowShadowRoot(section.$.paymentsList) + .querySelector<HTMLElement>( + '#expirationLabel')!.textContent!.trim()); + }); test('verifyCreditCardSummarySublabelWhenSublabelIsValid', async function() { const creditCard = createCreditCardEntry(); @@ -409,6 +433,9 @@ test( 'verifyCreditCardSummarySublabelWhenVirtualCardAvailable', async function() { + loadTimeData.overrideValues({ + enableNewFopDisplay: true, + }); const creditCard = createCreditCardEntry(); creditCard.metadata!.isLocal = false; creditCard.metadata!.isVirtualCardEnrollmentEligible = true; @@ -420,17 +447,22 @@ const creditCardList = section.$.paymentsList; assertTrue(!!creditCardList); assertEquals(1, getLocalAndServerCreditCardListItems().length); - assertFalse( - getCardRowShadowRoot(section.$.paymentsList) - .querySelector<HTMLElement>('#summarySublabel')!.hidden); - assertTrue(!!creditCard.expirationMonth); - assertTrue(!!creditCard.expirationYear); + + assertTrue(isVisible(getCardRowShadowRoot(section.$.paymentsList) + .querySelector<HTMLElement>('#label'))); + assertTrue( + isVisible(getCardRowShadowRoot(section.$.paymentsList) + .querySelector<HTMLElement>('#expirationLabel'))); assertEquals( - parseInt(creditCard.expirationMonth, 10) + '/' + - creditCard.expirationYear.substring(2), + creditCard.metadata!.summaryLabel, + getCardRowShadowRoot(section.$.paymentsList) + .querySelector<HTMLElement>('#label')!.textContent!.trim()); + assertEquals( + '· ' + parseInt(creditCard.expirationMonth!, 10) + '/' + + creditCard.expirationYear!.substring(2), getCardRowShadowRoot(section.$.paymentsList) .querySelector<HTMLElement>( - '#summarySublabel')!.textContent!.trim()); + '#expirationLabel')!.textContent!.trim()); }); test( @@ -637,6 +669,9 @@ benefitsAvailable, productTermsUrlAvailable, }) => { + loadTimeData.overrideValues({ + enableNewFopDisplay: true, + }); const benefitsStatus = benefitsAvailable ? 'Available' : 'NotAvailable'; const termUrlStatus = productTermsUrlAvailable ? 'Available' : 'NotAvailable'; @@ -660,16 +695,17 @@ assertTrue(!!paymentsList); assertEquals(1, paymentsList.length); - assertTrue( - isVisible(paymentsList[0]!.shadowRoot!.querySelector<HTMLElement>( - '#summarySublabel'))); + if (benefitsAvailable && productTermsUrlAvailable) { + assertTrue(isVisible( + paymentsList[0]!.shadowRoot!.querySelector<HTMLElement>( + '#summarySublabel'))); + } // Build the expected resulting sublabel based on which features are // enabled. - let benefitExpectedSublabel = serverCreditCard.expirationMonth + '/' + - serverCreditCard.expirationYear!.toString().substring(2); + let benefitExpectedSublabel = ''; if (benefitsAvailable && productTermsUrlAvailable) { - benefitExpectedSublabel += ' | ' + + benefitExpectedSublabel += loadTimeData.getString('benefitsTermsTagForCreditCardListEntry'); } @@ -685,8 +721,8 @@ assertTrue(!!termsLink); assertEquals(serverCreditCard.productTermsUrl, termsLink.href); } else { - assertFalse( - isVisible(paymentsList[0]!.shadowRoot!.querySelector<HTMLElement>( + assertFalse(isVisible( + paymentsList[0]!.shadowRoot!.querySelector<HTMLElement>( '#summaryTermsLink'))); } }); @@ -698,6 +734,9 @@ benefitsAvailable, productTermsUrlAvailable, }) => { + loadTimeData.overrideValues({ + enableNewFopDisplay: true, + }); const benefitsStatus = benefitsAvailable ? 'Available' : 'NotAvailable'; const termUrlStatus = productTermsUrlAvailable ? 'Available' : 'NotAvailable'; @@ -723,11 +762,20 @@ assertTrue(!!paymentsList); assertEquals(1, paymentsList.length); - assertTrue( - isVisible(paymentsList[0]!.shadowRoot!.querySelector<HTMLElement>( - '#summarySublabel'))); - let benefitExpectedSublabel = serverCreditCard.expirationMonth + '/' + - serverCreditCard.expirationYear!.toString().substring(2) + ' | ' + + + assertTrue(isVisible( + paymentsList[0]!.shadowRoot!.querySelector<HTMLElement>('#label'))); + assertTrue(isVisible( + paymentsList[0]!.shadowRoot!.querySelector<HTMLElement>( + '#expirationLabel'))); + assertEquals( + '· ' + parseInt(serverCreditCard.expirationMonth!, 10) + '/' + + serverCreditCard.expirationYear!.substring(2), + paymentsList[0]!.shadowRoot! + .querySelector<HTMLElement>( + '#expirationLabel')!.textContent!.trim()); + + let benefitExpectedSublabel = loadTimeData.getString('cvcTagForCreditCardListEntry'); if (benefitsAvailable && productTermsUrlAvailable) { benefitExpectedSublabel += ' | ' + @@ -745,8 +793,8 @@ assertTrue(!!termsLink); assertEquals(serverCreditCard.productTermsUrl, termsLink.href); } else { - assertFalse( - isVisible(paymentsList[0]!.shadowRoot!.querySelector<HTMLElement>( + assertFalse(isVisible( + paymentsList[0]!.shadowRoot!.querySelector<HTMLElement>( '#summaryTermsLink'))); } }); @@ -821,8 +869,8 @@ assertEquals(termsLink.ariaLabel, expectedAriaLabel); }); - // Test to verify the cvc tag is visible when cvc is present on a - // server/local cards. + // Test to verify the CVC tag is visible when CVC is present on a + // server/local card. [true, false].forEach(cvcOnServerCard => { test( 'verifyCvcTagPresentFor_' + @@ -844,6 +892,181 @@ /*payOverTimeIssuers=*/[], /*prefValues=*/ {}); + const serverCardExpectedSublabel = '· ' + + serverCreditCard.expirationMonth + '/' + + serverCreditCard.expirationYear!.toString().substring(2); + const localCardExpectedSublabel = '· ' + + localCreditCard.expirationMonth + '/' + + localCreditCard.expirationYear!.toString().substring(2); + const paymentsList = getLocalAndServerCreditCardListItems(); + + assertTrue(isVisible( + paymentsList[0]!.shadowRoot!.querySelector<HTMLElement>( + '#label'))); + assertEquals( + serverCreditCard.metadata!.summaryLabel, + paymentsList[0]!.shadowRoot!.querySelector<HTMLElement>( + '#label')!.textContent!.trim()); + assertTrue(isVisible( + paymentsList[1]!.shadowRoot!.querySelector<HTMLElement>( + '#label'))); + assertEquals( + localCreditCard.metadata!.summaryLabel, + paymentsList[1]!.shadowRoot!.querySelector<HTMLElement>( + '#label')!.textContent!.trim()); + if (cvcOnServerCard) { + assertTrue(isVisible( + paymentsList[0]!.shadowRoot!.querySelector<HTMLElement>( + '#expirationLabel'))); + assertEquals( + serverCardExpectedSublabel, + paymentsList[0]!.shadowRoot! + .querySelector<HTMLElement>( + '#expirationLabel')!.textContent!.trim()); + assertTrue(isVisible( + paymentsList[0]!.shadowRoot!.querySelector<HTMLElement>( + '#summarySublabel'))); + } else { + assertTrue(isVisible( + paymentsList[1]!.shadowRoot!.querySelector<HTMLElement>( + '#expirationLabel'))); + assertEquals( + localCardExpectedSublabel, + paymentsList[1]!.shadowRoot! + .querySelector<HTMLElement>( + '#expirationLabel')!.textContent!.trim()); + assertTrue(isVisible( + paymentsList[1]!.shadowRoot!.querySelector<HTMLElement>( + '#summarySublabel'))); + } + + let serverExpectedSublabel = ''; + let localExpectedSublabel = ''; + if (cvcOnServerCard) { + serverExpectedSublabel = + loadTimeData.getString('cvcTagForCreditCardListEntry'); + } else { + localExpectedSublabel = + loadTimeData.getString('cvcTagForCreditCardListEntry'); + } + + assertTrue(!!paymentsList); + assertEquals(2, paymentsList.length); + assertEquals( + serverExpectedSublabel, + paymentsList[0]!.shadowRoot! + .querySelector<HTMLElement>( + '#summarySublabel')!.textContent!.trim()); + assertEquals( + localExpectedSublabel, + paymentsList[1]!.shadowRoot! + .querySelector<HTMLElement>( + '#summarySublabel')!.textContent!.trim()); + }); + }); + + test('verifyCreditCardFields_newFopDisplayFlagOff', async function() { + loadTimeData.overrideValues({ + enableNewFopDisplay: false, + }); + const creditCard = createCreditCardEntry(/*isNewFopDisplay=*/ false, + /*hasIdentifier=*/ false); + const section = await createPaymentsSection( + [creditCard], /*ibans=*/[], /*payOverTimeIssuers=*/[], + /*prefValues=*/ {}); + const rowShadowRoot = getCardRowShadowRoot(section.$.paymentsList); + assertEquals( + creditCard.metadata!.summaryLabel, + rowShadowRoot.querySelector<HTMLElement>( + '#summaryLabel')!.textContent!.trim()); + }); + + test( + 'verifyCreditCardSummarySublabelWithExpirationDate_newFopDisplayOff', + async function() { + loadTimeData.overrideValues({ + enableNewFopDisplay: false, + }); + const creditCard = createCreditCardEntry(/*isNewFopDisplay=*/ false, + /*hasIdentifier=*/ false); + + const section = await createPaymentsSection( + [creditCard], /*ibans=*/[], /*payOverTimeIssuers=*/[], + /*prefValues=*/ {}); + + const creditCardList = section.$.paymentsList; + assertTrue(!!creditCardList); + assertEquals(1, getLocalAndServerCreditCardListItems().length); + assertFalse( + getCardRowShadowRoot(section.$.paymentsList) + .querySelector<HTMLElement>('#summarySublabel')!.hidden); + assertTrue(!!creditCard.expirationMonth); + assertTrue(!!creditCard.expirationYear); + assertEquals( + parseInt(creditCard.expirationMonth, 10) + '/' + + creditCard.expirationYear.substring(2), + getCardRowShadowRoot(section.$.paymentsList) + .querySelector<HTMLElement>( + '#summarySublabel')!.textContent!.trim()); + }); + + test( + 'verifyCreditCardSummarySublabelWhenVirtualCardAvailable_newFopDisplayOff', + async function() { + loadTimeData.overrideValues({ + enableNewFopDisplay: false, + }); + const creditCard = createCreditCardEntry(/*isNewFopDisplay=*/ false, + /*hasIdentifier=*/ false); + creditCard.metadata!.isLocal = false; + creditCard.metadata!.isVirtualCardEnrollmentEligible = true; + creditCard.metadata!.isVirtualCardEnrolled = false; + const section = await createPaymentsSection( + [creditCard], /*ibans=*/[], /*payOverTimeIssuers=*/[], + /*prefValues=*/ {}); + + const creditCardList = section.$.paymentsList; + assertTrue(!!creditCardList); + assertEquals(1, getLocalAndServerCreditCardListItems().length); + assertFalse( + getCardRowShadowRoot(section.$.paymentsList) + .querySelector<HTMLElement>('#summarySublabel')!.hidden); + assertTrue(!!creditCard.expirationMonth); + assertTrue(!!creditCard.expirationYear); + assertEquals( + parseInt(creditCard.expirationMonth, 10) + '/' + + creditCard.expirationYear.substring(2), + getCardRowShadowRoot(section.$.paymentsList) + .querySelector<HTMLElement>( + '#summarySublabel')!.textContent!.trim()); + }); + + // Test to verify the CVC tag is visible when CVC is present on a + // server/local card. + [true, false].forEach(cvcOnServerCard => { + test( + 'verifyCvcTagPresentFor_NewFopDisplayOff_' + + (cvcOnServerCard ? 'ServerCard' : 'LocalCard'), + async function() { + loadTimeData.overrideValues({ + cvcStorageAvailable: true, + enableNewFopDisplay: false, + }); + const serverCreditCard = + createCreditCardEntry(/*isNewFopDisplay=*/ false, + /*hasIdentifier=*/ false); + serverCreditCard.metadata!.isLocal = false; + const localCreditCard = createCreditCardEntry(); + if (cvcOnServerCard) { + serverCreditCard.cvc = '***'; + } else { + localCreditCard.cvc = '***'; + } + await createPaymentsSection( + [serverCreditCard, localCreditCard], /*ibans=*/[], + /*payOverTimeIssuers=*/[], + /*prefValues=*/ {}); + let serverCardExpectedSublabel = serverCreditCard.expirationMonth + '/' + serverCreditCard.expirationYear!.toString().substring(2); let localCardExpectedSublabel = localCreditCard.expirationMonth + @@ -877,6 +1100,132 @@ '#summarySublabel')!.textContent!.trim()); }); }); + + // Test to verify the existence of the benefits tag based on the existence of + // the benefits and the product terms URL on a server card without a CVC. + benefitsStatus.forEach(({ + benefitsAvailable, + productTermsUrlAvailable, + }) => { + const benefitsStatus = benefitsAvailable ? 'Available' : 'NotAvailable'; + const termUrlStatus = + productTermsUrlAvailable ? 'Available' : 'NotAvailable'; + const testName = `ServerCardSummarySublabel_NewFopDisplayOff_Benefits${ + benefitsStatus}_ProductTermsUrl${termUrlStatus}`; + test(testName, async () => { + loadTimeData.overrideValues({ + autofillCardBenefitsAvailable: benefitsAvailable, + enableNewFopDisplay: false, + }); + const serverCreditCard = createCreditCardEntry(/*isNewFopDisplay=*/ false, + /*hasIdentifier=*/ false); + assertTrue(!!serverCreditCard.metadata); + serverCreditCard.metadata.isLocal = false; + if (benefitsAvailable && productTermsUrlAvailable) { + serverCreditCard.productTermsUrl = 'https://google.com/'; + } + await createPaymentsSection( + [serverCreditCard], /*ibans=*/[], /*payOverTimeIssuers=*/[], + /*prefValues=*/ {}); + + const paymentsList = getLocalAndServerCreditCardListItems(); + + assertTrue(!!paymentsList); + assertEquals(1, paymentsList.length); + assertTrue(isVisible( + paymentsList[0]!.shadowRoot!.querySelector<HTMLElement>( + '#summarySublabel'))); + + // Build the expected resulting sublabel based on which features are + // enabled. + let benefitExpectedSublabel = serverCreditCard.expirationMonth + '/' + + serverCreditCard.expirationYear!.toString().substring(2); + if (benefitsAvailable && productTermsUrlAvailable) { + benefitExpectedSublabel += ' | ' + + loadTimeData.getString('benefitsTermsTagForCreditCardListEntry'); + } + + assertEquals( + benefitExpectedSublabel, + cleanUpWhitespace( + paymentsList[0]!.shadowRoot!.querySelector<HTMLElement>( + '#summarySublabel')!)); + if (benefitsAvailable && productTermsUrlAvailable) { + const termsLink = + paymentsList[0]!.shadowRoot!.querySelector<HTMLAnchorElement>( + '#summaryTermsLink'); + assertTrue(!!termsLink); + assertEquals(serverCreditCard.productTermsUrl, termsLink.href); + } else { + assertFalse(isVisible( + paymentsList[0]!.shadowRoot!.querySelector<HTMLElement>( + '#summaryTermsLink'))); + } + }); + }); + + // Test to verify the existence of the benefits tag based on the existence of + // the benefits and the product terms URL on a server card with a CVC saved. + benefitsStatus.forEach(({ + benefitsAvailable, + productTermsUrlAvailable, + }) => { + const benefitsStatus = benefitsAvailable ? 'Available' : 'NotAvailable'; + const termUrlStatus = + productTermsUrlAvailable ? 'Available' : 'NotAvailable'; + const testName = + `ServerCardWithCvcSummarySublabel_NewFopDisplayOff_Benefits${ + benefitsStatus}_ProductTermsUrl${termUrlStatus}`; + test(testName, async () => { + loadTimeData.overrideValues({ + cvcStorageAvailable: true, + autofillCardBenefitsAvailable: benefitsAvailable, + enableNewFopDisplay: false, + }); + const serverCreditCard = createCreditCardEntry(/*isNewFopDisplay=*/ false, + /*hasIdentifier=*/ false); + assertTrue(!!serverCreditCard.metadata); + serverCreditCard.metadata.isLocal = false; + serverCreditCard.cvc = '***'; + if (benefitsAvailable && productTermsUrlAvailable) { + serverCreditCard.productTermsUrl = 'https://google.com/'; + } + await createPaymentsSection( + [serverCreditCard], /*ibans=*/[], /*payOverTimeIssuers=*/[], + /*prefValues=*/ {}); + + const paymentsList = getLocalAndServerCreditCardListItems(); + + assertTrue(!!paymentsList); + assertEquals(1, paymentsList.length); + assertTrue(isVisible( + paymentsList[0]!.shadowRoot!.querySelector<HTMLElement>( + '#summarySublabel'))); + let benefitExpectedSublabel = serverCreditCard.expirationMonth + '/' + + serverCreditCard.expirationYear!.toString().substring(2) + ' | ' + + loadTimeData.getString('cvcTagForCreditCardListEntry'); + if (benefitsAvailable && productTermsUrlAvailable) { + benefitExpectedSublabel += ' | ' + + loadTimeData.getString('benefitsTermsTagForCreditCardListEntry'); + } + assertEquals( + benefitExpectedSublabel, + cleanUpWhitespace( + paymentsList[0]!.shadowRoot!.querySelector<HTMLElement>( + '#summarySublabel')!)); + if (benefitsAvailable && productTermsUrlAvailable) { + const termsLink = + paymentsList[0]!.shadowRoot!.querySelector<HTMLAnchorElement>( + '#summaryTermsLink'); + assertTrue(!!termsLink); + assertEquals(serverCreditCard.productTermsUrl, termsLink.href); + } else { + assertFalse(isVisible( + paymentsList[0]!.shadowRoot!.querySelector<HTMLElement>( + '#summaryTermsLink'))); + } + }); + }); }); suite('PaymentsSectionEditCreditCardLink', function() {
diff --git a/chrome/updater/mac/BUILD.gn b/chrome/updater/mac/BUILD.gn index e8da7ed5..47a2953 100644 --- a/chrome/updater/mac/BUILD.gn +++ b/chrome/updater/mac/BUILD.gn
@@ -332,6 +332,7 @@ "//chrome/updater:base_app", "//chrome/updater:ks_ticket", "//chrome/updater:public_sources", + "//chrome/updater:tagging", "//chrome/updater:version_header", ]
diff --git a/chrome/updater/mac/keystone/ksadmin.mm b/chrome/updater/mac/keystone/ksadmin.mm index e932462..12488675 100644 --- a/chrome/updater/mac/keystone/ksadmin.mm +++ b/chrome/updater/mac/keystone/ksadmin.mm
@@ -40,12 +40,14 @@ #include "base/task/single_thread_task_executor.h" #include "base/task/thread_pool/thread_pool_instance.h" #include "base/time/time.h" +#include "base/types/expected.h" #include "chrome/updater/app/app.h" #include "chrome/updater/constants.h" #include "chrome/updater/ipc/ipc_support.h" #include "chrome/updater/mac/setup/ks_tickets.h" #include "chrome/updater/registration_data.h" #include "chrome/updater/service_proxy_factory.h" +#include "chrome/updater/tag.h" #include "chrome/updater/update_service.h" #include "chrome/updater/updater_scope.h" #include "chrome/updater/updater_version.h" @@ -124,6 +126,7 @@ constexpr char kCommandVersionKey[] = "version-key"; constexpr char kCommandVersionPath[] = "version-path"; constexpr char kCommandXCPath[] = "xcpath"; +constexpr char kCommandXattrTagBrand[] = "print-xattr-tag-brand"; bool HasSwitch(const std::string& arg, const std::map<std::string, std::string>& switches) { @@ -257,6 +260,7 @@ void PrintUsage(const std::string& error_message); void PrintVersion(); void PrintTickets(); + void PrintXattrTagBrand(); void DoUpdateApp(UpdaterScope scope); void DoListAppUpdate(UpdaterScope scope); @@ -794,6 +798,27 @@ base::BindOnce(&KSAdminApp::Shutdown, this))); } +void KSAdminApp::PrintXattrTagBrand() { + const std::string path_str = SwitchValue(kCommandXattrTagBrand); + if (path_str.empty()) { + LOG(ERROR) << kCommandXattrTagBrand << " requires a path."; + Shutdown(1); + return; + } + const base::FilePath path = base::FilePath(path_str); + base::expected<tagging::TagArgs, tagging::ErrorCode> tag_result = + tagging::ReadTagFromApplicationInstanceXattr(path); + if (!tag_result.has_value()) { + LOG(ERROR) << "Can't read tag: " << tag_result.error(); + Shutdown(1); + return; + } + + // Empty brand code is not an error. + printf("%s\n", tag_result->brand_code.c_str()); + Shutdown(0); +} + void KSAdminApp::FirstTaskRun() { if ((geteuid() == 0) && (getuid() != 0)) { if (setuid(0) || setgid(0)) { @@ -810,6 +835,7 @@ {kCommandPrintTag, &KSAdminApp::PrintTag}, {kCommandPrintTickets, &KSAdminApp::PrintTickets}, {kCommandRegister, &KSAdminApp::Register}, + {kCommandXattrTagBrand, &KSAdminApp::PrintXattrTagBrand}, }; for (const auto& [command, method] : commands) { if (HasSwitch(command)) {
diff --git a/chrome/updater/test/integration_test_commands.h b/chrome/updater/test/integration_test_commands.h index b95a3af..b0c66db 100644 --- a/chrome/updater/test/integration_test_commands.h +++ b/chrome/updater/test/integration_test_commands.h
@@ -204,7 +204,11 @@ const base::FilePath& xc_path, std::optional<UpdaterScope> store_flag, std::optional<std::string> want_tag) const = 0; -#endif // BUILDFLAG(IS_WIN) + virtual void ExpectKSAdminXattrBrand( + bool elevate, + const base::FilePath& path, + std::optional<std::string> want_brand) const = 0; +#endif // BUILDFLAG(IS_MAC) virtual void ExpectLegacyUpdaterMigrated() const = 0; virtual void RunRecoveryComponent(const std::string& app_id, const base::Version& version) const = 0;
diff --git a/chrome/updater/test/integration_test_commands_system.cc b/chrome/updater/test/integration_test_commands_system.cc index 04a334e..980582e 100644 --- a/chrome/updater/test/integration_test_commands_system.cc +++ b/chrome/updater/test/integration_test_commands_system.cc
@@ -554,7 +554,16 @@ std::optional<UpdaterScope> store_flag, std::optional<std::string> want_tag) const override { updater::test::ExpectKSAdminFetchTag(updater_scope_, elevate, product_id, - xc_path, store_flag, want_tag); + xc_path, store_flag, + std::move(want_tag)); + } + + void ExpectKSAdminXattrBrand( + bool elevate, + const base::FilePath& path, + std::optional<std::string> want_brand) const override { + updater::test::ExpectKSAdminXattrBrand(updater_scope_, elevate, path, + std::move(want_brand)); } #endif // BUILDFLAG(IS_MAC)
diff --git a/chrome/updater/test/integration_test_commands_user.cc b/chrome/updater/test/integration_test_commands_user.cc index 80cc9986..3009aaa 100644 --- a/chrome/updater/test/integration_test_commands_user.cc +++ b/chrome/updater/test/integration_test_commands_user.cc
@@ -449,7 +449,16 @@ std::optional<UpdaterScope> store_flag, std::optional<std::string> want_tag) const override { updater::test::ExpectKSAdminFetchTag(updater_scope_, elevate, product_id, - xc_path, store_flag, want_tag); + xc_path, store_flag, + std::move(want_tag)); + } + + void ExpectKSAdminXattrBrand( + bool elevate, + const base::FilePath& path, + std::optional<std::string> want_brand) const override { + updater::test::ExpectKSAdminXattrBrand(updater_scope_, elevate, path, + std::move(want_brand)); } #endif // BUILDFLAG(IS_MAC)
diff --git a/chrome/updater/test/integration_tests.cc b/chrome/updater/test/integration_tests.cc index a8ad6b56..4230f4da 100644 --- a/chrome/updater/test/integration_tests.cc +++ b/chrome/updater/test/integration_tests.cc
@@ -694,6 +694,13 @@ store_flag, want_tag); } + void ExpectKSAdminXattrBrand(bool elevate, + const base::FilePath& path, + std::optional<std::string> want_brand) { + test_commands_->ExpectKSAdminXattrBrand(elevate, path, + std::move(want_brand)); + } + #endif // BUILDFLAG(IS_MAC) void ExpectAppInstalled(const std::string& appid, @@ -1770,6 +1777,36 @@ ASSERT_FALSE(read_result.has_value()); EXPECT_EQ(read_result.error(), tagging::ErrorCode::kTagNotFound); } + +TEST_F(IntegrationTest, KSAdminXattrTagReadBrandSuccess) { + ASSERT_NO_FATAL_FAILURE(Install({kEnableCecaExperimentSwitch})); + base::ScopedTempFile tag_me; + ASSERT_TRUE(tag_me.Create()); + EXPECT_TRUE(tagging::WriteTagStringToApplicationInstanceXattr( + tag_me.path(), + "brand=TEST&iid=TestInstallId&appguid=org.chromium.test&ap=example")); + ExpectKSAdminXattrBrand(false, tag_me.path(), "TEST"); + ASSERT_NO_FATAL_FAILURE(Uninstall()); +} + +TEST_F(IntegrationTest, KSAdminXattrTagReadNoBrandSuccess) { + ASSERT_NO_FATAL_FAILURE(Install({kEnableCecaExperimentSwitch})); + base::ScopedTempFile tag_me_without_brand; + ASSERT_TRUE(tag_me_without_brand.Create()); + EXPECT_TRUE(tagging::WriteTagStringToApplicationInstanceXattr( + tag_me_without_brand.path(), + "iid=TestInstallId&appguid=org.chromium.test&ap=example")); + ExpectKSAdminXattrBrand(false, tag_me_without_brand.path(), ""); + ASSERT_NO_FATAL_FAILURE(Uninstall()); +} + +TEST_F(IntegrationTest, KSAdminXattrTagBrandNoXattrFailure) { + ASSERT_NO_FATAL_FAILURE(Install({kEnableCecaExperimentSwitch})); + base::ScopedTempFile dont_tag_me; + ASSERT_TRUE(dont_tag_me.Create()); + ExpectKSAdminXattrBrand(false, dont_tag_me.path(), std::nullopt); + ASSERT_NO_FATAL_FAILURE(Uninstall()); +} #endif TEST_F(IntegrationTest, InstallId) {
diff --git a/chrome/updater/test/integration_tests_impl.h b/chrome/updater/test/integration_tests_impl.h index 7a86176..200e007 100644 --- a/chrome/updater/test/integration_tests_impl.h +++ b/chrome/updater/test/integration_tests_impl.h
@@ -624,6 +624,21 @@ std::optional<UpdaterScope> store_flag, std::optional<std::string> want_tag); +// Expect ksadmin to fetch the specified brand code from a tag stored in the +// `com.apple.application-instance` extended attribute of the item at the +// specified path, or to fail to retrieve a brand code. +// +// Params: +// scope -- Picks which ksadmin binary to use. +// elevate -- Whether to run as root instead of the current user. +// path -- Path to send to ksadmin via `--print-xattr-tag-brand`. +// want_brand -- if valid, the brand code that ksadmin is expected to +// successfully retrieve, which may be the empty string. If +// nullopt, specifies that ksadmin should return EXIT_FAILURE. +void ExpectKSAdminXattrBrand(UpdaterScope scope, + bool elevate, + const base::FilePath& path, + std::optional<std::string> want_brand); #endif // BUILDFLAG(IS_MAC) } // namespace updater::test
diff --git a/chrome/updater/test/integration_tests_mac.mm b/chrome/updater/test/integration_tests_mac.mm index e726c37..ffab1f5 100644 --- a/chrome/updater/test/integration_tests_mac.mm +++ b/chrome/updater/test/integration_tests_mac.mm
@@ -559,6 +559,20 @@ ExpectKSAdminResult(scope, elevate, switches, std::move(want_tag), want_exit); } +void ExpectKSAdminXattrBrand(UpdaterScope scope, + bool elevate, + const base::FilePath& path, + std::optional<std::string> want_brand) { + int want_exit = EXIT_FAILURE; + if (want_brand) { + *want_brand = base::StrCat({*want_brand, "\n"}); + want_exit = EXIT_SUCCESS; + } + ExpectKSAdminResult(scope, elevate, + {{"--print-xattr-tag-brand", path.value()}}, + std::move(want_brand), want_exit); +} + void ExpectCRURegistrationCannotFindKSAdmin() { @autoreleasepool { CRURegistration* registration = [[CRURegistration alloc]
diff --git a/chromeos/constants/chromeos_features.cc b/chromeos/constants/chromeos_features.cc index ce60496..7930a0a 100644 --- a/chromeos/constants/chromeos_features.cc +++ b/chromeos/constants/chromeos_features.cc
@@ -540,7 +540,8 @@ } bool IsSystemBlurEnabled() { - return !base::FeatureList::IsEnabled(kDisableSystemBlur); + static bool disable_blur = base::FeatureList::IsEnabled(kDisableSystemBlur); + return !disable_blur; } bool IsPkcs12ToChapsDualWriteEnabled() {
diff --git a/clank b/clank index 0b6dec3..75c0772 160000 --- a/clank +++ b/clank
@@ -1 +1 @@ -Subproject commit 0b6dec35eaa57d814798237cad2cd20a7453d50d +Subproject commit 75c07728388055f0e7b7a0496693ee75827e374d
diff --git a/components/autofill/android/java/res/layout/autofill_dropdown_item.xml b/components/autofill/android/java/res/layout/autofill_dropdown_item.xml index e1086d6a..43ef2a02 100644 --- a/components/autofill/android/java/res/layout/autofill_dropdown_item.xml +++ b/components/autofill/android/java/res/layout/autofill_dropdown_item.xml
@@ -14,13 +14,6 @@ android:orientation="horizontal" > <!-- These layout params are overwritten in DropdownAdapter.java --> - <ImageView - android:id="@+id/start_dropdown_icon" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_margin="@dimen/autofill_dropdown_icon_margin" - tools:ignore="ContentDescription" /> - <LinearLayout android:id="@+id/dropdown_label_wrapper" android:layout_width="0dp"
diff --git a/components/autofill/android/java/src/org/chromium/components/autofill/AutofillDropdownAdapter.java b/components/autofill/android/java/src/org/chromium/components/autofill/AutofillDropdownAdapter.java index 815760e..2e492fe 100644 --- a/components/autofill/android/java/src/org/chromium/components/autofill/AutofillDropdownAdapter.java +++ b/components/autofill/android/java/src/org/chromium/components/autofill/AutofillDropdownAdapter.java
@@ -165,18 +165,10 @@ secondarySublabelView.setTextColor(mContext.getColor(item.getSublabelFontColorResId())); } - ImageView iconViewStart = (ImageView) layout.findViewById(R.id.start_dropdown_icon); ImageView iconViewEnd = (ImageView) layout.findViewById(R.id.end_dropdown_icon); - if (item.isIconAtStart()) { - iconViewEnd.setVisibility(View.GONE); - iconViewStart.setVisibility(View.VISIBLE); - } else { - iconViewStart.setVisibility(View.GONE); - iconViewEnd.setVisibility(View.VISIBLE); - } + iconViewEnd.setVisibility(View.VISIBLE); - ImageView iconView = - populateIconView(item.isIconAtStart() ? iconViewStart : iconViewEnd, item); + ImageView iconView = populateIconView(iconViewEnd, item); if (iconView != null) { iconView.setLayoutParams(getSizeAndMarginParamsForIconView(iconView, item)); }
diff --git a/components/autofill/android/java/src/org/chromium/components/autofill/AutofillSuggestion.java b/components/autofill/android/java/src/org/chromium/components/autofill/AutofillSuggestion.java index 6d4cbe0..f1cbb86 100644 --- a/components/autofill/android/java/src/org/chromium/components/autofill/AutofillSuggestion.java +++ b/components/autofill/android/java/src/org/chromium/components/autofill/AutofillSuggestion.java
@@ -26,7 +26,6 @@ private final @Nullable String mSecondarySublabel; private final @Nullable String mLabelContentDescription; private final int mIconId; - private final boolean mIsIconAtStart; private final int mSuggestionType; private final boolean mIsDeletable; private final boolean mApplyDeactivatedStyle; @@ -44,7 +43,6 @@ * @param sublabel The describing sublabel of the Autofill suggestion. * @param iconId The resource ID for the icon associated with the suggestion, or {@code * DropdownItem.NO_ICON} for no icon. - * @param isIconAtStart {@code true} if {@code iconId} is displayed before {@code label}. * @param popupItemId The type of suggestion. * @param isDeletable Whether the item can be deleted by the user. * @param applyDeactivatedStyle Whether to apply deactivated style to the suggestion. @@ -62,7 +60,6 @@ @Nullable String secondarySublabel, @Nullable String labelContentDescription, int iconId, - boolean isIconAtStart, @SuggestionType int popupItemId, boolean isDeletable, boolean applyDeactivatedStyle, @@ -77,7 +74,6 @@ mSecondarySublabel = secondarySublabel; mLabelContentDescription = labelContentDescription; mIconId = iconId; - mIsIconAtStart = isIconAtStart; mSuggestionType = popupItemId; mIsDeletable = isDeletable; mApplyDeactivatedStyle = applyDeactivatedStyle; @@ -122,14 +118,6 @@ } @Override - public boolean isIconAtStart() { - if (mIsIconAtStart) { - return true; - } - return super.isIconAtStart(); - } - - @Override public @Nullable GURL getCustomIconUrl() { return mCustomIconUrl; } @@ -187,7 +175,6 @@ && Objects.equals(this.mSecondarySublabel, other.mSecondarySublabel) && Objects.equals(this.mLabelContentDescription, other.mLabelContentDescription) && this.mIconId == other.mIconId - && this.mIsIconAtStart == other.mIsIconAtStart && this.mSuggestionType == other.mSuggestionType && this.mIsDeletable == other.mIsDeletable && this.mApplyDeactivatedStyle == other.mApplyDeactivatedStyle @@ -203,7 +190,6 @@ private int mIconId; private @Nullable GURL mCustomIconUrl; private @Nullable Drawable mIconDrawable; - private boolean mIsIconAtStart; private boolean mIsDeletable; private boolean mApplyDeactivatedStyle; private boolean mShouldDisplayTermsAvailable; @@ -231,11 +217,6 @@ return this; } - public Builder setIsIconAtStart(boolean isIconAtStart) { - this.mIsIconAtStart = isIconAtStart; - return this; - } - public Builder setIsDeletable(boolean isDeletable) { this.mIsDeletable = isDeletable; return this; @@ -303,7 +284,6 @@ mSecondarySubLabel, mLabelContentDescription, mIconId, - mIsIconAtStart, mSuggestionType, mIsDeletable, mApplyDeactivatedStyle,
diff --git a/components/autofill/core/browser/metrics/payments/bnpl_metrics.cc b/components/autofill/core/browser/metrics/payments/bnpl_metrics.cc index 451444f5..3b26690e2c 100644 --- a/components/autofill/core/browser/metrics/payments/bnpl_metrics.cc +++ b/components/autofill/core/browser/metrics/payments/bnpl_metrics.cc
@@ -10,7 +10,7 @@ #include "components/autofill/core/browser/payments/constants.h" namespace autofill::autofill_metrics { -namespace { + std::string GetHistogramSuffixFromIssuerId(std::string_view issuer_id) { if (issuer_id == kBnplAffirmIssuerId) { return "Affirm"; @@ -21,7 +21,6 @@ } NOTREACHED(); } -} // namespace void LogBnplPrefToggled(bool enabled) { base::UmaHistogramBoolean("Autofill.SettingsPage.BnplToggled", enabled); @@ -44,4 +43,19 @@ reason); } +void LogBnplPopupWindowShown(std::string_view issuer_id) { + std::string histogram_name = + base::StrCat({"Autofill.Bnpl.PopupWindowShown.", + GetHistogramSuffixFromIssuerId(issuer_id)}); + base::UmaHistogramBoolean(histogram_name, /*sample=*/true); +} + +void LogBnplPopupWindowResult(std::string_view issuer_id, + BnplFlowResult result) { + std::string histogram_name = + base::StrCat({"Autofill.Bnpl.PopupWindowResult.", + GetHistogramSuffixFromIssuerId(issuer_id)}); + base::UmaHistogramEnumeration(histogram_name, result); +} + } // namespace autofill::autofill_metrics
diff --git a/components/autofill/core/browser/metrics/payments/bnpl_metrics.h b/components/autofill/core/browser/metrics/payments/bnpl_metrics.h index b9a2f45..0e4ecf0 100644 --- a/components/autofill/core/browser/metrics/payments/bnpl_metrics.h +++ b/components/autofill/core/browser/metrics/payments/bnpl_metrics.h
@@ -7,8 +7,12 @@ #include <string_view> +#include "components/autofill/core/browser/payments/payments_window_manager.h" + namespace autofill::autofill_metrics { +using BnplFlowResult = payments::PaymentsWindowManager::BnplFlowResult; + // The reason why a BNPL suggestion was not shown on the page. enum class BnplSuggestionNotShownReason { // The checkout amount could not be extracted from the page. This value is @@ -22,6 +26,9 @@ kMaxValue = kCheckoutAmountNotSupported, }; +// Returns the histogram suffix corresponding to the given issuer_id. +std::string GetHistogramSuffixFromIssuerId(std::string_view issuer_id); + // Logs if the buy-now-pay-later preference is changed by the user through the // pay-over-time toggle in the payment methods settings page. Records true when // the user switches on buy-now-pay-later. Records false when the user switches @@ -37,6 +44,13 @@ // Logs that the BNPL suggestion was not shown and the reason why. void LogBnplSuggestionNotShownReason(BnplSuggestionNotShownReason reason); +// Logs that the BNPL popup window was shown. +void LogBnplPopupWindowShown(std::string_view issuer_id); + +// Logs the result of the BNPL popup window. +void LogBnplPopupWindowResult(std::string_view issuer_id, + BnplFlowResult result); + } // namespace autofill::autofill_metrics #endif // COMPONENTS_AUTOFILL_CORE_BROWSER_METRICS_PAYMENTS_BNPL_METRICS_H_
diff --git a/components/autofill/core/browser/metrics/payments/bnpl_metrics_unittest.cc b/components/autofill/core/browser/metrics/payments/bnpl_metrics_unittest.cc index fbe428a..e19b08a 100644 --- a/components/autofill/core/browser/metrics/payments/bnpl_metrics_unittest.cc +++ b/components/autofill/core/browser/metrics/payments/bnpl_metrics_unittest.cc
@@ -13,7 +13,11 @@ namespace autofill::autofill_metrics { -class BnplMetricsTest : public AutofillMetricsBaseTest, public testing::Test { +// Params of the BnplMetricsTest: +// -- std::string_view issuer_id; +class BnplMetricsTest : public AutofillMetricsBaseTest, + public testing::Test, + public testing::WithParamInterface<std::string_view> { public: BnplMetricsTest() = default; ~BnplMetricsTest() override = default; @@ -21,6 +25,8 @@ void SetUp() override { SetUpHelper(); } void TearDown() override { TearDownHelper(); } + + std::string_view GetBnplIssuerId() { return GetParam(); } }; // BNPL is currently only available for desktop platforms. @@ -69,28 +75,16 @@ 2); } -TEST_F(BnplMetricsTest, LogBnplTosDialogShownZip) { +TEST_P(BnplMetricsTest, LogBnplTosDialogShown) { base::HistogramTester histogram_tester; - LogBnplTosDialogShown(kBnplZipIssuerId); - histogram_tester.ExpectUniqueSample("Autofill.Bnpl.TosDialogShown.Zip", - /*sample=*/true, - /*expected_bucket_count=*/1); -} + std::string_view issuer_id = GetBnplIssuerId(); -TEST_F(BnplMetricsTest, LogBnplTosDialogShownAffirm) { - base::HistogramTester histogram_tester; - LogBnplTosDialogShown(kBnplAffirmIssuerId); - histogram_tester.ExpectUniqueSample("Autofill.Bnpl.TosDialogShown.Affirm", - /*sample=*/true, - /*expected_bucket_count=*/1); -} - -TEST_F(BnplMetricsTest, LogBnplTosDialogShownAfterpay) { - base::HistogramTester histogram_tester; - LogBnplTosDialogShown(kBnplAfterpayIssuerId); - histogram_tester.ExpectUniqueSample("Autofill.Bnpl.TosDialogShown.Afterpay", - /*sample=*/true, - /*expected_bucket_count=*/1); + LogBnplTosDialogShown(issuer_id); + histogram_tester.ExpectUniqueSample( + base::StrCat({"Autofill.Bnpl.TosDialogShown.", + GetHistogramSuffixFromIssuerId(issuer_id)}), + /*sample=*/true, + /*expected_bucket_count=*/1); } TEST_F(BnplMetricsTest, @@ -117,6 +111,57 @@ BnplSuggestionNotShownReason::kCheckoutAmountNotSupported, 1); } +TEST_P(BnplMetricsTest, LogBnplPopupWindowShown) { + base::HistogramTester histogram_tester; + std::string_view issuer_id = GetBnplIssuerId(); + + LogBnplPopupWindowShown(issuer_id); + histogram_tester.ExpectUniqueSample( + base::StrCat({"Autofill.Bnpl.PopupWindowShown.", + GetHistogramSuffixFromIssuerId(issuer_id)}), + /*sample=*/true, + /*expected_bucket_count=*/1); +} + +TEST_P(BnplMetricsTest, LogBnplPopupWindowResult_Success) { + base::HistogramTester histogram_tester; + std::string_view issuer_id = GetBnplIssuerId(); + + LogBnplPopupWindowResult(issuer_id, BnplFlowResult::kSuccess); + histogram_tester.ExpectUniqueSample( + base::StrCat({"Autofill.Bnpl.PopupWindowResult.", + GetHistogramSuffixFromIssuerId(issuer_id)}), + BnplFlowResult::kSuccess, 1); +} + +TEST_P(BnplMetricsTest, LogBnplPopupWindowResult_Failure) { + base::HistogramTester histogram_tester; + std::string_view issuer_id = GetBnplIssuerId(); + + LogBnplPopupWindowResult(issuer_id, BnplFlowResult::kFailure); + histogram_tester.ExpectUniqueSample( + base::StrCat({"Autofill.Bnpl.PopupWindowResult.", + GetHistogramSuffixFromIssuerId(issuer_id)}), + BnplFlowResult::kFailure, 1); +} + +TEST_P(BnplMetricsTest, LogBnplPopupWindowResult_UserClosed) { + base::HistogramTester histogram_tester; + std::string_view issuer_id = GetBnplIssuerId(); + + LogBnplPopupWindowResult(issuer_id, BnplFlowResult::kUserClosed); + histogram_tester.ExpectUniqueSample( + base::StrCat({"Autofill.Bnpl.PopupWindowResult.", + GetHistogramSuffixFromIssuerId(issuer_id)}), + BnplFlowResult::kUserClosed, 1); +} + +INSTANTIATE_TEST_SUITE_P(, + BnplMetricsTest, + testing::Values(kBnplAffirmIssuerId, + kBnplZipIssuerId, + kBnplAfterpayIssuerId)); + #endif // BUILDFLAG(IS_WIN) || BUILDFLAG(IS_MAC) || BUILDFLAG(IS_LINUX) || // BUILDFLAG(IS_CHROMEOS)
diff --git a/components/autofill/core/browser/payments/bnpl_manager.cc b/components/autofill/core/browser/payments/bnpl_manager.cc index 3bd3a493..9992e92b 100644 --- a/components/autofill/core/browser/payments/bnpl_manager.cc +++ b/components/autofill/core/browser/payments/bnpl_manager.cc
@@ -347,15 +347,20 @@ ongoing_flow_state_->redirect_url = std::move(response.redirect_url); ongoing_flow_state_->context_token = std::move(response.context_token); - PaymentsWindowManager::BnplContext bnpl_context; - bnpl_context.initial_url = ongoing_flow_state_->redirect_url; - bnpl_context.success_url_prefix = std::move(response.success_url_prefix); - bnpl_context.failure_url_prefix = std::move(response.failure_url_prefix); - bnpl_context.completion_callback = base::BindOnce( + PaymentsWindowManager::BnplContext payments_window_bnpl_context; + payments_window_bnpl_context.issuer_id = + ongoing_flow_state_->issuer.issuer_id(); + payments_window_bnpl_context.initial_url = + ongoing_flow_state_->redirect_url; + payments_window_bnpl_context.success_url_prefix = + std::move(response.success_url_prefix); + payments_window_bnpl_context.failure_url_prefix = + std::move(response.failure_url_prefix); + payments_window_bnpl_context.completion_callback = base::BindOnce( &BnplManager::OnPopupWindowCompleted, weak_factory_.GetWeakPtr()); payments_autofill_client().GetPaymentsWindowManager()->InitBnplFlow( - std::move(bnpl_context)); + std::move(payments_window_bnpl_context)); } else { payments_autofill_client().ShowAutofillErrorDialog( AutofillErrorDialogContext::WithBnplPermanentOrTemporaryError(
diff --git a/components/autofill/core/browser/payments/bnpl_manager_unittest.cc b/components/autofill/core/browser/payments/bnpl_manager_unittest.cc index 97825be..8c62895 100644 --- a/components/autofill/core/browser/payments/bnpl_manager_unittest.cc +++ b/components/autofill/core/browser/payments/bnpl_manager_unittest.cc
@@ -534,7 +534,8 @@ EXPECT_CALL(*static_cast<MockPaymentsWindowManager*>( autofill_client_->GetPaymentsAutofillClient() ->GetPaymentsWindowManager()), - InitBnplFlow(FieldsAre(kRedirectUrl, response.success_url_prefix, + InitBnplFlow(FieldsAre(linked_issuer.issuer_id(), kRedirectUrl, + response.success_url_prefix, response.failure_url_prefix, /*completion_callback=*/_))) .Times(1);
diff --git a/components/autofill/core/browser/payments/payments_window_manager.h b/components/autofill/core/browser/payments/payments_window_manager.h index 28e6289..91019248 100644 --- a/components/autofill/core/browser/payments/payments_window_manager.h +++ b/components/autofill/core/browser/payments/payments_window_manager.h
@@ -73,6 +73,11 @@ // The result of the BNPL flow, which will be sent to the caller that // initiated the flow. + // + // These values are persisted to logs. Entries should not be renumbered and + // numeric values should never be reused. + // + // LINT.IfChange(BnplFlowResult) enum class BnplFlowResult { // The BNPL flow was successful. kSuccess = 0, @@ -80,7 +85,10 @@ kFailure = 1, // The user closed the pop-up which ended the BNPL flow. kUserClosed = 2, + + kMaxValue = kUserClosed, }; + // LINT.ThenChange(/tools/metrics/histograms/metadata/autofill/enums.xml:BnplPopupWindowResult) using OnVcn3dsAuthenticationCompleteCallback = base::OnceCallback<void(Vcn3dsAuthenticationResponse)>; @@ -122,6 +130,8 @@ BnplContext& operator=(BnplContext&&); ~BnplContext(); + // The ID of the issuer for the BNPL flow. + std::string issuer_id; // The starting location of the BNPL flow, which is an initial URL to // open inside of the pop-up. GURL initial_url;
diff --git a/components/autofill/core/browser/suggestions/payments/payments_suggestion_generator.cc b/components/autofill/core/browser/suggestions/payments/payments_suggestion_generator.cc index 7f63e98..88ebebb 100644 --- a/components/autofill/core/browser/suggestions/payments/payments_suggestion_generator.cc +++ b/components/autofill/core/browser/suggestions/payments/payments_suggestion_generator.cc
@@ -472,9 +472,7 @@ Suggestion::Acceptability::kUnacceptableWithDeactivatedStyle; } suggestion.iph_metadata = Suggestion::IPHMetadata( - suggestion.HasDeactivatedStyle() && - base::FeatureList::IsEnabled( - features::kAutofillEnableVcnGrayOutForMerchantOptOut) + suggestion.HasDeactivatedStyle() ? &feature_engagement:: kIPHAutofillDisabledVirtualCardSuggestionFeature : &feature_engagement::kIPHAutofillVirtualCardSuggestionFeature); @@ -657,31 +655,6 @@ return virtual_card_guid_to_last_four_map; } -// Returns true if we should show a virtual card option for the server card -// `card`, false otherwise. -bool ShouldShowVirtualCardOptionForServerCard(const CreditCard& card, - const AutofillClient& client) { - // If the card is not enrolled into virtual cards, we should not show a - // virtual card suggestion for it. - if (card.virtual_card_enrollment_state() != - CreditCard::VirtualCardEnrollmentState::kEnrolled) { - return false; - } - // We should not show a suggestion for this card if the autofill - // optimization guide returns that this suggestion should be blocked. - if (auto* autofill_optimization_guide = - client.GetAutofillOptimizationGuide()) { - return !autofill_optimization_guide->ShouldBlockFormFieldSuggestion( - client.GetLastCommittedPrimaryMainFrameOrigin().GetURL(), - card) || - base::FeatureList::IsEnabled( - features::kAutofillEnableVcnGrayOutForMerchantOptOut); - } - // No conditions to prevent displaying a virtual card suggestion were - // found, so return true. - return true; -} - // Returns display name based on `issuer_id` in a vector. std::u16string GetDisplayNameForIssuerId(const std::string& issuer_id) { if (issuer_id == "paypay") { @@ -733,8 +706,11 @@ return false; } candidate_card = candidate_server_card; - return ShouldShowVirtualCardOptionForServerCard(*candidate_server_card, - client); + + // Virtual card suggestion is shown only when the card is enrolled into + // virtual cards. + return candidate_card->virtual_card_enrollment_state() == + CreditCard::VirtualCardEnrollmentState::kEnrolled; } // Returns the local and server cards ordered by the Autofill ranking. @@ -820,10 +796,6 @@ ? Suggestion::Acceptability::kAcceptable : Suggestion::Acceptability::kUnacceptable; suggestion.payload = Suggestion::Guid(credit_card.guid()); -#if BUILDFLAG(IS_ANDROID) - // The card art icon should always be shown at the start of the suggestion. - suggestion.is_icon_at_start = true; -#endif // BUILDFLAG(IS_ANDROID) // Manual fallback suggestions labels are computed as if the triggering field // type was the credit card number.
diff --git a/components/autofill/core/browser/suggestions/payments/payments_suggestion_generator_unittest.cc b/components/autofill/core/browser/suggestions/payments/payments_suggestion_generator_unittest.cc index d2164c3..edd27b2d 100644 --- a/components/autofill/core/browser/suggestions/payments/payments_suggestion_generator_unittest.cc +++ b/components/autofill/core/browser/suggestions/payments/payments_suggestion_generator_unittest.cc
@@ -1569,9 +1569,6 @@ // of virtual cards. TEST_F(PaymentsSuggestionGeneratorTest, ShouldShowVirtualCardOption_InDisabledStateForOptedOutMerchants) { - base::test::ScopedFeatureList features( - features::kAutofillEnableVcnGrayOutForMerchantOptOut); - // Create an enrolled server card. CreditCard server_card = test::GetMaskedServerCardEnrolledIntoVirtualCardNumber(); @@ -1587,36 +1584,6 @@ ShouldShowVirtualCardOptionForTest(&server_card, *autofill_client())); } -// Test that the virtual card option is not shown if the merchant is opted-out -// of virtual cards. -TEST_F(PaymentsSuggestionGeneratorTest, - ShouldNotShowVirtualCardOption_MerchantOptedOutOfVirtualCards) { - base::test::ScopedFeatureList features; - features.InitAndDisableFeature( - features::kAutofillEnableVcnGrayOutForMerchantOptOut); - // Create an enrolled server card. - CreditCard server_card = - CreateServerCard(/*guid=*/"00000000-0000-0000-0000-000000000001"); - server_card.set_virtual_card_enrollment_state( - CreditCard::VirtualCardEnrollmentState::kEnrolled); - payments_data().AddServerCreditCard(server_card); - - // Create a local card with same information. - CreditCard local_card = - CreateLocalCard(/*guid=*/"00000000-0000-0000-0000-000000000002"); - - // If the URL is opted-out of virtual cards for `server_card`, do not display - // the virtual card suggestion. - auto* optimization_guide = autofill_client()->GetAutofillOptimizationGuide(); - ON_CALL(*static_cast<MockAutofillOptimizationGuide*>(optimization_guide), - ShouldBlockFormFieldSuggestion) - .WillByDefault(testing::Return(true)); - EXPECT_FALSE( - ShouldShowVirtualCardOptionForTest(&server_card, *autofill_client())); - EXPECT_FALSE( - ShouldShowVirtualCardOptionForTest(&local_card, *autofill_client())); -} - // Test that the virtual card option is not shown if the server card we might be // showing a virtual card option for is not enrolled into virtual card. TEST_F(PaymentsSuggestionGeneratorTest, @@ -2089,19 +2056,7 @@ // This class helps test the credit card contents that are displayed in // Autofill suggestions. It covers suggestions on Desktop/Android dropdown, // and on Android keyboard accessory. -class AutofillCreditCardSuggestionContentTest - : public PaymentsSuggestionGeneratorTest { - public: - AutofillCreditCardSuggestionContentTest() { - feature_list_metadata_.InitAndEnableFeature( - features::kAutofillEnableVcnGrayOutForMerchantOptOut); - } - - ~AutofillCreditCardSuggestionContentTest() override = default; - - private: - base::test::ScopedFeatureList feature_list_metadata_; -}; +using AutofillCreditCardSuggestionContentTest = PaymentsSuggestionGeneratorTest; // Verify that the suggestion's texts are populated correctly for a virtual card // suggestion when the cardholder name field is focused. @@ -2516,17 +2471,11 @@ private: void SetUp() override { AutofillCreditCardSuggestionContentTest::SetUp(); - // Content test is only needed when the gray-out feature is enabled. - // Otherwise user will not see a VCN for opted out merchants. - scoped_feature_list_.InitWithFeatureState( - features::kAutofillEnableVcnGrayOutForMerchantOptOut, true); - ON_CALL(*static_cast<MockAutofillOptimizationGuide*>( autofill_client()->GetAutofillOptimizationGuide()), ShouldBlockFormFieldSuggestion) .WillByDefault(testing::Return(is_merchant_opted_out())); } - base::test::ScopedFeatureList scoped_feature_list_; }; INSTANTIATE_TEST_SUITE_P(
diff --git a/components/autofill/core/browser/suggestions/suggestion.h b/components/autofill/core/browser/suggestions/suggestion.h index a5b7a3eb..7d25a995 100644 --- a/components/autofill/core/browser/suggestions/suggestion.h +++ b/components/autofill/core/browser/suggestions/suggestion.h
@@ -461,9 +461,6 @@ // submenus. std::vector<Suggestion> children; #if BUILDFLAG(IS_ANDROID) - // On Android, the icon can be at the start of the suggestion before the label - // or at the end of the label. - bool is_icon_at_start = false; // TODO(crbug.com/346469807): Remove once strings are passed directly. std::u16string iph_description_text; #endif // BUILDFLAG(IS_ANDROID)
diff --git a/components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/FullscreenAlertDialog.java b/components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/FullscreenAlertDialog.java index 5d2d287..3f1d1c8 100644 --- a/components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/FullscreenAlertDialog.java +++ b/components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/FullscreenAlertDialog.java
@@ -181,6 +181,8 @@ super.setView(automotiveLayout); } else if (mEdgeToEdgeLayout != null) { super.setView(mEdgeToEdgeLayout.wrapContentView(view)); + } else { + super.setView(view); } return this; }
diff --git a/components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/FullscreenAlertDialogUnitTest.java b/components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/FullscreenAlertDialogUnitTest.java index 73ee873..718b015 100644 --- a/components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/FullscreenAlertDialogUnitTest.java +++ b/components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/FullscreenAlertDialogUnitTest.java
@@ -71,6 +71,36 @@ } @Test + public void notPadForInsetsWithBuilder() { + launchActivity(); + + FrameLayout dialogView = new FrameLayout(mActivity); + AlertDialog dialog = + new FullscreenAlertDialog.Builder(mActivity, /* shouldPadForContent= */ false) + .setView(dialogView) + .create(); + dialog.show(); + + Assert.assertFalse( + "Dialog view should not be wrapped.", + dialogView.getParent() instanceof EdgeToEdgeBaseLayout); + } + + @Test + public void notPadForInsetsWithConstructor() { + launchActivity(); + + FrameLayout dialogView = new FrameLayout(mActivity); + AlertDialog dialog = new FullscreenAlertDialog(mActivity, /* shouldPadForContent= */ false); + dialog.setView(dialogView); + dialog.show(); + + Assert.assertFalse( + "Dialog view should not be wrapped.", + dialogView.getParent() instanceof EdgeToEdgeBaseLayout); + } + + @Test public void padForInsetsDisabledOnAutomotive() { mAutomotiveRule.setIsAutomotive(true); launchActivity();
diff --git a/components/collaboration/internal/metrics.cc b/components/collaboration/internal/metrics.cc index de4bef7..455bfffe 100644 --- a/components/collaboration/internal/metrics.cc +++ b/components/collaboration/internal/metrics.cc
@@ -9,6 +9,7 @@ #include "components/data_sharing/public/logger.h" #include "components/data_sharing/public/logger_common.mojom.h" #include "components/data_sharing/public/logger_utils.h" +#include "ui/base/page_transition_types.h" namespace collaboration::metrics { @@ -153,6 +154,16 @@ switch (entry) { case CollaborationServiceJoinEntryPoint::kUnknown: return "Unknown"; + case CollaborationServiceJoinEntryPoint::kLinkClick: + return "LinkClick"; + case CollaborationServiceJoinEntryPoint::kUserTyped: + return "UserTyped"; + case CollaborationServiceJoinEntryPoint::kExternalApp: + return "ExternalApp"; + case CollaborationServiceJoinEntryPoint::kForwardBackButton: + return "ForwardBackButton"; + case CollaborationServiceJoinEntryPoint::kRedirect: + return "Redirect"; } } @@ -250,6 +261,40 @@ logger, CreateJoinEntryLogToString(entry)); } +void RecordJoinPageTransitionType(data_sharing::Logger* logger, + ui::PageTransition transition) { + switch (ui::PageTransitionStripQualifier(transition)) { + case ui::PageTransition::PAGE_TRANSITION_LINK: + RecordJoinEntryPoint(logger, + CollaborationServiceJoinEntryPoint::kLinkClick); + break; + case ui::PageTransition::PAGE_TRANSITION_TYPED: + case ui::PageTransition::PAGE_TRANSITION_FROM_ADDRESS_BAR: + RecordJoinEntryPoint(logger, + CollaborationServiceJoinEntryPoint::kUserTyped); + break; + case ui::PageTransition::PAGE_TRANSITION_FROM_API: + RecordJoinEntryPoint(logger, + CollaborationServiceJoinEntryPoint::kExternalApp); + break; + case ui::PageTransition::PAGE_TRANSITION_FORWARD_BACK: + RecordJoinEntryPoint( + logger, CollaborationServiceJoinEntryPoint::kForwardBackButton); + break; + case ui::PageTransition::PAGE_TRANSITION_CHAIN_START: + case ui::PageTransition::PAGE_TRANSITION_CHAIN_END: + case ui::PageTransition::PAGE_TRANSITION_CLIENT_REDIRECT: + case ui::PageTransition::PAGE_TRANSITION_SERVER_REDIRECT: + case ui::PageTransition::PAGE_TRANSITION_IS_REDIRECT_MASK: + RecordJoinEntryPoint(logger, + CollaborationServiceJoinEntryPoint::kRedirect); + break; + default: + RecordJoinEntryPoint(logger, + CollaborationServiceJoinEntryPoint::kUnknown); + } +} + void RecordShareOrManageEntryPoint( data_sharing::Logger* logger, CollaborationServiceShareOrManageEntryPoint entry) {
diff --git a/components/collaboration/internal/metrics.h b/components/collaboration/internal/metrics.h index b8dec75fe..fc041e1 100644 --- a/components/collaboration/internal/metrics.h +++ b/components/collaboration/internal/metrics.h
@@ -7,6 +7,7 @@ #include "components/collaboration/public/collaboration_flow_entry_point.h" #include "components/collaboration/public/collaboration_flow_type.h" +#include "ui/base/page_transition_types.h" namespace data_sharing { class Logger; @@ -103,6 +104,8 @@ CollaborationServiceShareOrManageEvent share_or_manage_event); void RecordJoinEntryPoint(data_sharing::Logger* logger, CollaborationServiceJoinEntryPoint entry); +void RecordJoinPageTransitionType(data_sharing::Logger* logger, + ui::PageTransition transition); void RecordShareOrManageEntryPoint( data_sharing::Logger* logger, CollaborationServiceShareOrManageEntryPoint entry);
diff --git a/components/collaboration/public/collaboration_flow_entry_point.h b/components/collaboration/public/collaboration_flow_entry_point.h index 22c9851..fcd98454 100644 --- a/components/collaboration/public/collaboration_flow_entry_point.h +++ b/components/collaboration/public/collaboration_flow_entry_point.h
@@ -17,7 +17,12 @@ // org.chromium.components.collaboration) enum class CollaborationServiceJoinEntryPoint { kUnknown = 0, - kMaxValue = kUnknown, + kLinkClick = 1, + kUserTyped = 2, + kExternalApp = 3, + kForwardBackButton = 4, + kRedirect = 5, + kMaxValue = kRedirect, }; // LINT.ThenChange(//tools/metrics/histograms/metadata/collaboration_service/enums.xml:CollaborationServiceJoinEntryPoint)
diff --git a/components/device_signals/core/browser/signals_types.cc b/components/device_signals/core/browser/signals_types.cc index 3c2c205..34d3a306 100644 --- a/components/device_signals/core/browser/signals_types.cc +++ b/components/device_signals/core/browser/signals_types.cc
@@ -111,6 +111,18 @@ OsSignalsResponse::~OsSignalsResponse() = default; +ProfileSignalsResponse::ProfileSignalsResponse() = default; +ProfileSignalsResponse::ProfileSignalsResponse(const ProfileSignalsResponse&) = + default; + +ProfileSignalsResponse& ProfileSignalsResponse::operator=( + const ProfileSignalsResponse&) = default; + +bool ProfileSignalsResponse::operator==(const ProfileSignalsResponse&) const = + default; + +ProfileSignalsResponse::~ProfileSignalsResponse() = default; + FileSystemInfoResponse::FileSystemInfoResponse() = default; FileSystemInfoResponse::FileSystemInfoResponse(const FileSystemInfoResponse&) = default;
diff --git a/components/device_signals/core/browser/signals_types.h b/components/device_signals/core/browser/signals_types.h index 8d7efa6..0f752176 100644 --- a/components/device_signals/core/browser/signals_types.h +++ b/components/device_signals/core/browser/signals_types.h
@@ -12,6 +12,8 @@ #include "base/values.h" #include "build/build_config.h" #include "components/device_signals/core/common/common_types.h" +#include "components/enterprise/connectors/core/common.h" +#include "components/safe_browsing/core/common/safe_browsing_prefs.h" #if BUILDFLAG(IS_WIN) #include "components/device_signals/core/common/win/win_types.h" @@ -231,6 +233,28 @@ std::optional<std::string> windows_user_domain = std::nullopt; }; +struct ProfileSignalsResponse : BaseSignalResponse { + ProfileSignalsResponse(); + + ProfileSignalsResponse(const ProfileSignalsResponse&); + ProfileSignalsResponse& operator=(const ProfileSignalsResponse&); + + bool operator==(const ProfileSignalsResponse&) const; + + ~ProfileSignalsResponse() override; + + bool built_in_dns_client_enabled; + bool chrome_remote_desktop_app_blocked; + std::optional<safe_browsing::PasswordProtectionTrigger> + password_protection_warning_trigger = std::nullopt; + std::optional<std::string> profile_enrollment_domain = std::nullopt; + safe_browsing::SafeBrowsingState safe_browsing_protection_level; + bool site_isolation_enabled; + + // Enterprise cloud content analysis exclusive + enterprise_connectors::EnterpriseRealTimeUrlCheckMode realtime_url_check_mode; +}; + struct FileSystemInfoResponse : BaseSignalResponse { FileSystemInfoResponse(); @@ -308,6 +332,7 @@ #endif // BUILDFLAG(IS_WIN) std::optional<SettingsResponse> settings_response = std::nullopt; std::optional<OsSignalsResponse> os_signals_response = std::nullopt; + std::optional<ProfileSignalsResponse> profile_signals_response = std::nullopt; std::optional<FileSystemInfoResponse> file_system_info_response = std::nullopt;
diff --git a/components/dom_distiller/content/browser/distiller_page_web_contents.cc b/components/dom_distiller/content/browser/distiller_page_web_contents.cc index f0900d9..a4800b2e 100644 --- a/components/dom_distiller/content/browser/distiller_page_web_contents.cc +++ b/components/dom_distiller/content/browser/distiller_page_web_contents.cc
@@ -9,6 +9,7 @@ #include "base/functional/bind.h" #include "base/functional/callback.h" +#include "base/metrics/histogram_functions.h" #include "base/strings/utf_string_conversions.h" #include "components/dom_distiller/content/browser/distiller_javascript_utils.h" #include "components/dom_distiller/core/distiller_page.h" @@ -183,7 +184,8 @@ if (!javascript_start.is_null()) { base::TimeDelta javascript_time = base::TimeTicks::Now() - javascript_start; - DVLOG(1) << "DomDistiller.Time.RunJavaScript = " << javascript_time; + base::UmaHistogramTimes("DomDistiller.Time.RunDistillationJavaScript", + javascript_time); } DistillerPage::OnDistillationDone(page_url, &value);
diff --git a/components/dom_distiller/core/distiller.cc b/components/dom_distiller/core/distiller.cc index ea672c9..7d15ab5 100644 --- a/components/dom_distiller/core/distiller.cc +++ b/components/dom_distiller/core/distiller.cc
@@ -15,6 +15,7 @@ #include "base/functional/callback.h" #include "base/location.h" #include "base/memory/ptr_util.h" +#include "base/metrics/histogram_functions.h" #include "base/strings/string_number_conversions.h" #include "base/strings/utf_string_conversions.h" #include "base/task/single_thread_task_runner.h" @@ -240,6 +241,10 @@ distiller_result->content_images(img_num).url()); } + base::UmaHistogramCounts100000( + "DomDistiller.WordCount", + distiller_result->statistics_info().word_count()); + AddPageIfDone(page_num); DistillNextPage(); }
diff --git a/components/feed/feed_feature_list.cc b/components/feed/feed_feature_list.cc index 554a4d8c..5353c65 100644 --- a/components/feed/feed_feature_list.cc +++ b/components/feed/feed_feature_list.cc
@@ -17,6 +17,10 @@ namespace feed { +const char kFeedHeaderRemovalTreatmentParam[] = "treatment"; +const char kFeedHeaderRemovalTreatmentValue1[] = "label"; +const char kFeedHeaderRemovalTreatmentValue2[] = "none"; + // InterestFeedV2 takes precedence over InterestFeedContentSuggestions. // InterestFeedV2 is cached in ChromeCachedFlags. If the default value here is // changed, please update the cached one's default value in CachedFeatureFlags. @@ -109,6 +113,10 @@ "FeedStreaming", base::FEATURE_DISABLED_BY_DEFAULT); +BASE_FEATURE(kFeedHeaderRemoval, + "FeedHeaderRemoval", + base::FEATURE_DISABLED_BY_DEFAULT); + bool IsWebFeedEnabledForLocale(const std::string& country) { const std::vector<std::string> launched_countries = {"AU", "CA", "GB", "NZ", "US", "ZA"};
diff --git a/components/feed/feed_feature_list.h b/components/feed/feed_feature_list.h index 92083e2..6b7fd102 100644 --- a/components/feed/feed_feature_list.h +++ b/components/feed/feed_feature_list.h
@@ -24,6 +24,13 @@ } COMPONENT_EXPORT(COMPONENTS_FEED_FEATURE_LIST) +extern const char kFeedHeaderRemovalTreatmentParam[]; +COMPONENT_EXPORT(COMPONENTS_FEED_FEATURE_LIST) +extern const char kFeedHeaderRemovalTreatmentValue1[]; +COMPONENT_EXPORT(COMPONENTS_FEED_FEATURE_LIST) +extern const char kFeedHeaderRemovalTreatmentValue2[]; + +COMPONENT_EXPORT(COMPONENTS_FEED_FEATURE_LIST) BASE_DECLARE_FEATURE(kInterestFeedV2); // Use the new DiscoFeed endpoint. @@ -122,6 +129,10 @@ COMPONENT_EXPORT(COMPONENTS_FEED_FEATURE_LIST) BASE_DECLARE_FEATURE(kFeedStreaming); +// Feature that removes feed header. +COMPONENT_EXPORT(COMPONENTS_FEED_FEATURE_LIST) +BASE_DECLARE_FEATURE(kFeedHeaderRemoval); + COMPONENT_EXPORT(COMPONENTS_FEED_FEATURE_LIST) bool IsWebFeedEnabledForLocale(const std::string& country);
diff --git a/components/omnibox/browser/autocomplete_match.cc b/components/omnibox/browser/autocomplete_match.cc index e67b1fe..05106cc 100644 --- a/components/omnibox/browser/autocomplete_match.cc +++ b/components/omnibox/browser/autocomplete_match.cc
@@ -114,12 +114,13 @@ constexpr auto kProviderPrefMap = base::MakeFixedFlatMap<AutocompleteProvider::Type, int>({ - // Prefer live document suggestions. We check provider type instead + // Prefer live remote suggestions. We check provider type instead // of match type in order to distinguish live suggestions from the - // document provider from stale suggestions from the shortcuts - // providers, because the latter omits changing metadata such as last - // access date. + // from their stale counterparts from the shortcut, history, and + // bookmark providers. The latter often have stale metadata such as + // last access date. {AutocompleteProvider::TYPE_DOCUMENT, 2}, + {AutocompleteProvider::TYPE_ENTERPRISE_SEARCH_AGGREGATOR, 2}, // Prefer bookmark suggestions, as: // 1) Their titles may be explicitly set. // 2) They may display enhanced information such as the bookmark @@ -145,7 +146,8 @@ size_t ACMatchKeyHash<Args...>::operator()( const ACMatchKey<Args...>& key) const { size_t seed = 0; - // Compute a hash by applying `HashCombine` to each element of the "key" tuple. + // Compute a hash by applying `HashCombine` to each element of the "key" + // tuple. std::apply( [&seed](auto&&... args) { ((base::HashCombine(seed, args)), ...); }, key); return seed; @@ -1875,6 +1877,11 @@ duplicate_match.description_class_for_shortcuts; swap_contents_and_description = duplicate_match.swap_contents_and_description; + + // Image data should stay in sync with the suggestion text. + image_dominant_color = duplicate_match.image_dominant_color; + image_url = duplicate_match.image_url; + icon_url = duplicate_match.icon_url; } // Copy `rich_autocompletion_triggered` for counterfactual logging.
diff --git a/components/omnibox/common/omnibox_feature_configs.cc b/components/omnibox/common/omnibox_feature_configs.cc index d559ce1..28d00ea 100644 --- a/components/omnibox/common/omnibox_feature_configs.cc +++ b/components/omnibox/common/omnibox_feature_configs.cc
@@ -110,6 +110,9 @@ disable_drive = base::FeatureParam<bool>(&kSearchAggregatorProvider, "disable_drive", true) .Get(); + multiple_requests = base::FeatureParam<bool>(&kSearchAggregatorProvider, + "multiple_requests", false) + .Get(); scoring_max_matches_created_per_type = base::FeatureParam<size_t>(&kSearchAggregatorProvider,
diff --git a/components/omnibox/common/omnibox_feature_configs.h b/components/omnibox/common/omnibox_feature_configs.h index 3abd31c5..17125b2 100644 --- a/components/omnibox/common/omnibox_feature_configs.h +++ b/components/omnibox/common/omnibox_feature_configs.h
@@ -193,6 +193,9 @@ // provider will run unscoped. Either way, doc provider won't run when in the // enterprise scope. bool disable_drive; + // If true, the `EnterpriseSearchAggregatorSuggestionsService` will make + // parallel requests for each type of suggestion. + bool multiple_requests; // See comments in enterprise_search_aggregator_provider.cc size_t scoring_max_matches_created_per_type;
diff --git a/components/optimization_guide/core/bloom_filter.cc b/components/optimization_guide/core/bloom_filter.cc index ef284b11..755907be 100644 --- a/components/optimization_guide/core/bloom_filter.cc +++ b/components/optimization_guide/core/bloom_filter.cc
@@ -2,17 +2,13 @@ // 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 "components/optimization_guide/core/bloom_filter.h" #include <stddef.h> #include <stdint.h> #include "base/check_op.h" +#include "base/containers/span.h" #include "third_party/smhasher/src/src/MurmurHash3.h" namespace optimization_guide { @@ -33,19 +29,13 @@ } // namespace BloomFilter::BloomFilter(uint32_t num_hash_functions, uint32_t num_bits) - - : num_hash_functions_(num_hash_functions), - num_bits_(num_bits), - bytes_(((num_bits + 7) / 8), 0) { - // May be created on one thread but used on another. The first call to - // CalledOnValidSequence() will re-bind it. - DETACH_FROM_SEQUENCE(sequence_checker_); -} + : BloomFilter(num_hash_functions, + num_bits, + std::string(((num_bits + 7) / 8), 0)) {} BloomFilter::BloomFilter(uint32_t num_hash_functions, uint32_t num_bits, - std::string filter_data) - + const std::string& filter_data) : num_hash_functions_(num_hash_functions), num_bits_(num_bits), bytes_(filter_data.size()) { @@ -53,7 +43,7 @@ // CalledOnValidSequence() will re-bind it. DETACH_FROM_SEQUENCE(sequence_checker_); CHECK_GE(filter_data.size() * 8, num_bits); - memcpy(&bytes_[0], filter_data.data(), filter_data.size()); + base::span(bytes_).copy_from(base::as_byte_span(filter_data)); } BloomFilter::~BloomFilter() = default; @@ -64,8 +54,9 @@ uint64_t n = MurmurHash3(str, i) % num_bits_; uint32_t byte_index = (n / 8); uint32_t bit_index = n % 8; - if ((bytes_[byte_index] & (1 << bit_index)) == 0) + if ((bytes_[byte_index] & (1 << bit_index)) == 0) { return false; + } } return true; }
diff --git a/components/optimization_guide/core/bloom_filter.h b/components/optimization_guide/core/bloom_filter.h index b06a48c5..b62dea1 100644 --- a/components/optimization_guide/core/bloom_filter.h +++ b/components/optimization_guide/core/bloom_filter.h
@@ -30,7 +30,7 @@ // type) and using |num_hash_functions| per entry. BloomFilter(uint32_t num_hash_functions, uint32_t num_bits, - std::string filter_data); + const std::string& filter_data); BloomFilter(const BloomFilter&) = delete; BloomFilter& operator=(const BloomFilter&) = delete;
diff --git a/components/optimization_guide/core/hints_processing_util.cc b/components/optimization_guide/core/hints_processing_util.cc index 50fa81a7..6ff3609 100644 --- a/components/optimization_guide/core/hints_processing_util.cc +++ b/components/optimization_guide/core/hints_processing_util.cc
@@ -179,6 +179,8 @@ return "GlicContextualCueing"; case proto::OptimizationType::GLIC_ZERO_STATE_SUGGESTIONS: return "GlicZeroStateSuggestions"; + case proto::OptimizationType::GLIC_ACTION_PAGE_BLOCK: + return "GlicActionPageBlock"; } // The returned string is used to record histograms for the optimization type.
diff --git a/components/optimization_guide/core/mock_optimization_guide_model_executor.cc b/components/optimization_guide/core/mock_optimization_guide_model_executor.cc index 636b1df7..593c9c0 100644 --- a/components/optimization_guide/core/mock_optimization_guide_model_executor.cc +++ b/components/optimization_guide/core/mock_optimization_guide_model_executor.cc
@@ -61,6 +61,12 @@ .WillByDefault([impl](const auto& input, auto callback) { impl->ExecuteModel(input, std::move(callback)); }); + ON_CALL(*this, ExecuteModelWithResponseJsonSchema) + .WillByDefault([impl](const auto& input, const auto& response_json_schema, + auto callback) { + impl->ExecuteModelWithResponseJsonSchema(input, response_json_schema, + std::move(callback)); + }); ON_CALL(*this, GetSizeInTokens) .WillByDefault([impl](const auto& input, auto callback) { impl->GetSizeInTokens(input, std::move(callback));
diff --git a/components/optimization_guide/core/mock_optimization_guide_model_executor.h b/components/optimization_guide/core/mock_optimization_guide_model_executor.h index 2f7a8cda..5511972 100644 --- a/components/optimization_guide/core/mock_optimization_guide_model_executor.h +++ b/components/optimization_guide/core/mock_optimization_guide_model_executor.h
@@ -73,6 +73,12 @@ ExecuteModel, (const google::protobuf::MessageLite& request_metadata, OptimizationGuideModelExecutionResultStreamingCallback callback)); + MOCK_METHOD( + void, + ExecuteModelWithResponseJsonSchema, + (const google::protobuf::MessageLite& request_metadata, + const std::optional<std::string>& response_json_schema, + OptimizationGuideModelExecutionResultStreamingCallback callback)); MOCK_METHOD(void, GetSizeInTokens, (const std::string& text,
diff --git a/components/optimization_guide/core/model_execution/on_device_execution.cc b/components/optimization_guide/core/model_execution/on_device_execution.cc index d1f520e..035bd68f 100644 --- a/components/optimization_guide/core/model_execution/on_device_execution.cc +++ b/components/optimization_guide/core/model_execution/on_device_execution.cc
@@ -121,6 +121,7 @@ OnDeviceOptions opts, ExecuteRemoteFn execute_remote_fn, MultimodalMessage message, + std::optional<std::string> response_json_schema, std::unique_ptr<ResultLogger> logger, OptimizationGuideModelExecutionResultStreamingCallback callback, base::OnceCallback<void(bool)> cleanup_callback) @@ -128,6 +129,7 @@ opts_(std::move(opts)), execute_remote_fn_(execute_remote_fn), last_message_(std::move(message)), + response_json_schema_(std::move(response_json_schema)), histogram_logger_(std::move(logger)), callback_(std::move(callback)), cleanup_callback_(std::move(cleanup_callback)) { @@ -210,6 +212,7 @@ auto options = on_device_model::mojom::GenerateOptions::New(); options->max_output_tokens = opts_.token_limits.max_output_tokens; + options->response_json_schema = std::move(response_json_schema_); opts_.safety_checker->RunRequestChecks( last_message_,
diff --git a/components/optimization_guide/core/model_execution/on_device_execution.h b/components/optimization_guide/core/model_execution/on_device_execution.h index 23e8c2e5..826b352 100644 --- a/components/optimization_guide/core/model_execution/on_device_execution.h +++ b/components/optimization_guide/core/model_execution/on_device_execution.h
@@ -120,6 +120,7 @@ OnDeviceOptions opts, ExecuteRemoteFn execute_remote_fn, MultimodalMessage message, + std::optional<std::string> response_json_schema, std::unique_ptr<ResultLogger> logger, OptimizationGuideModelExecutionResultStreamingCallback callback, base::OnceCallback<void(bool)> cleanup_callback); @@ -228,6 +229,8 @@ // The request message. MultimodalMessage last_message_; + // A JSON schema defining structured output requirements for the response. + std::optional<std::string> response_json_schema_; // Time ExecuteModel() was called. base::TimeTicks start_; // Used to log the result of ExecuteModel().
diff --git a/components/optimization_guide/core/model_execution/session_impl.cc b/components/optimization_guide/core/model_execution/session_impl.cc index 0f236415..728df13 100644 --- a/components/optimization_guide/core/model_execution/session_impl.cc +++ b/components/optimization_guide/core/model_execution/session_impl.cc
@@ -176,6 +176,16 @@ const google::protobuf::MessageLite& request_metadata, optimization_guide::OptimizationGuideModelExecutionResultStreamingCallback callback) { + ExecuteModelWithResponseJsonSchema(request_metadata, + /*response_json_schema=*/std::nullopt, + std::move(callback)); +} + +void SessionImpl::ExecuteModelWithResponseJsonSchema( + const google::protobuf::MessageLite& request_metadata, + const std::optional<std::string>& response_json_schema, + optimization_guide::OptimizationGuideModelExecutionResultStreamingCallback + callback) { auto logger = std::make_unique<OnDeviceExecution::ResultLogger>(feature_); // Compute the amount of time context would have for processing assuming @@ -212,7 +222,8 @@ // Set new pending response. on_device_execution_.emplace( feature_, on_device_context_->opts(), execute_remote_fn_, - std::move(merged_request), std::move(logger), std::move(callback), + std::move(merged_request), std::move(response_json_schema), + std::move(logger), std::move(callback), base::BindOnce(&SessionImpl::OnDeviceExecutionTerminated, weak_ptr_factory_.GetWeakPtr()));
diff --git a/components/optimization_guide/core/model_execution/session_impl.h b/components/optimization_guide/core/model_execution/session_impl.h index 209d56f74..b30eebe 100644 --- a/components/optimization_guide/core/model_execution/session_impl.h +++ b/components/optimization_guide/core/model_execution/session_impl.h
@@ -72,6 +72,10 @@ void ExecuteModel( const google::protobuf::MessageLite& request_metadata, OptimizationGuideModelExecutionResultStreamingCallback callback) override; + void ExecuteModelWithResponseJsonSchema( + const google::protobuf::MessageLite& request_metadata, + const std::optional<std::string>& response_json_schema, + OptimizationGuideModelExecutionResultStreamingCallback callback) override; void GetSizeInTokens( const std::string& text, OptimizationGuideModelSizeInTokenCallback callback) override;
diff --git a/components/optimization_guide/core/optimization_guide_model_executor.h b/components/optimization_guide/core/optimization_guide_model_executor.h index f1a44f4b0f..729b0bf 100644 --- a/components/optimization_guide/core/optimization_guide_model_executor.h +++ b/components/optimization_guide/core/optimization_guide_model_executor.h
@@ -275,6 +275,13 @@ const google::protobuf::MessageLite& request_metadata, OptimizationGuideModelExecutionResultStreamingCallback callback) = 0; + // A JSON schema is provided to define structured output requirements for + // the response. + virtual void ExecuteModelWithResponseJsonSchema( + const google::protobuf::MessageLite& request_metadata, + const std::optional<std::string>& response_json_schema, + OptimizationGuideModelExecutionResultStreamingCallback callback) = 0; + // Call `GetSizeInTokens()` from the model to get the size of the given text // in tokens. The result will be passed back through the callback. virtual void GetSizeInTokens(
diff --git a/components/optimization_guide/proto/hints.proto b/components/optimization_guide/proto/hints.proto index 5ba0183..8577a4c9 100644 --- a/components/optimization_guide/proto/hints.proto +++ b/components/optimization_guide/proto/hints.proto
@@ -320,6 +320,8 @@ // Provides information about the contextual zero state suggestions for a // page. GLIC_ZERO_STATE_SUGGESTIONS = 91; + // Provides information about whether GLIC may act on a page. + GLIC_ACTION_PAGE_BLOCK = 92; } // Presents semantics for how page load URLs should be matched.
diff --git a/components/visited_url_ranking/internal/url_grouping/grouping_heuristics.cc b/components/visited_url_ranking/internal/url_grouping/grouping_heuristics.cc index 34b3407..f0ea094 100644 --- a/components/visited_url_ranking/internal/url_grouping/grouping_heuristics.cc +++ b/components/visited_url_ranking/internal/url_grouping/grouping_heuristics.cc
@@ -129,7 +129,13 @@ URLVisitAggregateRankingModelInputSignals::kTabParentId); const char* tab_group_sync_id_input = GetNameForInput( URLVisitAggregateRankingModelInputSignals::kTabGroupSyncId); + const char* tab_id_input = + GetNameForInput(URLVisitAggregateRankingModelInputSignals::kTabId); + std::unordered_map<float, float> tab_id_to_parent_id_map; + tab_id_to_parent_id_map.reserve(inputs.size()); + std::unordered_map<float, unsigned> tab_id_to_tab_index_map; + tab_id_to_tab_index_map.reserve(inputs.size()); for (unsigned i = 0; i < inputs.size(); ++i) { std::optional<ProcessedValue> tab_opened_by_user = inputs[i]->GetMetadataArgument(tab_opened_by_user_input); @@ -141,6 +147,8 @@ inputs[i]->GetMetadataArgument(tab_parent_id_input); std::optional<ProcessedValue> tab_group_sync_id = inputs[i]->GetMetadataArgument(tab_group_sync_id_input); + std::optional<ProcessedValue> tab_id = + inputs[i]->GetMetadataArgument(tab_id_input); if (!tab_opened_by_user || tab_opened_by_user->float_val == 0) { // Do not group tabs not opened by user. @@ -156,11 +164,37 @@ result[i] = base::FastHash(tab_launch_package_name->str_val); continue; } - if (tab_parent_id) { - result[i] = tab_parent_id->float_val; + if (tab_parent_id && tab_id) { + tab_id_to_parent_id_map[tab_id->float_val] = tab_parent_id->float_val; + tab_id_to_tab_index_map[tab_id->float_val] = i; continue; } - // TODO(ssid): Reconsider grouping based on launch types. + } + // Cluster tabs based on parent tab relationship by finding disjoint sets in + // the tab-parent DAG. + // A bool to track whether there are any cluster merge happen in each round. + bool merged = true; + while (merged) { + merged = false; + std::unordered_map<float, float> new_tab_id_to_parent_id_map; + new_tab_id_to_parent_id_map.reserve(tab_id_to_parent_id_map.size()); + for (const auto& pair : tab_id_to_parent_id_map) { + float tab_id = pair.first; + float parent_id = pair.second; + if (base::Contains(tab_id_to_parent_id_map, parent_id) && + tab_id_to_parent_id_map[parent_id] != parent_id) { + new_tab_id_to_parent_id_map[tab_id] = + tab_id_to_parent_id_map[parent_id]; + // Keep track of merge. + merged = true; + } else { + new_tab_id_to_parent_id_map[tab_id] = parent_id; + } + } + tab_id_to_parent_id_map.swap(new_tab_id_to_parent_id_map); + } + for (const auto& pair : tab_id_to_tab_index_map) { + result[pair.second] = tab_id_to_parent_id_map[pair.first]; } return result; }
diff --git a/components/visited_url_ranking/internal/url_grouping/grouping_heuristics_unittest.cc b/components/visited_url_ranking/internal/url_grouping/grouping_heuristics_unittest.cc index 605ceda..36e6689 100644 --- a/components/visited_url_ranking/internal/url_grouping/grouping_heuristics_unittest.cc +++ b/components/visited_url_ranking/internal/url_grouping/grouping_heuristics_unittest.cc
@@ -405,4 +405,42 @@ ASSERT_FALSE(suggestions.has_value()); } +TEST_F(GroupingHeuristicsTest, SimilarSourceHeuristic_SameParentTabCluster) { + std::vector<URLVisitAggregate> candidates = {}; + + // The parent tab relationship for the below 6 tabs are + // 111->112 + // (112, 113) -> 114 + // 115 -> 116 + // (111, 112, 113, 114) will be clustered. + candidates.push_back(CreateVisitForTab(base::Seconds(60), 111)); + GetTabMetadata(candidates[0]).parent_tab_id = 112; + + candidates.push_back(CreateVisitForTab(base::Seconds(250), 112)); + GetTabMetadata(candidates[1]).parent_tab_id = 114; + + candidates.push_back(CreateVisitForTab(base::Seconds(350), 113)); + GetTabMetadata(candidates[2]).parent_tab_id = 114; + + candidates.push_back(CreateVisitForTab(base::Seconds(800), 114)); + GetTabMetadata(candidates[3]).parent_tab_id = 114; + + // Not clustered since parent ID is different. + candidates.push_back(CreateVisitForTab(base::Seconds(350), 115)); + GetTabMetadata(candidates[4]).parent_tab_id = 116; + + candidates.push_back(CreateVisitForTab(base::Seconds(800), 116)); + GetTabMetadata(candidates[4]).parent_tab_id = 116; + + std::optional<GroupSuggestions> suggestions = GetSuggestionsFor( + std::move(candidates), GroupSuggestion::SuggestionReason::kSimilarSource); + + ASSERT_TRUE(suggestions.has_value()); + ASSERT_EQ(1u, suggestions->suggestions.size()); + const auto& suggestion = suggestions->suggestions[0]; + EXPECT_EQ(GroupSuggestion::SuggestionReason::kSimilarSource, + suggestion.suggestion_reason); + EXPECT_THAT(suggestion.tab_ids, ElementsAre(111, 112, 113, 114)); +} + } // namespace visited_url_ranking
diff --git a/components/visited_url_ranking/public/url_visit_schema.cc b/components/visited_url_ranking/public/url_visit_schema.cc index 8f5aab0..d253c9f 100644 --- a/components/visited_url_ranking/public/url_visit_schema.cc +++ b/components/visited_url_ranking/public/url_visit_schema.cc
@@ -43,6 +43,7 @@ const char kSignalTabParentId[] = "tab_parent_id"; const char kSignalTimeSinceTabCreationSec[] = "time_since_tab_creation_sec"; const char kSignalTabGroupSyncId[] = "tab_group_sync_id"; +const char kSignalTabId[] = "tab_id"; constexpr std::array<FieldSchema, kTabResumptionNumInputs> kURLVisitAggregateSchema = {{ @@ -138,6 +139,8 @@ .name = kSignalTimeSinceTabCreationSec}, {.signal = URLVisitAggregateRankingModelInputSignals::kTabGroupSyncId, .name = kSignalTabGroupSyncId}, + {.signal = URLVisitAggregateRankingModelInputSignals::kTabId, + .name = kSignalTabId}, }}; } // namespace visited_url_ranking
diff --git a/components/visited_url_ranking/public/url_visit_schema.h b/components/visited_url_ranking/public/url_visit_schema.h index 10da682..e21b9dc7 100644 --- a/components/visited_url_ranking/public/url_visit_schema.h +++ b/components/visited_url_ranking/public/url_visit_schema.h
@@ -44,6 +44,7 @@ kTabParentId = 29, kTimeSinceTabCreationSec = 30, kTabGroupSyncId = 31, + kTabId = 32, }; // Represents a field's metadata and is leveraged for the processing and @@ -62,7 +63,7 @@ kURLVisitAggregateSchema; // Collection of relevant fields for URL grouping computation. -static constexpr size_t kSuggestionsNumInputs = 9; +static constexpr size_t kSuggestionsNumInputs = 10; extern const std::array<FieldSchema, kSuggestionsNumInputs> kSuggestionsPredictionSchema;
diff --git a/components/visited_url_ranking/public/url_visit_util.cc b/components/visited_url_ranking/public/url_visit_util.cc index c10f82c..8f38189 100644 --- a/components/visited_url_ranking/public/url_visit_util.cc +++ b/components/visited_url_ranking/public/url_visit_util.cc
@@ -330,8 +330,6 @@ break; case kTabParentId: if (tab_data) { - // TODO(crbug.com/397221723): Add a field for tab ID to trace tab - // relationship beyond parent. value = ProcessedValue::FromFloat( tab_data->last_active_tab.tab_metadata.parent_tab_id == -1 ? tab_data->last_active_tab.id @@ -355,6 +353,11 @@ .local_tab_group_id->ToString()); } break; + case kTabId: + if (tab_data) { + value = ProcessedValue::FromFloat(tab_data->last_active_tab.id); + } + break; } signal_value_map.emplace(field_schema.name, std::move(value));
diff --git a/components/webcrypto/algorithms/ed25519.cc b/components/webcrypto/algorithms/ed25519.cc index 3508781..f9b164d 100644 --- a/components/webcrypto/algorithms/ed25519.cc +++ b/components/webcrypto/algorithms/ed25519.cc
@@ -302,14 +302,12 @@ JwkReader jwk; // 3. If the kty field of jwk is not "OKP", then throw a DataError. - // 5. If the alg field of jwk is present and is not "EdDSA", then throw a - // DataError. // 7. If the key_ops field of jwk is present, and is invalid according to the // requirements of JSON Web Key [JWK], or it does not contain all of the // specified usages values, then throw a DataError. // 8. If the ext field of jwk is present and has the value false and // extractable is true, then throw a DataError. - Status status = jwk.Init(key_data, extractable, usages, "OKP", "EdDSA"); + Status status = jwk.Init(key_data, extractable, usages, "OKP", ""); if (status.IsError()) return status; @@ -321,6 +319,18 @@ if (jwk_crv != "Ed25519") return Status::ErrorJwkIncorrectCrv(); + // 5. If the alg field of jwk is present and is not "EdDSA", then throw a + // DataError. + bool has_alg; + std::string jwk_alg; + status = jwk.GetAlg(&jwk_alg, &has_alg); + if (status.IsError()) { + return status; + } + if (has_alg && jwk_alg != "EdDSA" && jwk_alg != "Ed25519") { + return Status::ErrorJwkAlgorithmInconsistent(); + } + // Only private keys have a "d" parameter. The key may still be invalid, but // tentatively decide if it is a public or private key. bool is_private_key = jwk.HasMember("d"); @@ -422,9 +432,9 @@ return Status::OperationError(); DCHECK_EQ(keylen, sizeof(raw_public_key)); - // No "alg" is set for OKP keys. JwkWriter jwk(std::string(), key.Extractable(), key.Usages(), "OKP"); jwk.SetString("crv", "Ed25519"); + jwk.SetString("alg", "Ed25519"); // Set "x", and "d" if it is a private key. jwk.SetBytes("x", raw_public_key);
diff --git a/components/webxr/android/openxr_platform_helper_android.cc b/components/webxr/android/openxr_platform_helper_android.cc index 7f67aee7..47214e9 100644 --- a/components/webxr/android/openxr_platform_helper_android.cc +++ b/components/webxr/android/openxr_platform_helper_android.cc
@@ -27,7 +27,8 @@ std::unique_ptr<device::OpenXrGraphicsBinding> OpenXrPlatformHelperAndroid::GetGraphicsBinding() { - return std::make_unique<device::OpenXrGraphicsBindingOpenGLES>(); + return std::make_unique<device::OpenXrGraphicsBindingOpenGLES>( + GetExtensionEnumeration()); } void OpenXrPlatformHelperAndroid::GetPlatformCreateInfo(
diff --git a/content/browser/ai/echo_ai_language_model.cc b/content/browser/ai/echo_ai_language_model.cc index cd335cc..8f92148a 100644 --- a/content/browser/ai/echo_ai_language_model.cc +++ b/content/browser/ai/echo_ai_language_model.cc
@@ -60,6 +60,7 @@ void EchoAILanguageModel::Prompt( std::vector<blink::mojom::AILanguageModelPromptPtr> prompts, + const std::optional<std::string>& response_json_schema, mojo::PendingRemote<blink::mojom::ModelStreamingResponder> pending_responder) { if (is_destroyed_) {
diff --git a/content/browser/ai/echo_ai_language_model.h b/content/browser/ai/echo_ai_language_model.h index f81a112..2b5a719 100644 --- a/content/browser/ai/echo_ai_language_model.h +++ b/content/browser/ai/echo_ai_language_model.h
@@ -25,6 +25,7 @@ // `blink::mojom::AILanguageModel` implementation. void Prompt(std::vector<blink::mojom::AILanguageModelPromptPtr> prompts, + const std::optional<std::string>& response_json_schema, mojo::PendingRemote<blink::mojom::ModelStreamingResponder> pending_responder) override; void Fork(
diff --git a/content/browser/btm/btm_test_utils.cc b/content/browser/btm/btm_test_utils.cc index a3daef1..701d9b99 100644 --- a/content/browser/btm/btm_test_utils.cc +++ b/content/browser/btm/btm_test_utils.cc
@@ -526,8 +526,10 @@ } PausedCookieAccessObservers::PausedCookieAccessObservers( - NotifyCookiesAccessedCallback callback) - : CookieAccessObservers(std::move(callback)) {} + NotifyCookiesAccessedCallback callback, + PendingObserversWithContext observers) + : CookieAccessObservers(std::move(callback)), + pending_receivers_(std::move(observers)) {} PausedCookieAccessObservers::~PausedCookieAccessObservers() = default; @@ -537,16 +539,9 @@ pending_receivers_.emplace_back(std::move(receiver), source); } -std::vector<mojo::PendingReceiver<network::mojom::CookieAccessObserver>> -PausedCookieAccessObservers::TakeReceivers() { - std::vector<mojo::PendingReceiver<network::mojom::CookieAccessObserver>> - pending_receivers; - for (auto& [pending_receiver, source] : - std::exchange(pending_receivers_, {})) { - pending_receivers.push_back(std::move(pending_receiver)); - } - - return pending_receivers; +PausedCookieAccessObservers::PendingObserversWithContext +PausedCookieAccessObservers::TakeReceiversWithContext() { + return std::exchange(pending_receivers_, {}); } CookieAccessInterceptor::CookieAccessInterceptor(WebContents& web_contents) @@ -562,15 +557,8 @@ base::BindRepeating(&NavigationRequest::NotifyCookiesAccessed, // Unretained is safe here because ownership of the // observers is passed to the request below. - base::Unretained(&request))); - for (auto& receiver : request.TakeCookieObservers()) { - // Since we're taking the observers from the NavigationHandle we can assume - // the source is kNavigation. - // TODO: crbug.com/394059601 - Replace with TakeReceiversWithContext() once - // implemented. - observers->Add(std::move(receiver), - CookieAccessDetails::Source::kNavigation); - } + base::Unretained(&request)), + request.TakeCookieObservers()); request.SetCookieAccessObserversForTesting(std::move(observers)); }
diff --git a/content/browser/btm/btm_test_utils.h b/content/browser/btm/btm_test_utils.h index a974274..3110a12 100644 --- a/content/browser/btm/btm_test_utils.h +++ b/content/browser/btm/btm_test_utils.h
@@ -26,6 +26,7 @@ #include "content/public/browser/web_contents.h" #include "content/public/browser/web_contents_observer.h" #include "content/public/test/browser_test_utils.h" +#include "services/network/public/mojom/cookie_access_observer.mojom.h" #include "url/gurl.h" namespace testing { @@ -391,21 +392,18 @@ // them. class PausedCookieAccessObservers : public CookieAccessObservers { public: - explicit PausedCookieAccessObservers(NotifyCookiesAccessedCallback callback); + explicit PausedCookieAccessObservers(NotifyCookiesAccessedCallback callback, + PendingObserversWithContext observers); ~PausedCookieAccessObservers() override; // CookieAccessObservers void Add(mojo::PendingReceiver<network::mojom::CookieAccessObserver> receiver, CookieAccessDetails::Source source) override; - std::vector<mojo::PendingReceiver<network::mojom::CookieAccessObserver>> - TakeReceivers() override; + PendingObserversWithContext TakeReceiversWithContext() override; private: // Holds existing and new receivers. - std::vector< - std::pair<mojo::PendingReceiver<network::mojom::CookieAccessObserver>, - CookieAccessDetails::Source>> - pending_receivers_; + PendingObserversWithContext pending_receivers_; }; // Class used to pause all cookie access notifications in a WebContents.
diff --git a/content/browser/devtools/dedicated_worker_devtools_agent_host.cc b/content/browser/devtools/dedicated_worker_devtools_agent_host.cc index 063ad900..78e03b1 100644 --- a/content/browser/devtools/dedicated_worker_devtools_agent_host.cc +++ b/content/browser/devtools/dedicated_worker_devtools_agent_host.cc
@@ -23,9 +23,6 @@ // static void DedicatedWorkerDevToolsAgentHost::AddAllAgentHosts( DevToolsAgentHost::List* result) { - if (!base::FeatureList::IsEnabled(blink::features::kPlzDedicatedWorker)) { - return; - } WorkerDevToolsManager::GetInstance().AddAllAgentHosts(result); }
diff --git a/content/browser/devtools/devtools_renderer_channel.cc b/content/browser/devtools/devtools_renderer_channel.cc index 782b7e3..f1bfff9 100644 --- a/content/browser/devtools/devtools_renderer_channel.cc +++ b/content/browser/devtools/devtools_renderer_channel.cc
@@ -174,10 +174,9 @@ process->FilterURL(true /* empty_allowed */, &filtered_url); if (context_type == - blink::mojom::DevToolsExecutionContextType::kDedicatedWorker && - base::FeatureList::IsEnabled(blink::features::kPlzDedicatedWorker)) { - // WorkerDevToolsAgentHost for dedicated workers is already created on the - // browser process when PlzDedicatedWorker is enabled. + blink::mojom::DevToolsExecutionContextType::kDedicatedWorker) { + // WorkerDevToolsAgentHost for dedicated workers is already created in the + // browser process. DCHECK( content::DevToolsAgentHost::GetForId(devtools_worker_token.ToString())); scoped_refptr<DedicatedWorkerDevToolsAgentHost> agent_host = @@ -217,14 +216,8 @@ base::BindOnce(&DevToolsRendererChannel::ChildTargetDestroyed, weak_factory_.GetWeakPtr())); break; - case blink::mojom::DevToolsExecutionContextType::kDedicatedWorker: - CHECK( - !base::FeatureList::IsEnabled(blink::features::kPlzDedicatedWorker)); - agent_host = base::MakeRefCounted<DedicatedWorkerDevToolsAgentHost>( - process_id_, filtered_url, std::move(name), devtools_worker_token, - owner_->GetId(), - base::BindOnce(&DevToolsRendererChannel::ChildTargetDestroyed, - weak_factory_.GetWeakPtr())); + case blink::mojom::DevToolsExecutionContextType::kDedicatedWorker: + NOTREACHED(); } agent_host->SetRenderer(process_id_, std::move(worker_devtools_agent), std::move(host_receiver));
diff --git a/content/browser/devtools/worker_devtools_manager.cc b/content/browser/devtools/worker_devtools_manager.cc index ef36098..ec2af8b 100644 --- a/content/browser/devtools/worker_devtools_manager.cc +++ b/content/browser/devtools/worker_devtools_manager.cc
@@ -10,14 +10,12 @@ #include "content/browser/devtools/devtools_instrumentation.h" #include "content/browser/worker_host/dedicated_worker_host.h" #include "content/public/browser/browser_thread.h" -#include "third_party/blink/public/common/features.h" namespace content { // static WorkerDevToolsManager& WorkerDevToolsManager::GetInstance() { DCHECK_CURRENTLY_ON(BrowserThread::UI); - CHECK(base::FeatureList::IsEnabled(blink::features::kPlzDedicatedWorker)); return *base::Singleton<WorkerDevToolsManager>::get(); }
diff --git a/content/browser/devtools/worker_or_worklet_devtools_agent_host.cc b/content/browser/devtools/worker_or_worklet_devtools_agent_host.cc index 5738a6ca..4081f97 100644 --- a/content/browser/devtools/worker_or_worklet_devtools_agent_host.cc +++ b/content/browser/devtools/worker_or_worklet_devtools_agent_host.cc
@@ -10,7 +10,6 @@ #include "content/browser/renderer_host/render_process_host_impl.h" #include "content/browser/storage_partition_impl.h" #include "content/public/browser/child_process_host.h" -#include "third_party/blink/public/common/features.h" namespace content { @@ -56,8 +55,6 @@ const GURL& url, const std::string& name, base::OnceCallback<void(DevToolsAgentHostImpl*)> callback) { - DCHECK(base::FeatureList::IsEnabled(blink::features::kPlzDedicatedWorker)); - url_ = url; name_ = name; destroyed_callback_ = std::move(callback);
diff --git a/content/browser/renderer_host/cookie_access_observers.cc b/content/browser/renderer_host/cookie_access_observers.cc index 22d68b69..2be4ac5 100644 --- a/content/browser/renderer_host/cookie_access_observers.cc +++ b/content/browser/renderer_host/cookie_access_observers.cc
@@ -20,9 +20,9 @@ cookie_observer_set_.Add(this, std::move(receiver), source); } -std::vector<mojo::PendingReceiver<network::mojom::CookieAccessObserver>> -CookieAccessObservers::TakeReceivers() { - return cookie_observer_set_.TakeReceivers(); +CookieAccessObservers::PendingObserversWithContext +CookieAccessObservers::TakeReceiversWithContext() { + return cookie_observer_set_.TakeReceiversWithContext(); } void CookieAccessObservers::OnCookiesAccessed(
diff --git a/content/browser/renderer_host/cookie_access_observers.h b/content/browser/renderer_host/cookie_access_observers.h index 996e31a..b2f1c73b 100644 --- a/content/browser/renderer_host/cookie_access_observers.h +++ b/content/browser/renderer_host/cookie_access_observers.h
@@ -36,9 +36,10 @@ mojo::PendingReceiver<network::mojom::CookieAccessObserver> receiver, CookieAccessDetails::Source source); - virtual std::vector< - mojo::PendingReceiver<network::mojom::CookieAccessObserver>> - TakeReceivers(); + using PendingObserversWithContext = std::vector< + std::pair<mojo::PendingReceiver<network::mojom::CookieAccessObserver>, + CookieAccessDetails::Source>>; + virtual PendingObserversWithContext TakeReceiversWithContext(); // network::mojom::CookieAccessObserver void OnCookiesAccessed(std::vector<network::mojom::CookieAccessDetailsPtr>
diff --git a/content/browser/renderer_host/navigation_request.cc b/content/browser/renderer_host/navigation_request.cc index 13841f1..3c714ec 100644 --- a/content/browser/renderer_host/navigation_request.cc +++ b/content/browser/renderer_host/navigation_request.cc
@@ -5016,7 +5016,7 @@ mojo::PendingRemote<network::mojom::CookieAccessObserver> cookie_observer; cookie_observers_->Add(cookie_observer.InitWithNewPipeAndPassReceiver(), - CookieAccessDetails::Source::kNavigation); + CookieAccessDetails::Source::kNonNavigation); mojo::PendingRemote<network::mojom::TrustTokenAccessObserver> trust_token_observer; @@ -9935,9 +9935,11 @@ } } -std::vector<mojo::PendingReceiver<network::mojom::CookieAccessObserver>> +std::vector< + std::pair<mojo::PendingReceiver<network::mojom::CookieAccessObserver>, + CookieAccessDetails::Source>> NavigationRequest::TakeCookieObservers() { - return cookie_observers_->TakeReceivers(); + return cookie_observers_->TakeReceiversWithContext(); } void NavigationRequest::OnTrustTokensAccessed(
diff --git a/content/browser/renderer_host/navigation_request.h b/content/browser/renderer_host/navigation_request.h index 8b31f08..fce490ee 100644 --- a/content/browser/renderer_host/navigation_request.h +++ b/content/browser/renderer_host/navigation_request.h
@@ -903,7 +903,8 @@ // Typically this is called when navigation commits to move these observers to // the committed document. [[nodiscard]] std::vector< - mojo::PendingReceiver<network::mojom::CookieAccessObserver>> + std::pair<mojo::PendingReceiver<network::mojom::CookieAccessObserver>, + CookieAccessDetails::Source>> TakeCookieObservers(); void NotifyCookiesAccessed(
diff --git a/content/browser/renderer_host/private_network_access_browsertest.cc b/content/browser/renderer_host/private_network_access_browsertest.cc index 7a9830e..8945c85 100644 --- a/content/browser/renderer_host/private_network_access_browsertest.cc +++ b/content/browser/renderer_host/private_network_access_browsertest.cc
@@ -40,7 +40,6 @@ #include "services/network/public/cpp/features.h" #include "services/network/public/cpp/network_switches.h" #include "testing/gmock/include/gmock/gmock.h" -#include "third_party/blink/public/common/features.h" #include "url/gurl.h" #include "url/origin.h" @@ -576,7 +575,6 @@ PrivateNetworkAccessBrowserTest() : PrivateNetworkAccessBrowserTestBase( { - blink::features::kPlzDedicatedWorker, features::kBlockInsecurePrivateNetworkRequests, features::kPrivateNetworkAccessSendPreflights, }, @@ -601,13 +599,11 @@ PrivateNetworkAccessSandboxedDataBrowserTest() : PrivateNetworkAccessBrowserTestBase( GetParam() ? FeatureVec({ - blink::features::kPlzDedicatedWorker, features::kBlockInsecurePrivateNetworkRequests, features::kPrivateNetworkAccessSendPreflights, features::kOriginKeyedProcessesByDefault, }) : FeatureVec({ - blink::features::kPlzDedicatedWorker, features::kBlockInsecurePrivateNetworkRequests, features::kPrivateNetworkAccessSendPreflights, }),
diff --git a/content/browser/renderer_host/render_frame_host_impl.cc b/content/browser/renderer_host/render_frame_host_impl.cc index d1016b0f..6dbea82 100644 --- a/content/browser/renderer_host/render_frame_host_impl.cc +++ b/content/browser/renderer_host/render_frame_host_impl.cc
@@ -15397,9 +15397,8 @@ // still has a valid receiver, `this` will receive delayed IPC calls from // the network service. When the remote interface in the network service is // destructed, `mojo::ReceiverSet` automatically removes the receiver. - for (auto& receiver : navigation_request->TakeCookieObservers()) { - cookie_observers_.Add(std::move(receiver), - CookieAccessDetails::Source::kNavigation); + for (auto& [receiver, source] : navigation_request->TakeCookieObservers()) { + cookie_observers_.Add(std::move(receiver), source); } for (auto& receiver : navigation_request->TakeTrustTokenObservers()) { trust_token_observers_.Add(this, std::move(receiver));
diff --git a/content/browser/service_worker/service_worker_browsertest.cc b/content/browser/service_worker/service_worker_browsertest.cc index fe3d045..613fb1b 100644 --- a/content/browser/service_worker/service_worker_browsertest.cc +++ b/content/browser/service_worker/service_worker_browsertest.cc
@@ -4228,12 +4228,9 @@ : public base::test::WithFeatureOverride, public ServiceWorkerBrowserTest { public: - // Dedicated worker clients only exist with PlzDedicatedWorker enabled, so - // turn on that flag. ServiceWorkerBrowserTestWithStoragePartitioning() : base::test::WithFeatureOverride( - net::features::kThirdPartyStoragePartitioning), - scoped_feature_list_(blink::features::kPlzDedicatedWorker) {} + net::features::kThirdPartyStoragePartitioning) {} bool ThirdPartyStoragePartitioningEnabled() const { return IsParamFeatureEnabled(); } @@ -4372,9 +4369,6 @@ testing::UnorderedElementsAre(child_url)); } } - - private: - base::test::ScopedFeatureList scoped_feature_list_; }; INSTANTIATE_FEATURE_OVERRIDE_TEST_SUITE(
diff --git a/content/browser/service_worker/service_worker_container_host.cc b/content/browser/service_worker/service_worker_container_host.cc index e9a61e6..5992fcd 100644 --- a/content/browser/service_worker/service_worker_container_host.cc +++ b/content/browser/service_worker/service_worker_container_host.cc
@@ -27,7 +27,6 @@ #include "content/public/common/content_client.h" #include "content/public/common/origin_util.h" #include "mojo/public/cpp/bindings/callback_helpers.h" -#include "third_party/blink/public/common/features.h" #include "third_party/blink/public/common/storage_key/storage_key.h" #include "third_party/blink/public/mojom/service_worker/service_worker_registration_options.mojom.h" @@ -553,9 +552,6 @@ SCOPED_CRASH_KEY_NUMBER( "SWController", "client_type", static_cast<int>(service_worker_client().GetClientType())); - SCOPED_CRASH_KEY_BOOL( - "SWController", "PlzDedicatedWorker", - base::FeatureList::IsEnabled(blink::features::kPlzDedicatedWorker)); base::debug::DumpWithoutCrashing(); } }
diff --git a/content/browser/service_worker/service_worker_container_host_unittest.cc b/content/browser/service_worker/service_worker_container_host_unittest.cc index cf2305a..5383187 100644 --- a/content/browser/service_worker/service_worker_container_host_unittest.cc +++ b/content/browser/service_worker/service_worker_container_host_unittest.cc
@@ -44,7 +44,6 @@ #include "net/cookies/site_for_cookies.h" #include "services/network/public/cpp/is_potentially_trustworthy.h" #include "testing/gtest/include/gtest/gtest.h" -#include "third_party/blink/public/common/features.h" #include "third_party/blink/public/common/storage_key/storage_key.h" #include "third_party/blink/public/common/tokens/tokens.h" #include "third_party/blink/public/mojom/service_worker/service_worker_object.mojom.h" @@ -315,23 +314,6 @@ url::ScopedSchemeRegistryForTests scoped_registry_; }; -// Run tests with PlzDedicatedWorker. -// TODO(crbug.com/40093136): Merge this test fixture into -// ServiceWorkerContainerHostTest once PlzDedicatedWorker is enabled by default. -class ServiceWorkerContainerHostTestWithPlzDedicatedWorker - : public ServiceWorkerContainerHostTest { - public: - ServiceWorkerContainerHostTestWithPlzDedicatedWorker() { - // ServiceWorkerClient for dedicated workers is available only when - // PlzDedicatedWorker is enabled. - scoped_feature_list_.InitAndEnableFeature( - blink::features::kPlzDedicatedWorker); - } - - private: - base::test::ScopedFeatureList scoped_feature_list_; -}; - TEST_F(ServiceWorkerContainerHostTest, MatchRegistration) { ScopedServiceWorkerClient service_worker_client = CreateServiceWorkerClient( context_.get(), GURL("https://www.example.com/example1.html")); @@ -1097,20 +1079,13 @@ : public ServiceWorkerContainerHostTest, public testing::WithParamInterface<ClientType> { public: - ServiceWorkerContainerHostTestByClientType() { - // ServiceWorkerClient for dedicated workers is available only when - // PlzDedicatedWorker is enabled. - scoped_feature_list_.InitAndEnableFeature( - blink::features::kPlzDedicatedWorker); - } + ServiceWorkerContainerHostTestByClientType() = default; ScopedServiceWorkerClient CreateClient() { switch (GetParam()) { case ClientType::kWindow: return CreateServiceWorkerClient(context_.get()); case ClientType::kDedicatedWorker: - CHECK( - base::FeatureList::IsEnabled(blink::features::kPlzDedicatedWorker)); return ScopedServiceWorkerClient( helper_->context() ->service_worker_client_owner() @@ -1178,9 +1153,6 @@ break; } } - - private: - base::test::ScopedFeatureList scoped_feature_list_; }; // Test that a "reserved" (i.e., not execution ready) client is not included
diff --git a/content/browser/shared_storage/DEPS b/content/browser/shared_storage/DEPS new file mode 100644 index 0000000..4437dd3f9 --- /dev/null +++ b/content/browser/shared_storage/DEPS
@@ -0,0 +1,3 @@ +include_rules = [ + "+components/aggregation_service", +]
diff --git a/content/browser/shared_storage/shared_storage_browsertest.cc b/content/browser/shared_storage/shared_storage_browsertest.cc index 236ee96..5f0e9222 100644 --- a/content/browser/shared_storage/shared_storage_browsertest.cc +++ b/content/browser/shared_storage/shared_storage_browsertest.cc
@@ -595,8 +595,11 @@ "/shared_storage/simple_module.js"), /*worklet_id=*/0)}, {AccessScope::kWindow, AccessMethod::kRun, MainFrameId(), origin_str, - SharedStorageEventParams::CreateForRun( - "test-operation", blink::CloneableMessage(), /*worklet_id=*/0)}}); + SharedStorageEventParams::CreateForRunForTesting( + "test-operation", + /*keep_alive=*/false, + SharedStorageEventParams::PrivateAggregationConfigWrapper(), + blink::CloneableMessage(), /*worklet_id=*/0)}}); } IN_PROC_BROWSER_TEST_P(SharedStorageBrowserTest, @@ -882,8 +885,10 @@ "a.test", "/shared_storage/erroneous_function_module.js"), /*worklet_id=*/0)}, {AccessScope::kWindow, AccessMethod::kRun, MainFrameId(), origin_str, - SharedStorageEventParams::CreateForRun( - "test-operation", blink::CloneableMessage(), /*worklet_id=*/0)}}); + SharedStorageEventParams::CreateForRunForTesting( + "test-operation", /*keep_alive=*/false, + SharedStorageEventParams::PrivateAggregationConfigWrapper(), + blink::CloneableMessage(), /*worklet_id=*/0)}}); } IN_PROC_BROWSER_TEST_P( @@ -966,11 +971,15 @@ "/shared_storage/simple_module.js"), /*worklet_id=*/0)}, {AccessScope::kWindow, AccessMethod::kRun, MainFrameId(), origin_str, - SharedStorageEventParams::CreateForRun( - "test-operation", blink::CloneableMessage(), /*worklet_id=*/0)}, + SharedStorageEventParams::CreateForRunForTesting( + "test-operation", /*keep_alive=*/true, + SharedStorageEventParams::PrivateAggregationConfigWrapper(), + blink::CloneableMessage(), /*worklet_id=*/0)}, {AccessScope::kWindow, AccessMethod::kRun, MainFrameId(), origin_str, - SharedStorageEventParams::CreateForRun( - "test-operation", blink::CloneableMessage(), /*worklet_id=*/0)}}); + SharedStorageEventParams::CreateForRunForTesting( + "test-operation", /*keep_alive=*/false, + SharedStorageEventParams::PrivateAggregationConfigWrapper(), + blink::CloneableMessage(), /*worklet_id=*/0)}}); } IN_PROC_BROWSER_TEST_P( @@ -1040,8 +1049,10 @@ "/shared_storage/simple_module.js"), /*worklet_id=*/0)}, {AccessScope::kWindow, AccessMethod::kRun, MainFrameId(), origin_str, - SharedStorageEventParams::CreateForRun( - "test-operation", blink::CloneableMessage(), /*worklet_id=*/0)}}); + SharedStorageEventParams::CreateForRunForTesting( + "test-operation", /*keep_alive=*/false, + SharedStorageEventParams::PrivateAggregationConfigWrapper(), + blink::CloneableMessage(), /*worklet_id=*/0)}}); } IN_PROC_BROWSER_TEST_P( @@ -1110,8 +1121,10 @@ "/shared_storage/simple_module.js"), /*worklet_id=*/0)}, {AccessScope::kWindow, AccessMethod::kRun, MainFrameId(), origin_str, - SharedStorageEventParams::CreateForRun( - "test-operation", blink::CloneableMessage(), /*worklet_id=*/0)}}); + SharedStorageEventParams::CreateForRunForTesting( + "test-operation", /*keep_alive=*/false, + SharedStorageEventParams::PrivateAggregationConfigWrapper(), + blink::CloneableMessage(), /*worklet_id=*/0)}}); } IN_PROC_BROWSER_TEST_P(SharedStorageBrowserTest, WorkletDestroyed) { @@ -1430,8 +1443,10 @@ "/shared_storage/simple_module.js"), /*worklet_id=*/0)}, {AccessScope::kWindow, AccessMethod::kRun, MainFrameId(), origin_str, - SharedStorageEventParams::CreateForRun( - "test-operation", blink::CloneableMessage(), /*worklet_id=*/0)}}); + SharedStorageEventParams::CreateForRunForTesting( + "test-operation", /*keep_alive=*/false, + SharedStorageEventParams::PrivateAggregationConfigWrapper(), + blink::CloneableMessage(), /*worklet_id=*/0)}}); } IN_PROC_BROWSER_TEST_P( @@ -1552,8 +1567,10 @@ /*worklet_id=*/0)}, {AccessScope::kWindow, AccessMethod::kSelectURL, MainFrameId(), origin_str, - SharedStorageEventParams::CreateForSelectURL( - "test-url-selection-operation", blink::CloneableMessage(), + SharedStorageEventParams::CreateForSelectURLForTesting( + "test-url-selection-operation", /*keep_alive=*/false, + SharedStorageEventParams::PrivateAggregationConfigWrapper(), + blink::CloneableMessage(), std::vector<SharedStorageUrlSpecWithMetadata>( {{https_server()->GetURL("a.test", "/fenced_frames/title0.html"), @@ -1840,8 +1857,10 @@ /*worklet_id=*/0)}, {AccessScope::kWindow, AccessMethod::kSelectURL, MainFrameId(), origin_str, - SharedStorageEventParams::CreateForSelectURL( - "test-url-selection-operation", blink::CloneableMessage(), + SharedStorageEventParams::CreateForSelectURLForTesting( + "test-url-selection-operation", /*keep_alive=*/false, + SharedStorageEventParams::PrivateAggregationConfigWrapper(), + blink::CloneableMessage(), std::vector<SharedStorageUrlSpecWithMetadata>( {{https_server()->GetURL("a.test", "/fenced_frames/title0.html"), @@ -1951,8 +1970,10 @@ /*worklet_id=*/0)}, {AccessScope::kWindow, AccessMethod::kSelectURL, MainFrameId(), origin_str, - SharedStorageEventParams::CreateForSelectURL( - "test-url-selection-operation", blink::CloneableMessage(), + SharedStorageEventParams::CreateForSelectURLForTesting( + "test-url-selection-operation", /*keep_alive=*/false, + SharedStorageEventParams::PrivateAggregationConfigWrapper(), + blink::CloneableMessage(), std::vector<SharedStorageUrlSpecWithMetadata>( {{https_server()->GetURL("a.test", "/fenced_frames/title0.html"), @@ -2067,8 +2088,10 @@ /*worklet_id=*/0)}, {AccessScope::kWindow, AccessMethod::kSelectURL, MainFrameId(), origin_str, - SharedStorageEventParams::CreateForSelectURL( - "test-url-selection-operation", blink::CloneableMessage(), + SharedStorageEventParams::CreateForSelectURLForTesting( + "test-url-selection-operation", /*keep_alive=*/false, + SharedStorageEventParams::PrivateAggregationConfigWrapper(), + blink::CloneableMessage(), std::vector<SharedStorageUrlSpecWithMetadata>( {{https_server()->GetURL("b.test", "/fenced_frames/title0.html"), @@ -2214,8 +2237,10 @@ /*worklet_id=*/0)}, {AccessScope::kWindow, AccessMethod::kSelectURL, MainFrameId(), origin_str, - SharedStorageEventParams::CreateForSelectURL( - "test-url-selection-operation", blink::CloneableMessage(), + SharedStorageEventParams::CreateForSelectURLForTesting( + "test-url-selection-operation", /*keep_alive=*/true, + SharedStorageEventParams::PrivateAggregationConfigWrapper(), + blink::CloneableMessage(), std::vector<SharedStorageUrlSpecWithMetadata>( {{https_server()->GetURL("a.test", "/fenced_frames/title0.html"), @@ -2223,8 +2248,10 @@ /*worklet_id=*/0)}, {AccessScope::kWindow, AccessMethod::kSelectURL, MainFrameId(), origin_str, - SharedStorageEventParams::CreateForSelectURL( - "test-url-selection-operation", blink::CloneableMessage(), + SharedStorageEventParams::CreateForSelectURLForTesting( + "test-url-selection-operation", /*keep_alive=*/false, + SharedStorageEventParams::PrivateAggregationConfigWrapper(), + blink::CloneableMessage(), std::vector<SharedStorageUrlSpecWithMetadata>( {{https_server()->GetURL("a.test", "/fenced_frames/title0.html"), @@ -2342,8 +2369,10 @@ /*worklet_id=*/0)}, {AccessScope::kWindow, AccessMethod::kSelectURL, MainFrameId(), origin_str, - SharedStorageEventParams::CreateForSelectURL( - "test-url-selection-operation", blink::CloneableMessage(), + SharedStorageEventParams::CreateForSelectURLForTesting( + "test-url-selection-operation", /*keep_alive=*/false, + SharedStorageEventParams::PrivateAggregationConfigWrapper(), + blink::CloneableMessage(), std::vector<SharedStorageUrlSpecWithMetadata>( {{https_server()->GetURL("a.test", "/fenced_frames/title0.html"), @@ -2442,8 +2471,10 @@ /*worklet_id=*/0)}, {AccessScope::kWindow, AccessMethod::kSelectURL, MainFrameId(), origin_str, - SharedStorageEventParams::CreateForSelectURL( - "test-url-selection-operation", blink::CloneableMessage(), + SharedStorageEventParams::CreateForSelectURLForTesting( + "test-url-selection-operation", /*keep_alive=*/false, + SharedStorageEventParams::PrivateAggregationConfigWrapper(), + blink::CloneableMessage(), std::vector<SharedStorageUrlSpecWithMetadata>( {{https_server()->GetURL("a.test", "/fenced_frames/title0.html"), @@ -2533,8 +2564,10 @@ "/shared_storage/simple_module.js"), /*worklet_id=*/0)}, {AccessScope::kWindow, AccessMethod::kRun, MainFrameId(), origin_str, - SharedStorageEventParams::CreateForRun( - "test-operation", blink::CloneableMessage(), /*worklet_id=*/0)}}); + SharedStorageEventParams::CreateForRunForTesting( + "test-operation", /*keep_alive=*/false, + SharedStorageEventParams::PrivateAggregationConfigWrapper(), + blink::CloneableMessage(), /*worklet_id=*/0)}}); } IN_PROC_BROWSER_TEST_P(SharedStorageBrowserTest, @@ -2646,12 +2679,16 @@ "/shared_storage/simple_module.js"), /*worklet_id=*/0)}, {AccessScope::kWindow, AccessMethod::kRun, MainFrameId(), origin_str, - SharedStorageEventParams::CreateForRun( - "test-operation", blink::CloneableMessage(), /*worklet_id=*/0)}, + SharedStorageEventParams::CreateForRunForTesting( + "test-operation", /*keep_alive=*/true, + SharedStorageEventParams::PrivateAggregationConfigWrapper(), + blink::CloneableMessage(), /*worklet_id=*/0)}, {AccessScope::kWindow, AccessMethod::kSelectURL, MainFrameId(), origin_str, - SharedStorageEventParams::CreateForSelectURL( - "test-url-selection-operation", blink::CloneableMessage(), + SharedStorageEventParams::CreateForSelectURLForTesting( + "test-url-selection-operation", /*keep_alive=*/false, + SharedStorageEventParams::PrivateAggregationConfigWrapper(), + blink::CloneableMessage(), std::vector<SharedStorageUrlSpecWithMetadata>( {{https_server()->GetURL("a.test", "/fenced_frames/title0.html"), @@ -2740,8 +2777,10 @@ "/shared_storage/simple_module.js"), /*worklet_id=*/0)}, {AccessScope::kWindow, AccessMethod::kRun, MainFrameId(), origin_str, - SharedStorageEventParams::CreateForRun( - "test-operation", blink::CloneableMessage(), /*worklet_id=*/0)}}); + SharedStorageEventParams::CreateForRunForTesting( + "test-operation", /*keep_alive=*/false, + SharedStorageEventParams::PrivateAggregationConfigWrapper(), + blink::CloneableMessage(), /*worklet_id=*/0)}}); } IN_PROC_BROWSER_TEST_P(SharedStorageBrowserTest, @@ -2854,16 +2893,20 @@ /*worklet_id=*/0)}, {AccessScope::kWindow, AccessMethod::kSelectURL, MainFrameId(), origin_str, - SharedStorageEventParams::CreateForSelectURL( - "test-url-selection-operation", blink::CloneableMessage(), + SharedStorageEventParams::CreateForSelectURLForTesting( + "test-url-selection-operation", /*keep_alive=*/true, + SharedStorageEventParams::PrivateAggregationConfigWrapper(), + blink::CloneableMessage(), std::vector<SharedStorageUrlSpecWithMetadata>( {{https_server()->GetURL("a.test", "/fenced_frames/title0.html"), {}}}), /*worklet_id=*/0)}, {AccessScope::kWindow, AccessMethod::kRun, MainFrameId(), origin_str, - SharedStorageEventParams::CreateForRun( - "test-operation", blink::CloneableMessage(), /*worklet_id=*/0)}}); + SharedStorageEventParams::CreateForRunForTesting( + "test-operation", /*keep_alive=*/false, + SharedStorageEventParams::PrivateAggregationConfigWrapper(), + blink::CloneableMessage(), /*worklet_id=*/0)}}); } IN_PROC_BROWSER_TEST_P(SharedStorageBrowserTest, @@ -2966,8 +3009,10 @@ /*worklet_id=*/0)}, {AccessScope::kWindow, AccessMethod::kSelectURL, MainFrameId(), origin_str, - SharedStorageEventParams::CreateForSelectURL( - "test-url-selection-operation", blink::CloneableMessage(), + SharedStorageEventParams::CreateForSelectURLForTesting( + "test-url-selection-operation", /*keep_alive=*/false, + SharedStorageEventParams::PrivateAggregationConfigWrapper(), + blink::CloneableMessage(), std::vector<SharedStorageUrlSpecWithMetadata>( {{https_server()->GetURL("a.test", "/fenced_frames/title0.html"), @@ -3074,8 +3119,10 @@ /*worklet_id=*/0)}, {AccessScope::kWindow, AccessMethod::kSelectURL, MainFrameId(), origin_str, - SharedStorageEventParams::CreateForSelectURL( - "test-url-selection-operation", blink::CloneableMessage(), + SharedStorageEventParams::CreateForSelectURLForTesting( + "test-url-selection-operation", /*keep_alive=*/false, + SharedStorageEventParams::PrivateAggregationConfigWrapper(), + blink::CloneableMessage(), std::vector<SharedStorageUrlSpecWithMetadata>( {{https_server()->GetURL("a.test", "/fenced_frames/title0.html"), @@ -3174,8 +3221,10 @@ /*worklet_id=*/0)}, {AccessScope::kWindow, AccessMethod::kSelectURL, MainFrameId(), origin_str, - SharedStorageEventParams::CreateForSelectURL( - "test-url-selection-operation", blink::CloneableMessage(), + SharedStorageEventParams::CreateForSelectURLForTesting( + "test-url-selection-operation", /*keep_alive=*/false, + SharedStorageEventParams::PrivateAggregationConfigWrapper(), + blink::CloneableMessage(), std::vector<SharedStorageUrlSpecWithMetadata>( {{https_server()->GetURL("a.test", "/fenced_frames/title0.html"), @@ -3249,8 +3298,10 @@ SharedStorageEventParams::CreateForAddModule(out_script_url, /*worklet_id=*/0)}, {AccessScope::kWindow, AccessMethod::kRun, MainFrameId(), origin_str, - SharedStorageEventParams::CreateForRun( - "test-operation", blink::CloneableMessage(), /*worklet_id=*/0)}, + SharedStorageEventParams::CreateForRunForTesting( + "test-operation", /*keep_alive=*/true, + SharedStorageEventParams::PrivateAggregationConfigWrapper(), + blink::CloneableMessage(), /*worklet_id=*/0)}, {AccessScope::kSharedStorageWorklet, AccessMethod::kGet, MainFrameId(), origin_str, SharedStorageEventParams::CreateForGetOrDelete("key0", @@ -3313,8 +3364,10 @@ SharedStorageEventParams::CreateForAddModule(out_script_url, /*worklet_id=*/0)}, {AccessScope::kWindow, AccessMethod::kRun, MainFrameId(), origin_str, - SharedStorageEventParams::CreateForRun( - "test-operation", blink::CloneableMessage(), /*worklet_id=*/0)}, + SharedStorageEventParams::CreateForRunForTesting( + "test-operation", /*keep_alive=*/true, + SharedStorageEventParams::PrivateAggregationConfigWrapper(), + blink::CloneableMessage(), /*worklet_id=*/0)}, {AccessScope::kSharedStorageWorklet, AccessMethod::kLength, MainFrameId(), origin_str, SharedStorageEventParams::CreateWithWorkletId(/*worklet_id=*/0)}, @@ -3358,8 +3411,10 @@ SharedStorageEventParams::CreateForAddModule(out_script_url, /*worklet_id=*/0)}, {AccessScope::kWindow, AccessMethod::kRun, MainFrameId(), origin_str, - SharedStorageEventParams::CreateForRun( - "test-operation", blink::CloneableMessage(), /*worklet_id=*/0)}, + SharedStorageEventParams::CreateForRunForTesting( + "test-operation", /*keep_alive=*/true, + SharedStorageEventParams::PrivateAggregationConfigWrapper(), + blink::CloneableMessage(), /*worklet_id=*/0)}, {AccessScope::kSharedStorageWorklet, AccessMethod::kLength, MainFrameId(), origin_str, SharedStorageEventParams::CreateWithWorkletId(/*worklet_id=*/0)}}); @@ -3413,8 +3468,10 @@ SharedStorageEventParams::CreateForAddModule(out_script_url, /*worklet_id=*/0)}, {AccessScope::kWindow, AccessMethod::kRun, MainFrameId(), origin_str, - SharedStorageEventParams::CreateForRun( - "test-operation", blink::CloneableMessage(), /*worklet_id=*/0)}, + SharedStorageEventParams::CreateForRunForTesting( + "test-operation", /*keep_alive=*/true, + SharedStorageEventParams::PrivateAggregationConfigWrapper(), + blink::CloneableMessage(), /*worklet_id=*/0)}, {AccessScope::kSharedStorageWorklet, AccessMethod::kSet, MainFrameId(), origin_str, SharedStorageEventParams::CreateForSet("key0", "value0", false, @@ -3496,8 +3553,10 @@ SharedStorageEventParams::CreateForAddModule(out_script_url, /*worklet_id=*/0)}, {AccessScope::kWindow, AccessMethod::kRun, MainFrameId(), origin_str, - SharedStorageEventParams::CreateForRun( - "test-operation", blink::CloneableMessage(), /*worklet_id=*/0)}, + SharedStorageEventParams::CreateForRunForTesting( + "test-operation", /*keep_alive=*/true, + SharedStorageEventParams::PrivateAggregationConfigWrapper(), + blink::CloneableMessage(), /*worklet_id=*/0)}, {AccessScope::kSharedStorageWorklet, AccessMethod::kSet, MainFrameId(), origin_str, SharedStorageEventParams::CreateForSet("k", std::string(2621439, 'a'), @@ -3553,8 +3612,10 @@ SharedStorageEventParams::CreateForAddModule(out_script_url, /*worklet_id=*/0)}, {AccessScope::kWindow, AccessMethod::kRun, MainFrameId(), origin_str, - SharedStorageEventParams::CreateForRun( - "test-operation", blink::CloneableMessage(), /*worklet_id=*/0)}, + SharedStorageEventParams::CreateForRunForTesting( + "test-operation", /*keep_alive=*/true, + SharedStorageEventParams::PrivateAggregationConfigWrapper(), + blink::CloneableMessage(), /*worklet_id=*/0)}, {AccessScope::kSharedStorageWorklet, AccessMethod::kSet, MainFrameId(), origin_str, SharedStorageEventParams::CreateForSet("key0", "value0", false, @@ -3613,8 +3674,10 @@ SharedStorageEventParams::CreateForAddModule(out_script_url, /*worklet_id=*/0)}, {AccessScope::kWindow, AccessMethod::kRun, MainFrameId(), origin_str, - SharedStorageEventParams::CreateForRun( - "test-operation", blink::CloneableMessage(), /*worklet_id=*/0)}, + SharedStorageEventParams::CreateForRunForTesting( + "test-operation", /*keep_alive=*/true, + SharedStorageEventParams::PrivateAggregationConfigWrapper(), + blink::CloneableMessage(), /*worklet_id=*/0)}, {AccessScope::kSharedStorageWorklet, AccessMethod::kSet, MainFrameId(), origin_str, SharedStorageEventParams::CreateForSet("key0", "value0", false, @@ -3732,8 +3795,10 @@ SharedStorageEventParams::CreateForAddModule(script_url, /*worklet_id=*/0)}, {AccessScope::kWindow, AccessMethod::kRun, MainFrameId(), origin_str, - SharedStorageEventParams::CreateForRun( - "get-operation", blink::CloneableMessage(), /*worklet_id=*/0)}, + SharedStorageEventParams::CreateForRunForTesting( + "get-operation", /*keep_alive=*/true, + SharedStorageEventParams::PrivateAggregationConfigWrapper(), + blink::CloneableMessage(), /*worklet_id=*/0)}, {AccessScope::kSharedStorageWorklet, AccessMethod::kLength, MainFrameId(), origin_str, SharedStorageEventParams::CreateWithWorkletId(/*worklet_id=*/0)}, @@ -3742,8 +3807,10 @@ SharedStorageEventParams::CreateForGetOrDelete("key0", /*worklet_id=*/0)}, {AccessScope::kWindow, AccessMethod::kRun, MainFrameId(), origin_str, - SharedStorageEventParams::CreateForRun( - "get-operation", blink::CloneableMessage(), /*worklet_id=*/0)}, + SharedStorageEventParams::CreateForRunForTesting( + "get-operation", /*keep_alive=*/false, + SharedStorageEventParams::PrivateAggregationConfigWrapper(), + blink::CloneableMessage(), /*worklet_id=*/0)}, {AccessScope::kSharedStorageWorklet, AccessMethod::kLength, MainFrameId(), origin_str, SharedStorageEventParams::CreateWithWorkletId(/*worklet_id=*/0)}, @@ -3788,8 +3855,10 @@ SharedStorageEventParams::CreateForAddModule(out_script_url, /*worklet_id=*/0)}, {AccessScope::kWindow, AccessMethod::kRun, MainFrameId(), origin_str, - SharedStorageEventParams::CreateForRun( - "test-operation", blink::CloneableMessage(), /*worklet_id=*/0)}, + SharedStorageEventParams::CreateForRunForTesting( + "test-operation", /*keep_alive=*/true, + SharedStorageEventParams::PrivateAggregationConfigWrapper(), + blink::CloneableMessage(), /*worklet_id=*/0)}, {AccessScope::kSharedStorageWorklet, AccessMethod::kLength, MainFrameId(), origin_str, SharedStorageEventParams::CreateWithWorkletId(/*worklet_id=*/0)}}); @@ -3831,8 +3900,10 @@ SharedStorageEventParams::CreateForAddModule(out_script_url, /*worklet_id=*/0)}, {AccessScope::kWindow, AccessMethod::kRun, MainFrameId(), origin2_str, - SharedStorageEventParams::CreateForRun( - "test-operation", blink::CloneableMessage(), /*worklet_id=*/0)}, + SharedStorageEventParams::CreateForRunForTesting( + "test-operation", /*keep_alive=*/true, + SharedStorageEventParams::PrivateAggregationConfigWrapper(), + blink::CloneableMessage(), /*worklet_id=*/0)}, {AccessScope::kSharedStorageWorklet, AccessMethod::kLength, MainFrameId(), origin2_str, SharedStorageEventParams::CreateWithWorkletId(/*worklet_id=*/0)}}); @@ -3888,8 +3959,10 @@ SharedStorageEventParams::CreateForAddModule(out_script_url, /*worklet_id=*/0)}, {AccessScope::kWindow, AccessMethod::kRun, MainFrameId(), origin_str, - SharedStorageEventParams::CreateForRun( - "test-operation", blink::CloneableMessage(), /*worklet_id=*/0)}, + SharedStorageEventParams::CreateForRunForTesting( + "test-operation", /*keep_alive=*/true, + SharedStorageEventParams::PrivateAggregationConfigWrapper(), + blink::CloneableMessage(), /*worklet_id=*/0)}, {AccessScope::kSharedStorageWorklet, AccessMethod::kKeys, MainFrameId(), origin_str, SharedStorageEventParams::CreateWithWorkletId(/*worklet_id=*/0)}, @@ -3942,8 +4015,10 @@ SharedStorageEventParams::CreateForAddModule(out_script_url, /*worklet_id=*/0)}, {AccessScope::kWindow, AccessMethod::kRun, MainFrameId(), origin_str, - SharedStorageEventParams::CreateForRun( - "test-operation", blink::CloneableMessage(), /*worklet_id=*/0)}, + SharedStorageEventParams::CreateForRunForTesting( + "test-operation", /*keep_alive=*/true, + SharedStorageEventParams::PrivateAggregationConfigWrapper(), + blink::CloneableMessage(), /*worklet_id=*/0)}, {AccessScope::kSharedStorageWorklet, AccessMethod::kValues, MainFrameId(), origin_str, SharedStorageEventParams::CreateWithWorkletId(/*worklet_id=*/0)}}); @@ -4003,8 +4078,10 @@ out_script_url, /*worklet_id=*/0)); expected_accesses.emplace_back( AccessScope::kWindow, AccessMethod::kRun, MainFrameId(), origin_str, - SharedStorageEventParams::CreateForRun( - "test-operation", blink::CloneableMessage(), /*worklet_id=*/0)); + SharedStorageEventParams::CreateForRunForTesting( + "test-operation", /*keep_alive=*/true, + SharedStorageEventParams::PrivateAggregationConfigWrapper(), + blink::CloneableMessage(), /*worklet_id=*/0)); expected_accesses.emplace_back( AccessScope::kSharedStorageWorklet, AccessMethod::kKeys, MainFrameId(), origin_str, @@ -9112,8 +9189,10 @@ "context-origin", /*worklet_id=*/0)}, {AccessScope::kWindow, AccessMethod::kRun, MainFrameId(), origin_str, - SharedStorageEventParams::CreateForRun( - "test-operation", blink::CloneableMessage(), /*worklet_id=*/0)}, + SharedStorageEventParams::CreateForRunForTesting( + "test-operation", /*keep_alive=*/true, + SharedStorageEventParams::PrivateAggregationConfigWrapper(), + blink::CloneableMessage(), /*worklet_id=*/0)}, {AccessScope::kSharedStorageWorklet, AccessMethod::kSet, MainFrameId(), origin_str, SharedStorageEventParams::CreateForSet("key0", "value0", false, @@ -9135,8 +9214,10 @@ "context-origin", /*worklet_id=*/1)}, {AccessScope::kWindow, AccessMethod::kRun, MainFrameId(), origin_str, - SharedStorageEventParams::CreateForRun( - "test-operation", blink::CloneableMessage(), /*worklet_id=*/1)}, + SharedStorageEventParams::CreateForRunForTesting( + "test-operation", /*keep_alive=*/true, + SharedStorageEventParams::PrivateAggregationConfigWrapper(), + blink::CloneableMessage(), /*worklet_id=*/1)}, {AccessScope::kSharedStorageWorklet, AccessMethod::kSet, MainFrameId(), origin_str, SharedStorageEventParams::CreateForSet("key1", "value2", false,
diff --git a/content/browser/shared_storage/shared_storage_event_params.cc b/content/browser/shared_storage/shared_storage_event_params.cc index 4b5a39e..fc785f94 100644 --- a/content/browser/shared_storage/shared_storage_event_params.cc +++ b/content/browser/shared_storage/shared_storage_event_params.cc
@@ -4,6 +4,8 @@ #include "content/browser/shared_storage/shared_storage_event_params.h" +#include <stdint.h> + #include <algorithm> #include <ostream> #include <sstream> @@ -13,6 +15,8 @@ #include "base/strings/strcat.h" #include "base/strings/string_number_conversions.h" #include "base/strings/string_util.h" +#include "content/browser/private_aggregation/private_aggregation_host.h" +#include "third_party/blink/public/mojom/shared_storage/shared_storage.mojom.h" namespace content { @@ -22,7 +26,7 @@ const size_t kSharedStorageSerializedDataLengthLimitForEventParams = 1024; -std::string SerializeOptionalString(std::optional<std::string> str) { +std::string SerializeOptionalString(const std::optional<std::string>& str) { if (str) { return *str; } @@ -88,7 +92,8 @@ return escaped; } -std::string SerializeAndEscapeOptionalString(std::optional<std::string> str) { +std::string SerializeAndEscapeOptionalString( + const std::optional<std::string>& str) { if (str) { return Escape(*str); } @@ -96,6 +101,14 @@ return "std::nullopt"; } +std::string SerializeOptionalOrigin(const std::optional<url::Origin>& origin) { + if (origin) { + return origin->Serialize(); + } + + return "std::nullopt"; +} + std::string SerializeOptionalBool(std::optional<bool> b) { if (b) { return base::ToString(*b); @@ -112,8 +125,28 @@ return "std::nullopt"; } +std::string SerializeOptionalUInt16(std::optional<uint16_t> i) { + if (i) { + return base::NumberToString(static_cast<unsigned int>(*i)); + } + + return "std::nullopt"; +} + +std::string SerializeOptionalPrivateAggregationConfigWrapper( + const std::optional< + SharedStorageEventParams::PrivateAggregationConfigWrapper>& wrapper) { + if (!wrapper) { + return "std::nullopt"; + } + + std::ostringstream ss; + ss << *wrapper; + return ss.str(); +} + std::string SerializeOptionalUrlsWithMetadata( - std::optional<std::vector<SharedStorageUrlSpecWithMetadata>> + const std::optional<std::vector<SharedStorageUrlSpecWithMetadata>>& urls_with_metadata) { if (!urls_with_metadata) { return "std::nullopt"; @@ -148,6 +181,63 @@ } // namespace +SharedStorageEventParams::PrivateAggregationConfigWrapper:: + PrivateAggregationConfigWrapper() + : config(blink::mojom::PrivateAggregationConfig::New()) { + config->filtering_id_max_bytes = + PrivateAggregationHost::kDefaultFilteringIdMaxBytes; +} + +SharedStorageEventParams::PrivateAggregationConfigWrapper:: + PrivateAggregationConfigWrapper( + const std::optional<url::Origin>& aggregation_coordinator_origin, + const std::optional<std::string>& context_id, + uint32_t filtering_id_max_bytes, + std::optional<uint16_t> max_contributions) + : config(blink::mojom::PrivateAggregationConfig::New( + aggregation_coordinator_origin, + context_id, + filtering_id_max_bytes, + max_contributions)) {} + +SharedStorageEventParams::PrivateAggregationConfigWrapper:: + PrivateAggregationConfigWrapper( + const blink::mojom::PrivateAggregationConfigPtr& config) + : config(config.Clone()) {} + +SharedStorageEventParams::PrivateAggregationConfigWrapper:: + PrivateAggregationConfigWrapper( + const PrivateAggregationConfigWrapper& other) + : config(other.config.Clone()) {} + +SharedStorageEventParams::PrivateAggregationConfigWrapper:: + ~PrivateAggregationConfigWrapper() = default; + +SharedStorageEventParams::PrivateAggregationConfigWrapper& +SharedStorageEventParams::PrivateAggregationConfigWrapper::operator=( + const PrivateAggregationConfigWrapper& other) { + if (this != &other) { + config = other.config.Clone(); + } + return *this; +} + +bool SharedStorageEventParams::PrivateAggregationConfigWrapper::operator==( + const PrivateAggregationConfigWrapper& other) const = default; + +std::ostream& operator<<( + std::ostream& os, + const SharedStorageEventParams::PrivateAggregationConfigWrapper& wrapper) { + os << "{ Aggregation Coordinator Origin: " + << SerializeOptionalOrigin(wrapper.config->aggregation_coordinator_origin) + << "; Context ID: " << SerializeOptionalString(wrapper.config->context_id) + << "; Filtering ID Max Bytes: " << wrapper.config->filtering_id_max_bytes + << "; Max Contributions: " + << SerializeOptionalUInt16(wrapper.config->max_contributions) << " }"; + + return os; +} + SharedStorageEventParams::SharedStorageUrlSpecWithMetadata:: SharedStorageUrlSpecWithMetadata() = default; @@ -207,6 +297,8 @@ std::optional<std::string> script_source_url, std::optional<std::string> data_origin, std::optional<std::string> operation_name, + std::optional<bool> keep_alive, + std::optional<PrivateAggregationConfigWrapper> private_aggregation_config, std::optional<std::string> serialized_data, std::optional<std::vector<SharedStorageUrlSpecWithMetadata>> urls_with_metadata, @@ -217,6 +309,8 @@ : script_source_url(std::move(script_source_url)), data_origin(std::move(data_origin)), operation_name(std::move(operation_name)), + keep_alive(keep_alive), + private_aggregation_config(std::move(private_aggregation_config)), serialized_data(std::move(serialized_data)), urls_with_metadata(std::move(urls_with_metadata)), key(std::move(key)), @@ -244,22 +338,51 @@ // static SharedStorageEventParams SharedStorageEventParams::CreateForRun( const std::string& operation_name, + bool keep_alive, + const blink::mojom::PrivateAggregationConfigPtr& private_aggregation_config, const blink::CloneableMessage& serialized_data, int worklet_id) { return SharedStorageEventParams::CreateForWorkletOperation( - operation_name, serialized_data, + operation_name, keep_alive, private_aggregation_config, serialized_data, + /*urls_with_metadata=*/std::nullopt, worklet_id); +} + +// static +SharedStorageEventParams SharedStorageEventParams::CreateForRunForTesting( + const std::string& operation_name, + bool keep_alive, + PrivateAggregationConfigWrapper config_wrapper, + const blink::CloneableMessage& serialized_data, + int worklet_id) { + return SharedStorageEventParams::CreateForWorkletOperationForTesting( + operation_name, keep_alive, std::move(config_wrapper), serialized_data, /*urls_with_metadata=*/std::nullopt, worklet_id); } // static SharedStorageEventParams SharedStorageEventParams::CreateForSelectURL( const std::string& operation_name, + bool keep_alive, + const blink::mojom::PrivateAggregationConfigPtr& private_aggregation_config, const blink::CloneableMessage& serialized_data, std::vector<SharedStorageUrlSpecWithMetadata> urls_with_metadata, int worklet_id) { return SharedStorageEventParams::CreateForWorkletOperation( - operation_name, serialized_data, std::move(urls_with_metadata), - worklet_id); + operation_name, keep_alive, private_aggregation_config, serialized_data, + std::move(urls_with_metadata), worklet_id); +} + +// static +SharedStorageEventParams SharedStorageEventParams::CreateForSelectURLForTesting( + const std::string& operation_name, + bool keep_alive, + PrivateAggregationConfigWrapper config_wrapper, + const blink::CloneableMessage& serialized_data, + std::vector<SharedStorageUrlSpecWithMetadata> urls_with_metadata, + int worklet_id) { + return SharedStorageEventParams::CreateForWorkletOperationForTesting( + operation_name, keep_alive, std::move(config_wrapper), serialized_data, + std::move(urls_with_metadata), worklet_id); } // static @@ -314,6 +437,8 @@ return SharedStorageEventParams( script_source_url.spec(), std::move(data_origin), /*operation_name=*/std::nullopt, + /*keep_alive=*/std::nullopt, + /*private_aggregation_config=*/std::nullopt, /*serialized_data=*/std::nullopt, /*urls_with_metadata=*/std::nullopt, /*key=*/std::nullopt, @@ -324,18 +449,43 @@ // static SharedStorageEventParams SharedStorageEventParams::CreateForWorkletOperation( const std::string& operation_name, + bool keep_alive, + const blink::mojom::PrivateAggregationConfigPtr& private_aggregation_config, const blink::CloneableMessage& serialized_data, std::optional<std::vector<SharedStorageUrlSpecWithMetadata>> urls_with_metadata, int worklet_id) { - return SharedStorageEventParams(/*script_source_url=*/std::nullopt, - /*data_origin=*/std::nullopt, operation_name, - MaybeTruncateSerializedData(serialized_data), - std::move(urls_with_metadata), - /*key=*/std::nullopt, - /*value=*/std::nullopt, - /*ignore_if_present=*/std::nullopt, - worklet_id); + return SharedStorageEventParams( + /*script_source_url=*/std::nullopt, + /*data_origin=*/std::nullopt, operation_name, keep_alive, + PrivateAggregationConfigWrapper(private_aggregation_config), + MaybeTruncateSerializedData(serialized_data), + std::move(urls_with_metadata), + /*key=*/std::nullopt, + /*value=*/std::nullopt, + /*ignore_if_present=*/std::nullopt, worklet_id); +} + +// static +SharedStorageEventParams +SharedStorageEventParams::CreateForWorkletOperationForTesting( + const std::string& operation_name, + bool keep_alive, + PrivateAggregationConfigWrapper config_wrapper, + const blink::CloneableMessage& serialized_data, + std::optional<std::vector<SharedStorageUrlSpecWithMetadata>> + urls_with_metadata, + int worklet_id) { + return SharedStorageEventParams( + /*script_source_url=*/std::nullopt, + /*data_origin=*/std::nullopt, operation_name, keep_alive, + /*private_aggregation_config=*/ + std::make_optional(std::move(config_wrapper)), + MaybeTruncateSerializedData(serialized_data), + std::move(urls_with_metadata), + /*key=*/std::nullopt, + /*value=*/std::nullopt, + /*ignore_if_present=*/std::nullopt, worklet_id); } // static @@ -348,6 +498,8 @@ /*script_source_url=*/std::nullopt, /*data_origin=*/std::nullopt, /*operation_name=*/std::nullopt, + /*keep_alive=*/std::nullopt, + /*private_aggregation_config=*/std::nullopt, /*serialized_data*/ std::nullopt, /*urls_with_metadata=*/std::nullopt, std::move(key), std::move(value), ignore_if_present, worklet_id); @@ -359,6 +511,8 @@ return lhs.script_source_url == rhs.script_source_url && lhs.data_origin == rhs.data_origin && lhs.operation_name == rhs.operation_name && + lhs.keep_alive == rhs.keep_alive && + lhs.private_aggregation_config == rhs.private_aggregation_config && !!lhs.serialized_data == !!rhs.serialized_data && lhs.urls_with_metadata == rhs.urls_with_metadata && lhs.key == rhs.key && lhs.value == rhs.value && @@ -372,6 +526,10 @@ << SerializeOptionalString(params.script_source_url) << "; Data Origin: " << SerializeOptionalString(params.data_origin) << "; Operation Name: " << SerializeOptionalString(params.operation_name) + << "; Keep Alive: " << SerializeOptionalBool(params.keep_alive) + << "; Private Aggregation Config: " + << SerializeOptionalPrivateAggregationConfigWrapper( + params.private_aggregation_config) << "; Serialized Data: " << SerializeAndEscapeOptionalString(params.serialized_data) << "; URLs With Metadata: "
diff --git a/content/browser/shared_storage/shared_storage_event_params.h b/content/browser/shared_storage/shared_storage_event_params.h index 6dde29b..c8b675d 100644 --- a/content/browser/shared_storage/shared_storage_event_params.h +++ b/content/browser/shared_storage/shared_storage_event_params.h
@@ -11,7 +11,9 @@ #include "content/common/content_export.h" #include "third_party/blink/public/common/messaging/cloneable_message.h" +#include "third_party/blink/public/mojom/shared_storage/shared_storage.mojom.h" #include "url/gurl.h" +#include "url/origin.h" namespace content { @@ -19,6 +21,28 @@ // events. class CONTENT_EXPORT SharedStorageEventParams { public: + // Wraps a `blink::mojom::PrivateAggregationConfig` for DevTools shared + // storage integration. + struct CONTENT_EXPORT PrivateAggregationConfigWrapper { + blink::mojom::PrivateAggregationConfigPtr config; + PrivateAggregationConfigWrapper(); + PrivateAggregationConfigWrapper( + const std::optional<url::Origin>& aggregation_coordinator_origin, + const std::optional<std::string>& context_id, + uint32_t filtering_id_max_bytes, + std::optional<uint16_t> max_contributions); + explicit PrivateAggregationConfigWrapper( + const blink::mojom::PrivateAggregationConfigPtr& config); + PrivateAggregationConfigWrapper( + const PrivateAggregationConfigWrapper& other); + ~PrivateAggregationConfigWrapper(); + PrivateAggregationConfigWrapper& operator=( + const PrivateAggregationConfigWrapper& other); + bool operator==(const PrivateAggregationConfigWrapper&) const; + friend std::ostream& operator<<( + std::ostream& os, + const PrivateAggregationConfigWrapper& config); + }; // Bundles a URL's spec along with a map of any accompanying reporting // metadata for DevTools integration. struct CONTENT_EXPORT SharedStorageUrlSpecWithMetadata { @@ -47,10 +71,29 @@ int worklet_id); static SharedStorageEventParams CreateForRun( const std::string& operation_name, + bool keep_alive, + const blink::mojom::PrivateAggregationConfigPtr& + private_aggregation_config, + const blink::CloneableMessage& serialized_data, + int worklet_id); + static SharedStorageEventParams CreateForRunForTesting( + const std::string& operation_name, + bool keep_alive, + PrivateAggregationConfigWrapper config_wrapper, const blink::CloneableMessage& serialized_data, int worklet_id); static SharedStorageEventParams CreateForSelectURL( const std::string& operation_name, + bool keep_alive, + const blink::mojom::PrivateAggregationConfigPtr& + private_aggregation_config, + const blink::CloneableMessage& serialized_data, + std::vector<SharedStorageUrlSpecWithMetadata> urls_with_metadata, + int worklet_id); + static SharedStorageEventParams CreateForSelectURLForTesting( + const std::string& operation_name, + bool keep_alive, + PrivateAggregationConfigWrapper config_wrapper, const blink::CloneableMessage& serialized_data, std::vector<SharedStorageUrlSpecWithMetadata> urls_with_metadata, int worklet_id); @@ -78,6 +121,8 @@ std::optional<std::string> script_source_url; std::optional<std::string> data_origin; std::optional<std::string> operation_name; + std::optional<bool> keep_alive; + std::optional<PrivateAggregationConfigWrapper> private_aggregation_config; std::optional<std::string> serialized_data; std::optional<std::vector<SharedStorageUrlSpecWithMetadata>> urls_with_metadata; @@ -92,6 +137,8 @@ std::optional<std::string> script_source_url, std::optional<std::string> data_origin, std::optional<std::string> operation_name, + std::optional<bool> keep_alive, + std::optional<PrivateAggregationConfigWrapper> private_aggregation_config, std::optional<std::string> serialized_data, std::optional<std::vector<SharedStorageUrlSpecWithMetadata>> urls_with_metadata, @@ -107,6 +154,17 @@ static SharedStorageEventParams CreateForWorkletOperation( const std::string& operation_name, + bool keep_alive, + const blink::mojom::PrivateAggregationConfigPtr& + private_aggregation_config, + const blink::CloneableMessage& serialized_data, + std::optional<std::vector<SharedStorageUrlSpecWithMetadata>> + urls_with_metadata, + int worklet_id); + static SharedStorageEventParams CreateForWorkletOperationForTesting( + const std::string& operation_name, + bool keep_alive, + PrivateAggregationConfigWrapper config_wrapper, const blink::CloneableMessage& serialized_data, std::optional<std::vector<SharedStorageUrlSpecWithMetadata>> urls_with_metadata,
diff --git a/content/browser/shared_storage/shared_storage_event_params_unittest.cc b/content/browser/shared_storage/shared_storage_event_params_unittest.cc index c39e4bbc..042d4c7e 100644 --- a/content/browser/shared_storage/shared_storage_event_params_unittest.cc +++ b/content/browser/shared_storage/shared_storage_event_params_unittest.cc
@@ -62,8 +62,10 @@ blink::CloneableMessage serialized_data_message; SetCloneableMessageWithByteArray(serialized_data_message, data); - auto params = SharedStorageEventParams::CreateForRun( - "test-operation", serialized_data_message, /*worklet_id=*/0); + auto params = SharedStorageEventParams::CreateForRunForTesting( + "test-operation", /*keep_alive=*/false, + SharedStorageEventParams::PrivateAggregationConfigWrapper(), + serialized_data_message, /*worklet_id=*/0); EXPECT_FALSE(base::IsStringUTF8(GetSerializedDataDirectFromBytes(params))); @@ -88,8 +90,10 @@ blink::CloneableMessage serialized_data_message; SetCloneableMessageWithByteArray(serialized_data_message, data); - auto params = SharedStorageEventParams::CreateForRun( - "test-operation", serialized_data_message, /*worklet_id=*/0); + auto params = SharedStorageEventParams::CreateForRunForTesting( + "test-operation", /*keep_alive=*/false, + SharedStorageEventParams::PrivateAggregationConfigWrapper(), + serialized_data_message, /*worklet_id=*/0); std::string serialized_data_direct_from_bytes = GetSerializedDataDirectFromBytes(params);
diff --git a/content/browser/shared_storage/shared_storage_private_aggregation_browsertest.cc b/content/browser/shared_storage/shared_storage_private_aggregation_browsertest.cc index 4699687b..4d577c4 100644 --- a/content/browser/shared_storage/shared_storage_private_aggregation_browsertest.cc +++ b/content/browser/shared_storage/shared_storage_private_aggregation_browsertest.cc
@@ -28,6 +28,7 @@ #include "base/test/mock_callback.h" #include "base/test/scoped_feature_list.h" #include "base/time/time.h" +#include "components/aggregation_service/aggregation_coordinator_utils.h" #include "content/browser/aggregation_service/aggregatable_report.h" #include "content/browser/private_aggregation/private_aggregation_budget_key.h" #include "content/browser/private_aggregation/private_aggregation_budgeter.h" @@ -36,8 +37,10 @@ #include "content/browser/private_aggregation/private_aggregation_pending_contributions.h" #include "content/browser/private_aggregation/private_aggregation_test_utils.h" #include "content/browser/shared_storage/shared_storage_browsertest_base.h" +#include "content/browser/shared_storage/shared_storage_event_params.h" #include "content/browser/shared_storage/shared_storage_runtime_manager.h" #include "content/browser/shared_storage/shared_storage_worklet_host.h" +#include "content/browser/shared_storage/test_shared_storage_observer.h" #include "content/browser/shared_storage/test_shared_storage_runtime_manager.h" #include "content/browser/shared_storage/test_shared_storage_worklet_host.h" #include "content/browser/storage_partition_impl.h" @@ -76,6 +79,8 @@ using testing::FieldsAre; using testing::Ne; using testing::Optional; +using AccessScope = blink::SharedStorageAccessScope; +using AccessMethod = TestSharedStorageObserver::AccessMethod; class SharedStoragePrivateAggregationDisabledBrowserTest : public SharedStorageBrowserTestBase { @@ -355,6 +360,18 @@ EXPECT_TRUE(console_observer.messages().empty()); run_loop.Run(); + + ExpectAccessObserved( + {{AccessScope::kWindow, AccessMethod::kAddModule, MainFrameId(), + a_test_origin_.Serialize(), + SharedStorageEventParams::CreateForAddModule(out_script_url, + /*worklet_id=*/0)}, + {AccessScope::kWindow, AccessMethod::kRun, MainFrameId(), + a_test_origin_.Serialize(), + SharedStorageEventParams::CreateForRunForTesting( + "test-operation", /*keep_alive=*/true, + SharedStorageEventParams::PrivateAggregationConfigWrapper(), + blink::CloneableMessage(), /*worklet_id=*/0)}}); } IN_PROC_BROWSER_TEST_F(SharedStoragePrivateAggregationEnabledBrowserTest, @@ -574,6 +591,22 @@ EXPECT_TRUE(console_observer.messages().empty()); run_loop.Run(); + + ExpectAccessObserved( + {{AccessScope::kWindow, AccessMethod::kAddModule, MainFrameId(), + a_test_origin_.Serialize(), + SharedStorageEventParams::CreateForAddModule(out_script_url, + /*worklet_id=*/0)}, + {AccessScope::kWindow, AccessMethod::kRun, MainFrameId(), + a_test_origin_.Serialize(), + SharedStorageEventParams::CreateForRunForTesting( + "test-operation", /*keep_alive=*/true, + SharedStorageEventParams::PrivateAggregationConfigWrapper( + /*aggregation_coordinator_origin=*/std::nullopt, + /*context_id=*/"example_context_id", + /*filtering_id_max_bytes=*/1, + /*max_contributions=*/std::nullopt), + blink::CloneableMessage(), /*worklet_id=*/0)}}); } IN_PROC_BROWSER_TEST_F(SharedStoragePrivateAggregationEnabledBrowserTest, @@ -631,6 +664,21 @@ EXPECT_TRUE(console_observer.messages().empty()); run_loop.Run(); + + ExpectAccessObserved( + {{AccessScope::kWindow, AccessMethod::kAddModule, MainFrameId(), + a_test_origin_.Serialize(), + SharedStorageEventParams::CreateForAddModule(out_script_url, + /*worklet_id=*/0)}, + {AccessScope::kWindow, AccessMethod::kRun, MainFrameId(), + a_test_origin_.Serialize(), + SharedStorageEventParams::CreateForRunForTesting( + "test-operation", /*keep_alive=*/true, + SharedStorageEventParams::PrivateAggregationConfigWrapper( + /*aggregation_coordinator_origin=*/std::nullopt, + /*context_id=*/std::string(), /*filtering_id_max_bytes=*/1, + /*max_contributions=*/std::nullopt), + blink::CloneableMessage(), /*worklet_id=*/0)}}); } IN_PROC_BROWSER_TEST_F(SharedStoragePrivateAggregationEnabledBrowserTest, @@ -693,6 +741,23 @@ EXPECT_TRUE(console_observer.messages().empty()); run_loop.Run(); + + ExpectAccessObserved( + {{AccessScope::kWindow, AccessMethod::kAddModule, MainFrameId(), + a_test_origin_.Serialize(), + SharedStorageEventParams::CreateForAddModule(out_script_url, + /*worklet_id=*/0)}, + {AccessScope::kWindow, AccessMethod::kRun, MainFrameId(), + a_test_origin_.Serialize(), + SharedStorageEventParams::CreateForRunForTesting( + "test-operation", /*keep_alive=*/true, + SharedStorageEventParams::PrivateAggregationConfigWrapper( + /*aggregation_coordinator_origin=*/std::nullopt, /*context_id=*/ + "an_example_of_a_context_id_with_the_exact_maximum_allowed_" + "length", + /*filtering_id_max_bytes=*/1, + /*max_contributions=*/std::nullopt), + blink::CloneableMessage(), /*worklet_id=*/0)}}); } IN_PROC_BROWSER_TEST_F(SharedStoragePrivateAggregationEnabledBrowserTest, @@ -1242,6 +1307,21 @@ EXPECT_TRUE(console_observer.messages().empty()); run_loop.Run(); + + ExpectAccessObserved( + {{AccessScope::kWindow, AccessMethod::kAddModule, MainFrameId(), + a_test_origin_.Serialize(), + SharedStorageEventParams::CreateForAddModule(out_script_url, + /*worklet_id=*/0)}, + {AccessScope::kWindow, AccessMethod::kRun, MainFrameId(), + a_test_origin_.Serialize(), + SharedStorageEventParams::CreateForRunForTesting( + "test-operation", /*keep_alive=*/true, + SharedStorageEventParams::PrivateAggregationConfigWrapper( + /*aggregation_coordinator_origin=*/std::nullopt, + /*context_id=*/std::nullopt, /*filtering_id_max_bytes=*/8, + /*max_contributions=*/std::nullopt), + blink::CloneableMessage(), /*worklet_id=*/0)}}); } IN_PROC_BROWSER_TEST_F(SharedStoragePrivateAggregationEnabledBrowserTest, @@ -1317,6 +1397,21 @@ EXPECT_TRUE(console_observer.messages().empty()); run_loop.Run(); + + ExpectAccessObserved( + {{AccessScope::kWindow, AccessMethod::kAddModule, MainFrameId(), + a_test_origin_.Serialize(), + SharedStorageEventParams::CreateForAddModule(out_script_url, + /*worklet_id=*/0)}, + {AccessScope::kWindow, AccessMethod::kRun, MainFrameId(), + a_test_origin_.Serialize(), + SharedStorageEventParams::CreateForRunForTesting( + "test-operation", /*keep_alive=*/true, + SharedStorageEventParams::PrivateAggregationConfigWrapper( + /*aggregation_coordinator_origin=*/std::nullopt, + /*context_id=*/std::nullopt, /*filtering_id_max_bytes=*/8, + /*max_contributions=*/std::nullopt), + blink::CloneableMessage(), /*worklet_id=*/0)}}); } IN_PROC_BROWSER_TEST_F(SharedStoragePrivateAggregationEnabledBrowserTest, @@ -1383,6 +1478,21 @@ EXPECT_TRUE(console_observer.messages().empty()); run_loop.Run(); + + ExpectAccessObserved( + {{AccessScope::kWindow, AccessMethod::kAddModule, MainFrameId(), + a_test_origin_.Serialize(), + SharedStorageEventParams::CreateForAddModule(out_script_url, + /*worklet_id=*/0)}, + {AccessScope::kWindow, AccessMethod::kRun, MainFrameId(), + a_test_origin_.Serialize(), + SharedStorageEventParams::CreateForRunForTesting( + "test-operation", /*keep_alive=*/true, + SharedStorageEventParams::PrivateAggregationConfigWrapper( + /*aggregation_coordinator_origin=*/std::nullopt, + /*context_id=*/std::nullopt, /*filtering_id_max_bytes=*/8, + /*max_contributions=*/std::nullopt), + blink::CloneableMessage(), /*worklet_id=*/0)}}); } IN_PROC_BROWSER_TEST_F(SharedStoragePrivateAggregationEnabledBrowserTest, @@ -2065,6 +2175,30 @@ /*max_contributions=*/base::NumberToString(GetParam().max_contributions)); EXPECT_TRUE(console_observer.messages().empty()); run_loop.Run(); + + std::optional<uint16_t> expected_max_contributions; + if (GetParam().is_feature_enabled) { + expected_max_contributions = static_cast<uint16_t>( + std::min(GetParam().max_contributions, + static_cast<size_t>(std::numeric_limits<uint16_t>::max()))); + } + auto expected_config_to_observe = + SharedStorageEventParams::PrivateAggregationConfigWrapper( + /*aggregation_coordinator_origin=*/std::nullopt, + /*context_id=*/"example_context_id", + /*filtering_id_max_bytes=*/1, + /*max_contributions=*/expected_max_contributions); + ExpectAccessObserved( + {{AccessScope::kWindow, AccessMethod::kAddModule, MainFrameId(), + a_test_origin_.Serialize(), + SharedStorageEventParams::CreateForAddModule(out_script_url, + /*worklet_id=*/0)}, + {AccessScope::kWindow, AccessMethod::kRun, MainFrameId(), + a_test_origin_.Serialize(), + SharedStorageEventParams::CreateForRunForTesting( + "test-operation", /*keep_alive=*/true, expected_config_to_observe, + blink::CloneableMessage(), + /*worklet_id=*/0)}}); } class SharedStoragePrivateAggregationErrorReportingDisabledBrowserTest @@ -2993,6 +3127,247 @@ run_loop.Run(); } +IN_PROC_BROWSER_TEST_F(SharedStoragePrivateAggregationEnabledBrowserTest, + RunWithExplicitAggregationCoordinatorOriginValue) { + WebContentsConsoleObserver console_observer(shell()->web_contents()); + + base::RunLoop run_loop; + + EXPECT_CALL(mock_callback(), Run) + .WillOnce(testing::Invoke( + [&](PrivateAggregationHost::ReportRequestGenerator generator, + PrivateAggregationPendingContributions::Wrapper contributions, + PrivateAggregationBudgetKey budget_key, + PrivateAggregationHost::NullReportBehavior null_report_behavior) { + AggregatableReportRequest request = GenerateReportRequest( + std::move(generator), std::move(contributions), + null_report_behavior); + ASSERT_EQ(request.payload_contents().contributions.size(), 1u); + EXPECT_EQ(request.payload_contents().contributions[0].bucket, 1); + EXPECT_EQ(request.payload_contents().contributions[0].value, 2); + EXPECT_EQ(request.shared_info().reporting_origin, a_test_origin_); + EXPECT_EQ(budget_key.origin(), a_test_origin_); + EXPECT_EQ(budget_key.caller_api(), + PrivateAggregationCallerApi::kSharedStorage); + EXPECT_TRUE(request.additional_fields().empty()); + EXPECT_EQ( + null_report_behavior, + PrivateAggregationHost::NullReportBehavior::kDontSendReport); + run_loop.Quit(); + })); + + EXPECT_CALL(browser_client(), + LogWebFeatureForCurrentPage( + shell()->web_contents()->GetPrimaryMainFrame(), + blink::mojom::WebFeature::kPrivateAggregationApiAll)); + EXPECT_CALL( + browser_client(), + LogWebFeatureForCurrentPage( + shell()->web_contents()->GetPrimaryMainFrame(), + blink::mojom::WebFeature::kPrivateAggregationApiSharedStorage)); + ON_CALL(browser_client(), IsPrivateAggregationAllowed) + .WillByDefault(testing::Return(true)); + ON_CALL(browser_client(), IsSharedStorageAllowed) + .WillByDefault(testing::Return(true)); + + std::string worklet_script = R"( + privateAggregation.contributeToHistogram({bucket: 1n, value: 2}); + )"; + + base::StringPairs run_function_body_replacement; + run_function_body_replacement.emplace_back("{{RUN_FUNCTION_BODY}}", + worklet_script); + + RenderFrameHost* rfh = shell()->web_contents()->GetPrimaryMainFrame(); + std::string host = rfh->GetLastCommittedOrigin().host(); + + GURL out_script_url = https_server()->GetURL( + host, net::test_server::GetFilePathWithReplacements( + "/shared_storage/customizable_module.js", + run_function_body_replacement)); + + EXPECT_TRUE(ExecJs(shell(), JsReplace("sharedStorage.worklet.addModule($1)", + out_script_url))); + + auto* worklet_host = + test_runtime_manager().GetLastAttachedWorkletHostForFrameWithScriptSrc( + rfh, out_script_url); + ASSERT_TRUE(worklet_host); + + EXPECT_EQ(1u, test_runtime_manager().GetAttachedWorkletHostsCount()); + + EXPECT_EQ(worklet_host->creation_method(), + blink::mojom::SharedStorageWorkletCreationMethod::kAddModule); + + // There is 1 more "worklet operation": `run()`. + worklet_host->SetExpectedWorkletResponsesCount(1); + + std::string private_aggregation_config_js = JsReplace( + "privateAggregationConfig: {aggregationCoordinatorOrigin: $1, }", + aggregation_service::GetDefaultAggregationCoordinatorOrigin() + .Serialize()); + + std::string run_operation_script = + base::StrCat({"sharedStorage.run('test-operation', {", + private_aggregation_config_js, "});"}); + + EXPECT_TRUE(ExecJs(shell(), run_operation_script)); + + CHECK(worklet_host); + worklet_host->WaitForWorkletResponses(); + + EXPECT_TRUE(console_observer.messages().empty()); + + run_loop.Run(); + + ExpectAccessObserved( + {{AccessScope::kWindow, AccessMethod::kAddModule, MainFrameId(), + a_test_origin_.Serialize(), + SharedStorageEventParams::CreateForAddModule(out_script_url, + /*worklet_id=*/0)}, + {AccessScope::kWindow, AccessMethod::kRun, MainFrameId(), + a_test_origin_.Serialize(), + SharedStorageEventParams::CreateForRunForTesting( + "test-operation", /*keep_alive=*/false, + SharedStorageEventParams::PrivateAggregationConfigWrapper( + /*aggregation_coordinator_origin=*/aggregation_service:: + GetDefaultAggregationCoordinatorOrigin(), + /*context_id=*/std::nullopt, /*filtering_id_max_bytes=*/1, + /*max_contributions=*/std::nullopt), + blink::CloneableMessage(), /*worklet_id=*/0)}}); +} + +IN_PROC_BROWSER_TEST_F(SharedStoragePrivateAggregationEnabledBrowserTest, + SelectURLWithExplicitAggregationCoordinatorOriginValue) { + WebContentsConsoleObserver console_observer(shell()->web_contents()); + + base::RunLoop run_loop; + + EXPECT_CALL(mock_callback(), Run) + .WillOnce(testing::Invoke( + [&](PrivateAggregationHost::ReportRequestGenerator generator, + PrivateAggregationPendingContributions::Wrapper contributions, + PrivateAggregationBudgetKey budget_key, + PrivateAggregationHost::NullReportBehavior null_report_behavior) { + AggregatableReportRequest request = GenerateReportRequest( + std::move(generator), std::move(contributions), + null_report_behavior); + ASSERT_EQ(request.payload_contents().contributions.size(), 1u); + EXPECT_EQ(request.payload_contents().contributions[0].bucket, 1); + EXPECT_EQ(request.payload_contents().contributions[0].value, 2); + EXPECT_EQ(request.shared_info().reporting_origin, a_test_origin_); + EXPECT_EQ(budget_key.origin(), a_test_origin_); + EXPECT_EQ(budget_key.caller_api(), + PrivateAggregationCallerApi::kSharedStorage); + EXPECT_TRUE(request.additional_fields().empty()); + EXPECT_EQ( + null_report_behavior, + PrivateAggregationHost::NullReportBehavior::kDontSendReport); + run_loop.Quit(); + })); + + EXPECT_CALL(browser_client(), + LogWebFeatureForCurrentPage( + shell()->web_contents()->GetPrimaryMainFrame(), + blink::mojom::WebFeature::kPrivateAggregationApiAll)); + EXPECT_CALL( + browser_client(), + LogWebFeatureForCurrentPage( + shell()->web_contents()->GetPrimaryMainFrame(), + blink::mojom::WebFeature::kPrivateAggregationApiSharedStorage)); + ON_CALL(browser_client(), IsPrivateAggregationAllowed) + .WillByDefault(testing::Return(true)); + ON_CALL(browser_client(), IsSharedStorageAllowed) + .WillByDefault(testing::Return(true)); + + std::string worklet_script = R"( + privateAggregation.contributeToHistogram({bucket: 1n, value: 2}); + )"; + + base::StringPairs run_function_body_replacement; + run_function_body_replacement.emplace_back("{{RUN_FUNCTION_BODY}}", + worklet_script); + + RenderFrameHost* rfh = shell()->web_contents()->GetPrimaryMainFrame(); + std::string host = rfh->GetLastCommittedOrigin().host(); + + GURL out_script_url = https_server()->GetURL( + host, net::test_server::GetFilePathWithReplacements( + "/shared_storage/customizable_selecturl_module.js", + run_function_body_replacement)); + + EXPECT_TRUE(ExecJs(shell(), JsReplace("sharedStorage.worklet.addModule($1)", + out_script_url))); + + auto* worklet_host = + test_runtime_manager().GetLastAttachedWorkletHostForFrameWithScriptSrc( + rfh, out_script_url); + ASSERT_TRUE(worklet_host); + + EXPECT_EQ(1u, test_runtime_manager().GetAttachedWorkletHostsCount()); + + EXPECT_EQ(worklet_host->creation_method(), + blink::mojom::SharedStorageWorkletCreationMethod::kAddModule); + + // There is 1 more "worklet operation": `selectURL()`. + worklet_host->SetExpectedWorkletResponsesCount(1); + + std::string select_url_operation_script = JsReplace( + R"( + (async function() { + window.select_url_result = await sharedStorage.selectURL( + 'test-url-selection-operation', + [ + { + url: "fenced_frames/title0.html" + } + ], + { + privateAggregationConfig: {aggregationCoordinatorOrigin: $1}, + resolveToConfig: true + } + ); + if (!(select_url_result instanceof FencedFrameConfig)) { + throw new Error('selectURL() did not return a FencedFrameConfig.'); + } + return window.select_url_result; + })() + )", + aggregation_service::GetDefaultAggregationCoordinatorOrigin() + .Serialize()); + + EvalJsResult result = EvalJs(shell(), select_url_operation_script); + EXPECT_TRUE(result.error.empty()); + + CHECK(worklet_host); + worklet_host->WaitForWorkletResponses(); + + EXPECT_TRUE(console_observer.messages().empty()); + + run_loop.Run(); + + ExpectAccessObserved( + {{AccessScope::kWindow, AccessMethod::kAddModule, MainFrameId(), + a_test_origin_.Serialize(), + SharedStorageEventParams::CreateForAddModule(out_script_url, + /*worklet_id=*/0)}, + {AccessScope::kWindow, AccessMethod::kSelectURL, MainFrameId(), + a_test_origin_.Serialize(), + SharedStorageEventParams::CreateForSelectURLForTesting( + "test-url-selection-operation", /*keep_alive=*/false, + SharedStorageEventParams::PrivateAggregationConfigWrapper( + /*aggregation_coordinator_origin=*/aggregation_service:: + GetDefaultAggregationCoordinatorOrigin(), + /*context_id=*/std::nullopt, /*filtering_id_max_bytes=*/1, + /*max_contributions=*/std::nullopt), + blink::CloneableMessage(), + std::vector< + SharedStorageEventParams::SharedStorageUrlSpecWithMetadata>( + {{https_server()->GetURL(host, "/fenced_frames/title0.html"), + {}}}), + /*worklet_id=*/0)}}); +} + // TODO(alexmt): Consider testing that reserved.uncaught-exception not triggered // for selectURL if the incorrect type is returned.
diff --git a/content/browser/shared_storage/shared_storage_worklet_host.cc b/content/browser/shared_storage/shared_storage_worklet_host.cc index 9b8985b..54454c6c 100644 --- a/content/browser/shared_storage/shared_storage_worklet_host.cc +++ b/content/browser/shared_storage/shared_storage_worklet_host.cc
@@ -807,7 +807,8 @@ AccessScope::kWindow, AccessMethod::kSelectURL, document_service_->main_frame_id(), shared_storage_origin_.Serialize(), SharedStorageEventParams::CreateForSelectURL( - name, serialized_data, std::move(converted_urls), worklet_id_)); + name, keep_alive_after_operation, private_aggregation_config, + serialized_data, std::move(converted_urls), worklet_id_)); if (saved_queries_enabled_ && !saved_query_name.empty()) { auto saved_query_callback = base::BindOnce( @@ -959,8 +960,9 @@ shared_storage_runtime_manager_->NotifySharedStorageAccessed( AccessScope::kWindow, AccessMethod::kRun, document_service_->main_frame_id(), shared_storage_origin_.Serialize(), - SharedStorageEventParams::CreateForRun(name, serialized_data, - worklet_id_)); + SharedStorageEventParams::CreateForRun(name, keep_alive_after_operation, + private_aggregation_config, + serialized_data, worklet_id_)); GetAndConnectToSharedStorageWorkletService()->RunOperation( name, std::move(serialized_data),
diff --git a/content/browser/web_contents/web_contents_observer_browsertest.cc b/content/browser/web_contents/web_contents_observer_browsertest.cc index 74294586..ce45aff9 100644 --- a/content/browser/web_contents/web_contents_observer_browsertest.cc +++ b/content/browser/web_contents/web_contents_observer_browsertest.cc
@@ -7,6 +7,7 @@ #include "base/test/scoped_feature_list.h" #include "base/test/test_future.h" #include "build/build_config.h" +#include "components/network_session_configurator/common/network_switches.h" #include "content/browser/renderer_host/render_frame_host_impl.h" #include "content/browser/service_worker/embedded_worker_instance.h" #include "content/browser/web_contents/web_contents_impl.h" @@ -20,15 +21,20 @@ #include "content/public/test/browser_test_utils.h" #include "content/public/test/content_browser_test.h" #include "content/public/test/content_browser_test_content_browser_client.h" +#include "content/public/test/content_mock_cert_verifier.h" #include "content/public/test/mock_web_contents_observer.h" #include "content/public/test/test_utils.h" #include "content/shell/browser/shell.h" #include "net/base/features.h" +#include "net/cert/cert_verify_result.h" #include "net/cookies/cookie_access_result.h" #include "net/cookies/cookie_constants.h" #include "net/cookies/site_for_cookies.h" #include "net/dns/mock_host_resolver.h" +#include "net/test/cert_test_util.h" #include "net/test/embedded_test_server/default_handlers.h" +#include "net/test/quic_simple_test_server.h" +#include "net/test/test_data_directory.h" #include "testing/gmock/include/gmock/gmock-matchers.h" #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" @@ -988,17 +994,62 @@ } // namespace // Tests for the CookieAccessDetails::source reported by -// WebContentsObserver::OnCookiesAccessed(). +// WebContentsObserver::OnCookiesAccessed(). Some tests use QuicSimpleTestServer +// because Early Hints are only plumbed over HTTP/2 or HTTP/3 (QUIC). class CookieSourceBrowserTest : public ContentBrowserTest { public: + CookieSourceBrowserTest() { + feature_list_.InitWithFeatures( + std::vector<base::test::FeatureRef>{ + net::features::kSplitCacheByNetworkIsolationKey}, + std::vector<base::test::FeatureRef>{ + net::features::kMigrateSessionsOnNetworkChangeV2}); + } + + WebContents* web_contents() { return shell()->web_contents(); } + void SetUpOnMainThread() override { host_resolver()->AddRule("*", "127.0.0.1"); embedded_https_test_server().SetSSLConfig( net::EmbeddedTestServer::CERT_TEST_NAMES); ASSERT_TRUE(embedded_https_test_server().Start()); + + // Configure the certificate for the QUIC server. + auto test_cert = + net::ImportCertFromFile(net::GetTestCertsDirectory(), "quic-chain.pem"); + net::CertVerifyResult verify_result; + verify_result.verified_cert = test_cert; + mock_cert_verifier_.mock_cert_verifier()->AddResultForCert( + test_cert, verify_result, net::OK); + mock_cert_verifier_.mock_cert_verifier()->set_default_result(net::OK); } - WebContents* web_contents() { return shell()->web_contents(); } + void SetUpCommandLine(base::CommandLine* command_line) override { + ASSERT_TRUE(net::QuicSimpleTestServer::Start()); + command_line->AppendSwitchASCII( + switches::kOriginToForceQuicOn, + net::QuicSimpleTestServer::GetHostPort().ToString()); + mock_cert_verifier_.SetUpCommandLine(command_line); + } + + void TearDown() override { + // Needed by net::QuicSimpleTestServer::Shutdown() below. + base::ScopedAllowBaseSyncPrimitivesForTesting allow_wait; + net::QuicSimpleTestServer::Shutdown(); + } + + void SetUpInProcessBrowserTestFixture() override { + mock_cert_verifier_.SetUpInProcessBrowserTestFixture(); + } + + void TearDownInProcessBrowserTestFixture() override { + mock_cert_verifier_.TearDownInProcessBrowserTestFixture(); + } + + private: + base::test::ScopedFeatureList feature_list_; + + ContentMockCertVerifier mock_cert_verifier_; }; IN_PROC_BROWSER_TEST_F(CookieSourceBrowserTest, NavigationCookie) { @@ -1183,4 +1234,50 @@ CookieAccessDetails::Source::kNonNavigation); } +IN_PROC_BROWSER_TEST_F(CookieSourceBrowserTest, EarlyHints) { + // Register a response for /early.css + { + quiche::HttpHeaderBlock headers; + headers[":path"] = "/early.css"; + headers[":status"] = base::ToString(net::HTTP_OK); + headers["content-type"] = "text/css"; + headers["cache-control"] = "max-age=3600"; + headers["set-cookie"] = "foo=bar;"; + net::QuicSimpleTestServer::AddResponse("/early.css", std::move(headers), + "/* empty */"); + } + + // Register a response for /early_hints.html + { + quiche::HttpHeaderBlock headers; + headers[":path"] = "/early_hints.html"; + headers[":status"] = base::ToString(net::HTTP_OK); + headers["content-type"] = "text/html"; + + // Early Hint header. + quiche::HttpHeaderBlock early_hint_header; + early_hint_header["link"] = "</early.css>; rel=preload; as=style"; + std::vector<quiche::HttpHeaderBlock> early_hints; + early_hints.push_back(std::move(early_hint_header)); + + net::QuicSimpleTestServer::AddResponseWithEarlyHints( + /*path=*/"/early_hints.html", + /*response_headers=*/std::move(headers), + /*response_body=*/ + R"(<link rel="stylesheet" href="/early.css" />)", + /*early_hints=*/std::move(early_hints)); + } + const GURL page_url = + net::QuicSimpleTestServer::GetFileURL("/early_hints.html"); + const GURL early_url = net::QuicSimpleTestServer::GetFileURL("/early.css"); + + CookieObserver observer(web_contents(), early_url, + CookieAccessDetails::Type::kChange); + EXPECT_TRUE(NavigateToURL(web_contents(), page_url)); + ASSERT_TRUE(observer.Wait()); + EXPECT_EQ(observer.details().url, early_url); + EXPECT_EQ(observer.details().source, + CookieAccessDetails::Source::kNonNavigation); +} + } // namespace content
diff --git a/content/browser/webid/webid_browsertest.cc b/content/browser/webid/webid_browsertest.cc index 3b5c720e..4ac261d04 100644 --- a/content/browser/webid/webid_browsertest.cc +++ b/content/browser/webid/webid_browsertest.cc
@@ -1904,35 +1904,6 @@ } }; -// Verify that using mode: button in the API call logs to console. -IN_PROC_BROWSER_TEST_F(WebIdModeBrowserTest, UseModeButtonInsteadOfActive) { - idp_server()->SetConfigResponseDetails(BuildValidConfigDetails()); - - std::string script = R"( - (async () => { - var x = (await navigator.credentials.get({ - identity: { - providers: [{ - configURL: ')" + - BaseIdpUrl() + R"(', - clientId: 'client_id_1', - nonce: '12345', - }], - mode: 'button' - }, - })); - return x.token; - }) () - )"; - - WebContentsConsoleObserver console_observer(shell()->web_contents()); - console_observer.SetPattern( - "The mode button/widget are renamed to active/passive respectively and " - "will be deprecated soon."); - EXPECT_EQ(std::string(kToken), EvalJs(shell(), script)); - ASSERT_TRUE(console_observer.Wait()); -} - std::vector<uint8_t> TestSha256(std::string_view data) { std::string str = crypto::SHA256HashString(data); std::vector<uint8_t> result(str.begin(), str.end()); @@ -2059,7 +2030,7 @@ clientId: 'client_id_1', nonce: '12345', }], - mode: 'button' + mode: 'active' }, })); return x.token;
diff --git a/content/browser/worker_host/dedicated_worker_host.cc b/content/browser/worker_host/dedicated_worker_host.cc index 585138b..e9fe38c 100644 --- a/content/browser/worker_host/dedicated_worker_host.cc +++ b/content/browser/worker_host/dedicated_worker_host.cc
@@ -113,13 +113,6 @@ scoped_process_host_observation_.Observe(worker_process_host_.get()); - if (!base::FeatureList::IsEnabled(blink::features::kPlzDedicatedWorker)) { - // This is a workaround to make the worker's COEP have a value when - // PlzDedicatedWorker is disabled. When the feature is enabled, The value is - // initialized in DedicatedWorkerHost::DidStartScriptLoad(). - worker_client_security_state_ = creator_client_security_state_->Clone(); - } - service_->NotifyWorkerCreated(this); auto* ancestor_render_frame_host = @@ -166,9 +159,7 @@ service_->NotifyBeforeWorkerDestroyed(token_, creator_); - if (base::FeatureList::IsEnabled(blink::features::kPlzDedicatedWorker)) { - WorkerDevToolsManager::GetInstance().WorkerDestroyed(this); - } + WorkerDevToolsManager::GetInstance().WorkerDestroyed(this); } void DedicatedWorkerHost::BindBrowserInterfaceBrokerReceiver( @@ -234,7 +225,6 @@ TRACE_EVENT("loading", "DedicatedWorkerHost::StartScriptLoad", "script_url", script_url); DCHECK_CURRENTLY_ON(BrowserThread::UI); - DCHECK(base::FeatureList::IsEnabled(blink::features::kPlzDedicatedWorker)); DCHECK(!client_); DCHECK(client); @@ -366,7 +356,6 @@ void DedicatedWorkerHost::DidStartScriptLoad( std::optional<WorkerScriptFetcherResult> result) { DCHECK_CURRENTLY_ON(BrowserThread::UI); - DCHECK(base::FeatureList::IsEnabled(blink::features::kPlzDedicatedWorker)); TRACE_EVENT_NESTABLE_ASYNC_END0( "loading", "WorkerScriptFetcher CreateAndStart", TRACE_ID_LOCAL(this)); TRACE_EVENT("loading", "DedicatedWorkerHost::DidStartScriptLoad", @@ -564,7 +553,6 @@ DCHECK_CURRENTLY_ON(BrowserThread::UI); DCHECK(ancestor_render_frame_host); DCHECK(bypass_redirect_checks); - DCHECK(base::FeatureList::IsEnabled(blink::features::kPlzDedicatedWorker)); mojo::PendingRemote<network::mojom::CrossOriginEmbedderPolicyReporter> coep_reporter; @@ -625,7 +613,6 @@ // [spec] // https://html.spec.whatwg.org/C/#check-a-global-object's-embedder-policy bool DedicatedWorkerHost::CheckCOEP() { - DCHECK(base::FeatureList::IsEnabled(blink::features::kPlzDedicatedWorker)); DCHECK(final_response_url_); if (!creator_coep_reporter_) { @@ -763,9 +750,6 @@ void DedicatedWorkerHost::CreateNestedDedicatedWorker( mojo::PendingReceiver<blink::mojom::DedicatedWorkerHostFactory> receiver) { DCHECK_CURRENTLY_ON(BrowserThread::UI); - // For the non-PlzDedicatedWorker case, use ancestor's COEP reporter as a - // `creator_coep_reporter` to keep the current behavior, but it's not aligned - // with the spec. base::WeakPtr<CrossOriginEmbedderPolicyReporter> creator_coep_reporter = GetWorkerCoepReporter(); @@ -922,8 +906,6 @@ void DedicatedWorkerHost::ObserveNetworkServiceCrash( StoragePartitionImpl* storage_partition_impl) { - DCHECK(base::FeatureList::IsEnabled(blink::features::kPlzDedicatedWorker)); - auto params = network::mojom::URLLoaderFactoryParams::New(); params->process_id = worker_process_host_->GetDeprecatedID(); params->debug_tag = "DedicatedWorkerHost::ObserveNetworkServiceCrash"; @@ -942,7 +924,6 @@ DCHECK(subresource_loader_updater_.is_bound()); DCHECK(network_service_connection_error_handler_holder_); DCHECK(!network_service_connection_error_handler_holder_.is_connected()); - DCHECK(base::FeatureList::IsEnabled(blink::features::kPlzDedicatedWorker)); auto* storage_partition_impl = static_cast<StoragePartitionImpl*>( worker_process_host_->GetStoragePartition()); @@ -1001,113 +982,10 @@ std::move(subresource_loader_factories)); } -void DedicatedWorkerHost::MaybeCountWebFeature(const GURL& script_url) { - DCHECK_CURRENTLY_ON(BrowserThread::UI); - DCHECK(!base::FeatureList::IsEnabled(blink::features::kPlzDedicatedWorker)); - - RenderFrameHostImpl* ancestor_render_frame_host = - RenderFrameHostImpl::FromID(ancestor_render_frame_host_id_); - if (!ancestor_render_frame_host) { - return; - } - - base::WeakPtr<ServiceWorkerClient> service_worker_client = - ancestor_render_frame_host->GetLastCommittedServiceWorkerClient(); - if (!service_worker_client || !service_worker_client->controller()) { - return; - } - - if (!blink::ServiceWorkerScopeMatches( - service_worker_client->controller()->scope(), script_url) || - service_worker_client->key() != storage_key_) { - // Count the number of dedicated workers that 1) are controlled by a service - // worker that is inherited from a controlled document, and 2) will not be - // controlled by that service worker after PlzDedicatedWorker is enabled. - service_worker_client->CountFeature( - blink::mojom::WebFeature::kWorkerControlledByServiceWorkerOutOfScope); - - DCHECK_NE(service_worker_client->controller()->fetch_handler_existence(), - ServiceWorkerVersion::FetchHandlerExistence::UNKNOWN); - if (service_worker_client->controller()->fetch_handler_existence() == - ServiceWorkerVersion::FetchHandlerExistence::EXISTS) { - // Count the number of dedicated workers that 1) are controlled by a - // service worker that is inherited from a controlled document, 2) will - // not be controlled by that service worker after PlzDedicatedWorker is - // enabled, and 3) have a fetch event handler. - // `kControlledWorkerWillBeUncontrolled` excludes the cases if a - // dedicated worker is controlled by any registered service worker. - service_worker_client->CountFeature( - blink::mojom::WebFeature:: - kWorkerControlledByServiceWorkerWithFetchEventHandlerOutOfScope); - - ServiceWorkerContextWrapper* service_worker_context = - static_cast<StoragePartitionImpl*>( - worker_process_host_->GetStoragePartition()) - ->GetServiceWorkerContext(); - if (!service_worker_context) { - return; - } - - service_worker_context->GetRegistrationsForStorageKey( - blink::StorageKey::CreateFirstParty( - ancestor_render_frame_host->GetLastCommittedOrigin()), - base::BindOnce(&DedicatedWorkerHost::ContinueOnMaybeCountWebFeature, - weak_factory_.GetWeakPtr(), script_url, - std::move(service_worker_client))); - } - } -} - -void DedicatedWorkerHost::ContinueOnMaybeCountWebFeature( - const GURL& script_url, - base::WeakPtr<ServiceWorkerClient> ancestor_service_worker_client, - blink::ServiceWorkerStatusCode status, - const std::vector<scoped_refptr<ServiceWorkerRegistration>>& - registrations) { - DCHECK(!base::FeatureList::IsEnabled(blink::features::kPlzDedicatedWorker)); - if (!ancestor_service_worker_client || - status != blink::ServiceWorkerStatusCode::kOk) { - return; - } - - for (const auto& registration : registrations) { - // Do not record the UseCounter because a dedicated worker is in scope of - // one of service workers registered for the origin. The scope matched - // service worker may be different from the one that controls the ancestor - // frame. - if (blink::ServiceWorkerScopeMatches(registration->scope(), script_url) && - registration->key() == storage_key_) { - return; - } - } - - // Count the number of dedicated workers that are not controlled by any - // service worker registered for the origin after PlzDedicatedWorker is - // enabled. - ancestor_service_worker_client->CountFeature( - blink::mojom::WebFeature::kControlledWorkerWillBeUncontrolled); - - // Exclude the cases that `script_url` is a blob URL from - // kControlledWorkerWillBeUncontrolled. - if (!script_url.SchemeIsBlob()) { - ancestor_service_worker_client->CountFeature( - blink::mojom::WebFeature:: - kControlledNonBlobURLWorkerWillBeUncontrolled); - } -} - base::WeakPtr<CrossOriginEmbedderPolicyReporter> DedicatedWorkerHost::GetWorkerCoepReporter() { - if (base::FeatureList::IsEnabled(blink::features::kPlzDedicatedWorker)) { - DCHECK(coep_reporter_); - return coep_reporter_->GetWeakPtr(); - } - // For the non-PlzDedicatedWorker case, use ancestor's COEP reporter to keep - // the current behavior, but it's not aligned with the spec. - // `ancestor_coep_reporter_` is possible to be nullptr, which means the - // ancestor render frame has already been closed or navigated and this worker - // will also be terminated soon. - return ancestor_coep_reporter_; + DCHECK(coep_reporter_); + return coep_reporter_->GetWeakPtr(); } void DedicatedWorkerHost::EvictFromBackForwardCache(
diff --git a/content/browser/worker_host/dedicated_worker_host.h b/content/browser/worker_host/dedicated_worker_host.h index e305bcef..c079ef6 100644 --- a/content/browser/worker_host/dedicated_worker_host.h +++ b/content/browser/worker_host/dedicated_worker_host.h
@@ -73,7 +73,6 @@ class DedicatedWorkerServiceImpl; class ServiceWorkerClient; class ServiceWorkerMainResourceHandle; -class ServiceWorkerRegistration; class StoragePartitionImpl; struct WorkerScriptFetcherResult; @@ -179,7 +178,6 @@ void BindHidService(mojo::PendingReceiver<blink::mojom::HidService> receiver); #endif - // PlzDedicatedWorker: void StartScriptLoad( const GURL& script_url, network::mojom::CredentialsMode credentials_mode, @@ -191,18 +189,6 @@ void ReportNoBinderForInterface(const std::string& error); - // TODO(crbug.com/40093136): Remove this method once PlzDedicatedWorker is - // enabled by default. - void MaybeCountWebFeature(const GURL& script_url); - // TODO(crbug.com/40093136): Remove this method once PlzDedicatedWorker is - // enabled by default. - void ContinueOnMaybeCountWebFeature( - const GURL& script_url, - base::WeakPtr<ServiceWorkerClient> service_worker_client, - blink::ServiceWorkerStatusCode status, - const std::vector<scoped_refptr<ServiceWorkerRegistration>>& - registrations); - const net::NetworkIsolationKey& GetNetworkIsolationKey() const { return isolation_info_.network_isolation_key(); } @@ -373,15 +359,11 @@ // creator's client security state lazily instead of eagerly. const network::mojom::ClientSecurityStatePtr creator_client_security_state_; - // The client security state of this worker, used for subresource fetches. - // - // If PlzDedicatedWorker is disabled, it is cloned from - // `creator_client_security_state_` at construction time. - // - // Otherwise, it is nullptr until the script's response head is loaded, at - // which point it is calculated based on the response info. If the response is - // loaded from a URL with a local scheme, then the worker inherits its - // creator's client security state. + // The client security state of this worker, used for subresource fetches. It + // is nullptr until the script's response head is loaded, at which point it is + // calculated based on the response info. If the response is loaded from a URL + // with a local scheme, then the worker inherits its creator's client security + // state. network::mojom::ClientSecurityStatePtr worker_client_security_state_; // This is kept alive during the lifetime of the dedicated worker, since it's @@ -397,7 +379,7 @@ #endif // BUILDFLAG(ENABLE_COMPUTE_PRESSURE) // Script request URL used, only for DevTools and tracing. Only set after - // `StartScriptLoad()`. Only set and used if PlzDedicatedWorker is enabled. + // `StartScriptLoad()`. GURL script_request_url_; // BrowserInterfaceBroker implementation through which this @@ -424,17 +406,14 @@ mojo::Remote<blink::mojom::SubresourceLoaderUpdater> subresource_loader_updater_; - // For the PlzDedicatedWorker case. `coep_reporter_` is valid after - // DidStartScriptLoad() and remains non-null for the lifetime of `this`. + // `coep_reporter_` is valid after DidStartScriptLoad() and remains non-null + // for the lifetime of `this`. std::unique_ptr<CrossOriginEmbedderPolicyReporter> coep_reporter_; // TODO(crbug.com/40054797): Remove `creator_coep_reporter_` after this // class's lifetime is aligned with the associated frame. base::WeakPtr<CrossOriginEmbedderPolicyReporter> creator_coep_reporter_; - // For the non-PlzDedicatedWorker case. Sending reports to the ancestor frame - // is not the behavior defined in the spec, but keep the current behavior and - // not to lose reports. - // TODO(crbug.com/40093136): Remove `ancestor_coep_reporter_` once + // TODO(crbug.com/40093136): Remove `ancestor_coep_reporter_` now that // PlzDedicatedWorker is enabled by default. base::WeakPtr<CrossOriginEmbedderPolicyReporter> ancestor_coep_reporter_;
diff --git a/content/browser/worker_host/dedicated_worker_host_factory_impl.cc b/content/browser/worker_host/dedicated_worker_host_factory_impl.cc index c6de23d..66261dd 100644 --- a/content/browser/worker_host/dedicated_worker_host_factory_impl.cc +++ b/content/browser/worker_host/dedicated_worker_host_factory_impl.cc
@@ -80,10 +80,6 @@ "DedicatedWorkerHostFactoryImpl::CreateWorkerHostAndStartScriptLoad", "script_url", script_url); DCHECK_CURRENTLY_ON(BrowserThread::UI); - if (!base::FeatureList::IsEnabled(blink::features::kPlzDedicatedWorker)) { - mojo::ReportBadMessage("DWH_BROWSER_SCRIPT_FETCH_DISABLED"); - return; - } base::TimeTicks start_time = base::TimeTicks::Now(); // Get the dedicated worker service.
diff --git a/content/child/runtime_features.cc b/content/child/runtime_features.cc index 27d2f0d..2ac903fa 100644 --- a/content/child/runtime_features.cc +++ b/content/child/runtime_features.cc
@@ -372,13 +372,9 @@ {"OriginIsolationHeader", raw_ref(features::kOriginIsolationHeader)}, {"ReduceAcceptLanguage", raw_ref(network::features::kReduceAcceptLanguage)}, - {"Serial", #if BUILDFLAG(IS_ANDROID) - raw_ref(device::features::kBluetoothRfcommAndroid) -#else - raw_ref(device::features::kSerial) + {"Serial", raw_ref(device::features::kBluetoothRfcommAndroid)}, #endif - }, {"SerialPortConnected", raw_ref(features::kSerialPortConnected)}, {"SignatureBasedIntegrity", raw_ref(network::features::kSRIMessageSignatureEnforcement)},
diff --git a/content/public/browser/web_contents_observer.h b/content/public/browser/web_contents_observer.h index cfa13a9a..255420b 100644 --- a/content/public/browser/web_contents_observer.h +++ b/content/public/browser/web_contents_observer.h
@@ -517,6 +517,9 @@ // Called when a network request issued by the navigation reads or sets a // cookie. If a notification is received after the navigation has committed, // it will be attributed to the RenderFrameHost created by the navigation. + // This method not only includes accesses from the navigation's + // request/response, but also accesses from other requests/responses triggered + // by the navigation e.g. early hints requests. virtual void OnCookiesAccessed(NavigationHandle* navigation_handle, const CookieAccessDetails& details) {}
diff --git a/content/renderer/render_frame_impl.cc b/content/renderer/render_frame_impl.cc index 836eff5..a1df129e 100644 --- a/content/renderer/render_frame_impl.cc +++ b/content/renderer/render_frame_impl.cc
@@ -3584,9 +3584,8 @@ cors_exempt_header_list, web_cors_exempt_header_list.begin(), [](const auto& header) { return blink::WebString::FromLatin1(header); }); - // |pending_subresource_loader_updater| and - // |pending_resource_load_info_notifier| are not used for - // non-PlzDedicatedWorker and worklets. + // `pending_subresource_loader_updater` and + // `pending_resource_load_info_notifier` are not used for worklets. scoped_refptr<blink::WebDedicatedOrSharedWorkerFetchContext> web_dedicated_or_shared_worker_fetch_context = blink::WebDedicatedOrSharedWorkerFetchContext::Create( @@ -3614,7 +3613,6 @@ scoped_refptr<blink::WebWorkerFetchContext> RenderFrameImpl::CreateWorkerFetchContextForPlzDedicatedWorker( blink::WebDedicatedWorkerHostFactoryClient* factory_client) { - DCHECK(base::FeatureList::IsEnabled(blink::features::kPlzDedicatedWorker)); DCHECK(factory_client); mojo::PendingReceiver<blink::mojom::RendererPreferenceWatcher>
diff --git a/content/renderer/worker/dedicated_worker_host_factory_client.cc b/content/renderer/worker/dedicated_worker_host_factory_client.cc index 244f973db..04912a00 100644 --- a/content/renderer/worker/dedicated_worker_host_factory_client.cc +++ b/content/renderer/worker/dedicated_worker_host_factory_client.cc
@@ -14,7 +14,6 @@ #include "content/renderer/worker/fetch_client_settings_object_helpers.h" #include "mojo/public/cpp/bindings/pending_remote.h" #include "net/storage_access_api/status.h" -#include "third_party/blink/public/common/features.h" #include "third_party/blink/public/common/loader/worker_main_script_load_parameters.h" #include "third_party/blink/public/common/tokens/tokens.h" #include "third_party/blink/public/common/tokens/tokens_mojom_traits.h" @@ -51,7 +50,6 @@ blink::CrossVariantMojoRemote<blink::mojom::BlobURLTokenInterfaceBase> blob_url_token, net::StorageAccessApiStatus storage_access_api_status) { - DCHECK(base::FeatureList::IsEnabled(blink::features::kPlzDedicatedWorker)); factory_->CreateWorkerHostAndStartScriptLoad( dedicated_worker_token, script_url, credentials_mode, FetchClientSettingsObjectFromWebToMojom(fetch_client_settings_object), @@ -65,22 +63,15 @@ scoped_refptr<base::SingleThreadTaskRunner> task_runner) { scoped_refptr<blink::WebDedicatedOrSharedWorkerFetchContext> cloned_web_dedicated_or_shared_worker_fetch_context; - if (base::FeatureList::IsEnabled(blink::features::kPlzDedicatedWorker)) { - cloned_web_dedicated_or_shared_worker_fetch_context = - static_cast<blink::WebDedicatedOrSharedWorkerFetchContext*>( - web_worker_fetch_context) - ->CloneForNestedWorker( - service_worker_provider_context_.get(), - subresource_loader_factory_bundle_->Clone(), - subresource_loader_factory_bundle_->Clone(), - std::move(pending_subresource_loader_updater_), - std::move(task_runner)); - } else { - cloned_web_dedicated_or_shared_worker_fetch_context = - static_cast<blink::WebDedicatedOrSharedWorkerFetchContext*>( - web_worker_fetch_context) - ->CloneForNestedWorkerDeprecated(std::move(task_runner)); - } + cloned_web_dedicated_or_shared_worker_fetch_context = + static_cast<blink::WebDedicatedOrSharedWorkerFetchContext*>( + web_worker_fetch_context) + ->CloneForNestedWorker( + service_worker_provider_context_.get(), + subresource_loader_factory_bundle_->Clone(), + subresource_loader_factory_bundle_->Clone(), + std::move(pending_subresource_loader_updater_), + std::move(task_runner)); return cloned_web_dedicated_or_shared_worker_fetch_context; } @@ -91,7 +82,6 @@ watcher_receiver, mojo::PendingRemote<blink::mojom::ResourceLoadInfoNotifier> pending_resource_load_info_notifier) { - DCHECK(base::FeatureList::IsEnabled(blink::features::kPlzDedicatedWorker)); DCHECK(subresource_loader_factory_bundle_); std::vector<std::string> cors_exempt_header_list = RenderThreadImpl::current()->cors_exempt_header_list(); @@ -135,7 +125,6 @@ coep_reporting_observer, mojo::PendingReceiver<blink::mojom::ReportingObserver> dip_reporting_observer) { - DCHECK(base::FeatureList::IsEnabled(blink::features::kPlzDedicatedWorker)); DCHECK(main_script_load_params); DCHECK(pending_subresource_loader_factory_bundle); @@ -182,7 +171,6 @@ } void DedicatedWorkerHostFactoryClient::OnScriptLoadStartFailed() { - DCHECK(base::FeatureList::IsEnabled(blink::features::kPlzDedicatedWorker)); worker_->OnScriptLoadStartFailed(); // |this| may be destroyed at this point. }
diff --git a/content/test/content_test_bundle_data.filelist b/content/test/content_test_bundle_data.filelist index f470ce58..280cedb 100644 --- a/content/test/content_test_bundle_data.filelist +++ b/content/test/content_test_bundle_data.filelist
@@ -8151,6 +8151,7 @@ data/shared_dictionary/with_dict_header.html.mock-http-headers data/shared_storage/customizable_module.js data/shared_storage/customizable_script.js +data/shared_storage/customizable_selecturl_module.js data/shared_storage/erroneous_function_module.js data/shared_storage/erroneous_module.js data/shared_storage/getter_module.js
diff --git a/content/test/data/shared_storage/customizable_selecturl_module.js b/content/test/data/shared_storage/customizable_selecturl_module.js new file mode 100644 index 0000000..f4e557a2 --- /dev/null +++ b/content/test/data/shared_storage/customizable_selecturl_module.js
@@ -0,0 +1,12 @@ +// 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. + +class TestURLSelectionOperation { + async run(urls, data) { + {{RUN_FUNCTION_BODY}} + return 0; + } +} + +register('test-url-selection-operation', TestURLSelectionOperation);
diff --git a/crypto/BUILD.gn b/crypto/BUILD.gn index 9f76eb9..1ac8226 100644 --- a/crypto/BUILD.gn +++ b/crypto/BUILD.gn
@@ -38,6 +38,8 @@ "hmac.h", "kdf.cc", "kdf.h", + "keypair.cc", + "keypair.h", "openssl_util.cc", "openssl_util.h", "process_bound_string.cc", @@ -53,6 +55,8 @@ "secure_util.h", "sha2.cc", "sha2.h", + "sign.cc", + "sign.h", "signature_creator.cc", "signature_creator.h", "signature_verifier.cc", @@ -178,11 +182,13 @@ "hash_unittest.cc", "hmac_unittest.cc", "kdf_unittest.cc", + "keypair_unittest.cc", "process_bound_string_unittest.cc", "random_unittest.cc", "rsa_private_key_unittest.cc", "secure_hash_unittest.cc", "sha2_unittest.cc", + "sign_unittest.cc", "signature_creator_unittest.cc", "signature_verifier_unittest.cc", "unexportable_key_unittest.cc", @@ -235,6 +241,8 @@ "scoped_fake_unexportable_key_provider.h", "scoped_fake_user_verifying_key_provider.cc", "scoped_fake_user_verifying_key_provider.h", + "test_support.cc", + "test_support.h", ] if (use_nss_certs) {
diff --git a/crypto/keypair.cc b/crypto/keypair.cc new file mode 100644 index 0000000..eddf939 --- /dev/null +++ b/crypto/keypair.cc
@@ -0,0 +1,207 @@ +// 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 "crypto/keypair.h" + +#include "base/logging.h" +#include "crypto/openssl_util.h" +#include "third_party/boringssl/src/include/openssl/bn.h" +#include "third_party/boringssl/src/include/openssl/bytestring.h" +#include "third_party/boringssl/src/include/openssl/ec.h" +#include "third_party/boringssl/src/include/openssl/evp.h" +#include "third_party/boringssl/src/include/openssl/mem.h" +#include "third_party/boringssl/src/include/openssl/rsa.h" + +namespace crypto::keypair { + +namespace { + +bssl::UniquePtr<EVP_PKEY> GenerateRsa(size_t bits) { + OpenSSLErrStackTracer err_tracer(FROM_HERE); + + bssl::UniquePtr<RSA> rsa_key(RSA_new()); + bssl::UniquePtr<BIGNUM> bn(BN_new()); + + CHECK(rsa_key.get()); + CHECK(bn.get()); + CHECK(BN_set_word(bn.get(), 65537L)); + + CHECK(RSA_generate_key_ex(rsa_key.get(), bits, bn.get(), nullptr)); + + bssl::UniquePtr<EVP_PKEY> key(EVP_PKEY_new()); + CHECK(EVP_PKEY_set1_RSA(key.get(), rsa_key.get())); + + return key; +} + +bool IsSupportedEvpId(int evp_id) { + return evp_id == EVP_PKEY_RSA || evp_id == EVP_PKEY_EC; +} + +std::vector<uint8_t> ExportEVPPublicKey(EVP_PKEY* pkey) { + OpenSSLErrStackTracer err_tracer(FROM_HERE); + bssl::ScopedCBB cbb; + + CHECK(CBB_init(cbb.get(), 0)); + CHECK(EVP_marshal_public_key(cbb.get(), pkey)); + + uint8_t* data; + size_t len; + CHECK(CBB_finish(cbb.get(), &data, &len)); + + std::vector<uint8_t> result(len); + // SAFETY: OpenSSL freshly allocated data for us and ensured it pointed to at + // least len bytes. + UNSAFE_BUFFERS(result.assign(data, data + len)); + OPENSSL_free(data); + return result; +} + +} // namespace + +PrivateKey::PrivateKey(bssl::UniquePtr<EVP_PKEY> key, crypto::SubtlePassKey) + : PrivateKey(std::move(key)) {} +PrivateKey::~PrivateKey() = default; +PrivateKey::PrivateKey(PrivateKey&& other) = default; +PrivateKey::PrivateKey(const PrivateKey& other) + : key_(bssl::UpRef(const_cast<PrivateKey&>(other).key())) {} + +// static +PrivateKey PrivateKey::GenerateRsa2048() { + return PrivateKey(GenerateRsa(2048)); +} + +// static +PrivateKey PrivateKey::GenerateRsa4096() { + return PrivateKey(GenerateRsa(4096)); +} + +// static +PrivateKey PrivateKey::GenerateEcP256() { + OpenSSLErrStackTracer err_tracer(FROM_HERE); + + bssl::UniquePtr<EC_KEY> ec_key( + EC_KEY_new_by_curve_name(NID_X9_62_prime256v1)); + CHECK(ec_key); + CHECK(EC_KEY_generate_key(ec_key.get())); + + bssl::UniquePtr<EVP_PKEY> key(EVP_PKEY_new()); + CHECK(EVP_PKEY_set1_EC_KEY(key.get(), ec_key.get())); + return PrivateKey(std::move(key)); +} + +// static +std::optional<PrivateKey> PrivateKey::FromPrivateKeyInfo( + base::span<const uint8_t> pki) { + OpenSSLErrStackTracer err_tracer(FROM_HERE); + + CBS cbs(pki); + bssl::UniquePtr<EVP_PKEY> pkey(EVP_parse_private_key(&cbs)); + if (!pkey || CBS_len(&cbs) != 0) { + LOG(WARNING) << "Malformed PrivateKeyInfo or trailing data"; + return std::nullopt; + } + + auto id = EVP_PKEY_id(pkey.get()); + if (!IsSupportedEvpId(id)) { + LOG(WARNING) << "Unsupported key type (EVP ID: " << id << ")"; + return std::nullopt; + } + + return std::optional<PrivateKey>(PrivateKey(std::move(pkey))); +} + +std::vector<uint8_t> PrivateKey::ToPrivateKeyInfo() const { + OpenSSLErrStackTracer err_tracer(FROM_HERE); + bssl::ScopedCBB cbb; + + CHECK(CBB_init(cbb.get(), 0)); + CHECK(EVP_marshal_private_key(cbb.get(), key_.get())); + + uint8_t* data; + size_t len; + CHECK(CBB_finish(cbb.get(), &data, &len)); + + std::vector<uint8_t> result(len); + // SAFETY: OpenSSL freshly allocated data for us and ensured it pointed to at + // least len bytes. + UNSAFE_BUFFERS(result.assign(data, data + len)); + OPENSSL_free(data); + return result; +} + +std::vector<uint8_t> PrivateKey::ToSubjectPublicKeyInfo() const { + return ExportEVPPublicKey(key_.get()); +} + +std::vector<uint8_t> PrivateKey::ToUncompressedForm() const { + OpenSSLErrStackTracer err_tracer(FROM_HERE); + + std::vector<uint8_t> buf(65); + EC_KEY* ec_key = EVP_PKEY_get0_EC_KEY(key_.get()); + CHECK(EC_POINT_point2oct( + EC_KEY_get0_group(ec_key), EC_KEY_get0_public_key(ec_key), + POINT_CONVERSION_UNCOMPRESSED, buf.data(), buf.size(), /*ctx=*/nullptr)); + + return buf; +} + +PrivateKey::PrivateKey(bssl::UniquePtr<EVP_PKEY> key) : key_(std::move(key)) {} + +bool PrivateKey::IsRsa() const { + return EVP_PKEY_id(key_.get()) == EVP_PKEY_RSA; +} + +bool PrivateKey::IsEc() const { + return EVP_PKEY_id(key_.get()) == EVP_PKEY_EC; +} + +PublicKey::PublicKey(bssl::UniquePtr<EVP_PKEY> key, crypto::SubtlePassKey) + : PublicKey(std::move(key)) {} +PublicKey::~PublicKey() = default; +PublicKey::PublicKey(PublicKey&& other) = default; +PublicKey::PublicKey(const PublicKey& other) + : key_(bssl::UpRef(const_cast<PublicKey&>(other).key())) {} + +// static +PublicKey PublicKey::FromPrivateKey(const PrivateKey& key) { + return *FromSubjectPublicKeyInfo(key.ToSubjectPublicKeyInfo()); +} + +// static +std::optional<PublicKey> PublicKey::FromSubjectPublicKeyInfo( + base::span<const uint8_t> spki) { + OpenSSLErrStackTracer err_tracer(FROM_HERE); + + CBS cbs(spki); + bssl::UniquePtr<EVP_PKEY> pkey(EVP_parse_public_key(&cbs)); + if (!pkey || CBS_len(&cbs) != 0) { + LOG(WARNING) << "Malformed PublicKeyInfo or trailing data"; + return std::nullopt; + } + + auto id = EVP_PKEY_id(pkey.get()); + if (!IsSupportedEvpId(id)) { + LOG(WARNING) << "Unsupported key type (EVP ID: " << id << ")"; + return std::nullopt; + } + + return std::optional<PublicKey>(PublicKey(std::move(pkey))); +} + +std::vector<uint8_t> PublicKey::ToSubjectPublicKeyInfo() const { + return ExportEVPPublicKey(key_.get()); +} + +bool PublicKey::IsRsa() const { + return EVP_PKEY_id(key_.get()) == EVP_PKEY_RSA; +} + +bool PublicKey::IsEc() const { + return EVP_PKEY_id(key_.get()) == EVP_PKEY_EC; +} + +PublicKey::PublicKey(bssl::UniquePtr<EVP_PKEY> key) : key_(std::move(key)) {} + +} // namespace crypto::keypair
diff --git a/crypto/keypair.h b/crypto/keypair.h new file mode 100644 index 0000000..61e2c0f --- /dev/null +++ b/crypto/keypair.h
@@ -0,0 +1,107 @@ +// 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 CRYPTO_KEYPAIR_H_ +#define CRYPTO_KEYPAIR_H_ + +#include "base/containers/span.h" +#include "crypto/crypto_export.h" +#include "crypto/subtle_passkey.h" +#include "third_party/boringssl/src/include/openssl/base.h" + +namespace crypto::keypair { + +// This class wraps an EVP_PKEY containing a private key. Since EVP_PKEY is +// refcounted, PrivateKey is extremely cheap to copy and is intended to be +// passed around by value. All the public constructors are static functions that +// enforce constraints on the type of key they will generate or import; the +// constructor that accepts a raw EVP_PKEY requires a SubtlePassKey to +// discourage client code from dealing in EVP_PKEYs directly. +class CRYPTO_EXPORT PrivateKey { + public: + // Directly construct a PrivateKey from an EVP_PKEY. Prefer to use one of the + // static factory functions below, which do not require a SubtlePassKey. + PrivateKey(bssl::UniquePtr<EVP_PKEY> key, crypto::SubtlePassKey); + ~PrivateKey(); + PrivateKey(PrivateKey&& other); + PrivateKey(const PrivateKey& other); + + // These functions generate fresh, random RSA private keys of the named sizes + // with e = 65537. + // If you believe you need an RSA key of a size other than these, or with a + // different exponent, please contact a member of //CRYPTO_OWNERS. + static PrivateKey GenerateRsa2048(); + static PrivateKey GenerateRsa4096(); + + // Generates a fresh, random elliptic curve key on the NIST P-256 curve. + static PrivateKey GenerateEcP256(); + + // Imports a PKCS#8 PrivateKeyInfo block. Returns nullopt if the passed-in + // buffer is not a valid PrivateKeyInfo block, or if there is trailing data in + // it after the PrivateKeyInfo block. + static std::optional<PrivateKey> FromPrivateKeyInfo( + base::span<const uint8_t> pki); + + // Deliberately not present in this API: + // ECPrivateKey::CreateFromEncryptedPrivateKeyInfo(): imports a PKCS#8 + // EncryptedPrivateKeyInfo with a hardcoded empty password. There is no reason + // to ever do this and there is only one client (the GCM code). + + // Exports a PKCS#8 PrivateKeyInfo block. + std::vector<uint8_t> ToPrivateKeyInfo() const; + + // Computes and exports an X.509 SubjectPublicKeyInfo block corresponding to + // this key. + std::vector<uint8_t> ToSubjectPublicKeyInfo() const; + + // Exports an EC public key in X9.62 uncompressed form. It is illegal to call + // this on a non-EC PrivateKey. + std::vector<uint8_t> ToUncompressedForm() const; + + EVP_PKEY* key() { return key_.get(); } + + bool IsRsa() const; + bool IsEc() const; + + private: + explicit PrivateKey(bssl::UniquePtr<EVP_PKEY> key); + + bssl::UniquePtr<EVP_PKEY> key_; +}; + +class CRYPTO_EXPORT PublicKey { + public: + // Construct a PublicKey directly from an EVP_PKEY. Prefer to use one of the + // static factory functions below, which do not require a SubtlePassKey. + PublicKey(bssl::UniquePtr<EVP_PKEY> key, crypto::SubtlePassKey); + ~PublicKey(); + PublicKey(PublicKey&& other); + PublicKey(const PublicKey& other); + + // Produces the PublicKey corresponding to the given PrivateKey. This is + // mostly useful in tests but is fine to use in production as well. + static PublicKey FromPrivateKey(const PrivateKey& key); + + // Imports a PublicKey from an X.509 SubjectPublicKeyInfo. This may return + // nullopt if the SubjectPublicKeyInfo is ill-formed. + static std::optional<PublicKey> FromSubjectPublicKeyInfo( + base::span<const uint8_t> spki); + + // Exports a PublicKey as an X.509 SubjectPublicKeyInfo. + std::vector<uint8_t> ToSubjectPublicKeyInfo() const; + + EVP_PKEY* key() { return key_.get(); } + + bool IsRsa() const; + bool IsEc() const; + + private: + explicit PublicKey(bssl::UniquePtr<EVP_PKEY> key); + + bssl::UniquePtr<EVP_PKEY> key_; +}; + +} // namespace crypto::keypair + +#endif // CRYPTO_KEYPAIR_H_
diff --git a/crypto/keypair_unittest.cc b/crypto/keypair_unittest.cc new file mode 100644 index 0000000..f21a150 --- /dev/null +++ b/crypto/keypair_unittest.cc
@@ -0,0 +1,66 @@ +// 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. + +// crypto::keypair tests +#include "crypto/keypair.h" + +#include "crypto/test_support.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace { + +using crypto::keypair::PrivateKey; +using crypto::keypair::PublicKey; + +using crypto::test::FixedRsa2048PrivateKeyForTesting; +using crypto::test::FixedRsa4096PrivateKeyForTesting; + +using crypto::test::FixedRsa2048PublicKeyForTesting; +using crypto::test::FixedRsa4096PublicKeyForTesting; + +// Generate keys, roundtrip them through encoding/decoding to PrivateKeyInfo, +// and ensure the resulting key is equivalent. That gives some confidence that +// the generated key is actually a valid key because of the validation in +// FromPrivateKeyInfo(). +TEST(Keypair, GenerateAndRoundtripPrivateKey) { + auto expect_roundtrip = [](const PrivateKey& key) { + auto k = PrivateKey::FromPrivateKeyInfo(key.ToPrivateKeyInfo()); + ASSERT_TRUE(k); + EXPECT_EQ(key.ToPrivateKeyInfo(), k->ToPrivateKeyInfo()); + EXPECT_EQ(key.IsRsa(), k->IsRsa()); + EXPECT_EQ(key.IsEc(), k->IsEc()); + }; + + expect_roundtrip(PrivateKey::GenerateRsa2048()); + expect_roundtrip(PrivateKey::GenerateRsa4096()); + expect_roundtrip(PrivateKey::GenerateEcP256()); +} + +// Export a public key from each private key and ensure it matches the expected +// public key. +TEST(Keypair, ExportPublicKey) { + auto expect_export = [](const PrivateKey& priv, const PublicKey& pub) { + EXPECT_EQ(priv.ToSubjectPublicKeyInfo(), pub.ToSubjectPublicKeyInfo()); + }; + + expect_export(FixedRsa2048PrivateKeyForTesting(), + FixedRsa2048PublicKeyForTesting()); + expect_export(FixedRsa4096PrivateKeyForTesting(), + FixedRsa4096PublicKeyForTesting()); +} + +TEST(Keypair, ImportWithTrailingJunkFails) { + auto priv = FixedRsa2048PrivateKeyForTesting().ToPrivateKeyInfo(); + auto pub = FixedRsa2048PublicKeyForTesting().ToSubjectPublicKeyInfo(); + + EXPECT_TRUE(PrivateKey::FromPrivateKeyInfo(priv)); + priv.push_back(0); + EXPECT_FALSE(PrivateKey::FromPrivateKeyInfo(priv)); + + EXPECT_TRUE(PublicKey::FromSubjectPublicKeyInfo(pub)); + pub.push_back(0); + EXPECT_FALSE(PublicKey::FromSubjectPublicKeyInfo(pub)); +} + +} // namespace
diff --git a/crypto/sign.cc b/crypto/sign.cc new file mode 100644 index 0000000..02b9078 --- /dev/null +++ b/crypto/sign.cc
@@ -0,0 +1,130 @@ +// 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 "crypto/sign.h" + +#include "crypto/openssl_util.h" +#include "third_party/boringssl/src/include/openssl/evp.h" +#include "third_party/boringssl/src/include/openssl/rsa.h" + +namespace crypto::sign { + +namespace { + +bool CanUseKeyForSignatureKind(SignatureKind kind, + const crypto::keypair::PrivateKey& key) { + return true; +} + +bool CanUseKeyForSignatureKind(SignatureKind kind, + const crypto::keypair::PublicKey& key) { + return true; +} + +const EVP_MD* DigestForSignatureKind(SignatureKind kind) { + switch (kind) { + case RSA_PKCS1_SHA1: + return EVP_sha1(); + case RSA_PKCS1_SHA256: + return EVP_sha256(); + case RSA_PSS_SHA256: + return EVP_sha256(); + case ECDSA_SHA256: + return EVP_sha256(); + } +} + +} // namespace + +std::vector<uint8_t> Sign(SignatureKind kind, + const crypto::keypair::PrivateKey& key, + base::span<const uint8_t> data) { + Signer signer(kind, key); + signer.Update(data); + return signer.Finish(); +} + +bool Verify(SignatureKind kind, + const crypto::keypair::PublicKey& key, + base::span<const uint8_t> data, + base::span<const uint8_t> signature) { + Verifier verifier(kind, key, signature); + verifier.Update(data); + return verifier.Finish(); +} + +Signer::Signer(SignatureKind kind, crypto::keypair::PrivateKey key) + : key_(key), sign_context_(EVP_MD_CTX_new()) { + CHECK(CanUseKeyForSignatureKind(kind, key)); + OpenSSLErrStackTracer err_tracer(FROM_HERE); + + const EVP_MD* const md = DigestForSignatureKind(kind); + EVP_PKEY_CTX* pkctx; + CHECK( + EVP_DigestSignInit(sign_context_.get(), &pkctx, md, nullptr, key.key())); + + if (kind == RSA_PSS_SHA256) { + CHECK(EVP_PKEY_CTX_set_rsa_padding(pkctx, RSA_PKCS1_PSS_PADDING)); + CHECK(EVP_PKEY_CTX_set_rsa_mgf1_md(pkctx, md)); + // -1 here means "use digest's length" + CHECK(EVP_PKEY_CTX_set_rsa_pss_saltlen(pkctx, -1)); + } +} +Signer::~Signer() = default; + +void Signer::Update(base::span<const uint8_t> data) { + CHECK(EVP_DigestSignUpdate(sign_context_.get(), data.data(), data.size())); +} + +std::vector<uint8_t> Signer::Finish() { + OpenSSLErrStackTracer err_tracer(FROM_HERE); + + size_t len = 0; + // Determine the maximum length of the signature. + CHECK(EVP_DigestSignFinal(sign_context_.get(), nullptr, &len)); + + std::vector<uint8_t> signature(len); + CHECK(EVP_DigestSignFinal(sign_context_.get(), signature.data(), &len)); + signature.resize(len); + return signature; +} + +Verifier::Verifier(SignatureKind kind, + crypto::keypair::PublicKey key, + base::span<const uint8_t> signature) + : key_(key), verify_context_(EVP_MD_CTX_new()) { + OpenSSLErrStackTracer err_tracer(FROM_HERE); + CHECK(CanUseKeyForSignatureKind(kind, key)); + signature_.resize(signature.size()); + base::span(signature_).copy_from(signature); + + EVP_PKEY_CTX* pkctx; + const EVP_MD* const md = DigestForSignatureKind(kind); + CHECK(EVP_DigestVerifyInit(verify_context_.get(), &pkctx, md, nullptr, + key.key())); + + if (kind == RSA_PSS_SHA256) { + CHECK(EVP_PKEY_CTX_set_rsa_padding(pkctx, RSA_PKCS1_PSS_PADDING)); + CHECK(EVP_PKEY_CTX_set_rsa_mgf1_md(pkctx, md)); + // -1 here means "use digest's length" + CHECK(EVP_PKEY_CTX_set_rsa_pss_saltlen(pkctx, -1)); + } +} +Verifier::~Verifier() = default; + +void Verifier::Update(base::span<const uint8_t> data) { + OpenSSLErrStackTracer err_tracer(FROM_HERE); + CHECK_EQ( + EVP_DigestVerifyUpdate(verify_context_.get(), data.data(), data.size()), + 1); +} + +bool Verifier::Finish() { + OpenSSLErrStackTracer err_tracer(FROM_HERE); + int rv = EVP_DigestVerifyFinal(verify_context_.get(), signature_.data(), + signature_.size()); + return rv == 1; +} + +} // namespace crypto::sign
diff --git a/crypto/sign.h b/crypto/sign.h new file mode 100644 index 0000000..40d7255e --- /dev/null +++ b/crypto/sign.h
@@ -0,0 +1,79 @@ +// 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 CRYPTO_SIGN_H_ +#define CRYPTO_SIGN_H_ + +#include "base/containers/span.h" +#include "crypto/crypto_export.h" +#include "crypto/keypair.h" +#include "third_party/boringssl/src/include/openssl/base.h" +#include "third_party/boringssl/src/include/openssl/evp.h" + +namespace crypto::sign { + +enum SignatureKind { + RSA_PKCS1_SHA1, + RSA_PKCS1_SHA256, + // RSA-PSS with SHA-256 as both the signing hash and the MGF-1 hash, with a + // salt length of 32. + RSA_PSS_SHA256, + ECDSA_SHA256, +}; + +// One-shot signature function: produce a signature of `data` using `key`. +CRYPTO_EXPORT std::vector<uint8_t> Sign(SignatureKind kind, + const crypto::keypair::PrivateKey& key, + base::span<const uint8_t> data); + +// One-shot verification function: check a signature and return whether it is +// valid. +[[nodiscard]] CRYPTO_EXPORT bool Verify(SignatureKind kind, + const crypto::keypair::PublicKey& key, + base::span<const uint8_t> data, + base::span<const uint8_t> signature); + +// A streaming signer interface. Calling Finish() produces the final signature. +class CRYPTO_EXPORT Signer { + public: + Signer(SignatureKind kind, crypto::keypair::PrivateKey key); + ~Signer(); + + // Put more data into the signing function. + void Update(base::span<const uint8_t> data); + + // Finish the signature and return the signature value. After this is called, + // the Signer cannot be used any more. + std::vector<uint8_t> Finish(); + + private: + crypto::keypair::PrivateKey key_; + bssl::UniquePtr<EVP_MD_CTX> sign_context_; +}; + +// A streaming verifier interface. Calling Finish() checks the signature. +class CRYPTO_EXPORT Verifier { + public: + Verifier(SignatureKind kind, + crypto::keypair::PublicKey key, + base::span<const uint8_t> signature); + ~Verifier(); + + // Put more data into the verification function. + void Update(base::span<const uint8_t> data); + + // Finish the verification and return whether the signature matched the + // expected value provided at construction time. After this is called, the + // Verifier cannot be used any more. + [[nodiscard]] bool Finish(); + + private: + crypto::keypair::PublicKey key_; + std::vector<uint8_t> signature_; + bssl::UniquePtr<EVP_MD_CTX> verify_context_; +}; + +} // namespace crypto::sign + +#endif // CRYPTO_SIGN_H_
diff --git a/crypto/sign_unittest.cc b/crypto/sign_unittest.cc new file mode 100644 index 0000000..003b61e --- /dev/null +++ b/crypto/sign_unittest.cc
@@ -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. + +#include "crypto/sign.h" + +#include "crypto/test_support.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace { + +using crypto::keypair::PrivateKey; +using crypto::keypair::PublicKey; +using crypto::sign::SignatureKind; + +TEST(Sign, RoundTripSignVerify) { + constexpr auto data = std::to_array<uint8_t>({ + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, + 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, + 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, + }); + + auto expect_roundtrip = [&](const PrivateKey& priv, const PublicKey& pub, + SignatureKind kind) { + auto sig = crypto::sign::Sign(kind, priv, data); + EXPECT_TRUE(crypto::sign::Verify(kind, pub, data, sig)); + }; + + auto rsa_priv = crypto::test::FixedRsa2048PrivateKeyForTesting(); + auto rsa_pub = crypto::test::FixedRsa2048PublicKeyForTesting(); + + expect_roundtrip(rsa_priv, rsa_pub, SignatureKind::RSA_PKCS1_SHA1); + expect_roundtrip(rsa_priv, rsa_pub, SignatureKind::RSA_PKCS1_SHA256); + expect_roundtrip(rsa_priv, rsa_pub, SignatureKind::RSA_PSS_SHA256); + + auto ec_priv = PrivateKey::GenerateEcP256(); + auto ec_pub = PublicKey::FromPrivateKey(ec_priv); + + expect_roundtrip(ec_priv, ec_pub, SignatureKind::ECDSA_SHA256); +} + +} // namespace
diff --git a/crypto/test_support.cc b/crypto/test_support.cc new file mode 100644 index 0000000..e408fea --- /dev/null +++ b/crypto/test_support.cc
@@ -0,0 +1,412 @@ +// 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 "crypto/test_support.h" + +namespace crypto::test { + +// To generate these keys: +// $ openssl genrsa -out test.key $BITS +// $ openssl rsa -in test.key -outform DER | xxd -i > private.key +// $ openssl rsa -in test.key -outform DER -pubout | xxd -i > public.key +// +// That produces the big arrays of hex given below. These are DER-encoded +// private and public keys respectively. + +static constexpr auto kRsa2048Private = std::to_array<uint8_t>( + {0x30, 0x82, 0x04, 0xbd, 0x02, 0x01, 0x00, 0x30, 0x0d, 0x06, 0x09, 0x2a, + 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x04, 0x82, + 0x04, 0xa7, 0x30, 0x82, 0x04, 0xa3, 0x02, 0x01, 0x00, 0x02, 0x82, 0x01, + 0x01, 0x00, 0xb7, 0x73, 0x11, 0xa1, 0x47, 0x56, 0xb8, 0xdf, 0x45, 0xab, + 0x81, 0xc1, 0x17, 0x24, 0x00, 0x6d, 0x70, 0xc2, 0x3f, 0x54, 0xac, 0x71, + 0xda, 0xf5, 0x5d, 0xd8, 0x7c, 0x77, 0x36, 0xa6, 0x2e, 0x30, 0xbd, 0x07, + 0x27, 0x34, 0x20, 0xdf, 0xc5, 0xf7, 0xdb, 0x9d, 0x93, 0xdc, 0x68, 0xa1, + 0xc4, 0x02, 0x4f, 0xcb, 0xb1, 0x1e, 0xb9, 0xd0, 0x12, 0x92, 0xa2, 0xf0, + 0x0d, 0xfb, 0xe6, 0xa0, 0x62, 0x36, 0xd2, 0x6e, 0xae, 0x03, 0xd5, 0xa8, + 0x53, 0x46, 0x77, 0x7a, 0xe3, 0x83, 0xfe, 0x8d, 0x54, 0x29, 0x03, 0xc1, + 0x19, 0x6d, 0x22, 0x44, 0x0f, 0x1a, 0x58, 0x57, 0x01, 0xef, 0x81, 0x3f, + 0x0b, 0xf3, 0x96, 0x8e, 0x51, 0x40, 0xd3, 0x67, 0xd8, 0x85, 0x9f, 0x85, + 0x80, 0x8e, 0x84, 0xc6, 0xaa, 0x35, 0xae, 0x35, 0xff, 0x38, 0x20, 0xbb, + 0xe3, 0x95, 0xc5, 0xe3, 0xd6, 0xdd, 0xe9, 0x28, 0x87, 0xb9, 0x38, 0xce, + 0xbd, 0x53, 0x17, 0x97, 0x90, 0xed, 0x87, 0xd5, 0x08, 0x6c, 0x84, 0xef, + 0xb1, 0x9e, 0x71, 0x5e, 0x2c, 0x0b, 0xce, 0xc1, 0x76, 0xbb, 0xfb, 0xd6, + 0x5b, 0x55, 0x85, 0xe0, 0x4d, 0xf7, 0xaf, 0xcf, 0x34, 0xe1, 0x40, 0x0a, + 0x76, 0x1f, 0xb1, 0xdc, 0xd7, 0xb0, 0x63, 0xa2, 0xc7, 0x23, 0x85, 0x56, + 0xfd, 0x8c, 0xec, 0x6b, 0x0f, 0xfb, 0x6e, 0xcb, 0xba, 0x3b, 0x12, 0x04, + 0xbb, 0xf7, 0x5d, 0x28, 0xe2, 0x57, 0x9c, 0x38, 0x1d, 0x8f, 0xc9, 0x51, + 0x3f, 0x43, 0x8a, 0x6e, 0xb0, 0xa1, 0xbb, 0x6f, 0x19, 0x79, 0x8f, 0x81, + 0x5c, 0xd9, 0x89, 0x91, 0xde, 0xfa, 0xce, 0x09, 0x53, 0xb7, 0x37, 0x36, + 0x04, 0x48, 0x5f, 0x44, 0x7c, 0x19, 0x09, 0xb8, 0x1b, 0x8f, 0xf1, 0xf5, + 0x86, 0x3a, 0x81, 0xf8, 0x44, 0x76, 0x72, 0xc8, 0x4b, 0xbf, 0x70, 0x75, + 0xb7, 0xc1, 0x5d, 0xff, 0x91, 0xdd, 0x02, 0x03, 0x01, 0x00, 0x01, 0x02, + 0x82, 0x01, 0x00, 0x4a, 0x50, 0x5a, 0x69, 0x77, 0x81, 0x39, 0x85, 0xf4, + 0xee, 0xab, 0xb3, 0x01, 0x41, 0x4b, 0xcd, 0x09, 0xbb, 0x1e, 0xb5, 0x99, + 0xdf, 0xcb, 0x7f, 0xcf, 0x42, 0xf5, 0xf4, 0xc3, 0x16, 0x21, 0xab, 0x0b, + 0xc0, 0x1c, 0x91, 0x00, 0xea, 0x35, 0x83, 0x99, 0x1b, 0x25, 0xfd, 0x49, + 0x03, 0x92, 0xe8, 0x0e, 0xd4, 0x28, 0x8d, 0x96, 0x5f, 0x24, 0x4e, 0xf3, + 0xa3, 0x84, 0x3e, 0xb1, 0xa7, 0xf1, 0xf1, 0x5c, 0x60, 0x8a, 0xac, 0xb1, + 0xfe, 0x2c, 0xb1, 0xe4, 0x8f, 0xcb, 0x1f, 0xba, 0xdc, 0x1c, 0xa6, 0x3a, + 0xc7, 0x9c, 0x19, 0xba, 0x22, 0x50, 0xed, 0xee, 0xb0, 0x5c, 0x6f, 0xdd, + 0xef, 0x20, 0xd3, 0xcd, 0xff, 0x1c, 0x0a, 0x43, 0x39, 0x93, 0x9c, 0x59, + 0xc9, 0x56, 0x91, 0x25, 0x57, 0x67, 0x37, 0x34, 0xb9, 0xda, 0x08, 0x2e, + 0x36, 0x7e, 0xd4, 0xba, 0xe3, 0xf7, 0xb4, 0x50, 0x91, 0xe3, 0x1c, 0xa3, + 0x96, 0x0e, 0xec, 0x37, 0x52, 0xe7, 0xd9, 0x64, 0xb8, 0xa7, 0x54, 0x81, + 0xe6, 0x08, 0x87, 0xda, 0x71, 0x6a, 0xb7, 0x86, 0x00, 0xce, 0xe8, 0x22, + 0xcf, 0xed, 0x6d, 0x5f, 0x72, 0xa0, 0x16, 0xe4, 0x3b, 0x0f, 0xc0, 0xc5, + 0xf9, 0x57, 0x21, 0xee, 0x00, 0x2b, 0xf5, 0x0c, 0x37, 0x3d, 0x49, 0x00, + 0x91, 0x00, 0xc4, 0x0d, 0x1f, 0xa6, 0x3a, 0x76, 0x59, 0x6e, 0xd4, 0xeb, + 0x0f, 0x8b, 0x82, 0x0c, 0x98, 0x61, 0xaf, 0x4c, 0x98, 0x6a, 0xb4, 0x09, + 0xe0, 0xde, 0xb8, 0x12, 0x1a, 0xcc, 0xf9, 0xdd, 0xfb, 0x6d, 0x3d, 0x3f, + 0xa6, 0xe2, 0x43, 0x31, 0x01, 0x77, 0xcf, 0xfa, 0xf1, 0xd5, 0x32, 0xe3, + 0xd6, 0xe6, 0x1a, 0x92, 0x88, 0xbd, 0x78, 0xbb, 0x1e, 0xdc, 0x13, 0xbb, + 0xff, 0x1c, 0x12, 0x2f, 0x93, 0x28, 0x82, 0xe8, 0x03, 0xf6, 0xec, 0x56, + 0x4c, 0x08, 0xa2, 0xe7, 0xc3, 0xc9, 0x3f, 0x02, 0x81, 0x81, 0x00, 0xf8, + 0xcc, 0xfa, 0xca, 0xdf, 0x58, 0x59, 0x8e, 0x1d, 0x70, 0x99, 0xce, 0x78, + 0x9e, 0xdc, 0x56, 0x08, 0x91, 0x5f, 0x63, 0xb0, 0x92, 0x69, 0xbb, 0x17, + 0xc4, 0xd1, 0x69, 0xc4, 0xdb, 0xc3, 0xf0, 0x36, 0xe8, 0x0e, 0x76, 0x42, + 0x21, 0xd2, 0x9a, 0x2c, 0xe5, 0x38, 0x5d, 0x89, 0xe7, 0xe0, 0x9f, 0x4c, + 0xde, 0xb4, 0x9c, 0x7a, 0x7c, 0x00, 0x5c, 0x09, 0xed, 0x41, 0x98, 0x7f, + 0x3c, 0x90, 0xfd, 0x35, 0xac, 0x77, 0x3d, 0xa2, 0x66, 0x4a, 0x9d, 0x0c, + 0x9b, 0xdf, 0xab, 0xeb, 0x5e, 0xa9, 0xb3, 0x84, 0x71, 0x9a, 0xb9, 0x2a, + 0x9e, 0x1c, 0x38, 0xe4, 0xea, 0xaf, 0xf7, 0xa3, 0x46, 0x5b, 0x14, 0xdf, + 0x0f, 0xd9, 0xc4, 0x8c, 0x18, 0x70, 0x21, 0xfd, 0x2f, 0xd6, 0x1c, 0xb9, + 0x83, 0x36, 0x56, 0xe6, 0x15, 0xd2, 0x3a, 0xf9, 0xbf, 0x50, 0x5a, 0x8a, + 0x82, 0xd7, 0xc8, 0x23, 0xd7, 0x2e, 0x0b, 0x02, 0x81, 0x81, 0x00, 0xbc, + 0xc1, 0xfe, 0x10, 0x4c, 0xe4, 0x0e, 0x9d, 0x65, 0xfc, 0x31, 0x96, 0x44, + 0xdf, 0xf2, 0x46, 0x59, 0xd4, 0xf5, 0xe1, 0x44, 0xab, 0x67, 0x80, 0x43, + 0xb0, 0xa5, 0x73, 0xec, 0xcb, 0xb5, 0x9a, 0xc1, 0xce, 0xa7, 0x22, 0xb0, + 0x3e, 0xbf, 0x4f, 0x2e, 0x6c, 0x3c, 0xb3, 0x54, 0x44, 0x61, 0xb4, 0x70, + 0xc3, 0x99, 0xdf, 0xfd, 0xa5, 0x39, 0x27, 0x1d, 0x5c, 0x0b, 0xf7, 0xbe, + 0x6a, 0x65, 0x80, 0xb7, 0xb0, 0x59, 0xf5, 0xb3, 0xb8, 0x3f, 0xa5, 0x73, + 0x2b, 0x49, 0xe0, 0x9e, 0x55, 0xf8, 0x46, 0xdb, 0x22, 0xb4, 0x4b, 0xc1, + 0xb6, 0x50, 0x36, 0xd9, 0xa7, 0x1c, 0xf2, 0x7c, 0xca, 0x0e, 0xa7, 0xef, + 0xce, 0xa1, 0x42, 0xb7, 0x42, 0x17, 0x07, 0xc4, 0xc0, 0x3e, 0xd4, 0x56, + 0x27, 0x07, 0xd7, 0x31, 0x47, 0xe6, 0xbf, 0xe6, 0xa5, 0x2e, 0x2b, 0x7a, + 0x7d, 0xcc, 0x60, 0x12, 0x99, 0xf8, 0xb7, 0x02, 0x81, 0x80, 0x5a, 0xd5, + 0xc3, 0x8e, 0x83, 0xe2, 0x66, 0xb7, 0xdb, 0x09, 0xbc, 0x2d, 0xc4, 0x9e, + 0x03, 0x45, 0xa9, 0xd5, 0x21, 0x65, 0x6d, 0x16, 0xd7, 0x61, 0x46, 0x39, + 0x46, 0x57, 0x7e, 0x56, 0xd9, 0xff, 0x7e, 0x9c, 0x54, 0x83, 0x5a, 0x7b, + 0xac, 0xbf, 0x3b, 0x3a, 0xe8, 0xcc, 0x45, 0xc8, 0x11, 0x9b, 0x37, 0x5e, + 0x6b, 0xc4, 0x61, 0x77, 0x9a, 0x4e, 0x00, 0x15, 0xce, 0x08, 0x16, 0x14, + 0x0f, 0xbf, 0x52, 0x74, 0x48, 0x08, 0x89, 0x9d, 0x1d, 0x0a, 0x9f, 0x8a, + 0xdd, 0x2b, 0x90, 0x40, 0x3c, 0x66, 0xdd, 0x28, 0xf8, 0xdb, 0x37, 0xb3, + 0x08, 0x0c, 0xc1, 0x8e, 0xe9, 0x75, 0xd8, 0xf7, 0x9b, 0xd3, 0x4f, 0xe9, + 0x22, 0x91, 0x7e, 0xb0, 0x81, 0x67, 0xf7, 0x5f, 0x1a, 0xa5, 0xdc, 0x19, + 0x0a, 0xa2, 0xc9, 0x58, 0x18, 0x2c, 0x0d, 0xf8, 0x8a, 0x26, 0xb4, 0x41, + 0x36, 0xf4, 0xcc, 0x19, 0x08, 0xa7, 0x02, 0x81, 0x80, 0x62, 0x13, 0x59, + 0xf3, 0x16, 0x40, 0x98, 0xe7, 0x67, 0x8a, 0x36, 0x29, 0xa1, 0xf7, 0xca, + 0x66, 0x8b, 0x5e, 0x7f, 0xb3, 0x60, 0x7e, 0xbe, 0xf4, 0x82, 0x37, 0x52, + 0x80, 0x7d, 0x55, 0x0b, 0x33, 0x31, 0xe8, 0x32, 0x27, 0x6f, 0xf3, 0xea, + 0x6b, 0x35, 0xef, 0xbf, 0x4a, 0x5e, 0x4a, 0x79, 0x89, 0xcb, 0xdd, 0x96, + 0x22, 0x30, 0x24, 0x9d, 0x21, 0x99, 0xbb, 0xad, 0xec, 0x37, 0xe0, 0x08, + 0x85, 0x6c, 0xec, 0x10, 0x91, 0xfd, 0xa3, 0x8a, 0x4e, 0x69, 0x1c, 0xe0, + 0xf1, 0xf8, 0xd3, 0x2a, 0x81, 0x86, 0x72, 0xed, 0xc3, 0x3f, 0x0f, 0x7f, + 0x76, 0x40, 0x78, 0xf8, 0x2d, 0x76, 0x71, 0x76, 0x54, 0x03, 0xe2, 0x15, + 0x20, 0x19, 0x20, 0x19, 0xdf, 0x4b, 0x77, 0xa8, 0x2d, 0xa3, 0xe5, 0xfb, + 0xc8, 0xf0, 0x2e, 0x2f, 0xd3, 0x1e, 0x00, 0x4d, 0x91, 0x01, 0xc0, 0x43, + 0x64, 0xd7, 0xc5, 0x70, 0xd9, 0x02, 0x81, 0x81, 0x00, 0xe9, 0x63, 0xbf, + 0xee, 0xc7, 0x30, 0x95, 0x4e, 0x89, 0x41, 0xdb, 0x6f, 0x58, 0x10, 0x56, + 0x50, 0x4b, 0x37, 0xf6, 0x90, 0xa9, 0x20, 0x12, 0x95, 0xdd, 0xa1, 0x61, + 0x88, 0x3a, 0xe1, 0x85, 0x32, 0x2d, 0x4d, 0x41, 0x44, 0x8e, 0xae, 0x33, + 0x2e, 0x30, 0xc7, 0xf7, 0x2e, 0x14, 0xde, 0xb0, 0x38, 0x9d, 0x1e, 0x56, + 0x85, 0xb3, 0x31, 0x8e, 0x12, 0x55, 0x7a, 0x88, 0x53, 0xe8, 0x75, 0x3c, + 0x5b, 0x38, 0x98, 0xf3, 0x9b, 0x65, 0x70, 0x33, 0xf4, 0x62, 0x58, 0x04, + 0x6f, 0x55, 0x1d, 0xa5, 0xff, 0xb1, 0xa4, 0x48, 0x34, 0x4d, 0xf3, 0xbc, + 0xa9, 0xbc, 0xa5, 0x8d, 0x66, 0x18, 0xad, 0x60, 0x45, 0x20, 0x9b, 0xd7, + 0x5b, 0xd6, 0x53, 0x11, 0x59, 0x9d, 0x68, 0xf9, 0x02, 0x5a, 0x25, 0x77, + 0x41, 0x09, 0x6d, 0x9d, 0x6a, 0x99, 0x02, 0xc5, 0x25, 0x7a, 0x0b, 0xbc, + 0x0d, 0xcb, 0x2e, 0x84, 0xad}); + +static constexpr auto kRsa2048Public = std::to_array<uint8_t>( + {0x30, 0x82, 0x01, 0x22, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, + 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x03, 0x82, 0x01, 0x0f, 0x00, + 0x30, 0x82, 0x01, 0x0a, 0x02, 0x82, 0x01, 0x01, 0x00, 0xb7, 0x73, 0x11, + 0xa1, 0x47, 0x56, 0xb8, 0xdf, 0x45, 0xab, 0x81, 0xc1, 0x17, 0x24, 0x00, + 0x6d, 0x70, 0xc2, 0x3f, 0x54, 0xac, 0x71, 0xda, 0xf5, 0x5d, 0xd8, 0x7c, + 0x77, 0x36, 0xa6, 0x2e, 0x30, 0xbd, 0x07, 0x27, 0x34, 0x20, 0xdf, 0xc5, + 0xf7, 0xdb, 0x9d, 0x93, 0xdc, 0x68, 0xa1, 0xc4, 0x02, 0x4f, 0xcb, 0xb1, + 0x1e, 0xb9, 0xd0, 0x12, 0x92, 0xa2, 0xf0, 0x0d, 0xfb, 0xe6, 0xa0, 0x62, + 0x36, 0xd2, 0x6e, 0xae, 0x03, 0xd5, 0xa8, 0x53, 0x46, 0x77, 0x7a, 0xe3, + 0x83, 0xfe, 0x8d, 0x54, 0x29, 0x03, 0xc1, 0x19, 0x6d, 0x22, 0x44, 0x0f, + 0x1a, 0x58, 0x57, 0x01, 0xef, 0x81, 0x3f, 0x0b, 0xf3, 0x96, 0x8e, 0x51, + 0x40, 0xd3, 0x67, 0xd8, 0x85, 0x9f, 0x85, 0x80, 0x8e, 0x84, 0xc6, 0xaa, + 0x35, 0xae, 0x35, 0xff, 0x38, 0x20, 0xbb, 0xe3, 0x95, 0xc5, 0xe3, 0xd6, + 0xdd, 0xe9, 0x28, 0x87, 0xb9, 0x38, 0xce, 0xbd, 0x53, 0x17, 0x97, 0x90, + 0xed, 0x87, 0xd5, 0x08, 0x6c, 0x84, 0xef, 0xb1, 0x9e, 0x71, 0x5e, 0x2c, + 0x0b, 0xce, 0xc1, 0x76, 0xbb, 0xfb, 0xd6, 0x5b, 0x55, 0x85, 0xe0, 0x4d, + 0xf7, 0xaf, 0xcf, 0x34, 0xe1, 0x40, 0x0a, 0x76, 0x1f, 0xb1, 0xdc, 0xd7, + 0xb0, 0x63, 0xa2, 0xc7, 0x23, 0x85, 0x56, 0xfd, 0x8c, 0xec, 0x6b, 0x0f, + 0xfb, 0x6e, 0xcb, 0xba, 0x3b, 0x12, 0x04, 0xbb, 0xf7, 0x5d, 0x28, 0xe2, + 0x57, 0x9c, 0x38, 0x1d, 0x8f, 0xc9, 0x51, 0x3f, 0x43, 0x8a, 0x6e, 0xb0, + 0xa1, 0xbb, 0x6f, 0x19, 0x79, 0x8f, 0x81, 0x5c, 0xd9, 0x89, 0x91, 0xde, + 0xfa, 0xce, 0x09, 0x53, 0xb7, 0x37, 0x36, 0x04, 0x48, 0x5f, 0x44, 0x7c, + 0x19, 0x09, 0xb8, 0x1b, 0x8f, 0xf1, 0xf5, 0x86, 0x3a, 0x81, 0xf8, 0x44, + 0x76, 0x72, 0xc8, 0x4b, 0xbf, 0x70, 0x75, 0xb7, 0xc1, 0x5d, 0xff, 0x91, + 0xdd, 0x02, 0x03, 0x01, 0x00, 0x01}); + +static constexpr auto kRsa4096Private = std::to_array<uint8_t>( + {0x30, 0x82, 0x09, 0x43, 0x02, 0x01, 0x00, 0x30, 0x0d, 0x06, 0x09, 0x2a, + 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x04, 0x82, + 0x09, 0x2d, 0x30, 0x82, 0x09, 0x29, 0x02, 0x01, 0x00, 0x02, 0x82, 0x02, + 0x01, 0x00, 0xc5, 0xca, 0x53, 0x4b, 0x6e, 0x08, 0x5c, 0xa2, 0x96, 0x53, + 0xe3, 0x3e, 0xfd, 0xf8, 0x31, 0x93, 0x56, 0x50, 0x0b, 0xec, 0x46, 0x5c, + 0x21, 0x78, 0x66, 0x1a, 0x94, 0x2f, 0xcb, 0xec, 0x52, 0xba, 0xa7, 0xe5, + 0xeb, 0x39, 0x8a, 0x34, 0xd6, 0x94, 0x37, 0xd9, 0x4e, 0xd5, 0xab, 0xaa, + 0xb8, 0xde, 0xc4, 0xe4, 0x05, 0xa5, 0x51, 0xeb, 0x74, 0x15, 0x9a, 0x99, + 0x79, 0xe2, 0x51, 0xb2, 0x4d, 0xc9, 0x90, 0xd8, 0x47, 0x5b, 0x78, 0xee, + 0x15, 0xa4, 0xb7, 0x72, 0x94, 0x39, 0x09, 0x77, 0xf9, 0x6e, 0x48, 0xae, + 0x7c, 0xa5, 0x65, 0xf3, 0x92, 0xb2, 0xad, 0x3f, 0x43, 0x4a, 0xfb, 0x9f, + 0x33, 0x46, 0x18, 0x72, 0x08, 0xe2, 0x68, 0xf7, 0xa6, 0x65, 0x32, 0x0b, + 0xa4, 0xa7, 0xc4, 0x64, 0xbb, 0xd4, 0x6e, 0x60, 0x9f, 0x38, 0x3a, 0x5b, + 0x26, 0x82, 0x1c, 0x20, 0x66, 0x8f, 0x4b, 0x34, 0x44, 0x45, 0xdf, 0xd6, + 0xe2, 0x32, 0x1e, 0xa8, 0x4e, 0xb2, 0x3d, 0xa5, 0x5d, 0xe7, 0x22, 0x11, + 0x3f, 0x4a, 0xc0, 0xba, 0xea, 0x58, 0xf5, 0x22, 0xa4, 0x53, 0x49, 0xfa, + 0x83, 0xd1, 0xa1, 0x90, 0xd8, 0xfc, 0x5c, 0x02, 0x74, 0xa1, 0xd8, 0xaf, + 0xc7, 0xec, 0x97, 0x2f, 0x1f, 0xf9, 0xda, 0xb6, 0xd0, 0x4e, 0x7e, 0x48, + 0xd0, 0x22, 0xf7, 0x4d, 0x8a, 0x6d, 0xef, 0xe4, 0xc0, 0xf9, 0x04, 0xbb, + 0x1e, 0x84, 0x96, 0xa8, 0x78, 0xd0, 0x21, 0xb6, 0x24, 0x0c, 0x39, 0x2f, + 0xd0, 0x47, 0x3f, 0x1d, 0x6e, 0xcc, 0xef, 0x59, 0xf1, 0xd8, 0xba, 0x11, + 0xab, 0x2c, 0x72, 0x6b, 0xef, 0x81, 0xcd, 0xf7, 0x1b, 0x3d, 0xcf, 0x39, + 0x66, 0x7e, 0xf2, 0x11, 0x41, 0x8b, 0x19, 0x7c, 0x77, 0xdf, 0x89, 0x35, + 0x04, 0xd8, 0x55, 0xfa, 0xb1, 0xe5, 0xca, 0xdd, 0xca, 0x85, 0x03, 0xcb, + 0xb1, 0xca, 0xa2, 0x76, 0x70, 0xa8, 0x4a, 0x6e, 0x20, 0x0e, 0x25, 0xd4, + 0xb4, 0xda, 0xe6, 0x10, 0x04, 0x82, 0xd3, 0x70, 0x0c, 0xd3, 0x01, 0x82, + 0x46, 0x63, 0x1f, 0x96, 0x4c, 0x9a, 0x9b, 0x24, 0x9e, 0xab, 0xc3, 0x0b, + 0x9a, 0x3c, 0x94, 0x9e, 0xf0, 0xef, 0x1a, 0xac, 0xa0, 0x74, 0x3b, 0x0d, + 0x10, 0xdd, 0xbe, 0xca, 0x7a, 0x69, 0x5e, 0x70, 0x90, 0xaa, 0x96, 0xe2, + 0xa8, 0x46, 0x9b, 0x9c, 0xa2, 0x19, 0xc2, 0x55, 0x07, 0xbf, 0x6c, 0x1a, + 0x8b, 0x7f, 0x34, 0xf2, 0x3c, 0x6f, 0x21, 0x60, 0x6b, 0x7e, 0xc0, 0x79, + 0x35, 0x5b, 0x8e, 0x5a, 0x2e, 0xec, 0xfb, 0x92, 0xfb, 0xa7, 0x73, 0x6b, + 0xde, 0xe5, 0xd3, 0xbb, 0xa7, 0x83, 0xbc, 0xdb, 0x73, 0x1b, 0xdc, 0xb8, + 0xe1, 0x9c, 0x1c, 0xec, 0x39, 0x2c, 0xaf, 0xcd, 0x25, 0x45, 0xa6, 0x56, + 0x70, 0x9b, 0x31, 0x88, 0x66, 0x01, 0x09, 0x7c, 0x61, 0xfb, 0xca, 0x05, + 0x93, 0x7a, 0x16, 0x2a, 0xdb, 0xbd, 0xf2, 0xe1, 0x46, 0x01, 0x5e, 0x8e, + 0x82, 0x68, 0x8f, 0xf1, 0xd8, 0x9a, 0x30, 0xda, 0xa3, 0xf1, 0x9a, 0x67, + 0xd8, 0x59, 0xe8, 0x4c, 0x89, 0x70, 0x97, 0xdc, 0x6d, 0x83, 0x1a, 0x01, + 0xe8, 0x00, 0x74, 0xfd, 0x44, 0x73, 0x0f, 0x86, 0x72, 0x03, 0x71, 0x48, + 0x90, 0x01, 0x12, 0xd3, 0xd1, 0x85, 0x2e, 0x67, 0xf1, 0x07, 0x78, 0x8d, + 0x9f, 0xc8, 0x44, 0xd1, 0xa3, 0x41, 0x9f, 0xd7, 0xc6, 0x50, 0x44, 0x9d, + 0x09, 0xf4, 0xab, 0x7c, 0x12, 0x65, 0x59, 0xc4, 0x43, 0x51, 0x94, 0x25, + 0x9d, 0x0a, 0xb1, 0x1b, 0xd2, 0x3d, 0xeb, 0xd4, 0xef, 0xbf, 0xd7, 0x66, + 0xa3, 0x51, 0x67, 0xc1, 0x42, 0x88, 0xe2, 0x7a, 0x28, 0x9a, 0x40, 0x1a, + 0x60, 0xce, 0xa2, 0x42, 0x72, 0x45, 0x8a, 0x4c, 0x08, 0xcc, 0x06, 0xa0, + 0x6d, 0xab, 0xa3, 0xea, 0xee, 0x63, 0x65, 0x9e, 0x40, 0x19, 0x02, 0x03, + 0x01, 0x00, 0x01, 0x02, 0x82, 0x02, 0x00, 0x0d, 0xec, 0x84, 0x80, 0xdd, + 0xc4, 0x03, 0xae, 0x73, 0xf5, 0xff, 0x9c, 0x4c, 0x81, 0x7b, 0x8e, 0xf7, + 0x5f, 0x6e, 0xf9, 0x71, 0x0a, 0x15, 0x16, 0x9e, 0x5c, 0x7e, 0x64, 0x5f, + 0x7f, 0x19, 0x63, 0x57, 0xc4, 0xc3, 0x32, 0x93, 0xa8, 0xcc, 0xd6, 0xd6, + 0x18, 0x2b, 0x99, 0x35, 0xea, 0xb0, 0xb5, 0x49, 0x07, 0xd2, 0xe0, 0x6c, + 0xa4, 0x0e, 0x51, 0xe3, 0x86, 0x6c, 0xaa, 0xca, 0xac, 0xca, 0x56, 0x57, + 0x66, 0xac, 0x5e, 0x53, 0x84, 0xf2, 0x3b, 0xdc, 0x68, 0xb9, 0xe7, 0xca, + 0x83, 0x7a, 0x49, 0x21, 0xdf, 0x7b, 0xb9, 0xa2, 0x93, 0xdb, 0x36, 0xce, + 0x24, 0xb3, 0x1a, 0x6d, 0x1c, 0x8b, 0xdd, 0xe2, 0x44, 0x6b, 0xdc, 0xf1, + 0x7e, 0x06, 0xa6, 0x1e, 0xd7, 0xec, 0x3d, 0x5d, 0xc1, 0x68, 0x2a, 0x28, + 0x23, 0x8f, 0xed, 0xe3, 0xc8, 0xcd, 0x2d, 0x11, 0x69, 0x14, 0x03, 0x40, + 0x2c, 0x73, 0xa6, 0x76, 0x1f, 0xb0, 0xe0, 0x8d, 0xa1, 0x34, 0x5a, 0x0e, + 0xa6, 0x2c, 0xb6, 0x43, 0xe3, 0x12, 0x31, 0x33, 0x60, 0x6d, 0x35, 0x3a, + 0x3b, 0x98, 0x35, 0x1f, 0x52, 0xfe, 0x12, 0xe4, 0xc4, 0x77, 0x4c, 0x0b, + 0xb7, 0xa6, 0x09, 0x8b, 0x41, 0x33, 0xaa, 0x33, 0x89, 0xef, 0x1e, 0xb6, + 0x1f, 0x92, 0xca, 0x45, 0xd0, 0x5f, 0x2b, 0xa8, 0xfc, 0x79, 0xd3, 0x6b, + 0xb5, 0x0f, 0x43, 0x30, 0x8a, 0xb2, 0x37, 0x2c, 0x0a, 0x7b, 0xe7, 0xce, + 0x09, 0xbe, 0x1f, 0xa8, 0xaa, 0x75, 0xa7, 0x80, 0x59, 0x54, 0x76, 0xa3, + 0x9e, 0x71, 0xbc, 0xbb, 0xbb, 0x98, 0xf0, 0xe9, 0xf4, 0xb0, 0x81, 0x93, + 0x33, 0xf3, 0x44, 0x5e, 0x89, 0x58, 0x4e, 0x75, 0x40, 0xd2, 0xfc, 0x05, + 0x7d, 0x2a, 0x4c, 0xaf, 0xa2, 0x6e, 0x7f, 0x02, 0xcd, 0x81, 0x56, 0xcf, + 0x99, 0x5d, 0xc3, 0x8f, 0xac, 0xf8, 0x2d, 0x0f, 0x53, 0x16, 0x72, 0xd8, + 0xb5, 0xe6, 0x05, 0x2b, 0x1d, 0xc6, 0x4c, 0x2e, 0x71, 0xbf, 0x20, 0xfa, + 0xda, 0xef, 0x24, 0x9c, 0xba, 0x06, 0x47, 0x5b, 0xab, 0xc1, 0xf7, 0x5c, + 0x8f, 0x49, 0x6c, 0xfd, 0xb6, 0x10, 0x55, 0x88, 0x65, 0xd1, 0xed, 0x91, + 0x56, 0xa2, 0xc0, 0xd1, 0x76, 0x76, 0x3b, 0x77, 0x93, 0x18, 0x83, 0xcd, + 0xa4, 0xc7, 0x7c, 0xe2, 0xfb, 0x37, 0x88, 0xcc, 0x87, 0x84, 0xb1, 0x30, + 0x2e, 0x9a, 0x12, 0xca, 0xd8, 0xe5, 0xff, 0xe9, 0x90, 0x89, 0x9d, 0x6e, + 0xf8, 0xae, 0x8f, 0x4b, 0x33, 0x66, 0x81, 0xbb, 0xe4, 0xef, 0xe1, 0xb4, + 0xbe, 0x05, 0x7e, 0x40, 0x72, 0x7c, 0x42, 0x7f, 0xb8, 0x02, 0xd7, 0xd0, + 0x94, 0x2e, 0x47, 0xa5, 0x01, 0xe6, 0x0c, 0x60, 0xd2, 0xe7, 0x8e, 0x38, + 0x5a, 0x21, 0xf7, 0x4e, 0xf8, 0xa7, 0x7f, 0x9f, 0xaf, 0x2a, 0x8e, 0x6d, + 0x82, 0x89, 0x87, 0x76, 0xad, 0x29, 0xfe, 0x9d, 0xa4, 0x24, 0xe8, 0x68, + 0x12, 0x94, 0x80, 0x7f, 0x70, 0xe7, 0x7c, 0x8e, 0xfd, 0x8c, 0xb4, 0x5c, + 0xc0, 0x3a, 0xb0, 0xca, 0xbb, 0x6f, 0x31, 0x50, 0x71, 0x16, 0xa7, 0x71, + 0x00, 0xfa, 0x19, 0x8b, 0x45, 0xf2, 0xb4, 0x97, 0xee, 0x45, 0x4c, 0xb3, + 0xf4, 0x8c, 0x35, 0x5b, 0x29, 0x60, 0xc1, 0xb1, 0xb4, 0x98, 0x2f, 0x89, + 0xbe, 0xc5, 0xb5, 0x6d, 0xda, 0x1a, 0xc0, 0xa4, 0x24, 0x9e, 0xa3, 0x6c, + 0xa9, 0x02, 0x00, 0xd8, 0x7d, 0x9d, 0x04, 0xea, 0xe4, 0xbb, 0x85, 0xb1, + 0x7f, 0xf8, 0xd3, 0x51, 0x17, 0x86, 0xf1, 0x69, 0x68, 0x17, 0x2c, 0x27, + 0x64, 0x77, 0xf9, 0x62, 0x7c, 0xbb, 0x10, 0x3c, 0xd4, 0x2e, 0x9e, 0x5c, + 0xb3, 0xaa, 0xec, 0x97, 0x18, 0x50, 0x0d, 0xbc, 0x20, 0x2d, 0x7b, 0xd0, + 0x69, 0xe8, 0xae, 0x72, 0x3c, 0xdd, 0xdc, 0x40, 0xff, 0x57, 0xb4, 0xfe, + 0x08, 0x05, 0x81, 0x02, 0x82, 0x01, 0x01, 0x00, 0xf2, 0x9d, 0xdf, 0xd3, + 0xef, 0xa3, 0x95, 0xf9, 0xae, 0xc6, 0x73, 0xcb, 0xcb, 0x26, 0x65, 0x99, + 0x54, 0xd6, 0x3c, 0x3f, 0xfd, 0x86, 0xec, 0x61, 0x9d, 0x6a, 0xcf, 0x64, + 0x64, 0x3d, 0xe5, 0xeb, 0x9d, 0x49, 0xcb, 0x07, 0x3e, 0xf0, 0x17, 0xb3, + 0xcd, 0x00, 0x9b, 0xf6, 0x53, 0x13, 0x30, 0x23, 0x47, 0xe5, 0xec, 0xaa, + 0x43, 0xfb, 0x7a, 0x1b, 0x4b, 0xfb, 0x9c, 0xa6, 0xdd, 0x05, 0x70, 0xd2, + 0xab, 0xaa, 0xee, 0x10, 0x32, 0xdd, 0xcd, 0x81, 0x00, 0xe1, 0x52, 0x71, + 0x43, 0xa8, 0x64, 0x64, 0xb1, 0x8c, 0x2a, 0xa7, 0xbf, 0x05, 0x26, 0x99, + 0x6e, 0x65, 0xe1, 0x8c, 0x5d, 0x1c, 0x97, 0xc6, 0xa0, 0x47, 0xcc, 0xd1, + 0x06, 0xb8, 0xe8, 0x06, 0x97, 0x1c, 0x9c, 0x7c, 0xde, 0xd5, 0x28, 0xc1, + 0x9f, 0xa3, 0xce, 0x3e, 0x75, 0xe0, 0x8c, 0x6f, 0x06, 0xb0, 0xff, 0xc9, + 0x96, 0x34, 0x68, 0x56, 0xe1, 0xca, 0x21, 0x89, 0xc7, 0x66, 0xb5, 0x87, + 0x8b, 0xe2, 0xf4, 0x55, 0x6f, 0x85, 0x7d, 0x6e, 0x57, 0xbd, 0x0f, 0x4a, + 0x92, 0xfd, 0xa8, 0x8e, 0x6e, 0x87, 0x82, 0xb0, 0x4b, 0x50, 0x80, 0xeb, + 0x2c, 0x8b, 0xf4, 0xf1, 0x35, 0x23, 0xaf, 0x42, 0xfd, 0xf6, 0x36, 0xce, + 0xcb, 0x5e, 0xe8, 0x4f, 0xcc, 0x67, 0xa3, 0x43, 0x74, 0x10, 0x61, 0x88, + 0xc2, 0x1c, 0x61, 0xde, 0xc0, 0xfc, 0xd1, 0x60, 0xd1, 0xed, 0x0e, 0x07, + 0xd8, 0x05, 0xdf, 0xee, 0xcb, 0xcb, 0x26, 0x90, 0x90, 0xa1, 0xf9, 0x80, + 0x86, 0x84, 0xcb, 0x38, 0x0c, 0x30, 0xc3, 0x2d, 0x8b, 0x18, 0xc8, 0x4e, + 0xfc, 0x26, 0xad, 0x20, 0x8c, 0x0c, 0x2b, 0x08, 0x78, 0x21, 0x28, 0xcb, + 0xe4, 0x09, 0x70, 0x64, 0xbf, 0xdd, 0xfa, 0x3d, 0xff, 0xa7, 0xf4, 0xf5, + 0x0c, 0x51, 0x84, 0x6c, 0xda, 0x93, 0xc4, 0x0b, 0x75, 0x18, 0xdd, 0x99, + 0x02, 0x82, 0x01, 0x01, 0x00, 0xd0, 0xb3, 0x6e, 0xd7, 0x23, 0x36, 0x15, + 0xaa, 0x0d, 0xf9, 0x46, 0xfc, 0xa0, 0xfc, 0xef, 0x39, 0xa0, 0x51, 0x82, + 0xcb, 0x8a, 0x97, 0xba, 0xc5, 0xaa, 0x48, 0xe2, 0xc6, 0x36, 0xfe, 0xb2, + 0x95, 0x77, 0x84, 0x84, 0x3f, 0x87, 0x37, 0x9b, 0xa7, 0x9f, 0x2c, 0xad, + 0x65, 0x00, 0x1b, 0x94, 0x53, 0xef, 0xf5, 0x86, 0x10, 0x35, 0x7d, 0x1f, + 0x16, 0xe8, 0xa7, 0x5a, 0xfc, 0x56, 0xd0, 0xe1, 0xff, 0xaf, 0x34, 0x10, + 0x71, 0x50, 0x71, 0xfd, 0x5c, 0xb2, 0x0c, 0x6a, 0xab, 0x70, 0xa3, 0xa3, + 0x3d, 0xe8, 0x02, 0xbf, 0xdd, 0xe9, 0x4d, 0x80, 0x79, 0xe5, 0xef, 0xe3, + 0x17, 0x39, 0xd9, 0x05, 0xa2, 0xbe, 0xd1, 0x03, 0x59, 0x21, 0x00, 0xcf, + 0xa4, 0x06, 0xa6, 0xe2, 0x3d, 0xa4, 0x2f, 0x2f, 0xa1, 0x55, 0x8a, 0x86, + 0x6f, 0xba, 0x3b, 0x48, 0x13, 0x79, 0x65, 0x0a, 0x82, 0xed, 0x00, 0x26, + 0x77, 0xcb, 0x91, 0x56, 0xc2, 0x86, 0x61, 0xe7, 0x1f, 0x6a, 0x66, 0x6b, + 0xd1, 0xea, 0x76, 0x58, 0xd4, 0xf1, 0x5a, 0x09, 0xb3, 0xed, 0x5f, 0x1b, + 0x70, 0x14, 0x6b, 0x6a, 0x47, 0xe1, 0xb5, 0x95, 0x36, 0x7d, 0xb7, 0x34, + 0x4d, 0xd3, 0x3f, 0x3b, 0x09, 0x0f, 0x76, 0x36, 0x57, 0x89, 0x4d, 0x7d, + 0x36, 0x69, 0xcd, 0x05, 0x19, 0x26, 0x3a, 0x52, 0x2d, 0x3b, 0x7c, 0x85, + 0x58, 0x96, 0x29, 0x9a, 0x7d, 0xed, 0xe7, 0x05, 0x55, 0xfb, 0xfe, 0x7d, + 0x26, 0x1f, 0xbe, 0x53, 0x81, 0xe2, 0xb9, 0xb7, 0xa5, 0x4a, 0xb3, 0x82, + 0x98, 0xb7, 0x61, 0xb3, 0x87, 0xba, 0x83, 0xb0, 0x51, 0x0e, 0xa0, 0x0b, + 0x00, 0x23, 0x93, 0x54, 0x93, 0xcf, 0xba, 0xab, 0xef, 0x91, 0x6a, 0x89, + 0x69, 0x65, 0x53, 0xe8, 0x18, 0x31, 0x90, 0x4c, 0x39, 0x32, 0x19, 0x06, + 0xe9, 0x22, 0x92, 0xcb, 0xe9, 0x93, 0x2d, 0x06, 0x81, 0x02, 0x82, 0x01, + 0x01, 0x00, 0x85, 0x2a, 0xea, 0xa4, 0x65, 0xb0, 0xa0, 0xad, 0x3f, 0xa5, + 0x66, 0x01, 0xc2, 0x2b, 0xfd, 0x30, 0x40, 0x44, 0xa7, 0x25, 0x68, 0x7f, + 0x1a, 0x58, 0x2a, 0x13, 0x6a, 0x6a, 0x6c, 0x2b, 0x1d, 0x7c, 0x4a, 0x05, + 0x2b, 0x0f, 0x7b, 0x18, 0x45, 0xaa, 0x47, 0x27, 0xc2, 0x73, 0x5d, 0xfd, + 0xf6, 0x3a, 0x27, 0x48, 0xa8, 0xb2, 0x7c, 0x46, 0x18, 0x2b, 0xcb, 0x74, + 0xfb, 0xf3, 0x0a, 0xf5, 0xe5, 0x8b, 0x7c, 0xf6, 0x81, 0x5d, 0x1a, 0xce, + 0x6b, 0xf2, 0x17, 0x0d, 0x96, 0x36, 0xdd, 0x30, 0x1c, 0x8a, 0xb8, 0x79, + 0x7b, 0x20, 0x3f, 0xd5, 0x0f, 0xee, 0xbf, 0x8d, 0xe4, 0x53, 0x2a, 0xf8, + 0x7a, 0xc6, 0x67, 0x4b, 0x81, 0xbc, 0x69, 0xb8, 0x2c, 0x83, 0x11, 0x86, + 0x7e, 0xce, 0x7b, 0x70, 0xfc, 0xc1, 0xea, 0x61, 0xfa, 0xde, 0x85, 0xcd, + 0x0e, 0xc8, 0x12, 0x20, 0x2d, 0x05, 0xf5, 0x10, 0x27, 0x05, 0x29, 0x41, + 0xcd, 0x4e, 0xd6, 0xc8, 0x25, 0x73, 0x94, 0xf0, 0xa9, 0xb4, 0x0b, 0x56, + 0x76, 0x16, 0x8e, 0xca, 0x13, 0x0e, 0x97, 0xc7, 0xeb, 0x30, 0xe2, 0xb2, + 0x36, 0xa5, 0x95, 0x71, 0x46, 0x91, 0xcf, 0x0d, 0xb3, 0x10, 0x82, 0x5e, + 0x67, 0xe0, 0x99, 0x8c, 0xff, 0x60, 0x5a, 0x78, 0x69, 0x83, 0x03, 0x6b, + 0x0f, 0x4e, 0x02, 0xf2, 0xca, 0xd6, 0x49, 0xd5, 0x52, 0x52, 0xf6, 0x12, + 0xce, 0xca, 0x19, 0xef, 0xc7, 0xf7, 0xbe, 0x36, 0xdb, 0x47, 0x33, 0x4c, + 0xfa, 0x89, 0xf7, 0x19, 0x30, 0xdd, 0xbe, 0xf8, 0x3c, 0xa3, 0x32, 0xed, + 0xc0, 0xf3, 0xca, 0x99, 0x7b, 0xb8, 0xfe, 0xe6, 0x2b, 0xb8, 0xe5, 0xa5, + 0xf8, 0x28, 0xd9, 0xe7, 0x39, 0x81, 0x50, 0x55, 0x6d, 0xff, 0x9e, 0xe0, + 0xb4, 0x6e, 0x3b, 0x59, 0x71, 0x36, 0xdf, 0xe0, 0x0e, 0x93, 0xc1, 0x15, + 0xc2, 0x51, 0x97, 0xa0, 0x62, 0x61, 0x02, 0x82, 0x01, 0x00, 0x02, 0x1e, + 0xf0, 0xaf, 0x6b, 0x02, 0x2f, 0xb2, 0x2c, 0xb6, 0x2d, 0xcc, 0x7f, 0x6e, + 0x52, 0x98, 0x09, 0x53, 0x0a, 0xbb, 0x3a, 0xcb, 0x53, 0xf0, 0x92, 0x4c, + 0x6f, 0x51, 0x88, 0x59, 0x8a, 0x43, 0x0e, 0x95, 0xe0, 0x2a, 0x2d, 0x1b, + 0x99, 0x8f, 0x58, 0x84, 0xc1, 0xb6, 0x57, 0x0b, 0xf0, 0xb3, 0xf1, 0xaa, + 0x53, 0x14, 0x73, 0x16, 0xb4, 0x6c, 0x2d, 0x2d, 0x16, 0x35, 0x9e, 0x44, + 0x3d, 0x27, 0xb6, 0x06, 0x17, 0x6c, 0xaf, 0x5e, 0x99, 0x2e, 0x89, 0xf8, + 0xaa, 0x54, 0xd7, 0xae, 0x32, 0x08, 0x7d, 0x05, 0x1a, 0x22, 0x0d, 0x2e, + 0xe6, 0x71, 0x56, 0xae, 0xdb, 0x65, 0xef, 0x06, 0x8f, 0x92, 0x19, 0xd2, + 0x51, 0xf0, 0x63, 0xef, 0x78, 0x2c, 0xb8, 0x8c, 0x95, 0x9b, 0xfe, 0xc1, + 0x24, 0x00, 0xc5, 0xb7, 0xc4, 0xa9, 0xfa, 0x00, 0x84, 0x38, 0xfe, 0x70, + 0xd2, 0x6c, 0x86, 0x30, 0x0c, 0x34, 0x07, 0x73, 0x90, 0xa8, 0x25, 0x69, + 0x75, 0x49, 0xd2, 0x70, 0xfc, 0x03, 0x84, 0x18, 0x73, 0xca, 0xa6, 0x31, + 0x3e, 0x0c, 0x00, 0x0b, 0x89, 0x61, 0xd8, 0x33, 0x47, 0x3f, 0x37, 0xc1, + 0xfa, 0xa4, 0x35, 0x1e, 0xd9, 0x7f, 0x38, 0xf8, 0x59, 0x87, 0x3c, 0x0a, + 0xfd, 0x7e, 0x62, 0x3c, 0xd1, 0x5a, 0xee, 0x34, 0x51, 0x2a, 0xf2, 0x42, + 0x81, 0x77, 0x48, 0x35, 0x79, 0xbd, 0x6e, 0xb9, 0x39, 0x82, 0xb2, 0x1a, + 0x38, 0xe9, 0xa8, 0xc7, 0xeb, 0x49, 0xa9, 0xe4, 0xeb, 0x40, 0x54, 0xa7, + 0x82, 0x80, 0x41, 0x84, 0x15, 0x7d, 0xab, 0xcf, 0x68, 0x5d, 0xa6, 0xbd, + 0x93, 0xdb, 0x1f, 0x04, 0xed, 0x57, 0xb1, 0x04, 0xdc, 0x45, 0x2c, 0x45, + 0x3f, 0x5e, 0x0d, 0xe2, 0x41, 0x47, 0x3a, 0xea, 0x61, 0x5f, 0x6d, 0x91, + 0x83, 0xd4, 0xc8, 0xf7, 0x8c, 0x24, 0x6e, 0x5f, 0x83, 0x86, 0xfa, 0x21, + 0xe9, 0x01, 0x02, 0x82, 0x01, 0x01, 0x00, 0x9c, 0xe1, 0x80, 0x78, 0xc7, + 0x12, 0xb2, 0x59, 0xe6, 0x3a, 0xe4, 0x3b, 0x0e, 0xf3, 0x28, 0xec, 0x01, + 0xbf, 0x5a, 0xde, 0xa9, 0x40, 0x3a, 0x44, 0x44, 0x73, 0xaf, 0xe0, 0x2a, + 0xe7, 0x75, 0xec, 0x38, 0x9b, 0x25, 0x4f, 0x2d, 0xf6, 0x2f, 0x29, 0x90, + 0x13, 0x7b, 0xa3, 0x5a, 0xab, 0x55, 0xde, 0x13, 0xbd, 0x01, 0x94, 0x5f, + 0x8b, 0xfc, 0x31, 0x43, 0x7d, 0xb2, 0x25, 0xe0, 0xbe, 0xc4, 0xbb, 0x3f, + 0xf2, 0x9f, 0x05, 0x6a, 0x75, 0xe1, 0xf5, 0x49, 0xb2, 0xb3, 0xd2, 0x19, + 0x1b, 0x32, 0x58, 0x2b, 0x6b, 0x48, 0xc2, 0x70, 0x64, 0x0d, 0x25, 0x0b, + 0x44, 0x2e, 0x31, 0xf6, 0x50, 0xcd, 0x5b, 0xb5, 0xc4, 0xd9, 0xc3, 0x68, + 0x24, 0x2a, 0x2b, 0x01, 0x31, 0xf5, 0xb8, 0x13, 0x0d, 0x08, 0xd9, 0x6d, + 0x74, 0x69, 0xf4, 0x86, 0x65, 0x17, 0x0c, 0xe9, 0x36, 0x39, 0x98, 0xb0, + 0x41, 0x8c, 0xf3, 0xcc, 0x5b, 0x5e, 0x19, 0x58, 0x98, 0x49, 0xb6, 0xcf, + 0x81, 0xc2, 0x5f, 0x8e, 0x39, 0xf3, 0x00, 0x99, 0x4a, 0xca, 0x1a, 0xe5, + 0x29, 0x3a, 0xe0, 0x9e, 0xfc, 0x7c, 0xbb, 0x74, 0x55, 0x12, 0xa8, 0x58, + 0xe0, 0x60, 0x52, 0xbf, 0xd6, 0xcd, 0x75, 0x81, 0x1e, 0x62, 0xdb, 0x30, + 0x71, 0x5f, 0xb0, 0xf6, 0x1a, 0xa8, 0x45, 0xe2, 0x26, 0xc7, 0xa2, 0x73, + 0xfd, 0xd8, 0xeb, 0x14, 0xee, 0x43, 0x5f, 0x7d, 0x19, 0xa6, 0x9d, 0xed, + 0x12, 0x31, 0x91, 0x36, 0x1c, 0x87, 0x17, 0x97, 0xbb, 0x98, 0x74, 0x6c, + 0xce, 0x5a, 0xe4, 0x9a, 0x26, 0xa0, 0x6f, 0x67, 0x99, 0x70, 0x78, 0xe6, + 0x90, 0x01, 0x36, 0x8d, 0x38, 0x0e, 0x85, 0xeb, 0x04, 0x2c, 0xcb, 0xe0, + 0xd5, 0xca, 0xe1, 0x8b, 0xff, 0xea, 0xe7, 0xf1, 0x20, 0xed, 0x56, 0x6d, + 0x6a, 0x4c, 0xf6, 0xa2, 0xc7, 0x54, 0xf5, 0xbf, 0xc5, 0x48, 0x19}); + +static constexpr auto kRsa4096Public = std::to_array<uint8_t>( + {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, 0xc5, 0xca, 0x53, + 0x4b, 0x6e, 0x08, 0x5c, 0xa2, 0x96, 0x53, 0xe3, 0x3e, 0xfd, 0xf8, 0x31, + 0x93, 0x56, 0x50, 0x0b, 0xec, 0x46, 0x5c, 0x21, 0x78, 0x66, 0x1a, 0x94, + 0x2f, 0xcb, 0xec, 0x52, 0xba, 0xa7, 0xe5, 0xeb, 0x39, 0x8a, 0x34, 0xd6, + 0x94, 0x37, 0xd9, 0x4e, 0xd5, 0xab, 0xaa, 0xb8, 0xde, 0xc4, 0xe4, 0x05, + 0xa5, 0x51, 0xeb, 0x74, 0x15, 0x9a, 0x99, 0x79, 0xe2, 0x51, 0xb2, 0x4d, + 0xc9, 0x90, 0xd8, 0x47, 0x5b, 0x78, 0xee, 0x15, 0xa4, 0xb7, 0x72, 0x94, + 0x39, 0x09, 0x77, 0xf9, 0x6e, 0x48, 0xae, 0x7c, 0xa5, 0x65, 0xf3, 0x92, + 0xb2, 0xad, 0x3f, 0x43, 0x4a, 0xfb, 0x9f, 0x33, 0x46, 0x18, 0x72, 0x08, + 0xe2, 0x68, 0xf7, 0xa6, 0x65, 0x32, 0x0b, 0xa4, 0xa7, 0xc4, 0x64, 0xbb, + 0xd4, 0x6e, 0x60, 0x9f, 0x38, 0x3a, 0x5b, 0x26, 0x82, 0x1c, 0x20, 0x66, + 0x8f, 0x4b, 0x34, 0x44, 0x45, 0xdf, 0xd6, 0xe2, 0x32, 0x1e, 0xa8, 0x4e, + 0xb2, 0x3d, 0xa5, 0x5d, 0xe7, 0x22, 0x11, 0x3f, 0x4a, 0xc0, 0xba, 0xea, + 0x58, 0xf5, 0x22, 0xa4, 0x53, 0x49, 0xfa, 0x83, 0xd1, 0xa1, 0x90, 0xd8, + 0xfc, 0x5c, 0x02, 0x74, 0xa1, 0xd8, 0xaf, 0xc7, 0xec, 0x97, 0x2f, 0x1f, + 0xf9, 0xda, 0xb6, 0xd0, 0x4e, 0x7e, 0x48, 0xd0, 0x22, 0xf7, 0x4d, 0x8a, + 0x6d, 0xef, 0xe4, 0xc0, 0xf9, 0x04, 0xbb, 0x1e, 0x84, 0x96, 0xa8, 0x78, + 0xd0, 0x21, 0xb6, 0x24, 0x0c, 0x39, 0x2f, 0xd0, 0x47, 0x3f, 0x1d, 0x6e, + 0xcc, 0xef, 0x59, 0xf1, 0xd8, 0xba, 0x11, 0xab, 0x2c, 0x72, 0x6b, 0xef, + 0x81, 0xcd, 0xf7, 0x1b, 0x3d, 0xcf, 0x39, 0x66, 0x7e, 0xf2, 0x11, 0x41, + 0x8b, 0x19, 0x7c, 0x77, 0xdf, 0x89, 0x35, 0x04, 0xd8, 0x55, 0xfa, 0xb1, + 0xe5, 0xca, 0xdd, 0xca, 0x85, 0x03, 0xcb, 0xb1, 0xca, 0xa2, 0x76, 0x70, + 0xa8, 0x4a, 0x6e, 0x20, 0x0e, 0x25, 0xd4, 0xb4, 0xda, 0xe6, 0x10, 0x04, + 0x82, 0xd3, 0x70, 0x0c, 0xd3, 0x01, 0x82, 0x46, 0x63, 0x1f, 0x96, 0x4c, + 0x9a, 0x9b, 0x24, 0x9e, 0xab, 0xc3, 0x0b, 0x9a, 0x3c, 0x94, 0x9e, 0xf0, + 0xef, 0x1a, 0xac, 0xa0, 0x74, 0x3b, 0x0d, 0x10, 0xdd, 0xbe, 0xca, 0x7a, + 0x69, 0x5e, 0x70, 0x90, 0xaa, 0x96, 0xe2, 0xa8, 0x46, 0x9b, 0x9c, 0xa2, + 0x19, 0xc2, 0x55, 0x07, 0xbf, 0x6c, 0x1a, 0x8b, 0x7f, 0x34, 0xf2, 0x3c, + 0x6f, 0x21, 0x60, 0x6b, 0x7e, 0xc0, 0x79, 0x35, 0x5b, 0x8e, 0x5a, 0x2e, + 0xec, 0xfb, 0x92, 0xfb, 0xa7, 0x73, 0x6b, 0xde, 0xe5, 0xd3, 0xbb, 0xa7, + 0x83, 0xbc, 0xdb, 0x73, 0x1b, 0xdc, 0xb8, 0xe1, 0x9c, 0x1c, 0xec, 0x39, + 0x2c, 0xaf, 0xcd, 0x25, 0x45, 0xa6, 0x56, 0x70, 0x9b, 0x31, 0x88, 0x66, + 0x01, 0x09, 0x7c, 0x61, 0xfb, 0xca, 0x05, 0x93, 0x7a, 0x16, 0x2a, 0xdb, + 0xbd, 0xf2, 0xe1, 0x46, 0x01, 0x5e, 0x8e, 0x82, 0x68, 0x8f, 0xf1, 0xd8, + 0x9a, 0x30, 0xda, 0xa3, 0xf1, 0x9a, 0x67, 0xd8, 0x59, 0xe8, 0x4c, 0x89, + 0x70, 0x97, 0xdc, 0x6d, 0x83, 0x1a, 0x01, 0xe8, 0x00, 0x74, 0xfd, 0x44, + 0x73, 0x0f, 0x86, 0x72, 0x03, 0x71, 0x48, 0x90, 0x01, 0x12, 0xd3, 0xd1, + 0x85, 0x2e, 0x67, 0xf1, 0x07, 0x78, 0x8d, 0x9f, 0xc8, 0x44, 0xd1, 0xa3, + 0x41, 0x9f, 0xd7, 0xc6, 0x50, 0x44, 0x9d, 0x09, 0xf4, 0xab, 0x7c, 0x12, + 0x65, 0x59, 0xc4, 0x43, 0x51, 0x94, 0x25, 0x9d, 0x0a, 0xb1, 0x1b, 0xd2, + 0x3d, 0xeb, 0xd4, 0xef, 0xbf, 0xd7, 0x66, 0xa3, 0x51, 0x67, 0xc1, 0x42, + 0x88, 0xe2, 0x7a, 0x28, 0x9a, 0x40, 0x1a, 0x60, 0xce, 0xa2, 0x42, 0x72, + 0x45, 0x8a, 0x4c, 0x08, 0xcc, 0x06, 0xa0, 0x6d, 0xab, 0xa3, 0xea, 0xee, + 0x63, 0x65, 0x9e, 0x40, 0x19, 0x02, 0x03, 0x01, 0x00, 0x01}); + +PrivateKey FixedRsa2048PrivateKeyForTesting() { + return *PrivateKey::FromPrivateKeyInfo(kRsa2048Private); +} + +PublicKey FixedRsa2048PublicKeyForTesting() { + return *PublicKey::FromSubjectPublicKeyInfo(kRsa2048Public); +} + +PrivateKey FixedRsa4096PrivateKeyForTesting() { + return *PrivateKey::FromPrivateKeyInfo(kRsa4096Private); +} + +PublicKey FixedRsa4096PublicKeyForTesting() { + return *PublicKey::FromSubjectPublicKeyInfo(kRsa4096Public); +} + +} // namespace crypto::test
diff --git a/crypto/test_support.h b/crypto/test_support.h new file mode 100644 index 0000000..7e29810 --- /dev/null +++ b/crypto/test_support.h
@@ -0,0 +1,26 @@ +// 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 CRYPTO_TEST_SUPPORT_H_ +#define CRYPTO_TEST_SUPPORT_H_ + +#include "crypto/keypair.h" + +namespace crypto::test { + +using crypto::keypair::PrivateKey; +using crypto::keypair::PublicKey; + +// These create and return PrivateKey instances that wrap fixed, pre-generated +// private keys for use in tests. Tests should prefer these keys over freshly +// generating keys whenever practical, since they are much cheaper. +PrivateKey FixedRsa2048PrivateKeyForTesting(); +PrivateKey FixedRsa4096PrivateKeyForTesting(); + +PublicKey FixedRsa2048PublicKeyForTesting(); +PublicKey FixedRsa4096PublicKeyForTesting(); + +} // namespace crypto::test + +#endif // CRYPTO_TEST_SUPPORT_H_
diff --git a/device/base/features.cc b/device/base/features.cc index 39218e9..d10a101 100644 --- a/device/base/features.cc +++ b/device/base/features.cc
@@ -42,11 +42,6 @@ BASE_FEATURE(kBluetoothRfcommAndroid, "BluetoothRfcommAndroid", base::FEATURE_DISABLED_BY_DEFAULT); -#else -// Controls whether to enable Web serial. Blink runtime features don't allow -// pure Blink features on a subset of platforms, so we need a separate feature -// for non-Android platforms to keep the Finch switches. -BASE_FEATURE(kSerial, "Serial", base::FEATURE_ENABLED_BY_DEFAULT); #endif // BUILDFLAG(IS_ANDROID) } // namespace features
diff --git a/device/vr/BUILD.gn b/device/vr/BUILD.gn index b05a376..432dc29 100644 --- a/device/vr/BUILD.gn +++ b/device/vr/BUILD.gn
@@ -202,6 +202,8 @@ "openxr/openxr_interaction_profile_paths.h", "openxr/openxr_interaction_profiles.cc", "openxr/openxr_interaction_profiles.h", + "openxr/openxr_layers.cc", + "openxr/openxr_layers.h", "openxr/openxr_light_estimator.cc", "openxr/openxr_light_estimator.h", "openxr/openxr_path_helper.cc",
diff --git a/device/vr/openxr/android/openxr_graphics_binding_open_gles.cc b/device/vr/openxr/android/openxr_graphics_binding_open_gles.cc index 285c984..acf40979 100644 --- a/device/vr/openxr/android/openxr_graphics_binding_open_gles.cc +++ b/device/vr/openxr/android/openxr_graphics_binding_open_gles.cc
@@ -41,7 +41,9 @@ extensions.push_back(XR_KHR_OPENGL_ES_ENABLE_EXTENSION_NAME); } -OpenXrGraphicsBindingOpenGLES::OpenXrGraphicsBindingOpenGLES() = default; +OpenXrGraphicsBindingOpenGLES::OpenXrGraphicsBindingOpenGLES( + const OpenXrExtensionEnumeration* extension_enum) + : OpenXrGraphicsBinding(extension_enum) {} OpenXrGraphicsBindingOpenGLES::~OpenXrGraphicsBindingOpenGLES() { if (back_buffer_fbo_) { glDeleteFramebuffersEXT(1, &back_buffer_fbo_); @@ -199,6 +201,11 @@ return color_swapchain_images_; } +base::span<const SwapChainInfo> +OpenXrGraphicsBindingOpenGLES::GetSwapChainImages() const { + return color_swapchain_images_; +} + bool OpenXrGraphicsBindingOpenGLES::CanUseSharedImages() const { return true; } @@ -401,7 +408,7 @@ return true; } -bool OpenXrGraphicsBindingOpenGLES::ShouldFlipSubmittedImage() { +bool OpenXrGraphicsBindingOpenGLES::ShouldFlipSubmittedImage() const { // WebGPU produces textures that are y-flipped relative to WebGL, which needs // to be accounted for during frame submission. return IsWebGPUSession();
diff --git a/device/vr/openxr/android/openxr_graphics_binding_open_gles.h b/device/vr/openxr/android/openxr_graphics_binding_open_gles.h index 79bfe0e..a384a79 100644 --- a/device/vr/openxr/android/openxr_graphics_binding_open_gles.h +++ b/device/vr/openxr/android/openxr_graphics_binding_open_gles.h
@@ -29,7 +29,8 @@ class DEVICE_VR_EXPORT OpenXrGraphicsBindingOpenGLES : public OpenXrGraphicsBinding { public: - OpenXrGraphicsBindingOpenGLES(); + explicit OpenXrGraphicsBindingOpenGLES( + const OpenXrExtensionEnumeration* extension_enum); ~OpenXrGraphicsBindingOpenGLES() override; // OpenXrGraphicsBinding @@ -40,6 +41,7 @@ const XrSwapchain& color_swapchain) override; void ClearSwapchainImages() override; base::span<SwapChainInfo> GetSwapChainImages() override; + base::span<const SwapChainInfo> GetSwapChainImages() const override; bool CanUseSharedImages() const override; void CreateSharedImages(gpu::SharedImageInterface* sii) override; const SwapChainInfo& GetActiveSwapchainImage() override; @@ -47,7 +49,7 @@ const scoped_refptr<viz::ContextProvider>& context_provider) override; void CleanupWithoutSubmit() override; bool WaitOnFence(gfx::GpuFence& gpu_fence) override; - bool ShouldFlipSubmittedImage() override; + bool ShouldFlipSubmittedImage() const override; void SetOverlayAndWebXrVisibility(bool overlay_visible, bool webxr_visible) override; bool SetOverlayTexture(gfx::GpuMemoryBufferHandle texture,
diff --git a/device/vr/openxr/openxr_api_wrapper.cc b/device/vr/openxr/openxr_api_wrapper.cc index fc03e48..bdc2a2a 100644 --- a/device/vr/openxr/openxr_api_wrapper.cc +++ b/device/vr/openxr/openxr_api_wrapper.cc
@@ -23,6 +23,7 @@ #include "device/vr/openxr/openxr_extension_helper.h" #include "device/vr/openxr/openxr_graphics_binding.h" #include "device/vr/openxr/openxr_input_helper.h" +#include "device/vr/openxr/openxr_layers.h" #include "device/vr/openxr/openxr_stage_bounds_provider.h" #include "device/vr/openxr/openxr_util.h" #include "device/vr/openxr/openxr_view_configuration.h" @@ -1093,7 +1094,7 @@ // Each view configuration has its own layer, which was populated in // GraphicsBinding::PrepareViewConfigForRender. These layers are all put into // XrFrameEndInfo and passed to xrEndFrame. - OpenXrLayers layers(local_space_, blend_mode_, + OpenXrLayers layers(local_space_, blend_mode_, *graphics_binding_, primary_view_config_.ProjectionViews()); // Gather all the layers for active secondary views. @@ -1101,7 +1102,7 @@ for (const auto& secondary_view_config : secondary_view_configs_) { const OpenXrViewConfiguration& view_config = secondary_view_config.second; if (view_config.Active()) { - layers.AddSecondaryLayerForType(view_config.Type(), + layers.AddSecondaryLayerForType(*graphics_binding_, view_config.Type(), view_config.ProjectionViews()); } }
diff --git a/device/vr/openxr/openxr_graphics_binding.cc b/device/vr/openxr/openxr_graphics_binding.cc index 431f3fd..dc4ff3e2 100644 --- a/device/vr/openxr/openxr_graphics_binding.cc +++ b/device/vr/openxr/openxr_graphics_binding.cc
@@ -5,6 +5,7 @@ #include "device/vr/openxr/openxr_graphics_binding.h" #include "components/viz/common/gpu/context_provider.h" +#include "device/vr/openxr/openxr_extension_helper.h" #include "device/vr/openxr/openxr_util.h" #include "device/vr/openxr/openxr_view_configuration.h" #include "gpu/command_buffer/client/shared_image_interface.h" @@ -13,6 +14,11 @@ namespace device { +// static +std::vector<std::string> OpenXrGraphicsBinding::GetOptionalExtensions() { + return {XR_FB_COMPOSITION_LAYER_IMAGE_LAYOUT_EXTENSION_NAME}; +} + #if BUILDFLAG(IS_WIN) SwapChainInfo::SwapChainInfo(ID3D11Texture2D* d3d11_texture) : d3d11_texture(d3d11_texture) {} @@ -41,6 +47,17 @@ #endif } +OpenXrGraphicsBinding::OpenXrGraphicsBinding( + const OpenXrExtensionEnumeration* extension_enum) + : fb_composition_layer_ext_enabled_(extension_enum->ExtensionSupported( + XR_FB_COMPOSITION_LAYER_IMAGE_LAYOUT_EXTENSION_NAME)) { + if (fb_composition_layer_ext_enabled_) { + y_flip_layer_layout_.type = XR_TYPE_COMPOSITION_LAYER_IMAGE_LAYOUT_FB; + y_flip_layer_layout_.flags = + XR_COMPOSITION_LAYER_IMAGE_LAYOUT_VERTICAL_FLIP_BIT_FB; + } +} + void OpenXrGraphicsBinding::PrepareViewConfigForRender( const XrSwapchain& color_swapchain, OpenXrViewConfiguration& view_config) { @@ -77,8 +94,9 @@ // WebGL layers may give us flipped content. We need to instruct OpenXR // to flip the content before showing it to the user. Some XR runtimes // are able to efficiently do this as part of existing post processing - // steps. - if (ShouldFlipSubmittedImage()) { + // steps. However, if we have the composition layer extension enabled, we + // will instruct the runtime to invert the image in a different manner. + if (ShouldFlipSubmittedImage() && !fb_composition_layer_ext_enabled_) { projection_view.subImage.imageRect.offset.y = 0; projection_view.fov.angleUp = -view.fov.angleUp; projection_view.fov.angleDown = -view.fov.angleDown; @@ -86,7 +104,22 @@ } } -bool OpenXrGraphicsBinding::IsUsingSharedImages() { +void OpenXrGraphicsBinding::MaybeFlipLayer( + XrCompositionLayerProjection& layer) const { + // If we don't need to flip the image, then we have nothing to do here. + // If we do need to flip the image and `fb_composition_layer_ext_enabled_` + // is false, we have already flipped the image during + // `PrepareViewConfigForRender`. + if (!ShouldFlipSubmittedImage() || !fb_composition_layer_ext_enabled_) { + return; + } + + CHECK(layer.next == nullptr); + + layer.next = &y_flip_layer_layout_; +} + +bool OpenXrGraphicsBinding::IsUsingSharedImages() const { const auto swapchain_info = GetSwapChainImages(); return ((swapchain_info.size() > 1) && swapchain_info[0].shared_image); }
diff --git a/device/vr/openxr/openxr_graphics_binding.h b/device/vr/openxr/openxr_graphics_binding.h index fda97d5..799b94d4 100644 --- a/device/vr/openxr/openxr_graphics_binding.h +++ b/device/vr/openxr/openxr_graphics_binding.h
@@ -43,6 +43,7 @@ } // namespace viz namespace device { +class OpenXrExtensionEnumeration; class OpenXrViewConfiguration; // TODO(crbug.com/40909689): Refactor this class. @@ -105,6 +106,9 @@ // Gets the set of RequiredExtensions that need to be present on the platform. static void GetRequiredExtensions(std::vector<const char*>& extensions); + // Gets any OptionalExtensions that should be enabled if present. + static std::vector<std::string> GetOptionalExtensions(); + virtual ~OpenXrGraphicsBinding() = default; // Ensures that the GraphicsBinding is ready for use. @@ -128,6 +132,9 @@ // classes. virtual base::span<SwapChainInfo> GetSwapChainImages() = 0; + // Const getter of the above. + virtual base::span<const SwapChainInfo> GetSwapChainImages() const = 0; + // Returns whether or not the platform believes it can support using Shared // buffers/images. virtual bool CanUseSharedImages() const = 0; @@ -164,7 +171,7 @@ // Returns whether or not the current Swapchain is actually using SharedImages // or not. - bool IsUsingSharedImages(); + bool IsUsingSharedImages() const; // Returns the previously set swapchain image size, or 0,0 if one is not set. gfx::Size GetSwapchainImageSize(); @@ -239,14 +246,21 @@ void SetWebGPUSession(bool is_webgpu) { webgpu_session_ = is_webgpu; } bool IsWebGPUSession() const { return webgpu_session_; } + // Append any necessary data to the `layer` object to instruct the runtime to + // flip the layer if necessary. + void MaybeFlipLayer(XrCompositionLayerProjection& layer) const; + protected: + explicit OpenXrGraphicsBinding( + const OpenXrExtensionEnumeration* extension_enum); + // Internal helper to clear the list of images allocated during // `EnumerateSwapchainImages`, since the child classes own the actual list. virtual void ClearSwapchainImages() = 0; // Indicates whether the graphics binding expects the submitted image to need // to be flipped when being submitted to the runtime. - virtual bool ShouldFlipSubmittedImage() = 0; + virtual bool ShouldFlipSubmittedImage() const = 0; // Will be called when SetSwapchainImageSize is called, even if a change is // not made, to allow child classes/concrete implementations to override any @@ -278,6 +292,9 @@ uint32_t active_swapchain_index_; bool has_active_swapchain_image_ = false; bool webgpu_session_ = false; + bool fb_composition_layer_ext_enabled_ = false; + // This will only be valid if `fb_composition_layer_ext_enabled_` is true. + XrCompositionLayerImageLayoutFB y_flip_layer_layout_; }; } // namespace device
diff --git a/device/vr/openxr/openxr_layers.cc b/device/vr/openxr/openxr_layers.cc new file mode 100644 index 0000000..21abb80 --- /dev/null +++ b/device/vr/openxr/openxr_layers.cc
@@ -0,0 +1,62 @@ +// 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 "device/vr/openxr/openxr_layers.h" + +#include "device/vr/openxr/openxr_graphics_binding.h" + +namespace device { +OpenXrLayers::OpenXrLayers(XrSpace space, + XrEnvironmentBlendMode blend_mode, + const OpenXrGraphicsBinding& graphics_binding, + const std::vector<XrCompositionLayerProjectionView>& + primary_projection_views) + : space_(space), blend_mode_(blend_mode) { + InitializeLayer(graphics_binding, primary_projection_views, + primary_projection_layer_); +} + +OpenXrLayers::~OpenXrLayers() = default; + +void OpenXrLayers::AddSecondaryLayerForType( + const OpenXrGraphicsBinding& graphics_binding, + XrViewConfigurationType type, + const std::vector<XrCompositionLayerProjectionView>& projection_views) { + secondary_projection_layers_.emplace_back(); + InitializeLayer(graphics_binding, projection_views, + secondary_projection_layers_.back()); + secondary_composition_layers_.push_back( + reinterpret_cast<XrCompositionLayerBaseHeader*>( + &secondary_projection_layers_.back())); + + secondary_layer_info_.emplace_back(); + XrSecondaryViewConfigurationLayerInfoMSFT& layer_info = + secondary_layer_info_.back(); + layer_info.type = XR_TYPE_SECONDARY_VIEW_CONFIGURATION_LAYER_INFO_MSFT; + layer_info.viewConfigurationType = type; + layer_info.environmentBlendMode = blend_mode_; + layer_info.layerCount = 1; + layer_info.layers = &secondary_composition_layers_.back(); +} + +void OpenXrLayers::InitializeLayer( + const OpenXrGraphicsBinding& graphics_binding, + const std::vector<XrCompositionLayerProjectionView>& projection_views, + XrCompositionLayerProjection& layer) { + layer.type = XR_TYPE_COMPOSITION_LAYER_PROJECTION; + layer.layerFlags = 0; + layer.space = space_; + layer.viewCount = projection_views.size(); + layer.views = projection_views.data(); + + // GraphicsBinding::MaybeFlipLayer may modify `layer.next`. + layer.next = nullptr; + graphics_binding.MaybeFlipLayer(layer); + + if (blend_mode_ == XR_ENVIRONMENT_BLEND_MODE_ALPHA_BLEND) { + layer.layerFlags |= XR_COMPOSITION_LAYER_BLEND_TEXTURE_SOURCE_ALPHA_BIT; + } +} + +} // namespace device
diff --git a/device/vr/openxr/openxr_layers.h b/device/vr/openxr/openxr_layers.h new file mode 100644 index 0000000..9eedc72 --- /dev/null +++ b/device/vr/openxr/openxr_layers.h
@@ -0,0 +1,82 @@ +// Copyright 2025 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef DEVICE_VR_OPENXR_OPENXR_LAYERS_H_ +#define DEVICE_VR_OPENXR_OPENXR_LAYERS_H_ + +#include <vector> + +#include "base/memory/raw_ptr_exclusion.h" +#include "third_party/openxr/src/include/openxr/openxr.h" + +namespace device { +class OpenXrGraphicsBinding; + +// A wrapper around all of the layers to be submitted to a certain frame. Each +// frame creates its own OpenXrLayers object and populates it with all the +// layers of active view configurations. This information is passed into +// xrEndFrame to complete the frame. +class OpenXrLayers { + public: + OpenXrLayers(XrSpace space, + XrEnvironmentBlendMode blend_mode, + const OpenXrGraphicsBinding& graphics_binding, + const std::vector<XrCompositionLayerProjectionView>& + primary_projection_views); + ~OpenXrLayers(); + + void AddSecondaryLayerForType( + const OpenXrGraphicsBinding& graphics_binding, + XrViewConfigurationType type, + const std::vector<XrCompositionLayerProjectionView>& projection_views); + + uint32_t PrimaryLayerCount() const { return 1; } + + const XrCompositionLayerBaseHeader* const* PrimaryLayerData() const { + return &primary_composition_layer_; + } + + uint32_t SecondaryConfigCount() const { return secondary_layer_info_.size(); } + + const XrSecondaryViewConfigurationLayerInfoMSFT* SecondaryConfigData() const { + return secondary_layer_info_.data(); + } + + private: + void InitializeLayer( + const OpenXrGraphicsBinding& graphics_binding, + const std::vector<XrCompositionLayerProjectionView>& projection_views, + XrCompositionLayerProjection& layer); + + XrSpace space_ = XR_NULL_HANDLE; + XrEnvironmentBlendMode blend_mode_ = XR_ENVIRONMENT_BLEND_MODE_MAX_ENUM; + + // In OpenXR, it is possible to have multiple layers, as well as multiple + // types of layers (such as projection and quad layers). We currently only + // support a single projection layer. XrCompositionLayerBaseHeader* is needed + // because xrEndFrame expects an array containing pointers of all the layers. + XrCompositionLayerProjection primary_projection_layer_; + + // RAW_PTR_EXCLUSION: #addr-of (address returned from a function) + RAW_PTR_EXCLUSION XrCompositionLayerBaseHeader* primary_composition_layer_ = + reinterpret_cast<XrCompositionLayerBaseHeader*>( + &primary_projection_layer_); + + // The layers for secondary view configurations. We currently only support a + // single layer per view configuration, so each element in this vector is the + // layer for a specific view configuration. + std::vector<XrCompositionLayerProjection> secondary_projection_layers_; + // Pointers to the corresponding layer in secondary_projection_layers_. + // This field is not vector<raw_ptr<...>> due to interaction with third_party + // api. + RAW_PTR_EXCLUSION std::vector<XrCompositionLayerBaseHeader*> + secondary_composition_layers_; + + // The secondary view configuration layer info containing the data above, + // which is passed to xrEndFrame. + std::vector<XrSecondaryViewConfigurationLayerInfoMSFT> secondary_layer_info_; +}; +} // namespace device + +#endif // DEVICE_VR_OPENXR_OPENXR_LAYERS_H_
diff --git a/device/vr/openxr/openxr_platform_helper.cc b/device/vr/openxr/openxr_platform_helper.cc index 66b7fb8..619fb568 100644 --- a/device/vr/openxr/openxr_platform_helper.cc +++ b/device/vr/openxr/openxr_platform_helper.cc
@@ -146,6 +146,10 @@ factory_extensions.end()); } + for (const auto& extension : OpenXrGraphicsBinding::GetOptionalExtensions()) { + handled_extensions.insert(extension); + } + // Enable the required extensions for any controllers that both we and the // runtime support. for (const auto& interaction_profile :
diff --git a/device/vr/openxr/openxr_view_configuration.cc b/device/vr/openxr/openxr_view_configuration.cc index 8f8e1a9..489c0e4 100644 --- a/device/vr/openxr/openxr_view_configuration.cc +++ b/device/vr/openxr/openxr_view_configuration.cc
@@ -201,48 +201,4 @@ }); } -OpenXrLayers::OpenXrLayers(XrSpace space, - XrEnvironmentBlendMode blend_mode, - const std::vector<XrCompositionLayerProjectionView>& - primary_projection_views) - : space_(space), blend_mode_(blend_mode) { - InitializeLayer(primary_projection_views, primary_projection_layer_); -} - -OpenXrLayers::~OpenXrLayers() = default; - -void OpenXrLayers::AddSecondaryLayerForType( - XrViewConfigurationType type, - const std::vector<XrCompositionLayerProjectionView>& projection_views) { - secondary_projection_layers_.emplace_back(); - InitializeLayer(projection_views, secondary_projection_layers_.back()); - secondary_composition_layers_.push_back( - reinterpret_cast<XrCompositionLayerBaseHeader*>( - &secondary_projection_layers_.back())); - - secondary_layer_info_.emplace_back(); - XrSecondaryViewConfigurationLayerInfoMSFT& layer_info = - secondary_layer_info_.back(); - layer_info.type = XR_TYPE_SECONDARY_VIEW_CONFIGURATION_LAYER_INFO_MSFT; - layer_info.viewConfigurationType = type; - layer_info.environmentBlendMode = blend_mode_; - layer_info.layerCount = 1; - layer_info.layers = &secondary_composition_layers_.back(); -} - -void OpenXrLayers::InitializeLayer( - const std::vector<XrCompositionLayerProjectionView>& projection_views, - XrCompositionLayerProjection& layer) { - layer.type = XR_TYPE_COMPOSITION_LAYER_PROJECTION; - layer.next = nullptr; - layer.layerFlags = 0; - layer.space = space_; - layer.viewCount = projection_views.size(); - layer.views = projection_views.data(); - - if (blend_mode_ == XR_ENVIRONMENT_BLEND_MODE_ALPHA_BLEND) { - layer.layerFlags |= XR_COMPOSITION_LAYER_BLEND_TEXTURE_SOURCE_ALPHA_BIT; - } -} - } // namespace device
diff --git a/device/vr/openxr/openxr_view_configuration.h b/device/vr/openxr/openxr_view_configuration.h index bcf60dd..7d57a9d2 100644 --- a/device/vr/openxr/openxr_view_configuration.h +++ b/device/vr/openxr/openxr_view_configuration.h
@@ -7,7 +7,6 @@ #include <vector> -#include "base/memory/raw_ptr_exclusion.h" #include "device/vr/public/mojom/vr_service.mojom-forward.h" #include "third_party/openxr/src/include/openxr/openxr.h" #include "ui/gfx/geometry/rect.h" @@ -120,75 +119,13 @@ // The viewport is only set when active_ is true. Otherwise, the viewport is // meaningless. This is the viewport of this entire view configuration within // the single OpenXR texture. - gfx::Rect viewport_ = gfx::Rect(); + gfx::Rect viewport_; std::vector<OpenXrViewProperties> properties_; std::vector<XrView> local_from_view_; std::vector<XrCompositionLayerProjectionView> projection_views_; }; -// A wrapper around all of the layers to be submitted to a certain frame. Each -// frame creates its own OpenXrLayers object and populates it with all the -// layers of active view configurations. This information is passed into -// xrEndFrame to complete the frame. -class OpenXrLayers { - public: - OpenXrLayers(XrSpace space, - XrEnvironmentBlendMode blend_mode, - const std::vector<XrCompositionLayerProjectionView>& - primary_projection_views); - ~OpenXrLayers(); - - void AddSecondaryLayerForType( - XrViewConfigurationType type, - const std::vector<XrCompositionLayerProjectionView>& projection_views); - - uint32_t PrimaryLayerCount() const { return 1; } - - const XrCompositionLayerBaseHeader* const* PrimaryLayerData() const { - return &primary_composition_layer_; - } - - uint32_t SecondaryConfigCount() const { return secondary_layer_info_.size(); } - - const XrSecondaryViewConfigurationLayerInfoMSFT* SecondaryConfigData() const { - return secondary_layer_info_.data(); - } - - private: - void InitializeLayer( - const std::vector<XrCompositionLayerProjectionView>& projection_views, - XrCompositionLayerProjection& layer); - - XrSpace space_ = XR_NULL_HANDLE; - XrEnvironmentBlendMode blend_mode_ = XR_ENVIRONMENT_BLEND_MODE_MAX_ENUM; - - // In OpenXR, it is possible to have multiple layers, as well as multiple - // types of layers (such as projection and quad layers). We currently only - // support a single projection layer. XrCompositionLayerBaseHeader* is needed - // because xrEndFrame expects an array containing pointers of all the layers. - XrCompositionLayerProjection primary_projection_layer_; - - // RAW_PTR_EXCLUSION: #addr-of (address returned from a function) - RAW_PTR_EXCLUSION XrCompositionLayerBaseHeader* primary_composition_layer_ = - reinterpret_cast<XrCompositionLayerBaseHeader*>( - &primary_projection_layer_); - - // The layers for secondary view configurations. We currently only support a - // single layer per view configuration, so each element in this vector is the - // layer for a specific view configuration. - std::vector<XrCompositionLayerProjection> secondary_projection_layers_; - // Pointers to the corresponding layer in secondary_projection_layers_. - // This field is not vector<raw_ptr<...>> due to interaction with third_party - // api. - RAW_PTR_EXCLUSION std::vector<XrCompositionLayerBaseHeader*> - secondary_composition_layers_; - - // The secondary view configuration layer info containing the data above, - // which is passed to xrEndFrame. - std::vector<XrSecondaryViewConfigurationLayerInfoMSFT> secondary_layer_info_; -}; - } // namespace device #endif // DEVICE_VR_OPENXR_OPENXR_VIEW_CONFIGURATION_H_
diff --git a/device/vr/openxr/windows/openxr_graphics_binding_d3d11.cc b/device/vr/openxr/windows/openxr_graphics_binding_d3d11.cc index 9912252..39996df69 100644 --- a/device/vr/openxr/windows/openxr_graphics_binding_d3d11.cc +++ b/device/vr/openxr/windows/openxr_graphics_binding_d3d11.cc
@@ -38,7 +38,8 @@ OpenXrGraphicsBindingD3D11::OpenXrGraphicsBindingD3D11( base::WeakPtr<OpenXrPlatformHelperWindows> weak_platform_helper) - : texture_helper_(std::make_unique<D3D11TextureHelper>()), + : OpenXrGraphicsBinding(weak_platform_helper->GetExtensionEnumeration()), + texture_helper_(std::make_unique<D3D11TextureHelper>()), weak_platform_helper_(weak_platform_helper) {} OpenXrGraphicsBindingD3D11::~OpenXrGraphicsBindingD3D11() = default; @@ -126,6 +127,11 @@ return color_swapchain_images_; } +base::span<const SwapChainInfo> OpenXrGraphicsBindingD3D11::GetSwapChainImages() + const { + return color_swapchain_images_; +} + bool OpenXrGraphicsBindingD3D11::CanUseSharedImages() const { // Put shared image feature behind a flag until remaining issues with overlays // are resolved. WebGPU sessions always use SharedImages. @@ -338,7 +344,7 @@ texture_helper_->CleanupNoSubmit(); } -bool OpenXrGraphicsBindingD3D11::ShouldFlipSubmittedImage() { +bool OpenXrGraphicsBindingD3D11::ShouldFlipSubmittedImage() const { return IsUsingSharedImages() && !IsWebGPUSession(); }
diff --git a/device/vr/openxr/windows/openxr_graphics_binding_d3d11.h b/device/vr/openxr/windows/openxr_graphics_binding_d3d11.h index 5699d9d..f2834e47 100644 --- a/device/vr/openxr/windows/openxr_graphics_binding_d3d11.h +++ b/device/vr/openxr/windows/openxr_graphics_binding_d3d11.h
@@ -34,6 +34,7 @@ const XrSwapchain& color_swapchain) override; void ClearSwapchainImages() override; base::span<SwapChainInfo> GetSwapChainImages() override; + base::span<const SwapChainInfo> GetSwapChainImages() const override; bool CanUseSharedImages() const override; void CreateSharedImages(gpu::SharedImageInterface* sii) override; const SwapChainInfo& GetActiveSwapchainImage() override; @@ -41,7 +42,7 @@ bool Render( const scoped_refptr<viz::ContextProvider>& context_provider) override; void CleanupWithoutSubmit() override; - bool ShouldFlipSubmittedImage() override; + bool ShouldFlipSubmittedImage() const override; void SetOverlayAndWebXrVisibility(bool overlay_visible, bool webxr_visible) override; void SetWebXrTexture(mojo::PlatformHandle texture_handle,
diff --git a/docs/speed/perf_lab_platforms.md b/docs/speed/perf_lab_platforms.md index a815249..c954268 100644 --- a/docs/speed/perf_lab_platforms.md +++ b/docs/speed/perf_lab_platforms.md
@@ -18,6 +18,9 @@ * [android-pixel6-perf](https://ci.chromium.org/p/chrome/builders/ci/android-pixel6-perf): Android U. * [android-pixel6-perf-pgo](https://ci.chromium.org/p/chrome/builders/ci/android-pixel6-perf-pgo): Android U. * [android-pixel6-pro-perf](https://ci.chromium.org/p/chrome/builders/ci/android-pixel6-pro-perf): Android T. + * [android-pixel9-perf](https://ci.chromium.org/p/chrome/builders/ci/android-pixel9-perf): Android B. + * [android-pixel9-pro-perf](https://ci.chromium.org/p/chrome/builders/ci/android-pixel9-pro-perf): Android B. + * [android-pixel9-pro-xl-perf](https://ci.chromium.org/p/chrome/builders/ci/android-pixel9-pro-xl-perf): Android B. ### Linux
diff --git a/extensions/browser/content_verifier/content_verifier_unittest.cc b/extensions/browser/content_verifier/content_verifier_unittest.cc index 67e86a1..5e7116f 100644 --- a/extensions/browser/content_verifier/content_verifier_unittest.cc +++ b/extensions/browser/content_verifier/content_verifier_unittest.cc
@@ -37,9 +37,9 @@ kBackgroundPage, }; -base::FilePath kBackgroundScriptPath(FILE_PATH_LITERAL("foo/bg.txt")); +base::FilePath kBackgroundScriptPath(FILE_PATH_LITERAL("foo/bg.js")); base::FilePath kContentScriptPath(FILE_PATH_LITERAL("foo/content.js")); -base::FilePath kBackgroundPagePath(FILE_PATH_LITERAL("foo/page.txt")); +base::FilePath kBackgroundPagePath(FILE_PATH_LITERAL("foo/page.html")); base::FilePath kScriptFilePath(FILE_PATH_LITERAL("bar/code.js")); base::FilePath kUnknownTypeFilePath(FILE_PATH_LITERAL("bar/code.txt")); base::FilePath kHTMLFilePath(FILE_PATH_LITERAL("bar/page.html")); @@ -188,12 +188,13 @@ if (background_manifest_type_ == BackgroundManifestType::kBackgroundScript) { base::Value::List background_scripts; - background_scripts.Append("foo/bg.txt"); + background_scripts.Append(kBackgroundScriptPath.AsUTF8Unsafe()); manifest.SetByDottedPath(manifest_keys::kBackgroundScripts, std::move(background_scripts)); } else if (background_manifest_type_ == BackgroundManifestType::kBackgroundPage) { - manifest.SetByDottedPath(manifest_keys::kBackgroundPage, "foo/page.txt"); + manifest.SetByDottedPath(manifest_keys::kBackgroundPage, + kBackgroundPagePath.AsUTF8Unsafe()); } base::Value::List content_scripts; @@ -242,23 +243,11 @@ // some file paths even if those paths are specified as browser images. TEST_P(ContentVerifierTestWithBackgroundType, BrowserImagesShouldBeVerified) { std::vector<base::FilePath> files_to_be_verified = { - kContentScriptPath, kScriptFilePath, kHTMLFilePath, kHTMFilePath}; + kContentScriptPath, kScriptFilePath, kHTMLFilePath, + kHTMFilePath, kBackgroundScriptPath, kBackgroundPagePath}; std::vector<base::FilePath> files_not_to_be_verified{kIconPath, kUnknownTypeFilePath}; - if (GetBackgroundManifestType() == - BackgroundManifestType::kBackgroundScript) { - files_to_be_verified.push_back(kBackgroundScriptPath); - files_not_to_be_verified.push_back(kBackgroundPagePath); - } else if (GetBackgroundManifestType() == - BackgroundManifestType::kBackgroundPage) { - files_to_be_verified.push_back(kBackgroundPagePath); - files_not_to_be_verified.push_back(kBackgroundScriptPath); - } else { - files_not_to_be_verified.push_back(kBackgroundScriptPath); - files_not_to_be_verified.push_back(kBackgroundPagePath); - } - auto generate_test_cases = [](const std::vector<base::FilePath>& input) { std::set<base::FilePath> output; for (const auto& path : input) {
diff --git a/extensions/browser/extension_protocols.cc b/extensions/browser/extension_protocols.cc index f78f905..375e759 100644 --- a/extensions/browser/extension_protocols.cc +++ b/extensions/browser/extension_protocols.cc
@@ -101,7 +101,6 @@ #include "services/network/public/mojom/early_hints.mojom.h" #include "services/network/public/mojom/fetch_api.mojom.h" #include "services/network/public/mojom/url_response_head.mojom.h" -#include "third_party/blink/public/common/features.h" #include "third_party/blink/public/common/loader/resource_type_util.h" #include "url/origin.h" #include "url/url_util.h" @@ -220,14 +219,13 @@ // Frame navigations to extensions have already been checked in // the ExtensionNavigationThrottle. - // Dedicated Worker (with PlzDedicatedWorker) and Shared Worker main scripts - // can be loaded with extension URLs in browser process. - // Service Worker and the imported scripts can be loaded with extension URLs - // in browser process when PlzServiceWorker is enabled or during update check. + // Dedicated Worker and Shared Worker main scripts can be loaded with + // extension URLs in browser process. Service Worker and the imported scripts + // can be loaded with extension URLs in browser process when PlzServiceWorker + // is enabled or during update check. if (child_id == content::ChildProcessHost::kInvalidUniqueID && (blink::IsRequestDestinationFrame(destination) || - (base::FeatureList::IsEnabled(blink::features::kPlzDedicatedWorker) && - destination == network::mojom::RequestDestination::kWorker) || + destination == network::mojom::RequestDestination::kWorker || destination == network::mojom::RequestDestination::kSharedWorker || destination == network::mojom::RequestDestination::kScript || destination == network::mojom::RequestDestination::kServiceWorker)) {
diff --git a/extensions/common/manifest_constants.h b/extensions/common/manifest_constants.h index 47aefafb..6bcfc0e 100644 --- a/extensions/common/manifest_constants.h +++ b/extensions/common/manifest_constants.h
@@ -301,6 +301,10 @@ "Invalid value for 'background.scripts[*]'."; inline constexpr char16_t kInvalidBackgroundScripts[] = u"Invalid value for 'background.scripts'."; +inline constexpr char kInvalidBackgroundScriptMimeType[] = + "Invalid background script mime type for 'background.scripts[*]', a " + "background script can only be loaded from supported JavaScript files such " + "as .js files."; inline constexpr char16_t kInvalidBackgroundServiceWorkerScript[] = u"Invalid value for 'background.service_worker'."; inline constexpr char16_t kInvalidBackgroundServiceWorkerType[] =
diff --git a/extensions/common/manifest_handlers/background_info.cc b/extensions/common/manifest_handlers/background_info.cc index 43c62b2..a7df151 100644 --- a/extensions/common/manifest_handlers/background_info.cc +++ b/extensions/common/manifest_handlers/background_info.cc
@@ -9,6 +9,7 @@ #include <memory> #include "base/command_line.h" +#include "base/files/file_path.h" #include "base/files/file_util.h" #include "base/lazy_instance.h" #include "base/strings/string_number_conversions.h" @@ -21,6 +22,8 @@ #include "extensions/common/permissions/api_permission_set.h" #include "extensions/common/switches.h" #include "extensions/strings/grit/extensions_strings.h" +#include "net/base/mime_util.h" +#include "third_party/blink/public/common/mime_util/mime_util.h" #include "ui/base/l10n/l10n_util.h" using extensions::mojom::APIPermissionID; @@ -33,6 +36,10 @@ namespace { +BASE_FEATURE(kValidateBackgroundScriptMimeType, + "ValidateBackgroundScriptMimeType", + base::FEATURE_ENABLED_BY_DEFAULT); + const char kBackground[] = "background"; static base::LazyInstance<BackgroundInfo>::DestructorAtExit @@ -118,7 +125,7 @@ .background_service_worker_script_.has_value(); } -bool BackgroundInfo::Parse(const Extension* extension, std::u16string* error) { +bool BackgroundInfo::Parse(Extension* extension, std::u16string* error) { const std::string& bg_scripts_key = extension->is_platform_app() ? keys::kPlatformAppBackgroundScripts : keys::kBackgroundScripts; if (!LoadBackgroundScripts(extension, bg_scripts_key, error) || @@ -141,7 +148,7 @@ return true; } -bool BackgroundInfo::LoadBackgroundScripts(const Extension* extension, +bool BackgroundInfo::LoadBackgroundScripts(Extension* extension, const std::string& key, std::u16string* error) { const base::Value* background_scripts_value = @@ -164,7 +171,28 @@ errors::kInvalidBackgroundScript, base::NumberToString(i)); return false; } - background_scripts_.push_back(background_scripts[i].GetString()); + + const std::string& background_script = background_scripts[i].GetString(); + + std::string mime_type; + // TODO(https://crbug.com/40059598): Remove this if-check and always + // validate the mime type in M139. + if (base::FeatureList::IsEnabled(kValidateBackgroundScriptMimeType) && + (!net::GetWellKnownMimeTypeFromFile( + base::FilePath::FromUTF8Unsafe(background_script), &mime_type) || + !blink::IsSupportedJavascriptMimeType(mime_type))) { + // Issue a warning and ignore this file. This is a warning and not a + // hard-error to preserve both backwards compatibility and potential + // future-compatibility if mime types change. + extension->AddInstallWarning( + InstallWarning(ErrorUtils::FormatErrorMessage( + errors::kInvalidBackgroundScriptMimeType, + base::NumberToString(i)), + key)); + continue; + } + + background_scripts_.push_back(background_script); } return true;
diff --git a/extensions/common/manifest_handlers/background_info.h b/extensions/common/manifest_handlers/background_info.h index c3c348ce..d5b0583a 100644 --- a/extensions/common/manifest_handlers/background_info.h +++ b/extensions/common/manifest_handlers/background_info.h
@@ -58,10 +58,10 @@ return has_background_page() && !is_persistent_; } - bool Parse(const Extension* extension, std::u16string* error); + bool Parse(Extension* extension, std::u16string* error); private: - bool LoadBackgroundScripts(const Extension* extension, + bool LoadBackgroundScripts(Extension* extension, const std::string& key, std::u16string* error); bool LoadBackgroundPage(const Extension* extension,
diff --git a/gpu/command_buffer/service/shared_context_state.cc b/gpu/command_buffer/service/shared_context_state.cc index a995c01a..b6347ed 100644 --- a/gpu/command_buffer/service/shared_context_state.cc +++ b/gpu/command_buffer/service/shared_context_state.cc
@@ -105,23 +105,42 @@ #endif } +void ReportPrecompilationStats( + std::unique_ptr<skgpu::graphite::PrecompileContext> precompileContext) { + precompileContext->reportPipelineStats(); +} + void InitiatePrecompilation(skgpu::graphite::Context* context) { constexpr base::TaskTraits precompile_traits = { base::TaskPriority::BEST_EFFORT, base::TaskShutdownBehavior::SKIP_ON_SHUTDOWN}; - std::unique_ptr<skgpu::graphite::PrecompileContext> precompileContext = - context->makePrecompileContext(); + { + std::unique_ptr<skgpu::graphite::PrecompileContext> precompileContext = + context->makePrecompileContext(); - // TODO: crbug.com/358074434 - need to determine the actual delay or initiate - // precompilation at first idle - constexpr base::TimeDelta precompile_wait = base::Seconds(1); + // TODO: crbug.com/358074434 - need to determine the actual delay or + // initiate precompilation at first idle + constexpr base::TimeDelta precompile_wait = base::Seconds(1); - base::ThreadPool::PostDelayedTask( - FROM_HERE, precompile_traits, - base::BindOnce(&GraphitePerformPrecompilation, - std::move(precompileContext)), - precompile_wait); + base::ThreadPool::PostDelayedTask( + FROM_HERE, precompile_traits, + base::BindOnce(&GraphitePerformPrecompilation, + std::move(precompileContext)), + precompile_wait); + } + + { + std::unique_ptr<skgpu::graphite::PrecompileContext> precompileContext = + context->makePrecompileContext(); + + // After thirty minutes, report UMA statistics re Precompile Pipeline usage + base::ThreadPool::PostDelayedTask( + FROM_HERE, precompile_traits, + base::BindOnce(&ReportPrecompilationStats, + std::move(precompileContext)), + base::Minutes(30)); + } } // Creates a Graphite recorder, supplying it with a GraphiteImageProvider.
diff --git a/gpu/command_buffer/service/shared_image/iosurface_image_backing.h b/gpu/command_buffer/service/shared_image/iosurface_image_backing.h index 71eaa23..c30a65eb 100644 --- a/gpu/command_buffer/service/shared_image/iosurface_image_backing.h +++ b/gpu/command_buffer/service/shared_image/iosurface_image_backing.h
@@ -82,8 +82,8 @@ // The display for this GL representation. const EGLDisplay egl_display_; - scoped_refptr<gl::GLContext> context_; - scoped_refptr<gl::GLSurface> surface_; + const scoped_refptr<gl::GLContext> context_; + const scoped_refptr<gl::GLSurface> surface_; // The GL (not EGL) target to which this texture is to be bound. const GLuint gl_target_; @@ -101,7 +101,7 @@ }; class GPU_GLES2_EXPORT IOSurfaceImageBacking - : public SharedImageBacking, + : public ClearTrackingSharedImageBacking, public IOSurfaceBackingEGLState::Client { public: IOSurfaceImageBacking( @@ -130,15 +130,16 @@ bool InitializePixels(base::span<const uint8_t> pixel_data); - void AddWGPUDeviceWithPendingCommands(wgpu::Device device); - void WaitForDawnCommandsToBeScheduled(const wgpu::Device& device_to_exclude); + void AddWGPUDeviceWithPendingCommands(wgpu::Device device) + EXCLUSIVE_LOCKS_REQUIRED(lock_); + void WaitForDawnCommandsToBeScheduled(const wgpu::Device& device_to_exclude) + EXCLUSIVE_LOCKS_REQUIRED(lock_); - void AddEGLDisplayWithPendingCommands(gl::GLDisplayEGL* display); - void WaitForANGLECommandsToBeScheduled(); - void ClearEGLDisplaysWithPendingCommands(gl::GLDisplayEGL* display_to_keep); - - std::unique_ptr<gfx::GpuFence> GetLastWriteGpuFence(); - void SetReleaseFence(gfx::GpuFenceHandle release_fence); + void AddEGLDisplayWithPendingCommands(gl::GLDisplayEGL* display) + EXCLUSIVE_LOCKS_REQUIRED(lock_); + void WaitForANGLECommandsToBeScheduled() EXCLUSIVE_LOCKS_REQUIRED(lock_); + void ClearEGLDisplaysWithPendingCommands(gl::GLDisplayEGL* display_to_keep) + EXCLUSIVE_LOCKS_REQUIRED(lock_); private: class GLTextureIRepresentation; @@ -155,8 +156,7 @@ base::trace_event::ProcessMemoryDump* pmd, uint64_t client_tracing_id) override; SharedImageBackingType GetType() const override; - gfx::Rect ClearedRect() const final; - void SetClearedRect(const gfx::Rect& cleared_rect) final; + std::unique_ptr<GLTextureImageRepresentation> ProduceGLTexture( SharedImageManager* manager, MemoryTypeTracker* tracker) final; @@ -188,39 +188,47 @@ // IOSurfaceBackingEGLState::Client: bool IOSurfaceBackingEGLStateBeginAccess(IOSurfaceBackingEGLState* egl_state, - bool readonly) override; + bool readonly) + EXCLUSIVE_LOCKS_REQUIRED(lock_) override; void IOSurfaceBackingEGLStateEndAccess(IOSurfaceBackingEGLState* egl_state, - bool readonly) override; - void IOSurfaceBackingEGLStateBeingCreated( - IOSurfaceBackingEGLState* egl_state) override; + bool readonly) + EXCLUSIVE_LOCKS_REQUIRED(lock_) override; + void IOSurfaceBackingEGLStateBeingCreated(IOSurfaceBackingEGLState* egl_state) + EXCLUSIVE_LOCKS_REQUIRED(lock_) override; void IOSurfaceBackingEGLStateBeingDestroyed( IOSurfaceBackingEGLState* egl_state, - bool have_context) override; + bool have_context) EXCLUSIVE_LOCKS_REQUIRED(lock_) override; // Updates the read and write accesses tracker variables on BeginAccess. - bool BeginAccess(bool readonly); + bool BeginAccess(bool readonly) EXCLUSIVE_LOCKS_REQUIRED(lock_); // Updates the read and write accesses tracker variables on EndAccess. - void EndAccess(bool readonly); + void EndAccess(bool readonly) EXCLUSIVE_LOCKS_REQUIRED(lock_); void AddSharedEventForEndAccess(id<MTLSharedEvent> shared_event, uint64_t signal_value, - bool readonly); + bool readonly) + EXCLUSIVE_LOCKS_REQUIRED(lock_); template <typename Fn> - void ProcessSharedEventsForBeginAccess(bool readonly, const Fn& fn); + void ProcessSharedEventsForBeginAccess(bool readonly, const Fn& fn) + EXCLUSIVE_LOCKS_REQUIRED(lock_); + // Guarded by ScopedIOSurfaceLock instead of |lock_| for memory access. const gfx::ScopedIOSurface io_surface_; + const gfx::Size io_surface_size_; const uint32_t io_surface_format_; const gfx::GenericSharedMemoryId io_surface_id_; // DawnSharedTextureCache that keeps an internal cache of per-device // SharedTextureData that vends WebGPU textures for the underlying IOSurface. - scoped_refptr<DawnSharedTextureCache> dawn_texture_cache_; + scoped_refptr<DawnSharedTextureCache> dawn_texture_cache_ GUARDED_BY(lock_); - const scoped_refptr<DawnSharedTextureCache>& GetDawnTextureCache(); + const scoped_refptr<DawnSharedTextureCache>& GetDawnTextureCache() + EXCLUSIVE_LOCKS_REQUIRED(lock_); // Tracks the number of currently-ongoing accesses to a given WGPU texture. - base::flat_map<WGPUTexture, int> wgpu_texture_ongoing_accesses_; + base::flat_map<WGPUTexture, int> wgpu_texture_ongoing_accesses_ + GUARDED_BY(lock_); // Tracks the devices to invoke waitUntilScheduled. // TODO(dawn:2453): The below comparator should be implemented in @@ -230,49 +238,54 @@ return lhs.Get() < rhs.Get(); } }; - base::flat_set<wgpu::Device, WGPUDeviceCompare> wgpu_devices_pending_flush_; + base::flat_set<wgpu::Device, WGPUDeviceCompare> wgpu_devices_pending_flush_ + GUARDED_BY(lock_); // Returns the number of ongoing accesses that were already present on this // texture prior to beginning this access. - int TrackBeginAccessToWGPUTexture(wgpu::Texture texture); + int TrackBeginAccessToWGPUTexture(wgpu::Texture texture) + EXCLUSIVE_LOCKS_REQUIRED(lock_); // Returns the number of ongoing accesses that will still be present on this // texture after ending this access. - int TrackEndAccessToWGPUTexture(wgpu::Texture texture); + int TrackEndAccessToWGPUTexture(wgpu::Texture texture) + EXCLUSIVE_LOCKS_REQUIRED(lock_); const GLenum gl_target_; const bool framebuffer_attachment_angle_; // Used to determine whether to release the texture in EndAccess() in use // cases that need to ensure IOSurface synchronization. - uint num_ongoing_read_accesses_ = 0; + uint num_ongoing_read_accesses_ GUARDED_BY(lock_) = 0; // Used with the above variable to catch cases where clients are performing // disallowed concurrent read/write accesses. - bool ongoing_write_access_ = false; + bool ongoing_write_access_ GUARDED_BY(lock_) = false; - scoped_refptr<IOSurfaceBackingEGLState> RetainGLTexture(); - void ReleaseGLTexture(IOSurfaceBackingEGLState* egl_state, bool have_context); - - // This is the cleared rect used by ClearedRect and SetClearedRect when - // |texture_| is nullptr. - gfx::Rect cleared_rect_; + scoped_refptr<IOSurfaceBackingEGLState> RetainGLTexture() + EXCLUSIVE_LOCKS_REQUIRED(lock_); + void ReleaseGLTexture(IOSurfaceBackingEGLState* egl_state, bool have_context) + EXCLUSIVE_LOCKS_REQUIRED(lock_); // Whether or not the surface is currently purgeable. - bool purgeable_ = false; + bool purgeable_ GUARDED_BY(lock_) = false; // This map tracks all IOSurfaceBackingEGLState instances that exist. - base::flat_map<EGLDisplay, IOSurfaceBackingEGLState*> egl_state_map_; + base::flat_map<EGLDisplay, IOSurfaceBackingEGLState*> egl_state_map_ + GUARDED_BY(lock_); // GrContextType for SharedContextState used to distinguish between Ganesh // and Graphite. - GrContextType gr_context_type_; + const GrContextType gr_context_type_; // If Skia is using GL, this object creates a GL texture at construction time // for the Skia GL context and reuses it (for that context) for its lifetime. - scoped_refptr<IOSurfaceBackingEGLState> egl_state_for_skia_gl_context_; + // This egl_state is set in IOSurfaceImageBacking Ctor only. + scoped_refptr<IOSurfaceBackingEGLState> egl_state_for_skia_gl_context_ + GUARDED_BY(lock_); // Tracks the displays to invoke eglWaitUntilWorkScheduledANGLE(). - base::flat_set<gl::GLDisplayEGL*> egl_displays_pending_flush_; + base::flat_set<gl::GLDisplayEGL*> egl_displays_pending_flush_ + GUARDED_BY(lock_); using ScopedSharedEvent = base::apple::scoped_nsprotocol<id<MTLSharedEvent>>; struct SharedEventCompare { @@ -284,9 +297,9 @@ using SharedEventMap = base::flat_map<ScopedSharedEvent, uint64_t, SharedEventCompare>; // Shared events and signals for exclusive accesses. - SharedEventMap exclusive_shared_events_; + SharedEventMap exclusive_shared_events_ GUARDED_BY(lock_); // Shared events and signals for non-exclusive accesses. - SharedEventMap non_exclusive_shared_events_; + SharedEventMap non_exclusive_shared_events_ GUARDED_BY(lock_); base::WeakPtrFactory<IOSurfaceImageBacking> weak_factory_; };
diff --git a/gpu/command_buffer/service/shared_image/iosurface_image_backing.mm b/gpu/command_buffer/service/shared_image/iosurface_image_backing.mm index 4a5e90a..226a31b 100644 --- a/gpu/command_buffer/service/shared_image/iosurface_image_backing.mm +++ b/gpu/command_buffer/service/shared_image/iosurface_image_backing.mm
@@ -291,6 +291,7 @@ bool BeginAccess(GLenum mode) override { DCHECK(mode_ == 0); + AutoLock auto_lock(backing()); mode_ = mode; bool readonly = mode_ != GL_SHARED_IMAGE_ACCESS_MODE_READWRITE_CHROMIUM; return egl_state_->BeginAccess(readonly); @@ -298,6 +299,7 @@ void EndAccess() override { DCHECK(mode_ != 0); + AutoLock auto_lock(backing()); GLenum current_mode = mode_; mode_ = 0; egl_state_->EndAccess(current_mode != @@ -323,9 +325,6 @@ MemoryTypeTracker* tracker); ~SkiaGaneshRepresentation() override; - void SetBeginReadAccessCallback( - base::RepeatingClosure begin_read_access_callback); - private: // SkiaGaneshImageRepresentation: std::vector<sk_sp<SkSurface>> BeginWriteAccess( @@ -350,7 +349,7 @@ void CheckContext(); scoped_refptr<IOSurfaceBackingEGLState> egl_state_; - scoped_refptr<SharedContextState> context_state_; + const scoped_refptr<SharedContextState> context_state_; std::vector<sk_sp<GrPromiseImageTexture>> promise_textures_; std::vector<sk_sp<SkSurface>> write_surfaces_; #if DCHECK_IS_ON() @@ -400,7 +399,9 @@ std::vector<GrBackendSemaphore>* begin_semaphores, std::vector<GrBackendSemaphore>* end_semaphores, std::unique_ptr<skgpu::MutableTextureState>* end_state) { + AutoLock auto_lock(backing()); CheckContext(); + if (egl_state_) { DCHECK(context_state_->GrContextIsGL()); if (!egl_state_->BeginAccess(/*readonly=*/false)) { @@ -449,7 +450,9 @@ std::vector<GrBackendSemaphore>* begin_semaphores, std::vector<GrBackendSemaphore>* end_semaphores, std::unique_ptr<skgpu::MutableTextureState>* end_state) { + AutoLock auto_lock(backing()); CheckContext(); + if (egl_state_) { DCHECK(context_state_->GrContextIsGL()); if (!egl_state_->BeginAccess(/*readonly=*/false)) { @@ -463,6 +466,7 @@ } void IOSurfaceImageBacking::SkiaGaneshRepresentation::EndWriteAccess() { + AutoLock auto_lock(backing()); #if DCHECK_IS_ON() for (auto& surface : write_surfaces_) { DCHECK(surface->unique()); @@ -481,7 +485,9 @@ std::vector<GrBackendSemaphore>* begin_semaphores, std::vector<GrBackendSemaphore>* end_semaphores, std::unique_ptr<skgpu::MutableTextureState>* end_state) { + AutoLock auto_lock(backing()); CheckContext(); + if (egl_state_) { DCHECK(context_state_->GrContextIsGL()); if (!egl_state_->BeginAccess(/*readonly=*/true)) { @@ -495,8 +501,11 @@ } void IOSurfaceImageBacking::SkiaGaneshRepresentation::EndReadAccess() { - if (egl_state_) + AutoLock auto_lock(backing()); + + if (egl_state_) { egl_state_->EndAccess(/*readonly=*/true); + } } bool IOSurfaceImageBacking::SkiaGaneshRepresentation:: @@ -562,6 +571,8 @@ IOSurfaceImageBacking::SkiaGraphiteMetalRepresentation::BeginWriteAccess( const SkSurfaceProps& surface_props, const gfx::Rect& update_rect) { + AutoLock auto_lock(backing_impl()); + if (!write_surfaces_.empty()) { // Write access is already in progress. return {}; @@ -594,6 +605,8 @@ std::vector<scoped_refptr<GraphiteTextureHolder>> IOSurfaceImageBacking::SkiaGraphiteMetalRepresentation::BeginWriteAccess() { + AutoLock auto_lock(backing_impl()); + if (!backing_impl()->BeginAccess(/*readonly=*/false)) { return {}; } @@ -601,6 +614,7 @@ } void IOSurfaceImageBacking::SkiaGraphiteMetalRepresentation::EndWriteAccess() { + AutoLock auto_lock(backing_impl()); #if DCHECK_IS_ON() for (auto& surface : write_surfaces_) { DCHECK(surface->unique()); @@ -612,6 +626,7 @@ std::vector<scoped_refptr<GraphiteTextureHolder>> IOSurfaceImageBacking::SkiaGraphiteMetalRepresentation::BeginReadAccess() { + AutoLock auto_lock(backing_impl()); if (!backing_impl()->BeginAccess(/*readonly=*/true)) { return {}; } @@ -619,6 +634,7 @@ } void IOSurfaceImageBacking::SkiaGraphiteMetalRepresentation::EndReadAccess() { + AutoLock auto_lock(backing_impl()); backing_impl()->EndAccess(/*readonly=*/true); } #endif @@ -649,6 +665,7 @@ bool IOSurfaceImageBacking::OverlayRepresentation::BeginReadAccess( gfx::GpuFenceHandle& acquire_fence) { auto* iosurface_backing = static_cast<IOSurfaceImageBacking*>(backing()); + AutoLock auto_lock(iosurface_backing); if (!iosurface_backing->BeginAccess(/*readonly=*/true)) { return false; @@ -664,11 +681,10 @@ gl::GLContext* context = gl::GLContext::GetCurrent(); if (context) { - const auto& signals = static_cast<IOSurfaceImageBacking*>(backing()) - ->exclusive_shared_events_; std::vector<std::unique_ptr<BackpressureMetalSharedEvent>> backpressure_events; - for (const auto& [shared_event, signaled_value] : signals) { + for (const auto& [shared_event, signaled_value] : + iosurface_backing->exclusive_shared_events_) { backpressure_events.push_back( std::make_unique<BackpressureMetalSharedEventImpl>(shared_event, signaled_value)); @@ -682,8 +698,11 @@ void IOSurfaceImageBacking::OverlayRepresentation::EndReadAccess( gfx::GpuFenceHandle release_fence) { + auto* iosurface_backing = static_cast<IOSurfaceImageBacking*>(backing()); + AutoLock auto_lock(iosurface_backing); DCHECK(release_fence.is_null()); - static_cast<IOSurfaceImageBacking*>(backing())->EndAccess(/*readonly=*/true); + + iosurface_backing->EndAccess(/*readonly=*/true); } gfx::ScopedIOSurface @@ -752,11 +771,13 @@ wgpu::Texture IOSurfaceImageBacking::DawnRepresentation::BeginAccess( wgpu::TextureUsage wgpu_texture_usage, wgpu::TextureUsage internal_usage) { + IOSurfaceImageBacking* iosurface_backing = + static_cast<IOSurfaceImageBacking*>(backing()); + AutoLock auto_lock(iosurface_backing); + const bool readonly = (wgpu_texture_usage & ~kReadOnlyUsage) == 0 && (internal_usage & ~kReadOnlyUsage) == 0; - IOSurfaceImageBacking* iosurface_backing = - static_cast<IOSurfaceImageBacking*>(backing()); if (!iosurface_backing->BeginAccess(readonly)) { return {}; } @@ -797,12 +818,13 @@ return texture_; } + bool is_cleared = iosurface_backing->IsClearedInternal(); wgpu::SharedTextureMemoryBeginAccessDescriptor begin_access_desc = {}; - begin_access_desc.initialized = IsCleared(); + begin_access_desc.initialized = is_cleared; // NOTE: WebGPU allows reads of uncleared textures, in which case Dawn clears // the texture on its initial access. Such reads must take exclusive access. - begin_access_desc.concurrentRead = readonly && IsCleared(); + begin_access_desc.concurrentRead = readonly && is_cleared; std::vector<wgpu::SharedFence> shared_fences; std::vector<uint64_t> signaled_values; @@ -852,6 +874,10 @@ } void IOSurfaceImageBacking::DawnRepresentation::EndAccess() { + IOSurfaceImageBacking* iosurface_backing = + static_cast<IOSurfaceImageBacking*>(backing()); + AutoLock auto_lock(iosurface_backing); + if (!texture_) { // The only valid cases in which this could occur are (a) if // SharedTextureMemory::BeginAccess() failed, in which case we already @@ -864,10 +890,9 @@ // Inform the backing that an access has ended so that it can properly update // its state tracking. - IOSurfaceImageBacking* iosurface_backing = - static_cast<IOSurfaceImageBacking*>(backing()); const bool readonly = (usage_ & ~kReadOnlyUsage) == 0 && (internal_usage_ & ~kReadOnlyUsage) == 0; + iosurface_backing->EndAccess(readonly); int num_outstanding_accesses = iosurface_backing->TrackEndAccessToWGPUTexture(texture_); @@ -887,7 +912,7 @@ wgpu::Status::Success); if (end_access_desc.initialized) { - SetCleared(); + iosurface_backing->SetClearedInternal(); } // Not possible to reach this with any other type of backing. @@ -984,17 +1009,17 @@ bool is_thread_safe, GrContextType gr_context_type, std::optional<gfx::BufferUsage> buffer_usage) - : SharedImageBacking(mailbox, - format, - size, - color_space, - surface_origin, - alpha_type, - usage, - std::move(debug_label), - format.EstimatedSizeInBytes(size), - is_thread_safe, - std::move(buffer_usage)), + : ClearTrackingSharedImageBacking(mailbox, + format, + size, + color_space, + surface_origin, + alpha_type, + usage, + std::move(debug_label), + format.EstimatedSizeInBytes(size), + is_thread_safe, + std::move(buffer_usage)), io_surface_(std::move(io_surface)), io_surface_size_(IOSurfaceGetWidth(io_surface_.get()), IOSurfaceGetHeight(io_surface_.get())), @@ -1003,7 +1028,6 @@ dawn_texture_cache_(base::MakeRefCounted<DawnSharedTextureCache>()), gl_target_(gl_target), framebuffer_attachment_angle_(framebuffer_attachment_angle), - cleared_rect_(is_cleared ? gfx::Rect(size) : gfx::Rect()), gr_context_type_(gr_context_type), weak_factory_(this) { CHECK(io_surface_); @@ -1017,6 +1041,8 @@ return; } + SetClearedRectInternal((is_cleared ? gfx::Rect(size) : gfx::Rect())); + // NOTE: Mac currently retains GLTexture and reuses it. This might lead to // issues with context losses, but is also beneficial to performance at // least on perf benchmarks. @@ -1030,6 +1056,7 @@ } IOSurfaceImageBacking::~IOSurfaceImageBacking() { + AutoLock auto_lock(this); if (egl_state_for_skia_gl_context_) { egl_state_for_skia_gl_context_->WillRelease(have_context()); egl_state_for_skia_gl_context_ = nullptr; @@ -1039,6 +1066,7 @@ bool IOSurfaceImageBacking::ReadbackToMemory( const std::vector<SkPixmap>& pixmaps) { + AutoLock auto_lock(this); CHECK_LE(pixmaps.size(), 3u); // Make sure any pending ANGLE EGLDisplays and Dawn devices are flushed. @@ -1090,6 +1118,7 @@ bool IOSurfaceImageBacking::UploadFromMemory( const std::vector<SkPixmap>& pixmaps) { + AutoLock auto_lock(this); CHECK_LE(pixmaps.size(), 3u); // Make sure any pending ANGLE EGLDisplays and Dawn devices are flushed. @@ -1141,6 +1170,8 @@ scoped_refptr<IOSurfaceBackingEGLState> IOSurfaceImageBacking::RetainGLTexture() { + AssertLockAcquired(); + gl::GLContext* context = gl::GLContext::GetCurrent(); gl::GLDisplayEGL* display = context ? context->GetGLDisplayEGL() : nullptr; if (!display) { @@ -1161,7 +1192,8 @@ MakeTextureAndSetParameters(gl_target_, framebuffer_attachment_angle_, &gl_texture, nullptr); // Set the IOSurface to be initially unbound from the GL texture. - gl_texture->SetEstimatedSize(GetEstimatedSize()); + gl_texture->SetEstimatedSize(format().EstimatedSizeInBytes(size())); + gl_textures.push_back(std::move(gl_texture)); } @@ -1176,11 +1208,13 @@ void IOSurfaceImageBacking::ReleaseGLTexture( IOSurfaceBackingEGLState* egl_state, bool have_context) { + AssertLockAcquired(); DCHECK_EQ(static_cast<int>(egl_state->gl_textures_.size()), format().NumberOfPlanes()); DCHECK(egl_state->egl_surfaces_.empty() || static_cast<int>(egl_state->egl_surfaces_.size()) == format().NumberOfPlanes()); + if (!have_context) { for (const auto& texture : egl_state->gl_textures_) { texture->MarkContextLost(); @@ -1242,14 +1276,6 @@ return SharedImageBackingType::kIOSurface; } -gfx::Rect IOSurfaceImageBacking::ClearedRect() const { - return cleared_rect_; -} - -void IOSurfaceImageBacking::SetClearedRect(const gfx::Rect& cleared_rect) { - cleared_rect_ = cleared_rect; -} - std::unique_ptr<GLTextureImageRepresentation> IOSurfaceImageBacking::ProduceGLTexture(SharedImageManager* manager, MemoryTypeTracker* tracker) { @@ -1259,10 +1285,16 @@ std::unique_ptr<GLTexturePassthroughImageRepresentation> IOSurfaceImageBacking::ProduceGLTexturePassthrough(SharedImageManager* manager, MemoryTypeTracker* tracker) { + scoped_refptr<IOSurfaceBackingEGLState> egl_state; + { + AutoLock auto_lock(this); + egl_state = RetainGLTexture(); + } + // The corresponding release will be done when the returned representation is // destroyed, in GLTextureImageRepresentationBeingDestroyed. - return std::make_unique<GLTextureIRepresentation>(manager, this, - RetainGLTexture(), tracker); + return std::make_unique<GLTextureIRepresentation>( + manager, this, std::move(egl_state), tracker); } std::unique_ptr<OverlayImageRepresentation> @@ -1274,10 +1306,14 @@ int IOSurfaceImageBacking::TrackBeginAccessToWGPUTexture( wgpu::Texture texture) { + AssertLockAcquired(); + return wgpu_texture_ongoing_accesses_[texture.Get()]++; } int IOSurfaceImageBacking::TrackEndAccessToWGPUTexture(wgpu::Texture texture) { + AssertLockAcquired(); + if (!wgpu_texture_ongoing_accesses_.contains(texture.Get())) { return 0; } @@ -1295,16 +1331,19 @@ const scoped_refptr<DawnSharedTextureCache>& IOSurfaceImageBacking::GetDawnTextureCache() { + AssertLockAcquired(); return dawn_texture_cache_; } void IOSurfaceImageBacking::AddWGPUDeviceWithPendingCommands( wgpu::Device device) { + AssertLockAcquired(); wgpu_devices_pending_flush_.insert(std::move(device)); } void IOSurfaceImageBacking::WaitForDawnCommandsToBeScheduled( const wgpu::Device& device_to_exclude) { + AssertLockAcquired(); TRACE_EVENT0("gpu", "IOSurfaceImageBacking::WaitForDawnCommandsToBeScheduled"); bool excluded_device_was_pending_flush = false; @@ -1323,12 +1362,15 @@ void IOSurfaceImageBacking::AddEGLDisplayWithPendingCommands( gl::GLDisplayEGL* display) { + AssertLockAcquired(); egl_displays_pending_flush_.insert(display); } void IOSurfaceImageBacking::WaitForANGLECommandsToBeScheduled() { + AssertLockAcquired(); TRACE_EVENT0("gpu", "IOSurfaceImageBacking::WaitForANGLECommandsToBeScheduled"); + for (auto* display : std::move(egl_displays_pending_flush_)) { eglWaitUntilWorkScheduledANGLE(display->GetDisplay()); } @@ -1336,6 +1378,8 @@ void IOSurfaceImageBacking::ClearEGLDisplaysWithPendingCommands( gl::GLDisplayEGL* display_to_keep) { + AssertLockAcquired(); + if (std::move(egl_displays_pending_flush_).contains(display_to_keep)) { egl_displays_pending_flush_.insert(display_to_keep); } @@ -1360,55 +1404,64 @@ } if (backend_type == wgpu::BackendType::Metal) { - // Clear out any cached SharedTextureMemory instances for which the - // associated Device has been lost - this both saves memory and more - // importantly ensures that a new SharedTextureMemory instance will be - // created if another Device occupies the same memory as a previously-used, - // now-lost Device. - dawn_texture_cache_->EraseDataIfDeviceLost(); + wgpu::SharedTextureMemory shared_texture_memory; + { + AutoLock auto_lock(this); - CHECK(device.HasFeature(wgpu::FeatureName::SharedTextureMemoryIOSurface)); + // Clear out any cached SharedTextureMemory instances for which the + // associated Device has been lost - this both saves memory and more + // importantly ensures that a new SharedTextureMemory instance will be + // created if another Device occupies the same memory as a + // previously-used, now-lost Device. + dawn_texture_cache_->EraseDataIfDeviceLost(); - wgpu::SharedTextureMemory shared_texture_memory = - dawn_texture_cache_->GetSharedTextureMemory(device); - if (!shared_texture_memory) { - // NOTE: `shared_dawn_context` may be null if Graphite is not being used. - const auto* shared_dawn_context = context_state->dawn_context_provider(); - const bool is_graphite_device = - shared_dawn_context && - shared_dawn_context->GetDevice().Get() == device.Get(); + CHECK(device.HasFeature(wgpu::FeatureName::SharedTextureMemoryIOSurface)); - wgpu::SharedTextureMemoryIOSurfaceDescriptor io_surface_desc; - io_surface_desc.ioSurface = io_surface_.get(); - // Set storage binding usage only if explicitly needed for WebGPU - this - // forces the MTLTexture wrapping the IOSurface to have ShaderWrite usage - // which in turn prevents texture compression. It's possible this doesn't - // have any effect given that IOSurfaces have linear layout, but it might - // if the kernel chooses to create a separate allocation for the GPU. - io_surface_desc.allowStorageBinding = - (usage() & SHARED_IMAGE_USAGE_WEBGPU_STORAGE_TEXTURE) && - !is_graphite_device; - - wgpu::SharedTextureMemoryDescriptor desc = {}; - desc.nextInChain = &io_surface_desc; - - shared_texture_memory = device.ImportSharedTextureMemory(&desc); + shared_texture_memory = + dawn_texture_cache_->GetSharedTextureMemory(device); if (!shared_texture_memory) { - LOG(ERROR) << "Unable to create SharedTextureMemory - device lost?"; - return nullptr; - } + // NOTE: `shared_dawn_context` may be null if Graphite is not being + // used. + const auto* shared_dawn_context = + context_state->dawn_context_provider(); + const bool is_graphite_device = + shared_dawn_context && + shared_dawn_context->GetDevice().Get() == device.Get(); - // We cache the SharedTextureMemory instance that is associated with the - // Graphite device. - // TODO(crbug.com/345674550): Extend caching to WebGPU devices as well. - if (is_graphite_device) { - // This is the Graphite device, so we cache its SharedTextureMemory - // instance. - dawn_texture_cache_->MaybeCacheSharedTextureMemory( - device, shared_texture_memory); + wgpu::SharedTextureMemoryIOSurfaceDescriptor io_surface_desc; + io_surface_desc.ioSurface = io_surface_.get(); + // Set storage binding usage only if explicitly needed for WebGPU - this + // forces the MTLTexture wrapping the IOSurface to have ShaderWrite + // usage which in turn prevents texture compression. It's possible this + // doesn't have any effect given that IOSurfaces have linear layout, but + // it might if the kernel chooses to create a separate allocation for + // the GPU. + io_surface_desc.allowStorageBinding = + (usage() & SHARED_IMAGE_USAGE_WEBGPU_STORAGE_TEXTURE) && + !is_graphite_device; + + wgpu::SharedTextureMemoryDescriptor desc = {}; + desc.nextInChain = &io_surface_desc; + + shared_texture_memory = device.ImportSharedTextureMemory(&desc); + if (!shared_texture_memory) { + LOG(ERROR) << "Unable to create SharedTextureMemory - device lost?"; + return nullptr; + } + + // We cache the SharedTextureMemory instance that is associated with the + // Graphite device. + // TODO(crbug.com/345674550): Extend caching to WebGPU devices as well. + if (is_graphite_device) { + // This is the Graphite device, so we cache its SharedTextureMemory + // instance. + dawn_texture_cache_->MaybeCacheSharedTextureMemory( + device, shared_texture_memory); + } } } + // SharedImageRepresentation handles lock. return std::make_unique<DawnRepresentation>( manager, this, tracker, wgpu::Device(device), std::move(shared_texture_memory), io_surface_size_, wgpu_format, @@ -1429,27 +1482,31 @@ scoped_refptr<IOSurfaceBackingEGLState> egl_state; std::vector<sk_sp<GrPromiseImageTexture>> promise_textures; - if (context_state->GrContextIsGL()) { - egl_state = RetainGLTexture(); - } - - for (int plane_index = 0; plane_index < format().NumberOfPlanes(); - plane_index++) { - GLFormatDesc format_desc = - context_state->GetGLFormatCaps().ToGLFormatDesc(format(), plane_index); - GrBackendTexture backend_texture; - auto plane_size = format().GetPlaneSize(plane_index, size()); - GetGrBackendTexture(context_state->feature_info(), egl_state->GetGLTarget(), - plane_size, egl_state->GetGLServiceId(plane_index), - format_desc.storage_internal_format, - context_state->gr_context()->threadSafeProxy(), - &backend_texture); - sk_sp<GrPromiseImageTexture> promise_texture = - GrPromiseImageTexture::Make(backend_texture); - if (!promise_texture) { - return nullptr; + { + AutoLock auto_lock(this); + if (context_state->GrContextIsGL()) { + egl_state = RetainGLTexture(); } - promise_textures.push_back(std::move(promise_texture)); + + for (int plane_index = 0; plane_index < format().NumberOfPlanes(); + plane_index++) { + GLFormatDesc format_desc = + context_state->GetGLFormatCaps().ToGLFormatDesc(format(), + plane_index); + GrBackendTexture backend_texture; + auto plane_size = format().GetPlaneSize(plane_index, size()); + GetGrBackendTexture( + context_state->feature_info(), egl_state->GetGLTarget(), plane_size, + egl_state->GetGLServiceId(plane_index), + format_desc.storage_internal_format, + context_state->gr_context()->threadSafeProxy(), &backend_texture); + sk_sp<GrPromiseImageTexture> promise_texture = + GrPromiseImageTexture::Make(backend_texture); + if (!promise_texture) { + return nullptr; + } + promise_textures.push_back(std::move(promise_texture)); + } } return std::make_unique<SkiaGaneshRepresentation>(manager, this, egl_state, @@ -1465,6 +1522,7 @@ CHECK(context_state); if (context_state->IsGraphiteDawn()) { #if BUILDFLAG(SKIA_USE_DAWN) + // No AutoLock here. Lock is handled in ProduceDawn(). auto device = context_state->dawn_context_provider()->GetDevice(); auto backend_type = context_state->dawn_context_provider()->backend_type(); auto dawn_representation = @@ -1522,6 +1580,7 @@ } void IOSurfaceImageBacking::SetPurgeable(bool purgeable) { + AutoLock auto_lock(this); if (purgeable_ == purgeable) return; purgeable_ = purgeable; @@ -1531,7 +1590,7 @@ DCHECK(!ongoing_write_access_); DCHECK(!num_ongoing_read_accesses_); - SetClearedRect(gfx::Rect()); + SetClearedRectInternal(gfx::Rect()); } uint32_t old_state; @@ -1539,10 +1598,12 @@ } bool IOSurfaceImageBacking::IsPurgeable() const { + AutoLock auto_lock(this); return purgeable_; } void IOSurfaceImageBacking::Update(std::unique_ptr<gfx::GpuFence> in_fence) { + AutoLock auto_lock(this); if (in_fence) { // TODO(dcastagna): Don't wait for the fence if the SharedImage is going // to be scanned out as an HW overlay. Currently we don't know that at @@ -1565,6 +1626,8 @@ } bool IOSurfaceImageBacking::BeginAccess(bool readonly) { + AssertLockAcquired(); + if (!readonly && ongoing_write_access_) { DLOG(ERROR) << "Unable to begin write access because another " "write access is in progress"; @@ -1594,6 +1657,8 @@ } void IOSurfaceImageBacking::EndAccess(bool readonly) { + AssertLockAcquired(); + if (readonly) { CHECK_GT(num_ongoing_read_accesses_, 0u); if (!(usage().Has(SHARED_IMAGE_USAGE_CONCURRENT_READ_WRITE))) { @@ -1612,6 +1677,8 @@ bool IOSurfaceImageBacking::IOSurfaceBackingEGLStateBeginAccess( IOSurfaceBackingEGLState* egl_state, bool readonly) { + AssertLockAcquired(); + // It is in error to read or write an IOSurface while it is purgeable. CHECK(!purgeable_); if (!BeginAccess(readonly)) { @@ -1713,6 +1780,8 @@ void IOSurfaceImageBacking::IOSurfaceBackingEGLStateEndAccess( IOSurfaceBackingEGLState* egl_state, bool readonly) { + AssertLockAcquired(); + EndAccess(readonly); // Early out if BeginAccess didn't succeed and we didn't bind any surfaces. @@ -1782,6 +1851,8 @@ void IOSurfaceImageBacking::IOSurfaceBackingEGLStateBeingCreated( IOSurfaceBackingEGLState* egl_state) { + AssertLockAcquired(); + auto insert_result = egl_state_map_.insert(std::make_pair(egl_state->egl_display_, egl_state)); CHECK(insert_result.second); @@ -1790,6 +1861,7 @@ void IOSurfaceImageBacking::IOSurfaceBackingEGLStateBeingDestroyed( IOSurfaceBackingEGLState* egl_state, bool has_context) { + AssertLockAcquired(); ReleaseGLTexture(egl_state, has_context); egl_state->egl_surfaces_.clear(); @@ -1803,6 +1875,7 @@ bool IOSurfaceImageBacking::InitializePixels( base::span<const uint8_t> pixel_data) { + AutoLock auto_lock(this); CHECK(format().is_single_plane()); ScopedIOSurfaceLock io_surface_lock(io_surface_.get(), kIOSurfaceLockAvoidSync); @@ -1833,6 +1906,8 @@ id<MTLSharedEvent> shared_event, uint64_t signal_value, bool readonly) { + AssertLockAcquired(); + SharedEventMap& shared_events = readonly ? non_exclusive_shared_events_ : exclusive_shared_events_; auto [it, _] = shared_events.insert( @@ -1843,6 +1918,8 @@ template <typename Fn> void IOSurfaceImageBacking::ProcessSharedEventsForBeginAccess(bool readonly, const Fn& fn) { + AssertLockAcquired(); + // Always need wait on exclusive access end events. for (const auto& [shared_event, signal_value] : exclusive_shared_events_) { fn(shared_event.get(), signal_value);
diff --git a/gpu/command_buffer/service/shared_image/iosurface_image_backing_factory.h b/gpu/command_buffer/service/shared_image/iosurface_image_backing_factory.h index 9e10a03b..73e9e414 100644 --- a/gpu/command_buffer/service/shared_image/iosurface_image_backing_factory.h +++ b/gpu/command_buffer/service/shared_image/iosurface_image_backing_factory.h
@@ -135,7 +135,7 @@ // long. const raw_ptr<gl::ProgressReporter> progress_reporter_; - uint32_t texture_target_; + const uint32_t texture_target_; }; } // namespace gpu
diff --git a/gpu/command_buffer/service/shared_image/shared_image_backing.cc b/gpu/command_buffer/service/shared_image/shared_image_backing.cc index 6878fb8..fecd98a 100644 --- a/gpu/command_buffer/service/shared_image/shared_image_backing.cc +++ b/gpu/command_buffer/service/shared_image/shared_image_backing.cc
@@ -473,6 +473,14 @@ cleared_rect_ = cleared_rect; } +void ClearTrackingSharedImageBacking::SetClearedInternal() { + cleared_rect_ = gfx::Rect(size()); +} + +bool ClearTrackingSharedImageBacking::IsClearedInternal() const { + return cleared_rect_ == gfx::Rect(size()); +} + scoped_refptr<gfx::NativePixmap> SharedImageBacking::GetNativePixmap() { return nullptr; }
diff --git a/gpu/command_buffer/service/shared_image/shared_image_backing.h b/gpu/command_buffer/service/shared_image/shared_image_backing.h index d05801f3..bf886a8 100644 --- a/gpu/command_buffer/service/shared_image/shared_image_backing.h +++ b/gpu/command_buffer/service/shared_image/shared_image_backing.h
@@ -362,6 +362,12 @@ return factory_; } + void AssertLockAcquired() const { + if (lock_) { + lock_->AssertAcquired(); + } + } + // Helper class used by subclasses to acquire |lock_| if it exists. class SCOPED_LOCKABLE GPU_GLES2_EXPORT AutoLock { STACK_ALLOCATED(); @@ -450,6 +456,8 @@ gfx::Rect ClearedRectInternal() const EXCLUSIVE_LOCKS_REQUIRED(lock_); void SetClearedRectInternal(const gfx::Rect& cleared_rect) EXCLUSIVE_LOCKS_REQUIRED(lock_); + void SetClearedInternal() EXCLUSIVE_LOCKS_REQUIRED(lock_); + bool IsClearedInternal() const EXCLUSIVE_LOCKS_REQUIRED(lock_); private: gfx::Rect cleared_rect_ GUARDED_BY(lock_);
diff --git a/infra/config/generated/luci/cr-buildbucket.cfg b/infra/config/generated/luci/cr-buildbucket.cfg index 9208a30f4..baa8173 100644 --- a/infra/config/generated/luci/cr-buildbucket.cfg +++ b/infra/config/generated/luci/cr-buildbucket.cfg
@@ -47271,6 +47271,10 @@ ' "sdk_package_name": "system-images;android-34;google_apis;x86_64"' ' },' ' {' + ' "cipd_yaml": "third_party/android_sdk/cipd/system_images/android-34/android-automotive/x86_64.yaml",' + ' "sdk_package_name": "system-images;android-34-ext9;android-automotive;x86_64"' + ' },' + ' {' ' "cipd_yaml": "third_party/android_sdk/cipd/system_images/android-35/google_apis/x86_64.yaml",' ' "sdk_package_name": "system-images;android-35;google_apis;x86_64"' ' },'
diff --git a/infra/config/subprojects/chromium/ci/chromium.infra.star b/infra/config/subprojects/chromium/ci/chromium.infra.star index 5d1adb0e..4950923 100644 --- a/infra/config/subprojects/chromium/ci/chromium.infra.star +++ b/infra/config/subprojects/chromium/ci/chromium.infra.star
@@ -337,6 +337,10 @@ "cipd_yaml": "third_party/android_sdk/cipd/system_images/android-34/google_apis/x86_64.yaml", }, { + "sdk_package_name": "system-images;android-34-ext9;android-automotive;x86_64", + "cipd_yaml": "third_party/android_sdk/cipd/system_images/android-34/android-automotive/x86_64.yaml", + }, + { "sdk_package_name": "system-images;android-35;google_apis;x86_64", "cipd_yaml": "third_party/android_sdk/cipd/system_images/android-35/google_apis/x86_64.yaml", },
diff --git a/internal b/internal index 063254c..a4e4632 160000 --- a/internal +++ b/internal
@@ -1 +1 @@ -Subproject commit 063254c1cbbe2562ca28802b862f9d4162ff25f4 +Subproject commit a4e46326699b3f0b96d8071f454f5b762710a6eb
diff --git a/ios/chrome/app/BUILD.gn b/ios/chrome/app/BUILD.gn index 33df3a9..a6733f9f 100644 --- a/ios/chrome/app/BUILD.gn +++ b/ios/chrome/app/BUILD.gn
@@ -538,6 +538,10 @@ "//ui/strings:ui_strings", ] + if (target_environment != "catalyst") { + deps += [ "//ios/chrome/browser/default_browser/model/default_status" ] + } + if (ios_enable_credential_provider_extension) { deps += [ ":credential_provider_migrator_app_agent" ] }
diff --git a/ios/chrome/app/main_controller.mm b/ios/chrome/app/main_controller.mm index dc40126c..166af03 100644 --- a/ios/chrome/app/main_controller.mm +++ b/ios/chrome/app/main_controller.mm
@@ -159,6 +159,10 @@ #import "ios/chrome/browser/rlz/rlz_tracker_delegate_impl.h" // nogncheck #endif +#if !BUILDFLAG(IS_IOS_MACCATALYST) +#import "ios/chrome/browser/default_browser/model/default_status/default_status_helper.h" +#endif // !BUILDFLAG(IS_IOS_MACCATALYST) + @interface MainController (ForUnloadProfileMarkedForDeletion) - (void)unloadProfileMarkedForDeletion:(std::string_view)profileName @@ -218,6 +222,9 @@ // Constant for deferred automatic download deletion. NSString* const kAutoDeletionFileRemoval = @"AutoDeletionFileRemoval"; +// Constant for deferred default browser status API check. +NSString* const kDefaultBrowserStatusCheck = @"DefaultBrowserStatusCheck"; + // Adapted from chrome/browser/ui/browser_init.cc. void RegisterComponentsForUpdate() { component_updater::ComponentUpdateService* cus = @@ -1429,6 +1436,7 @@ [self scheduleEnterpriseManagedDeviceCheck]; [self scheduleMemoryExperimentation]; [self scheduleAutoDeletionFileRemoval]; + [self scheduleDefaultBrowserStatusCheck]; #if BUILDFLAG(IOS_ENABLE_SANDBOX_DUMP) [self scheduleDumpDocumentsStatistics]; #endif // BUILDFLAG(IOS_ENABLE_SANDBOX_DUMP) @@ -1481,6 +1489,16 @@ }]; } +- (void)scheduleDefaultBrowserStatusCheck { +#if !BUILDFLAG(IS_IOS_MACCATALYST) + [_appState.deferredRunner + enqueueBlockNamed:kDefaultBrowserStatusCheck + block:^{ + default_status::TriggerDefaultStatusCheck(); + }]; +#endif // !BUILDFLAG(IS_IOS_MACCATALYST) +} + #if BUILDFLAG(IOS_ENABLE_SANDBOX_DUMP) - (void)scheduleDumpDocumentsStatistics { if ([[NSUserDefaults standardUserDefaults]
diff --git a/ios/chrome/app/strings/ios_strings.grd b/ios/chrome/app/strings/ios_strings.grd index 9c6c74988..a1bfe18 100644 --- a/ios/chrome/app/strings/ios_strings.grd +++ b/ios/chrome/app/strings/ios_strings.grd
@@ -2178,6 +2178,15 @@ <message name="IDS_IOS_EDIT_PASSWORD_DESCRIPTION" desc="Message inside confirmation alert when the user is trying to edit password [iOS only]" meaning="Telling user to ensure provided password matches password on the website."> Make sure the password you are saving matches your password for <ph name="WEBSITE">$1<ex>twitter.com</ex></ph> </message> + <message name="IDS_IOS_ENHANCED_CALENDAR_BOTTOM_SHEET_CANCEL_BUTTON" translateable="false" desc="The title for the enhanced calendar page of the AI prototyping menu [Length: unlimited]."> + Cancel + </message> + <message name="IDS_IOS_ENHANCED_CALENDAR_BOTTOM_SHEET_SUBTITLE" translateable="false" desc="The subtitle for the Enhanced Calendar bottom sheet."> + Hang tight while we use AI to supercharge your calendar event using the web page's context. + </message> + <message name="IDS_IOS_ENHANCED_CALENDAR_BOTTOM_SHEET_TITLE" translateable="false" desc="The title for the Enhanced Calendar bottom sheet."> + Enhanced Calendar + </message> <message name="IDS_IOS_ENHANCED_SAFE_BROWSING_PROMO_INSTRUCTIONS_STEP2" desc="Step 2 of instructions telling the user how to enable Enhanced Safe Browsing."> Tap "Privacy and Security" and then "Safe Browsing" </message>
diff --git a/ios/chrome/browser/DEPS b/ios/chrome/browser/DEPS index 093d252e..81bf915 100644 --- a/ios/chrome/browser/DEPS +++ b/ios/chrome/browser/DEPS
@@ -175,6 +175,7 @@ # Features should add explicit dependencies. "-ios/chrome/browser", "+ios/chrome/browser/profile/model", + "+ios/chrome/browser/default_browser/model", # Shared dependencies. "+ios/chrome/browser/shared/coordinator",
diff --git a/ios/chrome/browser/browser_container/ui_bundled/browser_container_coordinator.mm b/ios/chrome/browser/browser_container/ui_bundled/browser_container_coordinator.mm index ada38f1..75bc2345 100644 --- a/ios/chrome/browser/browser_container/ui_bundled/browser_container_coordinator.mm +++ b/ios/chrome/browser/browser_container/ui_bundled/browser_container_coordinator.mm
@@ -38,39 +38,41 @@ #endif @interface BrowserContainerCoordinator () <EditMenuAlertDelegate> -// Whether the coordinator is started. -@property(nonatomic, assign, getter=isStarted) BOOL started; + // Redefine property as readwrite. @property(nonatomic, strong, readwrite) BrowserContainerViewController* viewController; -// The mediator used to configure the BrowserContainerConsumer. -@property(nonatomic, strong) BrowserContainerMediator* mediator; -// The mediator used for the Link to Text feature. -@property(nonatomic, strong) LinkToTextMediator* linkToTextMediator; -// The mediator used for the Partial Translate feature. -@property(nonatomic, strong) PartialTranslateMediator* partialTranslateMediator; -// The mediator used for the Search With feature. -@property(nonatomic, strong) SearchWithMediator* searchWithMediator; // The handler for the edit menu. @property(nonatomic, strong) BrowserEditMenuHandler* browserEditMenuHandler; -// The overlay container coordinator for OverlayModality::kWebContentArea. -@property(nonatomic, strong) - OverlayContainerCoordinator* webContentAreaOverlayContainerCoordinator; -// The coodinator that manages ScreenTime. -@property(nonatomic, strong) ChromeCoordinator* screenTimeCoordinator; -// Coordinator used to present alerts to the user. -@property(nonatomic, strong) AlertCoordinator* alertCoordinator; + @end -@implementation BrowserContainerCoordinator +@implementation BrowserContainerCoordinator { + // Whether the coordinator is started. + BOOL _started; + // Coordinator used to present alerts to the user. + AlertCoordinator* _alertCoordinator; + // The mediator used for the Search With feature. + SearchWithMediator* _searchWithMediator; + // The coodinator that manages ScreenTime. + ChromeCoordinator* _screenTimeCoordinator; + // The overlay container coordinator for OverlayModality::kWebContentArea. + OverlayContainerCoordinator* _webContentAreaOverlayContainerCoordinator; + // The mediator used for the Partial Translate feature. + PartialTranslateMediator* _partialTranslateMediator; + // The mediator used to configure the BrowserContainerConsumer. + BrowserContainerMediator* _mediator; + // The mediator used for the Link to Text feature. + LinkToTextMediator* _linkToTextMediator; +} #pragma mark - ChromeCoordinator - (void)start { - if (self.started) { + if (_started) { return; } - self.started = YES; + _started = YES; DCHECK(self.browser); DCHECK(!_viewController); Browser* browser = self.browser; @@ -78,63 +80,62 @@ ProfileIOS* profile = browser->GetProfile(); BOOL incognito = profile->IsOffTheRecord(); self.viewController = [[BrowserContainerViewController alloc] init]; - self.webContentAreaOverlayContainerCoordinator = + _webContentAreaOverlayContainerCoordinator = [[OverlayContainerCoordinator alloc] initWithBaseViewController:self.viewController browser:browser modality:OverlayModality::kWebContentArea]; - self.linkToTextMediator = + _linkToTextMediator = [[LinkToTextMediator alloc] initWithWebStateList:webStateList]; - self.linkToTextMediator.alertDelegate = self; - self.linkToTextMediator.activityServiceHandler = HandlerForProtocol( + _linkToTextMediator.alertDelegate = self; + _linkToTextMediator.activityServiceHandler = HandlerForProtocol( browser->GetCommandDispatcher(), ActivityServiceCommands); self.browserEditMenuHandler = [[BrowserEditMenuHandler alloc] init]; self.viewController.browserEditMenuHandler = self.browserEditMenuHandler; - self.browserEditMenuHandler.linkToTextDelegate = self.linkToTextMediator; - self.viewController.linkToTextDelegate = self.linkToTextMediator; + self.browserEditMenuHandler.linkToTextDelegate = _linkToTextMediator; + self.viewController.linkToTextDelegate = _linkToTextMediator; PrefService* prefService = profile->GetOriginalProfile()->GetPrefs(); FullscreenController* fullscreenController = FullscreenController::FromBrowser(self.browser); - self.partialTranslateMediator = [[PartialTranslateMediator alloc] + _partialTranslateMediator = [[PartialTranslateMediator alloc] initWithWebStateList:webStateList withBaseViewController:self.viewController prefService:prefService fullscreenController:fullscreenController incognito:incognito]; - self.partialTranslateMediator.alertDelegate = self; + _partialTranslateMediator.alertDelegate = self; CommandDispatcher* dispatcher = browser->GetCommandDispatcher(); id<BrowserCoordinatorCommands> browserCommandsHandler = HandlerForProtocol(dispatcher, BrowserCoordinatorCommands); - self.partialTranslateMediator.browserHandler = browserCommandsHandler; + _partialTranslateMediator.browserHandler = browserCommandsHandler; self.browserEditMenuHandler.partialTranslateDelegate = - self.partialTranslateMediator; + _partialTranslateMediator; TemplateURLService* templateURLService = ios::TemplateURLServiceFactory::GetForProfile(profile); - self.searchWithMediator = + _searchWithMediator = [[SearchWithMediator alloc] initWithWebStateList:webStateList templateURLService:templateURLService incognito:incognito]; id<ApplicationCommands> applicationCommandsHandler = HandlerForProtocol(dispatcher, ApplicationCommands); - self.searchWithMediator.applicationCommandHandler = - applicationCommandsHandler; - self.browserEditMenuHandler.searchWithDelegate = self.searchWithMediator; + _searchWithMediator.applicationCommandHandler = applicationCommandsHandler; + self.browserEditMenuHandler.searchWithDelegate = _searchWithMediator; - [self.webContentAreaOverlayContainerCoordinator start]; + [_webContentAreaOverlayContainerCoordinator start]; self.viewController.webContentsOverlayContainerViewController = - self.webContentAreaOverlayContainerCoordinator.viewController; + _webContentAreaOverlayContainerCoordinator.viewController; OverlayPresenter* overlayPresenter = OverlayPresenter::FromBrowser(browser, OverlayModality::kWebContentArea); - self.mediator = + _mediator = [[BrowserContainerMediator alloc] initWithWebStateList:webStateList webContentAreaOverlayPresenter:overlayPresenter]; - self.mediator.consumer = self.viewController; + _mediator.consumer = self.viewController; [self setUpScreenTimeIfEnabled]; @@ -142,20 +143,20 @@ } - (void)stop { - if (!self.started) { + if (!_started) { return; } [self dismissAlertCoordinator]; - self.started = NO; - [self.webContentAreaOverlayContainerCoordinator stop]; - [self.screenTimeCoordinator stop]; - [self.partialTranslateMediator shutdown]; - [self.searchWithMediator shutdown]; + _started = NO; + [_webContentAreaOverlayContainerCoordinator stop]; + [_screenTimeCoordinator stop]; + [_partialTranslateMediator shutdown]; + [_searchWithMediator shutdown]; self.viewController = nil; - self.mediator = nil; - self.linkToTextMediator = nil; - self.partialTranslateMediator = nil; - self.searchWithMediator = nil; + _mediator = nil; + _linkToTextMediator = nil; + _partialTranslateMediator = nil; + _searchWithMediator = nil; [super stop]; } @@ -168,23 +169,23 @@ - (void)showAlertWithTitle:(NSString*)title message:(NSString*)message actions:(NSArray<EditMenuAlertDelegateAction*>*)actions { - self.alertCoordinator = + _alertCoordinator = [[AlertCoordinator alloc] initWithBaseViewController:self.viewController browser:self.browser title:title message:message]; __weak BrowserContainerCoordinator* weakSelf = self; for (EditMenuAlertDelegateAction* action in actions) { - [self.alertCoordinator addItemWithTitle:action.title - action:^{ - action.action(); - [weakSelf dismissAlertCoordinator]; - } - style:action.style - preferred:action.preferred - enabled:YES]; + [_alertCoordinator addItemWithTitle:action.title + action:^{ + action.action(); + [weakSelf dismissAlertCoordinator]; + } + style:action.style + preferred:action.preferred + enabled:YES]; } - [self.alertCoordinator start]; + [_alertCoordinator start]; } #pragma mark - Private methods @@ -203,14 +204,14 @@ [screenTimeCoordinator start]; self.viewController.screenTimeViewController = screenTimeCoordinator.viewController; - self.screenTimeCoordinator = screenTimeCoordinator; + _screenTimeCoordinator = screenTimeCoordinator; #endif } - (void)dismissAlertCoordinator { - [self.alertCoordinator stop]; - self.alertCoordinator = nil; + [_alertCoordinator stop]; + _alertCoordinator = nil; } @end
diff --git a/ios/chrome/browser/browser_view/ui_bundled/BUILD.gn b/ios/chrome/browser/browser_view/ui_bundled/BUILD.gn index 13ebc6b..26153ab 100644 --- a/ios/chrome/browser/browser_view/ui_bundled/BUILD.gn +++ b/ios/chrome/browser/browser_view/ui_bundled/BUILD.gn
@@ -131,6 +131,7 @@ "//ios/chrome/browser/incognito_reauth/ui_bundled:ui", "//ios/chrome/browser/infobars/model", "//ios/chrome/browser/infobars/ui_bundled:public", + "//ios/chrome/browser/intelligence/enhanced_calendar/coordinator", "//ios/chrome/browser/intents/model:model_donation_helper", "//ios/chrome/browser/itunes_urls/model", "//ios/chrome/browser/keyboard/ui_bundled",
diff --git a/ios/chrome/browser/browser_view/ui_bundled/DEPS b/ios/chrome/browser/browser_view/ui_bundled/DEPS index d7b96597..290f553f 100644 --- a/ios/chrome/browser/browser_view/ui_bundled/DEPS +++ b/ios/chrome/browser/browser_view/ui_bundled/DEPS
@@ -37,6 +37,7 @@ "+ios/chrome/browser/google_one/coordinator", "+ios/chrome/browser/incognito_reauth/ui_bundled", "+ios/chrome/browser/infobars/model", + "+ios/chrome/browser/intelligence/enhanced_calendar/coordinator/enhanced_calendar_coordinator.h", "+ios/chrome/browser/intents/model/intents_donation_helper.h", "+ios/chrome/browser/bubble/model/tab_based_iph_browser_agent.h", "+ios/chrome/browser/itunes_urls/model/itunes_urls_handler_tab_helper.h",
diff --git a/ios/chrome/browser/browser_view/ui_bundled/browser_coordinator.mm b/ios/chrome/browser/browser_view/ui_bundled/browser_coordinator.mm index 05620b5..fb835c67 100644 --- a/ios/chrome/browser/browser_view/ui_bundled/browser_coordinator.mm +++ b/ios/chrome/browser/browser_view/ui_bundled/browser_coordinator.mm
@@ -117,6 +117,7 @@ #import "ios/chrome/browser/incognito_reauth/ui_bundled/incognito_reauth_scene_agent.h" #import "ios/chrome/browser/infobars/model/infobar_ios.h" #import "ios/chrome/browser/infobars/model/infobar_manager_impl.h" +#import "ios/chrome/browser/intelligence/enhanced_calendar/coordinator/enhanced_calendar_coordinator.h" #import "ios/chrome/browser/intents/model/intents_donation_helper.h" #import "ios/chrome/browser/lens/ui_bundled/lens_coordinator.h" #import "ios/chrome/browser/lens_overlay/coordinator/lens_overlay_availability.h" @@ -197,6 +198,7 @@ #import "ios/chrome/browser/shared/public/commands/contextual_sheet_commands.h" #import "ios/chrome/browser/shared/public/commands/country_code_picker_commands.h" #import "ios/chrome/browser/shared/public/commands/drive_file_picker_commands.h" +#import "ios/chrome/browser/shared/public/commands/enhanced_calendar_commands.h" #import "ios/chrome/browser/shared/public/commands/feed_commands.h" #import "ios/chrome/browser/shared/public/commands/find_in_page_commands.h" #import "ios/chrome/browser/shared/public/commands/google_one_commands.h" @@ -328,6 +330,7 @@ DefaultBrowserGenericPromoCommands, DefaultPromoNonModalPresentationDelegate, DriveFilePickerCommands, + EnhancedCalendarCommands, EditMenuBuilder, EnterprisePromptCoordinatorDelegate, FormInputAccessoryCoordinatorNavigator, @@ -649,6 +652,9 @@ LensPromoCoordinator* _lensPromoCoordinator; EnhancedSafeBrowsingPromoCoordinator* _enhancedSafeBrowsingPromoCoordinator; AutoDeletionCoordinator* _autoDeletionCoordinator; + + // The coordinator for the Enhanced Calendar feature UI (bottom sheet). + EnhancedCalendarCoordinator* _enhancedCalendarCoordinator; } #pragma mark - ChromeCoordinator @@ -1047,6 +1053,7 @@ @protocol(ContextualSheetCommands), @protocol(DefaultBrowserPromoNonModalCommands), @protocol(DriveFilePickerCommands), + @protocol(EnhancedCalendarCommands), @protocol(FeedCommands), @protocol(PromosManagerCommands), @protocol(FindInPageCommands), @@ -1638,6 +1645,9 @@ [_quickDeleteCoordinator stop]; _quickDeleteCoordinator = nil; + [_enhancedCalendarCoordinator stop]; + _enhancedCalendarCoordinator = nil; + [self hideDriveFilePicker]; [self hideContextualSheet]; [self dismissEditAddressBottomSheet]; @@ -2526,6 +2536,22 @@ [_driveFilePickerCoordinator setSelectedIdentity:selectedIdentity]; } +#pragma mark - EnhancedCalendarCommands + +- (void)showEnhancedCalendarBottomSheetWithIntegrationProvider: + (ios::provider::AddToCalendarIntegrationProvider)integrationProvider { + _enhancedCalendarCoordinator = [[EnhancedCalendarCoordinator alloc] + initWithBaseViewController:self.viewController + browser:self.browser + integrationProvider:integrationProvider]; + [_enhancedCalendarCoordinator start]; +} + +- (void)hideEnhancedCalendarBottomSheet { + [_enhancedCalendarCoordinator stop]; + _enhancedCalendarCoordinator = nil; +} + #pragma mark - FeedCommands - (void)showFirstFollowUIForWebSite:(FollowedWebSite*)followedWebSite {
diff --git a/ios/chrome/browser/content_suggestions/ui_bundled/shop_card/BUILD.gn b/ios/chrome/browser/content_suggestions/ui_bundled/shop_card/BUILD.gn index 393f694a..e7e539f 100644 --- a/ios/chrome/browser/content_suggestions/ui_bundled/shop_card/BUILD.gn +++ b/ios/chrome/browser/content_suggestions/ui_bundled/shop_card/BUILD.gn
@@ -36,6 +36,7 @@ "//ios/chrome/browser/price_notifications/ui_bundled/cells:cells", "//ios/chrome/browser/shared/model/application_context:application_context", "//ios/chrome/browser/shared/model/prefs:pref_names", + "//ios/chrome/browser/shared/ui/symbols", "//ios/chrome/browser/shared/ui/util:util", "//ios/chrome/common/ui/colors:colors", "//ios/chrome/common/ui/elements:elements",
diff --git a/ios/chrome/browser/content_suggestions/ui_bundled/shop_card/shop_card_mediator.mm b/ios/chrome/browser/content_suggestions/ui_bundled/shop_card/shop_card_mediator.mm index 7b157192..3d31c56 100644 --- a/ios/chrome/browser/content_suggestions/ui_bundled/shop_card/shop_card_mediator.mm +++ b/ios/chrome/browser/content_suggestions/ui_bundled/shop_card/shop_card_mediator.mm
@@ -231,17 +231,16 @@ length:imageData.size()]; if (data) { self->_shopCardItem.shopCardData.productImage = data; - - // Fetch favicon if product image available. - // TODO(crbug.com/392970898): add support for no product image case - __weak ShopCardMediator* weakSelf = self; - _faviconLoader->FaviconForPageUrl( - productUrl, kDesiredSmallFaviconSizePt, kMinFaviconSizePt, - /*fallback_to_google_server=*/false, ^(FaviconAttributes* attributes) { - [weakSelf onFaviconReceived:attributes]; - }); } [self.delegate insertShopCard]; + + // Fetch favicon, regardless of whether product image available. + __weak ShopCardMediator* weakSelf = self; + _faviconLoader->FaviconForPageUrl( + productUrl, kDesiredSmallFaviconSizePt, kMinFaviconSizePt, + /*fallback_to_google_server=*/false, ^(FaviconAttributes* attributes) { + [weakSelf onFaviconReceived:attributes]; + }); } - (void)onFaviconReceived:(FaviconAttributes*)attributes {
diff --git a/ios/chrome/browser/content_suggestions/ui_bundled/shop_card/shop_card_view.mm b/ios/chrome/browser/content_suggestions/ui_bundled/shop_card/shop_card_view.mm index 017a239..5397c3ad2 100644 --- a/ios/chrome/browser/content_suggestions/ui_bundled/shop_card/shop_card_view.mm +++ b/ios/chrome/browser/content_suggestions/ui_bundled/shop_card/shop_card_view.mm
@@ -13,6 +13,7 @@ #import "ios/chrome/browser/content_suggestions/ui_bundled/shop_card/shop_card_item.h" #import "ios/chrome/browser/content_suggestions/ui_bundled/shop_card/shop_card_mediator.h" #import "ios/chrome/browser/price_notifications/ui_bundled/cells/price_notifications_price_chip_view.h" +#import "ios/chrome/browser/shared/ui/symbols/symbols.h" #import "ios/chrome/browser/shared/ui/util/uikit_ui_util.h" #import "ios/chrome/common/ui/colors/semantic_color_names.h" #import "ios/chrome/common/ui/elements/gradient_view.h" @@ -23,19 +24,23 @@ namespace { const CGFloat kHorizontalStackSpacing = 16.0f; const CGFloat kVerticalStackSpacing = 6.0f; -const CGFloat kProductImageWidthHeight = 72.0; +const CGFloat kCenterSymbolSize = 20.0; +const CGFloat kShopCardProductImageWidthHeight = 72.0; +const CGFloat kProductImageEmptyWidthHeight = 56.0; const CGFloat kFaviconImageWidthHeight = 24.0; +const CGFloat kFaviconDefaultImageWidthHeight = 22.0; +const CGFloat kFaviconCenterImageWidthHeight = 30.0; const CGFloat kProductCornerRadius = 12.0; const CGFloat kFaviconImageContainerTrailingMargin = -5.0; const CGFloat kFaviconCornerRadius = 4.0; const CGFloat kFaviconImageContainerTrailingCornerRadius = 8.0; -// Alpha for top of gradient overlay. -// TODO(crbug.com/392970898): add support for no product image case -const CGFloat kGradientOverlayTopAlpha = 0.0; +const CGFloat kFaviconContainerShadowVerticalOffset = 4.0; +const CGFloat kFaviconShadowOpacity = 0.12; +const CGFloat kFaviconShadowRadius = 11.0; -// Alpha for bottom of gradient overlay. -// TODO(crbug.com/392970898): add support for no product image case +// Alpha for top and bottom of gradient overlay. +const CGFloat kGradientOverlayTopAlpha = 0.0; const CGFloat kGradientOverlayBottomAlpha = 0.14; } // namespace @@ -56,9 +61,12 @@ // Left side of ShopCard, holds product image with favicon. UIView* _productAndFaviconContainer; - UIView* _faviconImageContainer; - UIImageView* _productImage; - UIImageView* _faviconImage; + UIView* _faviconImageContainer; // container, or grey if using default + // favicon globe image + UIImageView* + _productImage; // the product image, or a placeholder gray if no product + UIImageView* + _faviconImage; // the favicon image, or a placeholder globe if no favicon UIView* _gradientOverlay; PriceNotificationsPriceChipView* _priceNotificationsChip; @@ -87,86 +95,99 @@ #pragma mark - ShopCardFaviconConsumer - (void)faviconCompleted:(UIImage*)faviconImage { - [self populateFaviconImageAndContainer:faviconImage]; - [self addFaviconToViewIfPresent]; - [self addFaviconImageAndContainerConstraintsIfPresent]; + _item.shopCardData.faviconImage = faviconImage; + [self configureViewForTrackedProducts:_item]; } - (void)configureViewForTrackedProducts:(ShopCardItem*)configItem { - _titleLabel = [[UILabel alloc] init]; - _titleLabel.textColor = [UIColor colorNamed:kTextPrimaryColor]; - _titleLabel.numberOfLines = 1; - _titleLabel.lineBreakMode = NSLineBreakByTruncatingTail; - _titleLabel.font = - CreateDynamicFont(UIFontTextStyleFootnote, UIFontWeightSemibold, self); - _titleLabel.adjustsFontForContentSizeCategory = YES; - _titleLabel.text = _item.shopCardData.productTitle; + [self populateTitleLabel]; + [self populateUrlLabel]; + [self populatePriceNotificationChip]; - _urlLabel = [[UILabel alloc] init]; - _urlLabel.text = [self hostnameFromGURL:_item.shopCardData.productURL]; - _urlLabel.numberOfLines = 1; - _urlLabel.lineBreakMode = NSLineBreakByTruncatingTail; - _urlLabel.font = [UIFont preferredFontForTextStyle:UIFontTextStyleFootnote]; - _urlLabel.adjustsFontForContentSizeCategory = YES; - _urlLabel.textColor = [UIColor colorNamed:kTextSecondaryColor]; + // Case 1: both are present, favicon on bottom right. + if (_item.shopCardData.productImage && _item.shopCardData.faviconImage) { + // Styling + [self addProductImageAndOverlay]; + [self addFaviconImageAndContainer:_item.shopCardData.faviconImage]; + _faviconImageContainer.layer.mask = + [self faviconMaskWithRadius:kFaviconImageContainerTrailingCornerRadius + imageHeightWidth:kFaviconImageWidthHeight]; - _priceNotificationsChip = [[PriceNotificationsPriceChipView alloc] init]; - _priceNotificationsChip.previousPriceFont = - CreateDynamicFont(UIFontTextStyleFootnote, UIFontWeightMedium); - _priceNotificationsChip.currentPriceFont = - CreateDynamicFont(UIFontTextStyleFootnote, UIFontWeightMedium); - _priceNotificationsChip.strikeoutPreviousPrice = YES; - [_priceNotificationsChip - setPriceDrop:_item.shopCardData.priceDrop->current_price - previousPrice:_item.shopCardData.priceDrop->previous_price]; + // Hierarchy + [_productAndFaviconContainer addSubview:_productImage]; + [_productImage addSubview:_gradientOverlay]; + [_productAndFaviconContainer addSubview:_faviconImageContainer]; + [_faviconImageContainer addSubview:_faviconImage]; - _productAndFaviconContainer = [[UIView alloc] init]; - _productImage = [[UIImageView alloc] init]; - UIImage* retrievedProductImage = - [UIImage imageWithData:_item.shopCardData.productImage - scale:[UIScreen mainScreen].scale]; - - _productImage.image = retrievedProductImage; - _productImage.contentMode = UIViewContentModeScaleAspectFill; - _productImage.translatesAutoresizingMaskIntoConstraints = NO; - - _productImage.layer.borderWidth = 0; - _productImage.layer.cornerRadius = kProductCornerRadius; - _productImage.layer.masksToBounds = YES; - _productImage.backgroundColor = UIColor.whiteColor; - - _gradientOverlay = [[GradientView alloc] - initWithTopColor:[[UIColor blackColor] - colorWithAlphaComponent:kGradientOverlayTopAlpha] - bottomColor:[[UIColor blackColor] colorWithAlphaComponent: - kGradientOverlayBottomAlpha]]; - _gradientOverlay.layer.masksToBounds = YES; - _gradientOverlay.translatesAutoresizingMaskIntoConstraints = NO; - - if (configItem.shopCardData && configItem.shopCardData.faviconImage) { - [self - populateFaviconImageAndContainer:configItem.shopCardData.faviconImage]; + // Constraints + [self addWidthConstraintsForProductImage:kShopCardProductImageWidthHeight]; + [_productAndFaviconContainer bringSubviewToFront:_gradientOverlay]; + AddSameConstraints(_gradientOverlay, _productImage); + AddSameConstraints(_productImage, _productAndFaviconContainer); + [self addWidthConstraintsForFaviconImage:kFaviconImageWidthHeight]; + AddSameConstraints(_faviconImage, _faviconImageContainer); + [self addConstraintsForFaviconContainerToTrailingEdge]; } - // Define hierarchy - [_productAndFaviconContainer addSubview:_productImage]; - [_productImage addSubview:_gradientOverlay]; + // Case 2: product image and overlay only, favicon absent. + if (_item.shopCardData.productImage && !_item.shopCardData.faviconImage) { + // Styling + [self addProductImageAndOverlay]; - [self addFaviconToViewIfPresent]; + // Hierarchy + [_productAndFaviconContainer addSubview:_productImage]; + [_productImage addSubview:_gradientOverlay]; - // Add constraints after the hierarchy is defined - [_productAndFaviconContainer bringSubviewToFront:_gradientOverlay]; - [self addConstraintsForProductImage]; - [NSLayoutConstraint activateConstraints:@[ - [_gradientOverlay.heightAnchor - constraintEqualToConstant:kProductImageWidthHeight], - [_gradientOverlay.widthAnchor - constraintEqualToAnchor:_gradientOverlay.heightAnchor] - ]]; - AddSameConstraints(_gradientOverlay, _productImage); - AddSameConstraints(_productImage, _productAndFaviconContainer); + // Constraints + [self addWidthConstraintsForProductImage:kShopCardProductImageWidthHeight]; + [self addGradientOverlayConstraints]; + [_productAndFaviconContainer bringSubviewToFront:_gradientOverlay]; + AddSameConstraints(_gradientOverlay, _productImage); + AddSameConstraints(_productImage, _productAndFaviconContainer); + } - [self addFaviconImageAndContainerConstraintsIfPresent]; + // Case 3: no product only favicon in the center. product image is empty - + // grey. + if (!_item.shopCardData.productImage && _item.shopCardData.faviconImage) { + // Styling + [self addProductImageEmptyGray]; + [self addFaviconImageAndContainer:_item.shopCardData.faviconImage]; + [self addShadowForFaviconContainer]; + + // Hierarchy + [_productAndFaviconContainer addSubview:_productImage]; + [_productAndFaviconContainer addSubview:_faviconImageContainer]; + [_faviconImageContainer addSubview:_faviconImage]; + + // Constraints + [self addWidthConstraintsForProductImage:kProductImageEmptyWidthHeight]; + [self addWidthConstraintsForFaviconImage:kFaviconCenterImageWidthHeight]; + AddSameConstraints(_productImage, _productAndFaviconContainer); + AddSameConstraints(_faviconImage, _faviconImageContainer); + [self addConstraintsForFaviconContainerToProductContainerCenter]; + } + + // Case 4: no favicon or product. globe in the center, product image is grey. + if (!_item.shopCardData.productImage && !_item.shopCardData.faviconImage) { + // Styling + [self addProductImageEmptyGray]; + [self addFaviconImageAndContainer:[self makeDefaultFaviconUIImage]]; + [self addFaviconImageContainerColorForGlobe]; + [self addShadowForFaviconContainer]; + + // Hierarchy + [_productAndFaviconContainer addSubview:_productImage]; + [_productAndFaviconContainer addSubview:_faviconImageContainer]; + [_faviconImageContainer addSubview:_faviconImage]; + + // Constraints + [self addWidthConstraintsForProductImage:kProductImageEmptyWidthHeight]; + [self addWidthConstraintsForFaviconImage:kFaviconDefaultImageWidthHeight]; + [self addWidthConstraintsForFaviconContainerDefaultGlobe]; + AddSameConstraints(_productImage, _productAndFaviconContainer); + [self addConstraintsForFaviconImageToFaviconImageContainerCenter]; + [self addConstraintsForFaviconContainerToProductContainerCenter]; + } // Lay out text stack _textStack = [[UIStackView alloc] initWithArrangedSubviews:@[ @@ -193,21 +214,50 @@ URL)); } -- (void)addConstraintsForProductImage { - if (_productImage) { - [NSLayoutConstraint activateConstraints:@[ - [_productImage.heightAnchor - constraintEqualToConstant:kProductImageWidthHeight], - [_productImage.widthAnchor - constraintEqualToAnchor:_productImage.heightAnchor] - ]]; - } +- (void)populateTitleLabel { + _titleLabel = [[UILabel alloc] init]; + _titleLabel.textColor = [UIColor colorNamed:kTextPrimaryColor]; + _titleLabel.numberOfLines = 1; + _titleLabel.lineBreakMode = NSLineBreakByTruncatingTail; + _titleLabel.font = + CreateDynamicFont(UIFontTextStyleFootnote, UIFontWeightSemibold, self); + _titleLabel.adjustsFontForContentSizeCategory = YES; + _titleLabel.text = _item.shopCardData.productTitle; } -- (void)addConstraintsForFaviconImage { +- (void)populateUrlLabel { + _urlLabel = [[UILabel alloc] init]; + _urlLabel.text = [self hostnameFromGURL:_item.shopCardData.productURL]; + _urlLabel.numberOfLines = 1; + _urlLabel.lineBreakMode = NSLineBreakByTruncatingTail; + _urlLabel.font = [UIFont preferredFontForTextStyle:UIFontTextStyleFootnote]; + _urlLabel.adjustsFontForContentSizeCategory = YES; + _urlLabel.textColor = [UIColor colorNamed:kTextSecondaryColor]; +} + +- (void)populatePriceNotificationChip { + _priceNotificationsChip = [[PriceNotificationsPriceChipView alloc] init]; + _priceNotificationsChip.previousPriceFont = + CreateDynamicFont(UIFontTextStyleFootnote, UIFontWeightMedium); + _priceNotificationsChip.currentPriceFont = + CreateDynamicFont(UIFontTextStyleFootnote, UIFontWeightMedium); + _priceNotificationsChip.strikeoutPreviousPrice = YES; + [_priceNotificationsChip + setPriceDrop:_item.shopCardData.priceDrop->current_price + previousPrice:_item.shopCardData.priceDrop->previous_price]; +} + +- (void)addWidthConstraintsForProductImage:(const CGFloat)width { [NSLayoutConstraint activateConstraints:@[ - [_faviconImage.heightAnchor - constraintEqualToConstant:kFaviconImageWidthHeight], + [_productImage.heightAnchor constraintEqualToConstant:width], + [_productImage.widthAnchor + constraintEqualToAnchor:_productImage.heightAnchor] + ]]; +} + +- (void)addWidthConstraintsForFaviconImage:(const CGFloat)faviconWidth { + [NSLayoutConstraint activateConstraints:@[ + [_faviconImage.heightAnchor constraintEqualToConstant:faviconWidth], [_faviconImage.widthAnchor constraintEqualToAnchor:_faviconImage.heightAnchor] ]]; @@ -224,6 +274,15 @@ ]]; } +- (void)addConstraintsForFaviconContainerToProductContainerCenter { + [NSLayoutConstraint activateConstraints:@[ + [_faviconImageContainer.centerXAnchor + constraintEqualToAnchor:_productAndFaviconContainer.centerXAnchor], + [_faviconImageContainer.centerYAnchor + constraintEqualToAnchor:_productAndFaviconContainer.centerYAnchor] + ]]; +} + // Create a mask with radius applied to the trailing corner - (CAShapeLayer*)faviconMaskWithRadius:(CGFloat)trailingCornerRadius imageHeightWidth:(CGFloat)imageHeightWidth { @@ -252,7 +311,11 @@ [self.commandHandler openShopCardItem:_item]; } -- (void)populateFaviconImageAndContainer:(UIImage*)faviconImage { +- (UIImage*)makeDefaultFaviconUIImage { + return DefaultSymbolWithPointSize(kGlobeAmericasSymbol, kCenterSymbolSize); +} + +- (void)addFaviconImageAndContainer:(UIImage*)faviconImage { _faviconImageContainer = [[UIView alloc] init]; _faviconImage = [[UIImageView alloc] init]; _faviconImage.image = faviconImage; @@ -263,27 +326,85 @@ _faviconImage.layer.masksToBounds = YES; _faviconImageContainer.layer.cornerRadius = kFaviconCornerRadius; _faviconImageContainer.layer.masksToBounds = YES; - - _faviconImageContainer.layer.mask = - [self faviconMaskWithRadius:kFaviconImageContainerTrailingCornerRadius - imageHeightWidth:kFaviconImageWidthHeight]; + _faviconImageContainer.translatesAutoresizingMaskIntoConstraints = NO; } -- (void)addFaviconToViewIfPresent { - if (_faviconImage) { - [_productAndFaviconContainer addSubview:_faviconImageContainer]; - [_faviconImageContainer addSubview:_faviconImage]; - } +- (void)addShadowForFaviconContainer { + _faviconImageContainer.layer.shadowColor = [UIColor blackColor].CGColor; + _faviconImageContainer.layer.shadowOffset = + CGSizeMake(0, kFaviconContainerShadowVerticalOffset /*4*/); + _faviconImageContainer.layer.shadowOpacity = kFaviconShadowOpacity; // 12% + _faviconImageContainer.layer.shadowRadius = kFaviconShadowRadius; // 11 } -- (void)addFaviconImageAndContainerConstraintsIfPresent { - if (_faviconImage) { - [self addConstraintsForFaviconImage]; - AddSameConstraints(_faviconImage, _faviconImageContainer); - // Place the favicon to trailing-bottom of product image - _faviconImageContainer.translatesAutoresizingMaskIntoConstraints = NO; - [self addConstraintsForFaviconContainerToTrailingEdge]; - } +- (void)addFaviconImageContainerColorForGlobe { + _faviconImageContainer.backgroundColor = [UIColor colorNamed:kBlue500Color]; + // Color inside the globe icon + _faviconImageContainer.tintColor = UIColor.whiteColor; +} + +- (void)addConstraintsForFaviconImageToFaviconImageContainerCenter { + [NSLayoutConstraint activateConstraints:@[ + [_faviconImage.centerXAnchor + constraintEqualToAnchor:_faviconImageContainer.centerXAnchor], + [_faviconImage.centerYAnchor + constraintEqualToAnchor:_faviconImageContainer.centerYAnchor], + ]]; +} + +- (void)addWidthConstraintsForFaviconContainerDefaultGlobe { + [NSLayoutConstraint activateConstraints:@[ + [_faviconImageContainer.widthAnchor + constraintEqualToConstant:kFaviconCenterImageWidthHeight], + [_faviconImageContainer.heightAnchor + constraintEqualToAnchor:_faviconImageContainer.widthAnchor], + ]]; +} + +- (void)addProductImageEmptyGray { + _productAndFaviconContainer = [[UIView alloc] init]; + _productImage = [[UIImageView alloc] init]; + _productImage.backgroundColor = + [UIColor colorNamed:kTertiaryBackgroundColor]; + _productImage.contentMode = UIViewContentModeScaleAspectFill; + _productImage.translatesAutoresizingMaskIntoConstraints = NO; + _productImage.layer.borderWidth = 0; + _productImage.layer.cornerRadius = kProductCornerRadius; + _productImage.layer.masksToBounds = YES; +} + +- (void)addProductImageAndOverlay { + _productAndFaviconContainer = [[UIView alloc] init]; + _productImage = [[UIImageView alloc] init]; + + UIImage* retrievedProductImage = + [UIImage imageWithData:_item.shopCardData.productImage + scale:[UIScreen mainScreen].scale]; + _productImage.image = retrievedProductImage; + _productImage.backgroundColor = UIColor.whiteColor; + _gradientOverlay = [[GradientView alloc] + initWithTopColor:[[UIColor blackColor] + colorWithAlphaComponent:kGradientOverlayTopAlpha] + bottomColor:[[UIColor blackColor] colorWithAlphaComponent: + kGradientOverlayBottomAlpha]]; + _gradientOverlay.layer.masksToBounds = YES; + _gradientOverlay.layer.zPosition = 1; + _gradientOverlay.translatesAutoresizingMaskIntoConstraints = NO; + + _productImage.contentMode = UIViewContentModeScaleAspectFill; + _productImage.translatesAutoresizingMaskIntoConstraints = NO; + _productImage.layer.borderWidth = 0; + _productImage.layer.cornerRadius = kProductCornerRadius; + _productImage.layer.masksToBounds = YES; +} + +- (void)addGradientOverlayConstraints { + [NSLayoutConstraint activateConstraints:@[ + [_gradientOverlay.heightAnchor + constraintEqualToConstant:kShopCardProductImageWidthHeight], + [_gradientOverlay.widthAnchor + constraintEqualToAnchor:_gradientOverlay.heightAnchor] + ]]; } @end
diff --git a/ios/chrome/browser/default_browser/model/default_status/BUILD.gn b/ios/chrome/browser/default_browser/model/default_status/BUILD.gn new file mode 100644 index 0000000..5059e6a --- /dev/null +++ b/ios/chrome/browser/default_browser/model/default_status/BUILD.gn
@@ -0,0 +1,45 @@ +# 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/apple/mobile_config.gni") + +# MacCatalyst does not allow querying the default browser status. +assert(target_environment != "catalyst") + +source_set("default_status") { + sources = [ + "default_status_helper.h", + "default_status_helper.mm", + "default_status_helper_constants.h", + "default_status_helper_constants.mm", + "default_status_helper_metrics.h", + "default_status_helper_metrics.mm", + "default_status_helper_prefs.h", + "default_status_helper_prefs.mm", + "default_status_helper_types.h", + ] + deps = [] + public_deps = [ + "//base", + "//components/prefs", + "//ios/chrome/browser/default_browser/model:utils", + "//ios/chrome/browser/shared/model/application_context", + "//ios/chrome/browser/shared/model/utils", + "//ios/chrome/browser/shared/public/features", + ] +} + +source_set("unit_tests") { + testonly = true + sources = [ + "default_status_helper_metrics_unittest.mm", + "default_status_helper_unittest.mm", + ] + deps = [ + ":default_status", + "//base/test:test_support", + "//components/prefs:test_support", + "//ios/chrome/browser/shared/model/utils:test_support", + ] +}
diff --git a/ios/chrome/browser/default_browser/model/default_status/default_status_helper.h b/ios/chrome/browser/default_browser/model/default_status/default_status_helper.h new file mode 100644 index 0000000..a4bd83bab --- /dev/null +++ b/ios/chrome/browser/default_browser/model/default_status/default_status_helper.h
@@ -0,0 +1,94 @@ +// 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 IOS_CHROME_BROWSER_DEFAULT_BROWSER_MODEL_DEFAULT_STATUS_DEFAULT_STATUS_HELPER_H_ +#define IOS_CHROME_BROWSER_DEFAULT_BROWSER_MODEL_DEFAULT_STATUS_DEFAULT_STATUS_HELPER_H_ + +#import <UIKit/UIKit.h> + +#import "base/functional/callback.h" + +class PrefService; + +namespace base { +class Time; +} // namespace base + +// This helper contains the necessary logic to integrate with the default status +// system API. At a high level, the integration works as follows: +// - To spread the reporting out throughout the year, clients are randomly +// assigned to a monthly cohort. The cohort assignment is persisted to +// prefs. +// - There are currently 4 cohorts, which correspond to the following months: +// - Cohort 1: January, May, September +// - Cohort 2: February, June, October +// - Cohort 3: March, July, November +// - Cohort 4: April, August, December +// - At startup, if the current date is at or after the assigned cohort's +// reporting window, a call to the default status API is triggered and +// relevant metrics and prefs are recorded. +namespace default_status { + +enum class DefaultStatusAPIResult; +enum class DefaultStatusRetention; + +// Do not call these functions; they are exposed for unit testing only. +namespace internal { + +// Returns true if the current client is too recent (based on time since +// completing the first run experience) to be assigned to a cohort. +bool IsNewClient(); + +// Returns true if the current client is already part of a cohort. +bool HasCohortAssigned(PrefService* local_state); + +// Returns a random cohort number between 1 and the maximum number of cohorts. +int GenerateRandomCohort(); + +// Returns a base::Time representing the first day of the next reporting window +// for the given cohort number. By default, if the current date is already +// within the specified cohort's reporting window, then the first day of the +// current month is returned. If `after_current_month` is set to true, the +// computation to find the next reporting window starts at the beginning of next +// month. All time computations are in UTC. +base::Time GetCohortNextStartDate(int cohort_number, + bool after_current_month = false); + +// If eligible (correct system API availability, minimum client age met, no +// cohort already assigned), randomly assigns the client to a reporting cohort +// and sets the default status API call's next retry date to that cohort's next +// reporting window. +void AssignNewCohortIfNeeded(PrefService* local_state); + +// Converts the system's default status enum value to the one used by this +// helper. +DefaultStatusAPIResult SystemToLocalEnum( + UIApplicationCategoryDefaultStatus system_enum) API_AVAILABLE(ios(18.4)); + +// Returns the retention type based on the previous and current default status +// result. +DefaultStatusRetention DetermineRetentionStatus(DefaultStatusAPIResult previous, + DefaultStatusAPIResult current); + +// Initiates the system API call if this client is outside the cooldown period, +// and logs all relevant metrics based on the result. +void QueryDefaultStatusIfReadyAndLogResults(PrefService* local_state); + +// Use in tests only. The specified callback will be invoked instead of making a +// real call to the default status API. +void OverrideSystemCallForTesting( + base::OnceCallback<UIApplicationCategoryDefaultStatus(NSError**)> cb) + API_AVAILABLE(ios(18.4)); + +} // namespace internal + +// Queries the system API to determine whether this is the default browser, and +// logs the result. This is NOT meant to be used by feature code, as the system +// call is rate limited. Instead, use the helpers in default browser utils to +// check whether this is the likely the default browser. +void TriggerDefaultStatusCheck(); + +} // namespace default_status + +#endif // IOS_CHROME_BROWSER_DEFAULT_BROWSER_MODEL_DEFAULT_STATUS_DEFAULT_STATUS_HELPER_H_
diff --git a/ios/chrome/browser/default_browser/model/default_status/default_status_helper.mm b/ios/chrome/browser/default_browser/model/default_status/default_status_helper.mm new file mode 100644 index 0000000..8fe109e --- /dev/null +++ b/ios/chrome/browser/default_browser/model/default_status/default_status_helper.mm
@@ -0,0 +1,278 @@ +// 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 "ios/chrome/browser/default_browser/model/default_status/default_status_helper.h" + +#import <UIKit/UIKit.h> + +#import "base/check.h" +#import "base/check_is_test.h" +#import "base/functional/callback.h" +#import "base/rand_util.h" +#import "base/time/time.h" +#import "components/prefs/pref_service.h" +#import "ios/chrome/browser/default_browser/model/default_status/default_status_helper_constants.h" +#import "ios/chrome/browser/default_browser/model/default_status/default_status_helper_metrics.h" +#import "ios/chrome/browser/default_browser/model/default_status/default_status_helper_prefs.h" +#import "ios/chrome/browser/default_browser/model/default_status/default_status_helper_types.h" +#import "ios/chrome/browser/shared/model/application_context/application_context.h" +#import "ios/chrome/browser/shared/model/utils/first_run_util.h" +#import "ios/chrome/browser/shared/public/features/features.h" + +namespace default_status { + +namespace internal { + +namespace { + +// The minimum time required since completing the first run experience to be +// assigned to a cohort. +const base::TimeDelta kMinimumNewClientFirstRunAge = base::Days(28); + +// Gets the system call to use/override for testing. Uses a function so the +// static variable can be contained inside the function. +base::OnceCallback<UIApplicationCategoryDefaultStatus(NSError**)>& +GetSystemCallForTesting() API_AVAILABLE(ios(18.4)) { + static base::NoDestructor< + base::OnceCallback<UIApplicationCategoryDefaultStatus(NSError**)>> + g_system_call_for_testing; + return *g_system_call_for_testing; +} + +// Performs the actual system call to the default status check API. Wrapped in +// its own function to facilitate mocking in tests. +UIApplicationCategoryDefaultStatus MakeSystemCall(NSError** error) + API_AVAILABLE(ios(18.4)) { + if (GetSystemCallForTesting()) { + CHECK_IS_TEST(); + return std::move(GetSystemCallForTesting()).Run(error); // IN-TEST + } + return [[UIApplication sharedApplication] + defaultStatusForCategory:UIApplicationCategoryWebBrowser + error:error]; +} + +} // namespace + +bool IsNewClient() { + return IsFirstRunRecent(kMinimumNewClientFirstRunAge); +} + +bool HasCohortAssigned(PrefService* local_state) { + return local_state->GetInteger(kDefaultStatusAPICohort) != 0; +} + +int GenerateRandomCohort() { + // The arguments passed to RandInt are inclusive. + return base::RandInt(1, kCohortCount); +} + +base::Time GetCohortNextStartDate(int cohort_number, bool after_current_month) { + DUMP_WILL_BE_CHECK(cohort_number > 0 && cohort_number <= kCohortCount); + + base::Time::Exploded now_exploded; + base::Time::Now().UTCExplode(&now_exploded); + DUMP_WILL_BE_CHECK(now_exploded.HasValidValues()); + base::Time::Exploded target_date; + target_date.year = now_exploded.year; + target_date.month = now_exploded.month; + // Day of week does not matter when converting back to base::Time. + target_date.day_of_week = 0; + target_date.day_of_month = 1; + target_date.hour = 0; + target_date.minute = 0; + target_date.second = 0; + target_date.millisecond = 0; + + // Determine which cohort number the current month corresponds to. This + // requires a modulo on the cohort count, but because the cohort number is + // 1-based, substract one before and add one after. + int current_cohort = (now_exploded.month - 1) % kCohortCount + 1; + + if (current_cohort == cohort_number && !after_current_month) { + // If the current cohort is the same as the specified cohort, the target + // month is the current month. + target_date.year = now_exploded.year; + target_date.month = now_exploded.month; + } else { + // Otherwise, find the "cohort cycle" in which the target month is - the + // target month is the start of that cycle plus the cohort number. If the + // specified cohort is after the current cohort, then the target month is + // later in this cohort cycle. If it's before, it means the desired cohort + // has already passed this cycle, so the target month is in the next. + int target_cycle = (now_exploded.month - 1) / kCohortCount; + if (current_cohort >= cohort_number) { + ++target_cycle; + } + int target_month = target_cycle * kCohortCount + cohort_number; + + // If the target month is greater than 12, decrease it by 12 and bump the + // target year. + if (target_month > 12) { + ++target_date.year; + target_month -= 12; + } + + target_date.month = target_month; + } + + base::Time result; + DUMP_WILL_BE_CHECK(base::Time::FromUTCExploded(target_date, &result)); + return result; +} + +void AssignNewCohortIfNeeded(PrefService* local_state) { + if (!@available(iOS 18.4, *)) { + return; + } + + if (IsNewClient()) { + return; + } + + if (HasCohortAssigned(local_state)) { + return; + } + + int cohort = GenerateRandomCohort(); + local_state->SetInteger(kDefaultStatusAPICohort, cohort); + base::Time next_retry = GetCohortNextStartDate(cohort); + local_state->SetTime(kDefaultStatusAPINextRetry, next_retry); +} + +DefaultStatusAPIResult SystemToLocalEnum( + UIApplicationCategoryDefaultStatus system_enum) API_AVAILABLE(ios(18.4)) { + switch (system_enum) { + case UIApplicationCategoryDefaultStatusIsDefault: + return DefaultStatusAPIResult::kIsDefault; + case UIApplicationCategoryDefaultStatusNotDefault: + return DefaultStatusAPIResult::kIsNotDefault; + case UIApplicationCategoryDefaultStatusUnavailable: + // If the call was unavailable, the default status of the user is + // considered unknown. + return DefaultStatusAPIResult::kUnknown; + default: + return DefaultStatusAPIResult::kUnknown; + } +} + +DefaultStatusRetention DetermineRetentionStatus( + DefaultStatusAPIResult previous, + DefaultStatusAPIResult current) { + DUMP_WILL_BE_CHECK(previous != DefaultStatusAPIResult::kUnknown && + current != DefaultStatusAPIResult::kUnknown); + if (previous == DefaultStatusAPIResult::kIsDefault) { + if (current == DefaultStatusAPIResult::kIsDefault) { + return DefaultStatusRetention::kRemainedDefault; + } else { + return DefaultStatusRetention::kBecameNonDefault; + } + } else { + if (current == DefaultStatusAPIResult::kIsDefault) { + return DefaultStatusRetention::kBecameDefault; + } else { + return DefaultStatusRetention::kRemainedNonDefault; + } + } +} + +void QueryDefaultStatusIfReadyAndLogResults(PrefService* local_state) { + if (@available(iOS 18.4, *)) { + int cohort_number = local_state->GetInteger(kDefaultStatusAPICohort); + DUMP_WILL_BE_CHECK(cohort_number > 0 && cohort_number <= kCohortCount); + + base::Time now = base::Time::Now(); + base::Time next_retry = local_state->GetTime(kDefaultStatusAPINextRetry); + if (now < next_retry) { + return; + } + + NSError* error = nil; + UIApplicationCategoryDefaultStatus default_status = MakeSystemCall(&error); + + if (error) { + if ([error code] == UIApplicationCategoryDefaultErrorRateLimited) { + DUMP_WILL_BE_CHECK( + error.userInfo + [UIApplicationCategoryDefaultRetryAvailabilityDateErrorKey] != + nil); + RecordDefaultStatusAPIOutcomeType( + DefaultStatusAPIOutcomeType::kCooldownError); + next_retry = base::Time::FromNSDate( + error.userInfo + [UIApplicationCategoryDefaultRetryAvailabilityDateErrorKey]); + local_state->SetTime(kDefaultStatusAPINextRetry, next_retry); + int days_left = (next_retry - now).InDays(); + RecordCooldownErrorDaysLeft(days_left); + } else { + RecordDefaultStatusAPIOutcomeType( + DefaultStatusAPIOutcomeType::kOtherError); + } + } else { + RecordDefaultStatusAPIOutcomeType(DefaultStatusAPIOutcomeType::kSuccess); + + DefaultStatusAPIResult current_result = SystemToLocalEnum(default_status); + DUMP_WILL_BE_CHECK(current_result != DefaultStatusAPIResult::kUnknown); + + base::Time cohort_start = GetCohortNextStartDate(cohort_number); + int next_cohort_number = cohort_number % kCohortCount + 1; + base::Time next_cohort_start = GetCohortNextStartDate(next_cohort_number); + bool is_within_cohort_window = + now >= cohort_start && now < next_cohort_start; + bool is_default = current_result == DefaultStatusAPIResult::kIsDefault; + RecordDefaultStatusAPIResult(is_default, cohort_number, + is_within_cohort_window); + RecordHeuristicAssessments(is_default); + + DefaultStatusAPIResult previous_result = + static_cast<DefaultStatusAPIResult>( + local_state->GetInteger(kDefaultStatusAPIResult)); + if (previous_result != DefaultStatusAPIResult::kUnknown) { + base::Time last_successful_call = + local_state->GetTime(kDefaultStatusAPILastSuccessfulCall); + RecordDaysSinceLastSuccessfulCall( + (now - last_successful_call).InDays()); + RecordDefaultStatusRetention( + DetermineRetentionStatus(previous_result, current_result)); + } + + local_state->SetTime(kDefaultStatusAPILastSuccessfulCall, now); + local_state->SetInteger(kDefaultStatusAPIResult, + static_cast<int>(current_result)); + next_retry = + GetCohortNextStartDate(cohort_number, /*after_current_month=*/true); + local_state->SetTime(kDefaultStatusAPINextRetry, next_retry); + } + } +} + +void OverrideSystemCallForTesting( // IN-TEST + base::OnceCallback<UIApplicationCategoryDefaultStatus(NSError**)> cb) + API_AVAILABLE(ios(18.4)) { + CHECK_IS_TEST(); + internal::GetSystemCallForTesting() = std::move(cb); +} + +} // namespace internal + +void TriggerDefaultStatusCheck() { + if (!IsRunDefaultStatusCheckEnabled()) { + return; + } + + if (!@available(iOS 18.4, *)) { + return; + } + + if (internal::IsNewClient()) { + return; + } + + ApplicationContext* applicationContext = GetApplicationContext(); + PrefService* localState = applicationContext->GetLocalState(); + internal::AssignNewCohortIfNeeded(localState); + internal::QueryDefaultStatusIfReadyAndLogResults(localState); +} + +} // namespace default_status
diff --git a/ios/chrome/browser/default_browser/model/default_status/default_status_helper_constants.h b/ios/chrome/browser/default_browser/model/default_status/default_status_helper_constants.h new file mode 100644 index 0000000..64c09465 --- /dev/null +++ b/ios/chrome/browser/default_browser/model/default_status/default_status_helper_constants.h
@@ -0,0 +1,12 @@ +// 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 IOS_CHROME_BROWSER_DEFAULT_BROWSER_MODEL_DEFAULT_STATUS_DEFAULT_STATUS_HELPER_CONSTANTS_H_ +#define IOS_CHROME_BROWSER_DEFAULT_BROWSER_MODEL_DEFAULT_STATUS_DEFAULT_STATUS_HELPER_CONSTANTS_H_ + +// The number of reporting cohorts for the default status API call. See the +// comments in `default_status_helper.h` for more info. +extern const int kCohortCount; + +#endif // IOS_CHROME_BROWSER_DEFAULT_BROWSER_MODEL_DEFAULT_STATUS_DEFAULT_STATUS_HELPER_CONSTANTS_H_
diff --git a/ios/chrome/browser/default_browser/model/default_status/default_status_helper_constants.mm b/ios/chrome/browser/default_browser/model/default_status/default_status_helper_constants.mm new file mode 100644 index 0000000..021b3d0 --- /dev/null +++ b/ios/chrome/browser/default_browser/model/default_status/default_status_helper_constants.mm
@@ -0,0 +1,18 @@ +// 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 "ios/chrome/browser/default_browser/model/default_status/default_status_helper_constants.h" + +// The cohorts are monthly, aligned with the Gregorian calendar. The logic in +// this file should work for any cohort count between 1 and 12. Anything higher +// than that means the cohorts are no longer aligned to calendar months, so +// logic needs to be revisited. +// +// Changing this value requires updating the +// IOS.DefaultStatusAPI.IsDefaultBrowser.Cohort{Number} histogram to make sure +// there is a variant for each cohort. +// +// LINT.IfChange(kCohortCount) +const int kCohortCount = 4; +// LINT.ThenChange(/tools/metrics/histograms/metadata/ios/histograms.xml:CohortNumber)
diff --git a/ios/chrome/browser/default_browser/model/default_status/default_status_helper_metrics.h b/ios/chrome/browser/default_browser/model/default_status/default_status_helper_metrics.h new file mode 100644 index 0000000..460f37b --- /dev/null +++ b/ios/chrome/browser/default_browser/model/default_status/default_status_helper_metrics.h
@@ -0,0 +1,51 @@ +// 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 IOS_CHROME_BROWSER_DEFAULT_BROWSER_MODEL_DEFAULT_STATUS_DEFAULT_STATUS_HELPER_METRICS_H_ +#define IOS_CHROME_BROWSER_DEFAULT_BROWSER_MODEL_DEFAULT_STATUS_DEFAULT_STATUS_HELPER_METRICS_H_ + +namespace default_status { + +enum class DefaultStatusAPIOutcomeType; +enum class DefaultStatusHeuristicAssessment; +enum class DefaultStatusRetention; + +// Do not call these functions; they are exposed for unit testing only. +namespace internal { + +// Helper function to compare the heuristic and the system results. Exposed for +// unit testing. +DefaultStatusHeuristicAssessment AssessHeuristic(bool system_is_default, + bool heuristic_is_default); +} // namespace internal + +// Records the number of days left in the system call cooldown period. +void RecordCooldownErrorDaysLeft(int days_left); + +// Records how many days have passed since the last successful default status +// API call. +void RecordDaysSinceLastSuccessfulCall(int days); + +// Helper to record the type of outcome (error or success) of the default status +// API call. +void RecordDefaultStatusAPIOutcomeType( + DefaultStatusAPIOutcomeType outcome_type); + +// Helper to record the result of a successful default status API call to +// various histograms based on the client's cohort. +void RecordDefaultStatusAPIResult(bool is_default, + int cohort_number, + bool is_within_cohort_window); + +// Records the default status retention since the last successful default status +// API call. +void RecordDefaultStatusRetention(DefaultStatusRetention retention); + +// Records the difference between the provided default status according to the +// system API, and Chrome's heuristics. +void RecordHeuristicAssessments(bool is_default_in_system_api); + +} // namespace default_status + +#endif // IOS_CHROME_BROWSER_DEFAULT_BROWSER_MODEL_DEFAULT_STATUS_DEFAULT_STATUS_HELPER_METRICS_H_
diff --git a/ios/chrome/browser/default_browser/model/default_status/default_status_helper_metrics.mm b/ios/chrome/browser/default_browser/model/default_status/default_status_helper_metrics.mm new file mode 100644 index 0000000..a92f3fc --- /dev/null +++ b/ios/chrome/browser/default_browser/model/default_status/default_status_helper_metrics.mm
@@ -0,0 +1,110 @@ +// 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 "ios/chrome/browser/default_browser/model/default_status/default_status_helper_metrics.h" + +#import "base/metrics/histogram_functions.h" +#import "base/strings/stringprintf.h" +#import "ios/chrome/browser/default_browser/model/default_status/default_status_helper_constants.h" +#import "ios/chrome/browser/default_browser/model/default_status/default_status_helper_types.h" +#import "ios/chrome/browser/default_browser/model/utils.h" + +namespace default_status { + +namespace internal { + +DefaultStatusHeuristicAssessment AssessHeuristic(bool system_is_default, + bool heuristic_is_default) { + if (heuristic_is_default) { + if (system_is_default) { + return DefaultStatusHeuristicAssessment::kTruePositive; + } else { + return DefaultStatusHeuristicAssessment::kFalsePositive; + } + } else { + if (system_is_default) { + return DefaultStatusHeuristicAssessment::kFalseNegative; + } else { + return DefaultStatusHeuristicAssessment::kTrueNegative; + } + } +} + +} // namespace internal + +void RecordCooldownErrorDaysLeft(int days_left) { + base::UmaHistogramCounts1000("IOS.DefaultStatusAPI.CooldownError.DaysLeft", + days_left); +} + +void RecordDaysSinceLastSuccessfulCall(int days) { + base::UmaHistogramCounts1000( + "IOS.DefaultStatusAPI.DaysSinceLastSuccessfulCall", days); +} + +void RecordDefaultStatusAPIOutcomeType( + DefaultStatusAPIOutcomeType outcome_type) { + base::UmaHistogramEnumeration("IOS.DefaultStatusAPI.OutcomeType", + outcome_type); +} + +void RecordDefaultStatusAPIResult(bool is_default, + int cohort_number, + bool is_within_cohort_window) { + DUMP_WILL_BE_CHECK(cohort_number >= 1 && cohort_number <= kCohortCount); + + base::UmaHistogramBoolean("IOS.DefaultStatusAPI.IsDefaultBrowser", + is_default); + + base::UmaHistogramBoolean( + base::StringPrintf("IOS.DefaultStatusAPI.IsDefaultBrowser.Cohort%d", + cohort_number), + is_default); + if (is_within_cohort_window) { + base::UmaHistogramBoolean( + "IOS.DefaultStatusAPI.IsDefaultBrowser.StrictCohorts", is_default); + } +} + +void RecordDefaultStatusRetention(DefaultStatusRetention retention) { + base::UmaHistogramEnumeration("IOS.DefaultStatusAPI.DefaultStatusRetention", + retention); +} + +void RecordHeuristicAssessments(bool is_default_in_system_api) { + DefaultStatusHeuristicAssessment assessment = internal::AssessHeuristic( + is_default_in_system_api, IsChromeLikelyDefaultBrowserXDays(1)); + base::UmaHistogramEnumeration("IOS.DefaultStatusAPI.HeuristicAssessment1", + assessment); + assessment = internal::AssessHeuristic(is_default_in_system_api, + IsChromeLikelyDefaultBrowserXDays(3)); + base::UmaHistogramEnumeration("IOS.DefaultStatusAPI.HeuristicAssessment3", + assessment); + assessment = internal::AssessHeuristic(is_default_in_system_api, + IsChromeLikelyDefaultBrowserXDays(7)); + base::UmaHistogramEnumeration("IOS.DefaultStatusAPI.HeuristicAssessment7", + assessment); + assessment = internal::AssessHeuristic(is_default_in_system_api, + IsChromeLikelyDefaultBrowserXDays(14)); + base::UmaHistogramEnumeration("IOS.DefaultStatusAPI.HeuristicAssessment14", + assessment); + assessment = internal::AssessHeuristic(is_default_in_system_api, + IsChromeLikelyDefaultBrowserXDays(21)); + base::UmaHistogramEnumeration("IOS.DefaultStatusAPI.HeuristicAssessment21", + assessment); + assessment = internal::AssessHeuristic(is_default_in_system_api, + IsChromeLikelyDefaultBrowserXDays(28)); + base::UmaHistogramEnumeration("IOS.DefaultStatusAPI.HeuristicAssessment28", + assessment); + assessment = internal::AssessHeuristic(is_default_in_system_api, + IsChromeLikelyDefaultBrowserXDays(35)); + base::UmaHistogramEnumeration("IOS.DefaultStatusAPI.HeuristicAssessment35", + assessment); + assessment = internal::AssessHeuristic(is_default_in_system_api, + IsChromeLikelyDefaultBrowserXDays(42)); + base::UmaHistogramEnumeration("IOS.DefaultStatusAPI.HeuristicAssessment42", + assessment); +} + +} // namespace default_status
diff --git a/ios/chrome/browser/default_browser/model/default_status/default_status_helper_metrics_unittest.mm b/ios/chrome/browser/default_browser/model/default_status/default_status_helper_metrics_unittest.mm new file mode 100644 index 0000000..8661ba6 --- /dev/null +++ b/ios/chrome/browser/default_browser/model/default_status/default_status_helper_metrics_unittest.mm
@@ -0,0 +1,787 @@ +// 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 "ios/chrome/browser/default_browser/model/default_status/default_status_helper_metrics.h" + +#import "base/test/metrics/histogram_tester.h" +#import "ios/chrome/browser/default_browser/model/default_status/default_status_helper_types.h" +#import "ios/chrome/browser/default_browser/model/utils.h" +#import "ios/chrome/browser/shared/public/features/features.h" +#import "testing/platform_test.h" + +namespace default_status { + +// Storage key for recording the last time an http link was opened via Chrome, +// which indicates that it's set as default browser. +NSString* const kLastHTTPURLOpenTime = @"lastHTTPURLOpenTime"; + +// Test fixture for testing the default status metrics. +class DefaultStatusHelperMetricsTest : public PlatformTest {}; + +// Tests that the heuristic assessment returns the correct value. +TEST_F(DefaultStatusHelperMetricsTest, HeuristicAssessment) { + EXPECT_EQ(internal::AssessHeuristic(/*system_is_default=*/false, + /*heuristic_is_default=*/false), + DefaultStatusHeuristicAssessment::kTrueNegative); + EXPECT_EQ(internal::AssessHeuristic(/*system_is_default=*/false, + /*heuristic_is_default=*/true), + DefaultStatusHeuristicAssessment::kFalsePositive); + EXPECT_EQ(internal::AssessHeuristic(/*system_is_default=*/true, + /*heuristic_is_default=*/false), + DefaultStatusHeuristicAssessment::kFalseNegative); + EXPECT_EQ(internal::AssessHeuristic(/*system_is_default=*/true, + /*heuristic_is_default=*/true), + DefaultStatusHeuristicAssessment::kTruePositive); +} + +// Tests that the RecordCooldownErrorDaysLeft function records the correct +// values. +TEST_F(DefaultStatusHelperMetricsTest, RecordCooldownErrorDaysLeft) { + base::HistogramTester histogram_tester; + + RecordCooldownErrorDaysLeft(15); + histogram_tester.ExpectBucketCount( + "IOS.DefaultStatusAPI.CooldownError.DaysLeft", 15, 1); + + RecordCooldownErrorDaysLeft(1005); + RecordCooldownErrorDaysLeft(1105); + histogram_tester.ExpectBucketCount( + "IOS.DefaultStatusAPI.CooldownError.DaysLeft", 1000, 2); + + histogram_tester.ExpectTotalCount( + "IOS.DefaultStatusAPI.CooldownError.DaysLeft", 3); +} + +// Tests that the RecordDaysSinceLastSuccessfulCall function records the +// correct values. +TEST_F(DefaultStatusHelperMetricsTest, RecordDaysSinceLastSuccessfulCall) { + base::HistogramTester histogram_tester; + + RecordDaysSinceLastSuccessfulCall(15); + histogram_tester.ExpectBucketCount( + "IOS.DefaultStatusAPI.DaysSinceLastSuccessfulCall", 15, 1); + + RecordDaysSinceLastSuccessfulCall(1005); + RecordDaysSinceLastSuccessfulCall(1105); + histogram_tester.ExpectBucketCount( + "IOS.DefaultStatusAPI.DaysSinceLastSuccessfulCall", 1000, 2); + + histogram_tester.ExpectTotalCount( + "IOS.DefaultStatusAPI.DaysSinceLastSuccessfulCall", 3); +} + +// Tests that the RecordDefaultStatusAPIOutcomeType function records the +// correct values. +TEST_F(DefaultStatusHelperMetricsTest, RecordDefaultStatusAPIOutcomeType) { + base::HistogramTester histogram_tester; + + RecordDefaultStatusAPIOutcomeType(DefaultStatusAPIOutcomeType::kSuccess); + histogram_tester.ExpectBucketCount("IOS.DefaultStatusAPI.OutcomeType", + DefaultStatusAPIOutcomeType::kSuccess, 1); + + RecordDefaultStatusAPIOutcomeType( + DefaultStatusAPIOutcomeType::kCooldownError); + histogram_tester.ExpectBucketCount( + "IOS.DefaultStatusAPI.OutcomeType", + DefaultStatusAPIOutcomeType::kCooldownError, 1); + + RecordDefaultStatusAPIOutcomeType(DefaultStatusAPIOutcomeType::kOtherError); + histogram_tester.ExpectBucketCount("IOS.DefaultStatusAPI.OutcomeType", + DefaultStatusAPIOutcomeType::kOtherError, + 1); + + histogram_tester.ExpectTotalCount("IOS.DefaultStatusAPI.OutcomeType", 3); +} + +// Tests that the RecordDefaultStatusAPIResult function records the correct +// values. +TEST_F(DefaultStatusHelperMetricsTest, RecordDefaultStatusAPIResult) { + base::HistogramTester histogram_tester; + + // Cohort 1. + RecordDefaultStatusAPIResult(/*is_default=*/true, /*cohort_number=*/1, + /*is_within_cohort_window=*/true); + histogram_tester.ExpectBucketCount("IOS.DefaultStatusAPI.IsDefaultBrowser", + true, 1); + histogram_tester.ExpectBucketCount( + "IOS.DefaultStatusAPI.IsDefaultBrowser.StrictCohorts", true, 1); + histogram_tester.ExpectBucketCount( + "IOS.DefaultStatusAPI.IsDefaultBrowser.Cohort1", true, 1); + + RecordDefaultStatusAPIResult(/*is_default=*/false, /*cohort_number=*/1, + /*is_within_cohort_window=*/true); + histogram_tester.ExpectBucketCount("IOS.DefaultStatusAPI.IsDefaultBrowser", + false, 1); + histogram_tester.ExpectBucketCount( + "IOS.DefaultStatusAPI.IsDefaultBrowser.StrictCohorts", false, 1); + histogram_tester.ExpectBucketCount( + "IOS.DefaultStatusAPI.IsDefaultBrowser.Cohort1", false, 1); + + RecordDefaultStatusAPIResult(/*is_default=*/true, /*cohort_number=*/1, + /*is_within_cohort_window=*/false); + histogram_tester.ExpectBucketCount("IOS.DefaultStatusAPI.IsDefaultBrowser", + true, 2); + histogram_tester.ExpectBucketCount( + "IOS.DefaultStatusAPI.IsDefaultBrowser.Cohort1", true, 2); + + RecordDefaultStatusAPIResult(/*is_default=*/false, /*cohort_number=*/1, + /*is_within_cohort_window=*/false); + histogram_tester.ExpectBucketCount("IOS.DefaultStatusAPI.IsDefaultBrowser", + false, 2); + histogram_tester.ExpectBucketCount( + "IOS.DefaultStatusAPI.IsDefaultBrowser.Cohort1", false, 2); + + // Cohort 2. + RecordDefaultStatusAPIResult(/*is_default=*/true, /*cohort_number=*/2, + /*is_within_cohort_window=*/true); + histogram_tester.ExpectBucketCount("IOS.DefaultStatusAPI.IsDefaultBrowser", + true, 3); + histogram_tester.ExpectBucketCount( + "IOS.DefaultStatusAPI.IsDefaultBrowser.StrictCohorts", true, 2); + histogram_tester.ExpectBucketCount( + "IOS.DefaultStatusAPI.IsDefaultBrowser.Cohort2", true, 1); + + RecordDefaultStatusAPIResult(/*is_default=*/false, /*cohort_number=*/2, + /*is_within_cohort_window=*/true); + histogram_tester.ExpectBucketCount("IOS.DefaultStatusAPI.IsDefaultBrowser", + false, 3); + histogram_tester.ExpectBucketCount( + "IOS.DefaultStatusAPI.IsDefaultBrowser.StrictCohorts", false, 2); + histogram_tester.ExpectBucketCount( + "IOS.DefaultStatusAPI.IsDefaultBrowser.Cohort2", false, 1); + + RecordDefaultStatusAPIResult(/*is_default=*/true, /*cohort_number=*/2, + /*is_within_cohort_window=*/false); + histogram_tester.ExpectBucketCount("IOS.DefaultStatusAPI.IsDefaultBrowser", + true, 4); + histogram_tester.ExpectBucketCount( + "IOS.DefaultStatusAPI.IsDefaultBrowser.Cohort2", true, 2); + + RecordDefaultStatusAPIResult(/*is_default=*/false, /*cohort_number=*/2, + /*is_within_cohort_window=*/false); + histogram_tester.ExpectBucketCount("IOS.DefaultStatusAPI.IsDefaultBrowser", + false, 4); + histogram_tester.ExpectBucketCount( + "IOS.DefaultStatusAPI.IsDefaultBrowser.Cohort2", false, 2); + + // Cohort 3. + RecordDefaultStatusAPIResult(/*is_default=*/true, /*cohort_number=*/3, + /*is_within_cohort_window=*/true); + histogram_tester.ExpectBucketCount("IOS.DefaultStatusAPI.IsDefaultBrowser", + true, 5); + histogram_tester.ExpectBucketCount( + "IOS.DefaultStatusAPI.IsDefaultBrowser.StrictCohorts", true, 3); + histogram_tester.ExpectBucketCount( + "IOS.DefaultStatusAPI.IsDefaultBrowser.Cohort3", true, 1); + + RecordDefaultStatusAPIResult(/*is_default=*/false, /*cohort_number=*/3, + /*is_within_cohort_window=*/true); + histogram_tester.ExpectBucketCount("IOS.DefaultStatusAPI.IsDefaultBrowser", + false, 5); + histogram_tester.ExpectBucketCount( + "IOS.DefaultStatusAPI.IsDefaultBrowser.StrictCohorts", false, 3); + histogram_tester.ExpectBucketCount( + "IOS.DefaultStatusAPI.IsDefaultBrowser.Cohort3", false, 1); + + RecordDefaultStatusAPIResult(/*is_default=*/true, /*cohort_number=*/3, + /*is_within_cohort_window=*/false); + histogram_tester.ExpectBucketCount("IOS.DefaultStatusAPI.IsDefaultBrowser", + true, 6); + histogram_tester.ExpectBucketCount( + "IOS.DefaultStatusAPI.IsDefaultBrowser.Cohort3", true, 2); + + RecordDefaultStatusAPIResult(/*is_default=*/false, /*cohort_number=*/3, + /*is_within_cohort_window=*/false); + histogram_tester.ExpectBucketCount("IOS.DefaultStatusAPI.IsDefaultBrowser", + false, 6); + histogram_tester.ExpectBucketCount( + "IOS.DefaultStatusAPI.IsDefaultBrowser.Cohort3", false, 2); + + // Cohort 4. + RecordDefaultStatusAPIResult(/*is_default=*/true, /*cohort_number=*/4, + /*is_within_cohort_window=*/true); + histogram_tester.ExpectBucketCount("IOS.DefaultStatusAPI.IsDefaultBrowser", + true, 7); + histogram_tester.ExpectBucketCount( + "IOS.DefaultStatusAPI.IsDefaultBrowser.StrictCohorts", true, 4); + histogram_tester.ExpectBucketCount( + "IOS.DefaultStatusAPI.IsDefaultBrowser.Cohort4", true, 1); + + RecordDefaultStatusAPIResult(/*is_default=*/false, /*cohort_number=*/4, + /*is_within_cohort_window=*/true); + histogram_tester.ExpectBucketCount("IOS.DefaultStatusAPI.IsDefaultBrowser", + false, 7); + histogram_tester.ExpectBucketCount( + "IOS.DefaultStatusAPI.IsDefaultBrowser.StrictCohorts", false, 4); + histogram_tester.ExpectBucketCount( + "IOS.DefaultStatusAPI.IsDefaultBrowser.Cohort4", false, 1); + + RecordDefaultStatusAPIResult(/*is_default=*/true, /*cohort_number=*/4, + /*is_within_cohort_window=*/false); + histogram_tester.ExpectBucketCount("IOS.DefaultStatusAPI.IsDefaultBrowser", + true, 8); + histogram_tester.ExpectBucketCount( + "IOS.DefaultStatusAPI.IsDefaultBrowser.Cohort4", true, 2); + + RecordDefaultStatusAPIResult(/*is_default=*/false, /*cohort_number=*/4, + /*is_within_cohort_window=*/false); + histogram_tester.ExpectBucketCount("IOS.DefaultStatusAPI.IsDefaultBrowser", + false, 8); + histogram_tester.ExpectBucketCount( + "IOS.DefaultStatusAPI.IsDefaultBrowser.Cohort4", false, 2); + + // Summary. + histogram_tester.ExpectTotalCount("IOS.DefaultStatusAPI.IsDefaultBrowser", + 16); + histogram_tester.ExpectTotalCount( + "IOS.DefaultStatusAPI.IsDefaultBrowser.StrictCohorts", 8); + histogram_tester.ExpectTotalCount( + "IOS.DefaultStatusAPI.IsDefaultBrowser.Cohort1", 4); + histogram_tester.ExpectTotalCount( + "IOS.DefaultStatusAPI.IsDefaultBrowser.Cohort2", 4); + histogram_tester.ExpectTotalCount( + "IOS.DefaultStatusAPI.IsDefaultBrowser.Cohort3", 4); + histogram_tester.ExpectTotalCount( + "IOS.DefaultStatusAPI.IsDefaultBrowser.Cohort4", 4); +} + +// Tests that the RecordDefaultStatusRetention function records the correct +// values. +TEST_F(DefaultStatusHelperMetricsTest, RecordDefaultStatusRetention) { + base::HistogramTester histogram_tester; + + RecordDefaultStatusRetention(DefaultStatusRetention::kBecameDefault); + histogram_tester.ExpectBucketCount( + "IOS.DefaultStatusAPI.DefaultStatusRetention", + DefaultStatusRetention::kBecameDefault, 1); + + RecordDefaultStatusRetention(DefaultStatusRetention::kBecameNonDefault); + histogram_tester.ExpectBucketCount( + "IOS.DefaultStatusAPI.DefaultStatusRetention", + DefaultStatusRetention::kBecameNonDefault, 1); + + RecordDefaultStatusRetention(DefaultStatusRetention::kRemainedDefault); + histogram_tester.ExpectBucketCount( + "IOS.DefaultStatusAPI.DefaultStatusRetention", + DefaultStatusRetention::kRemainedDefault, 1); + + RecordDefaultStatusRetention(DefaultStatusRetention::kRemainedNonDefault); + histogram_tester.ExpectBucketCount( + "IOS.DefaultStatusAPI.DefaultStatusRetention", + DefaultStatusRetention::kRemainedNonDefault, 1); + + histogram_tester.ExpectTotalCount( + "IOS.DefaultStatusAPI.DefaultStatusRetention", 4); +} + +// Tests that the RecordHeuristicAssessments function records the correct +// values. +TEST_F(DefaultStatusHelperMetricsTest, RecordHeuristicAssessments) { + base::HistogramTester histogram_tester; + + NSDate* under_one_day_ago = + (base::Time::Now() - base::Days(1) + base::Minutes(10)).ToNSDate(); + SetObjectIntoStorageForKey(kLastHTTPURLOpenTime, under_one_day_ago); + RecordHeuristicAssessments(/*is_default_in_system_api=*/true); + histogram_tester.ExpectBucketCount( + "IOS.DefaultStatusAPI.HeuristicAssessment1", + DefaultStatusHeuristicAssessment::kTruePositive, 1); + histogram_tester.ExpectBucketCount( + "IOS.DefaultStatusAPI.HeuristicAssessment3", + DefaultStatusHeuristicAssessment::kTruePositive, 1); + histogram_tester.ExpectBucketCount( + "IOS.DefaultStatusAPI.HeuristicAssessment7", + DefaultStatusHeuristicAssessment::kTruePositive, 1); + histogram_tester.ExpectBucketCount( + "IOS.DefaultStatusAPI.HeuristicAssessment14", + DefaultStatusHeuristicAssessment::kTruePositive, 1); + histogram_tester.ExpectBucketCount( + "IOS.DefaultStatusAPI.HeuristicAssessment21", + DefaultStatusHeuristicAssessment::kTruePositive, 1); + histogram_tester.ExpectBucketCount( + "IOS.DefaultStatusAPI.HeuristicAssessment28", + DefaultStatusHeuristicAssessment::kTruePositive, 1); + histogram_tester.ExpectBucketCount( + "IOS.DefaultStatusAPI.HeuristicAssessment35", + DefaultStatusHeuristicAssessment::kTruePositive, 1); + histogram_tester.ExpectBucketCount( + "IOS.DefaultStatusAPI.HeuristicAssessment42", + DefaultStatusHeuristicAssessment::kTruePositive, 1); + RecordHeuristicAssessments(/*is_default_in_system_api=*/false); + histogram_tester.ExpectBucketCount( + "IOS.DefaultStatusAPI.HeuristicAssessment1", + DefaultStatusHeuristicAssessment::kFalsePositive, 1); + histogram_tester.ExpectBucketCount( + "IOS.DefaultStatusAPI.HeuristicAssessment3", + DefaultStatusHeuristicAssessment::kFalsePositive, 1); + histogram_tester.ExpectBucketCount( + "IOS.DefaultStatusAPI.HeuristicAssessment7", + DefaultStatusHeuristicAssessment::kFalsePositive, 1); + histogram_tester.ExpectBucketCount( + "IOS.DefaultStatusAPI.HeuristicAssessment14", + DefaultStatusHeuristicAssessment::kFalsePositive, 1); + histogram_tester.ExpectBucketCount( + "IOS.DefaultStatusAPI.HeuristicAssessment21", + DefaultStatusHeuristicAssessment::kFalsePositive, 1); + histogram_tester.ExpectBucketCount( + "IOS.DefaultStatusAPI.HeuristicAssessment28", + DefaultStatusHeuristicAssessment::kFalsePositive, 1); + histogram_tester.ExpectBucketCount( + "IOS.DefaultStatusAPI.HeuristicAssessment35", + DefaultStatusHeuristicAssessment::kFalsePositive, 1); + histogram_tester.ExpectBucketCount( + "IOS.DefaultStatusAPI.HeuristicAssessment42", + DefaultStatusHeuristicAssessment::kFalsePositive, 1); + + NSDate* under_three_days_ago = + (base::Time::Now() - base::Days(3) + base::Minutes(10)).ToNSDate(); + SetObjectIntoStorageForKey(kLastHTTPURLOpenTime, under_three_days_ago); + RecordHeuristicAssessments(/*is_default_in_system_api=*/true); + histogram_tester.ExpectBucketCount( + "IOS.DefaultStatusAPI.HeuristicAssessment1", + DefaultStatusHeuristicAssessment::kFalseNegative, 1); + histogram_tester.ExpectBucketCount( + "IOS.DefaultStatusAPI.HeuristicAssessment3", + DefaultStatusHeuristicAssessment::kTruePositive, 2); + histogram_tester.ExpectBucketCount( + "IOS.DefaultStatusAPI.HeuristicAssessment7", + DefaultStatusHeuristicAssessment::kTruePositive, 2); + histogram_tester.ExpectBucketCount( + "IOS.DefaultStatusAPI.HeuristicAssessment14", + DefaultStatusHeuristicAssessment::kTruePositive, 2); + histogram_tester.ExpectBucketCount( + "IOS.DefaultStatusAPI.HeuristicAssessment21", + DefaultStatusHeuristicAssessment::kTruePositive, 2); + histogram_tester.ExpectBucketCount( + "IOS.DefaultStatusAPI.HeuristicAssessment28", + DefaultStatusHeuristicAssessment::kTruePositive, 2); + histogram_tester.ExpectBucketCount( + "IOS.DefaultStatusAPI.HeuristicAssessment35", + DefaultStatusHeuristicAssessment::kTruePositive, 2); + histogram_tester.ExpectBucketCount( + "IOS.DefaultStatusAPI.HeuristicAssessment42", + DefaultStatusHeuristicAssessment::kTruePositive, 2); + RecordHeuristicAssessments(/*is_default_in_system_api=*/false); + histogram_tester.ExpectBucketCount( + "IOS.DefaultStatusAPI.HeuristicAssessment1", + DefaultStatusHeuristicAssessment::kTrueNegative, 1); + histogram_tester.ExpectBucketCount( + "IOS.DefaultStatusAPI.HeuristicAssessment3", + DefaultStatusHeuristicAssessment::kFalsePositive, 2); + histogram_tester.ExpectBucketCount( + "IOS.DefaultStatusAPI.HeuristicAssessment7", + DefaultStatusHeuristicAssessment::kFalsePositive, 2); + histogram_tester.ExpectBucketCount( + "IOS.DefaultStatusAPI.HeuristicAssessment14", + DefaultStatusHeuristicAssessment::kFalsePositive, 2); + histogram_tester.ExpectBucketCount( + "IOS.DefaultStatusAPI.HeuristicAssessment21", + DefaultStatusHeuristicAssessment::kFalsePositive, 2); + histogram_tester.ExpectBucketCount( + "IOS.DefaultStatusAPI.HeuristicAssessment28", + DefaultStatusHeuristicAssessment::kFalsePositive, 2); + histogram_tester.ExpectBucketCount( + "IOS.DefaultStatusAPI.HeuristicAssessment35", + DefaultStatusHeuristicAssessment::kFalsePositive, 2); + histogram_tester.ExpectBucketCount( + "IOS.DefaultStatusAPI.HeuristicAssessment42", + DefaultStatusHeuristicAssessment::kFalsePositive, 2); + + NSDate* under_seven_days_ago = + (base::Time::Now() - base::Days(7) + base::Minutes(10)).ToNSDate(); + SetObjectIntoStorageForKey(kLastHTTPURLOpenTime, under_seven_days_ago); + RecordHeuristicAssessments(/*is_default_in_system_api=*/true); + histogram_tester.ExpectBucketCount( + "IOS.DefaultStatusAPI.HeuristicAssessment1", + DefaultStatusHeuristicAssessment::kFalseNegative, 2); + histogram_tester.ExpectBucketCount( + "IOS.DefaultStatusAPI.HeuristicAssessment3", + DefaultStatusHeuristicAssessment::kFalseNegative, 1); + histogram_tester.ExpectBucketCount( + "IOS.DefaultStatusAPI.HeuristicAssessment7", + DefaultStatusHeuristicAssessment::kTruePositive, 3); + histogram_tester.ExpectBucketCount( + "IOS.DefaultStatusAPI.HeuristicAssessment14", + DefaultStatusHeuristicAssessment::kTruePositive, 3); + histogram_tester.ExpectBucketCount( + "IOS.DefaultStatusAPI.HeuristicAssessment21", + DefaultStatusHeuristicAssessment::kTruePositive, 3); + histogram_tester.ExpectBucketCount( + "IOS.DefaultStatusAPI.HeuristicAssessment28", + DefaultStatusHeuristicAssessment::kTruePositive, 3); + histogram_tester.ExpectBucketCount( + "IOS.DefaultStatusAPI.HeuristicAssessment35", + DefaultStatusHeuristicAssessment::kTruePositive, 3); + histogram_tester.ExpectBucketCount( + "IOS.DefaultStatusAPI.HeuristicAssessment42", + DefaultStatusHeuristicAssessment::kTruePositive, 3); + RecordHeuristicAssessments(/*is_default_in_system_api=*/false); + histogram_tester.ExpectBucketCount( + "IOS.DefaultStatusAPI.HeuristicAssessment1", + DefaultStatusHeuristicAssessment::kTrueNegative, 2); + histogram_tester.ExpectBucketCount( + "IOS.DefaultStatusAPI.HeuristicAssessment3", + DefaultStatusHeuristicAssessment::kTrueNegative, 1); + histogram_tester.ExpectBucketCount( + "IOS.DefaultStatusAPI.HeuristicAssessment7", + DefaultStatusHeuristicAssessment::kFalsePositive, 3); + histogram_tester.ExpectBucketCount( + "IOS.DefaultStatusAPI.HeuristicAssessment14", + DefaultStatusHeuristicAssessment::kFalsePositive, 3); + histogram_tester.ExpectBucketCount( + "IOS.DefaultStatusAPI.HeuristicAssessment21", + DefaultStatusHeuristicAssessment::kFalsePositive, 3); + histogram_tester.ExpectBucketCount( + "IOS.DefaultStatusAPI.HeuristicAssessment28", + DefaultStatusHeuristicAssessment::kFalsePositive, 3); + histogram_tester.ExpectBucketCount( + "IOS.DefaultStatusAPI.HeuristicAssessment35", + DefaultStatusHeuristicAssessment::kFalsePositive, 3); + histogram_tester.ExpectBucketCount( + "IOS.DefaultStatusAPI.HeuristicAssessment42", + DefaultStatusHeuristicAssessment::kFalsePositive, 3); + + NSDate* under_fourteen_days_ago = + (base::Time::Now() - base::Days(14) + base::Minutes(10)).ToNSDate(); + SetObjectIntoStorageForKey(kLastHTTPURLOpenTime, under_fourteen_days_ago); + RecordHeuristicAssessments(/*is_default_in_system_api=*/true); + histogram_tester.ExpectBucketCount( + "IOS.DefaultStatusAPI.HeuristicAssessment1", + DefaultStatusHeuristicAssessment::kFalseNegative, 3); + histogram_tester.ExpectBucketCount( + "IOS.DefaultStatusAPI.HeuristicAssessment3", + DefaultStatusHeuristicAssessment::kFalseNegative, 2); + histogram_tester.ExpectBucketCount( + "IOS.DefaultStatusAPI.HeuristicAssessment7", + DefaultStatusHeuristicAssessment::kFalseNegative, 1); + histogram_tester.ExpectBucketCount( + "IOS.DefaultStatusAPI.HeuristicAssessment14", + DefaultStatusHeuristicAssessment::kTruePositive, 4); + histogram_tester.ExpectBucketCount( + "IOS.DefaultStatusAPI.HeuristicAssessment21", + DefaultStatusHeuristicAssessment::kTruePositive, 4); + histogram_tester.ExpectBucketCount( + "IOS.DefaultStatusAPI.HeuristicAssessment28", + DefaultStatusHeuristicAssessment::kTruePositive, 4); + histogram_tester.ExpectBucketCount( + "IOS.DefaultStatusAPI.HeuristicAssessment35", + DefaultStatusHeuristicAssessment::kTruePositive, 4); + histogram_tester.ExpectBucketCount( + "IOS.DefaultStatusAPI.HeuristicAssessment42", + DefaultStatusHeuristicAssessment::kTruePositive, 4); + RecordHeuristicAssessments(/*is_default_in_system_api=*/false); + histogram_tester.ExpectBucketCount( + "IOS.DefaultStatusAPI.HeuristicAssessment1", + DefaultStatusHeuristicAssessment::kTrueNegative, 3); + histogram_tester.ExpectBucketCount( + "IOS.DefaultStatusAPI.HeuristicAssessment3", + DefaultStatusHeuristicAssessment::kTrueNegative, 2); + histogram_tester.ExpectBucketCount( + "IOS.DefaultStatusAPI.HeuristicAssessment7", + DefaultStatusHeuristicAssessment::kTrueNegative, 1); + histogram_tester.ExpectBucketCount( + "IOS.DefaultStatusAPI.HeuristicAssessment14", + DefaultStatusHeuristicAssessment::kFalsePositive, 4); + histogram_tester.ExpectBucketCount( + "IOS.DefaultStatusAPI.HeuristicAssessment21", + DefaultStatusHeuristicAssessment::kFalsePositive, 4); + histogram_tester.ExpectBucketCount( + "IOS.DefaultStatusAPI.HeuristicAssessment28", + DefaultStatusHeuristicAssessment::kFalsePositive, 4); + histogram_tester.ExpectBucketCount( + "IOS.DefaultStatusAPI.HeuristicAssessment35", + DefaultStatusHeuristicAssessment::kFalsePositive, 4); + histogram_tester.ExpectBucketCount( + "IOS.DefaultStatusAPI.HeuristicAssessment42", + DefaultStatusHeuristicAssessment::kFalsePositive, 4); + + NSDate* under_twenty_one_days_ago = + (base::Time::Now() - base::Days(21) + base::Minutes(10)).ToNSDate(); + SetObjectIntoStorageForKey(kLastHTTPURLOpenTime, under_twenty_one_days_ago); + RecordHeuristicAssessments(/*is_default_in_system_api=*/true); + histogram_tester.ExpectBucketCount( + "IOS.DefaultStatusAPI.HeuristicAssessment1", + DefaultStatusHeuristicAssessment::kFalseNegative, 4); + histogram_tester.ExpectBucketCount( + "IOS.DefaultStatusAPI.HeuristicAssessment3", + DefaultStatusHeuristicAssessment::kFalseNegative, 3); + histogram_tester.ExpectBucketCount( + "IOS.DefaultStatusAPI.HeuristicAssessment7", + DefaultStatusHeuristicAssessment::kFalseNegative, 2); + histogram_tester.ExpectBucketCount( + "IOS.DefaultStatusAPI.HeuristicAssessment14", + DefaultStatusHeuristicAssessment::kFalseNegative, 1); + histogram_tester.ExpectBucketCount( + "IOS.DefaultStatusAPI.HeuristicAssessment21", + DefaultStatusHeuristicAssessment::kTruePositive, 5); + histogram_tester.ExpectBucketCount( + "IOS.DefaultStatusAPI.HeuristicAssessment28", + DefaultStatusHeuristicAssessment::kTruePositive, 5); + histogram_tester.ExpectBucketCount( + "IOS.DefaultStatusAPI.HeuristicAssessment35", + DefaultStatusHeuristicAssessment::kTruePositive, 5); + histogram_tester.ExpectBucketCount( + "IOS.DefaultStatusAPI.HeuristicAssessment42", + DefaultStatusHeuristicAssessment::kTruePositive, 5); + RecordHeuristicAssessments(/*is_default_in_system_api=*/false); + histogram_tester.ExpectBucketCount( + "IOS.DefaultStatusAPI.HeuristicAssessment1", + DefaultStatusHeuristicAssessment::kTrueNegative, 4); + histogram_tester.ExpectBucketCount( + "IOS.DefaultStatusAPI.HeuristicAssessment3", + DefaultStatusHeuristicAssessment::kTrueNegative, 3); + histogram_tester.ExpectBucketCount( + "IOS.DefaultStatusAPI.HeuristicAssessment7", + DefaultStatusHeuristicAssessment::kTrueNegative, 2); + histogram_tester.ExpectBucketCount( + "IOS.DefaultStatusAPI.HeuristicAssessment14", + DefaultStatusHeuristicAssessment::kTrueNegative, 1); + histogram_tester.ExpectBucketCount( + "IOS.DefaultStatusAPI.HeuristicAssessment21", + DefaultStatusHeuristicAssessment::kFalsePositive, 5); + histogram_tester.ExpectBucketCount( + "IOS.DefaultStatusAPI.HeuristicAssessment28", + DefaultStatusHeuristicAssessment::kFalsePositive, 5); + histogram_tester.ExpectBucketCount( + "IOS.DefaultStatusAPI.HeuristicAssessment35", + DefaultStatusHeuristicAssessment::kFalsePositive, 5); + histogram_tester.ExpectBucketCount( + "IOS.DefaultStatusAPI.HeuristicAssessment42", + DefaultStatusHeuristicAssessment::kFalsePositive, 5); + + NSDate* under_twenty_eight_days_ago = + (base::Time::Now() - base::Days(28) + base::Minutes(10)).ToNSDate(); + SetObjectIntoStorageForKey(kLastHTTPURLOpenTime, under_twenty_eight_days_ago); + RecordHeuristicAssessments(/*is_default_in_system_api=*/true); + histogram_tester.ExpectBucketCount( + "IOS.DefaultStatusAPI.HeuristicAssessment1", + DefaultStatusHeuristicAssessment::kFalseNegative, 5); + histogram_tester.ExpectBucketCount( + "IOS.DefaultStatusAPI.HeuristicAssessment3", + DefaultStatusHeuristicAssessment::kFalseNegative, 4); + histogram_tester.ExpectBucketCount( + "IOS.DefaultStatusAPI.HeuristicAssessment7", + DefaultStatusHeuristicAssessment::kFalseNegative, 3); + histogram_tester.ExpectBucketCount( + "IOS.DefaultStatusAPI.HeuristicAssessment14", + DefaultStatusHeuristicAssessment::kFalseNegative, 2); + histogram_tester.ExpectBucketCount( + "IOS.DefaultStatusAPI.HeuristicAssessment21", + DefaultStatusHeuristicAssessment::kFalseNegative, 1); + histogram_tester.ExpectBucketCount( + "IOS.DefaultStatusAPI.HeuristicAssessment28", + DefaultStatusHeuristicAssessment::kTruePositive, 6); + histogram_tester.ExpectBucketCount( + "IOS.DefaultStatusAPI.HeuristicAssessment35", + DefaultStatusHeuristicAssessment::kTruePositive, 6); + histogram_tester.ExpectBucketCount( + "IOS.DefaultStatusAPI.HeuristicAssessment42", + DefaultStatusHeuristicAssessment::kTruePositive, 6); + RecordHeuristicAssessments(/*is_default_in_system_api=*/false); + histogram_tester.ExpectBucketCount( + "IOS.DefaultStatusAPI.HeuristicAssessment1", + DefaultStatusHeuristicAssessment::kTrueNegative, 5); + histogram_tester.ExpectBucketCount( + "IOS.DefaultStatusAPI.HeuristicAssessment3", + DefaultStatusHeuristicAssessment::kTrueNegative, 4); + histogram_tester.ExpectBucketCount( + "IOS.DefaultStatusAPI.HeuristicAssessment7", + DefaultStatusHeuristicAssessment::kTrueNegative, 3); + histogram_tester.ExpectBucketCount( + "IOS.DefaultStatusAPI.HeuristicAssessment14", + DefaultStatusHeuristicAssessment::kTrueNegative, 2); + histogram_tester.ExpectBucketCount( + "IOS.DefaultStatusAPI.HeuristicAssessment21", + DefaultStatusHeuristicAssessment::kTrueNegative, 1); + histogram_tester.ExpectBucketCount( + "IOS.DefaultStatusAPI.HeuristicAssessment28", + DefaultStatusHeuristicAssessment::kFalsePositive, 6); + histogram_tester.ExpectBucketCount( + "IOS.DefaultStatusAPI.HeuristicAssessment35", + DefaultStatusHeuristicAssessment::kFalsePositive, 6); + histogram_tester.ExpectBucketCount( + "IOS.DefaultStatusAPI.HeuristicAssessment42", + DefaultStatusHeuristicAssessment::kFalsePositive, 6); + + NSDate* under_thirty_five_days_ago = + (base::Time::Now() - base::Days(35) + base::Minutes(10)).ToNSDate(); + SetObjectIntoStorageForKey(kLastHTTPURLOpenTime, under_thirty_five_days_ago); + RecordHeuristicAssessments(/*is_default_in_system_api=*/true); + histogram_tester.ExpectBucketCount( + "IOS.DefaultStatusAPI.HeuristicAssessment1", + DefaultStatusHeuristicAssessment::kFalseNegative, 6); + histogram_tester.ExpectBucketCount( + "IOS.DefaultStatusAPI.HeuristicAssessment3", + DefaultStatusHeuristicAssessment::kFalseNegative, 5); + histogram_tester.ExpectBucketCount( + "IOS.DefaultStatusAPI.HeuristicAssessment7", + DefaultStatusHeuristicAssessment::kFalseNegative, 4); + histogram_tester.ExpectBucketCount( + "IOS.DefaultStatusAPI.HeuristicAssessment14", + DefaultStatusHeuristicAssessment::kFalseNegative, 3); + histogram_tester.ExpectBucketCount( + "IOS.DefaultStatusAPI.HeuristicAssessment21", + DefaultStatusHeuristicAssessment::kFalseNegative, 2); + histogram_tester.ExpectBucketCount( + "IOS.DefaultStatusAPI.HeuristicAssessment28", + DefaultStatusHeuristicAssessment::kFalseNegative, 1); + histogram_tester.ExpectBucketCount( + "IOS.DefaultStatusAPI.HeuristicAssessment35", + DefaultStatusHeuristicAssessment::kTruePositive, 7); + histogram_tester.ExpectBucketCount( + "IOS.DefaultStatusAPI.HeuristicAssessment42", + DefaultStatusHeuristicAssessment::kTruePositive, 7); + RecordHeuristicAssessments(/*is_default_in_system_api=*/false); + histogram_tester.ExpectBucketCount( + "IOS.DefaultStatusAPI.HeuristicAssessment1", + DefaultStatusHeuristicAssessment::kTrueNegative, 6); + histogram_tester.ExpectBucketCount( + "IOS.DefaultStatusAPI.HeuristicAssessment3", + DefaultStatusHeuristicAssessment::kTrueNegative, 5); + histogram_tester.ExpectBucketCount( + "IOS.DefaultStatusAPI.HeuristicAssessment7", + DefaultStatusHeuristicAssessment::kTrueNegative, 4); + histogram_tester.ExpectBucketCount( + "IOS.DefaultStatusAPI.HeuristicAssessment14", + DefaultStatusHeuristicAssessment::kTrueNegative, 3); + histogram_tester.ExpectBucketCount( + "IOS.DefaultStatusAPI.HeuristicAssessment21", + DefaultStatusHeuristicAssessment::kTrueNegative, 2); + histogram_tester.ExpectBucketCount( + "IOS.DefaultStatusAPI.HeuristicAssessment28", + DefaultStatusHeuristicAssessment::kTrueNegative, 1); + histogram_tester.ExpectBucketCount( + "IOS.DefaultStatusAPI.HeuristicAssessment35", + DefaultStatusHeuristicAssessment::kFalsePositive, 7); + histogram_tester.ExpectBucketCount( + "IOS.DefaultStatusAPI.HeuristicAssessment42", + DefaultStatusHeuristicAssessment::kFalsePositive, 7); + + NSDate* under_forty_two_days_ago = + (base::Time::Now() - base::Days(42) + base::Minutes(10)).ToNSDate(); + SetObjectIntoStorageForKey(kLastHTTPURLOpenTime, under_forty_two_days_ago); + RecordHeuristicAssessments(/*is_default_in_system_api=*/true); + histogram_tester.ExpectBucketCount( + "IOS.DefaultStatusAPI.HeuristicAssessment1", + DefaultStatusHeuristicAssessment::kFalseNegative, 7); + histogram_tester.ExpectBucketCount( + "IOS.DefaultStatusAPI.HeuristicAssessment3", + DefaultStatusHeuristicAssessment::kFalseNegative, 6); + histogram_tester.ExpectBucketCount( + "IOS.DefaultStatusAPI.HeuristicAssessment7", + DefaultStatusHeuristicAssessment::kFalseNegative, 5); + histogram_tester.ExpectBucketCount( + "IOS.DefaultStatusAPI.HeuristicAssessment14", + DefaultStatusHeuristicAssessment::kFalseNegative, 4); + histogram_tester.ExpectBucketCount( + "IOS.DefaultStatusAPI.HeuristicAssessment21", + DefaultStatusHeuristicAssessment::kFalseNegative, 3); + histogram_tester.ExpectBucketCount( + "IOS.DefaultStatusAPI.HeuristicAssessment28", + DefaultStatusHeuristicAssessment::kFalseNegative, 2); + histogram_tester.ExpectBucketCount( + "IOS.DefaultStatusAPI.HeuristicAssessment35", + DefaultStatusHeuristicAssessment::kFalseNegative, 1); + histogram_tester.ExpectBucketCount( + "IOS.DefaultStatusAPI.HeuristicAssessment42", + DefaultStatusHeuristicAssessment::kTruePositive, 8); + RecordHeuristicAssessments(/*is_default_in_system_api=*/false); + histogram_tester.ExpectBucketCount( + "IOS.DefaultStatusAPI.HeuristicAssessment1", + DefaultStatusHeuristicAssessment::kTrueNegative, 7); + histogram_tester.ExpectBucketCount( + "IOS.DefaultStatusAPI.HeuristicAssessment3", + DefaultStatusHeuristicAssessment::kTrueNegative, 6); + histogram_tester.ExpectBucketCount( + "IOS.DefaultStatusAPI.HeuristicAssessment7", + DefaultStatusHeuristicAssessment::kTrueNegative, 5); + histogram_tester.ExpectBucketCount( + "IOS.DefaultStatusAPI.HeuristicAssessment14", + DefaultStatusHeuristicAssessment::kTrueNegative, 4); + histogram_tester.ExpectBucketCount( + "IOS.DefaultStatusAPI.HeuristicAssessment21", + DefaultStatusHeuristicAssessment::kTrueNegative, 3); + histogram_tester.ExpectBucketCount( + "IOS.DefaultStatusAPI.HeuristicAssessment28", + DefaultStatusHeuristicAssessment::kTrueNegative, 2); + histogram_tester.ExpectBucketCount( + "IOS.DefaultStatusAPI.HeuristicAssessment35", + DefaultStatusHeuristicAssessment::kTrueNegative, 1); + histogram_tester.ExpectBucketCount( + "IOS.DefaultStatusAPI.HeuristicAssessment42", + DefaultStatusHeuristicAssessment::kFalsePositive, 8); + + NSDate* over_forty_two_days_ago = + (base::Time::Now() - base::Days(45)).ToNSDate(); + SetObjectIntoStorageForKey(kLastHTTPURLOpenTime, over_forty_two_days_ago); + RecordHeuristicAssessments(/*is_default_in_system_api=*/true); + histogram_tester.ExpectBucketCount( + "IOS.DefaultStatusAPI.HeuristicAssessment1", + DefaultStatusHeuristicAssessment::kFalseNegative, 8); + histogram_tester.ExpectBucketCount( + "IOS.DefaultStatusAPI.HeuristicAssessment3", + DefaultStatusHeuristicAssessment::kFalseNegative, 7); + histogram_tester.ExpectBucketCount( + "IOS.DefaultStatusAPI.HeuristicAssessment7", + DefaultStatusHeuristicAssessment::kFalseNegative, 6); + histogram_tester.ExpectBucketCount( + "IOS.DefaultStatusAPI.HeuristicAssessment14", + DefaultStatusHeuristicAssessment::kFalseNegative, 5); + histogram_tester.ExpectBucketCount( + "IOS.DefaultStatusAPI.HeuristicAssessment21", + DefaultStatusHeuristicAssessment::kFalseNegative, 4); + histogram_tester.ExpectBucketCount( + "IOS.DefaultStatusAPI.HeuristicAssessment28", + DefaultStatusHeuristicAssessment::kFalseNegative, 3); + histogram_tester.ExpectBucketCount( + "IOS.DefaultStatusAPI.HeuristicAssessment35", + DefaultStatusHeuristicAssessment::kFalseNegative, 2); + histogram_tester.ExpectBucketCount( + "IOS.DefaultStatusAPI.HeuristicAssessment42", + DefaultStatusHeuristicAssessment::kFalseNegative, 1); + RecordHeuristicAssessments(/*is_default_in_system_api=*/false); + histogram_tester.ExpectBucketCount( + "IOS.DefaultStatusAPI.HeuristicAssessment1", + DefaultStatusHeuristicAssessment::kTrueNegative, 8); + histogram_tester.ExpectBucketCount( + "IOS.DefaultStatusAPI.HeuristicAssessment3", + DefaultStatusHeuristicAssessment::kTrueNegative, 7); + histogram_tester.ExpectBucketCount( + "IOS.DefaultStatusAPI.HeuristicAssessment7", + DefaultStatusHeuristicAssessment::kTrueNegative, 6); + histogram_tester.ExpectBucketCount( + "IOS.DefaultStatusAPI.HeuristicAssessment14", + DefaultStatusHeuristicAssessment::kTrueNegative, 5); + histogram_tester.ExpectBucketCount( + "IOS.DefaultStatusAPI.HeuristicAssessment21", + DefaultStatusHeuristicAssessment::kTrueNegative, 4); + histogram_tester.ExpectBucketCount( + "IOS.DefaultStatusAPI.HeuristicAssessment28", + DefaultStatusHeuristicAssessment::kTrueNegative, 3); + histogram_tester.ExpectBucketCount( + "IOS.DefaultStatusAPI.HeuristicAssessment35", + DefaultStatusHeuristicAssessment::kTrueNegative, 2); + histogram_tester.ExpectBucketCount( + "IOS.DefaultStatusAPI.HeuristicAssessment42", + DefaultStatusHeuristicAssessment::kTrueNegative, 1); + + histogram_tester.ExpectTotalCount("IOS.DefaultStatusAPI.HeuristicAssessment1", + 18); + histogram_tester.ExpectTotalCount("IOS.DefaultStatusAPI.HeuristicAssessment3", + 18); + histogram_tester.ExpectTotalCount("IOS.DefaultStatusAPI.HeuristicAssessment7", + 18); + histogram_tester.ExpectTotalCount( + "IOS.DefaultStatusAPI.HeuristicAssessment14", 18); + histogram_tester.ExpectTotalCount( + "IOS.DefaultStatusAPI.HeuristicAssessment21", 18); + histogram_tester.ExpectTotalCount( + "IOS.DefaultStatusAPI.HeuristicAssessment28", 18); + histogram_tester.ExpectTotalCount( + "IOS.DefaultStatusAPI.HeuristicAssessment35", 18); + histogram_tester.ExpectTotalCount( + "IOS.DefaultStatusAPI.HeuristicAssessment42", 18); +} + +} // namespace default_status
diff --git a/ios/chrome/browser/default_browser/model/default_status/default_status_helper_prefs.h b/ios/chrome/browser/default_browser/model/default_status/default_status_helper_prefs.h new file mode 100644 index 0000000..039196b --- /dev/null +++ b/ios/chrome/browser/default_browser/model/default_status/default_status_helper_prefs.h
@@ -0,0 +1,34 @@ +// Copyright 2025 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef IOS_CHROME_BROWSER_DEFAULT_BROWSER_MODEL_DEFAULT_STATUS_DEFAULT_STATUS_HELPER_PREFS_H_ +#define IOS_CHROME_BROWSER_DEFAULT_BROWSER_MODEL_DEFAULT_STATUS_DEFAULT_STATUS_HELPER_PREFS_H_ + +class PrefRegistrySimple; + +namespace default_status { + +// The cohort this client belongs to for the purpose of reporting the result of +// the default status system API call. +extern const char kDefaultStatusAPICohort[]; + +// The timestsamp of the last successful call to the default status API call. A +// successful call is one that does not yield an error, regardless of the +// default status result. +extern const char kDefaultStatusAPILastSuccessfulCall[]; + +// The timestamp after which the default status API should be called again for +// this client. +extern const char kDefaultStatusAPINextRetry[]; + +// The result of the last successful default status API call. The value is of +// type DefaultStatusCheckResult and defaults to kUnknown. +extern const char kDefaultStatusAPIResult[]; + +// Registers the prefs associated with the default status helper. +void RegisterDefaultStatusPrefs(PrefRegistrySimple* registry); + +} // namespace default_status + +#endif // IOS_CHROME_BROWSER_DEFAULT_BROWSER_MODEL_DEFAULT_STATUS_DEFAULT_STATUS_HELPER_PREFS_H_
diff --git a/ios/chrome/browser/default_browser/model/default_status/default_status_helper_prefs.mm b/ios/chrome/browser/default_browser/model/default_status/default_status_helper_prefs.mm new file mode 100644 index 0000000..8f078cea3 --- /dev/null +++ b/ios/chrome/browser/default_browser/model/default_status/default_status_helper_prefs.mm
@@ -0,0 +1,28 @@ +// 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 "ios/chrome/browser/default_browser/model/default_status/default_status_helper_prefs.h" + +#import "base/time/time.h" +#import "components/prefs/pref_registry_simple.h" +#import "ios/chrome/browser/default_browser/model/default_status/default_status_helper_types.h" + +namespace default_status { + +const char kDefaultStatusAPICohort[] = "DefaultStatusAPICohort"; +const char kDefaultStatusAPILastSuccessfulCall[] = + "DefaultStatusAPILastSuccessfulCall"; +const char kDefaultStatusAPINextRetry[] = "DefaultStatusAPINextRetry"; +const char kDefaultStatusAPIResult[] = "DefaultStatusAPIResult"; + +void RegisterDefaultStatusPrefs(PrefRegistrySimple* registry) { + registry->RegisterIntegerPref(kDefaultStatusAPICohort, 0); + registry->RegisterTimePref(kDefaultStatusAPILastSuccessfulCall, base::Time()); + registry->RegisterTimePref(kDefaultStatusAPINextRetry, base::Time()); + registry->RegisterIntegerPref( + kDefaultStatusAPIResult, + static_cast<int>(DefaultStatusAPIResult::kUnknown)); +} + +} // namespace default_status
diff --git a/ios/chrome/browser/default_browser/model/default_status/default_status_helper_types.h b/ios/chrome/browser/default_browser/model/default_status/default_status_helper_types.h new file mode 100644 index 0000000..76cd632 --- /dev/null +++ b/ios/chrome/browser/default_browser/model/default_status/default_status_helper_types.h
@@ -0,0 +1,61 @@ +// 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 IOS_CHROME_BROWSER_DEFAULT_BROWSER_MODEL_DEFAULT_STATUS_DEFAULT_STATUS_HELPER_TYPES_H_ +#define IOS_CHROME_BROWSER_DEFAULT_BROWSER_MODEL_DEFAULT_STATUS_DEFAULT_STATUS_HELPER_TYPES_H_ + +namespace default_status { + +// The possible outcomes of querying the system default status API. This enum +// must match the UMA histogram enum IOSDefaultStatusAPIOutcomeType. +// +// LINT.IfChange(DefaultStatusAPIOutcomeType) +enum class DefaultStatusAPIOutcomeType { + kSuccess = 1, + kCooldownError = 2, + kOtherError = 3, + kMaxValue = kOtherError +}; +// LINT.ThenChange(/tools/metrics/histograms/metadata/ios/enums.xml:IOSDefaultStatusAPIOutcomeType) + +// The true / false result of querying the system default status API, with an +// added "unknown" value to distinguish cases where no successful call has been +// made yet. +enum class DefaultStatusAPIResult { + kUnknown = 0, + kIsDefault = 1, + kIsNotDefault = 2, + kMaxValue = kIsNotDefault +}; + +// The assessment of the value returned by the IsChromeLikelyDefaultBrowserXDays +// heuristic based on the actual result from the default status system API. This +// enum must match the UMA histogram enum IOSDefaultStatusHeuristicAssessment. +// +// LINT.IfChange(DefaultStatusHeuristicAssessment) +enum class DefaultStatusHeuristicAssessment { + kTrueNegative = 0, + kFalseNegative = 1, + kTruePositive = 2, + kFalsePositive = 3, + kMaxValue = kFalsePositive +}; +// LINT.ThenChange(/tools/metrics/histograms/metadata/ios/enums.xml:IOSDefaultStatusHeuristicAssessment) + +// The possible changes in default status year over year. This enum must match +// the UMA histogram enum IOSDefaultStatusRetention. +// +// LINT.IfChange(DefaultStatusRetention) +enum class DefaultStatusRetention { + kBecameDefault = 0, + kBecameNonDefault = 1, + kRemainedDefault = 2, + kRemainedNonDefault = 3, + kMaxValue = kRemainedNonDefault +}; +// LINT.ThenChange(/tools/metrics/histograms/metadata/ios/enums.xml:IOSDefaultStatusRetention) + +} // namespace default_status + +#endif // IOS_CHROME_BROWSER_DEFAULT_BROWSER_MODEL_DEFAULT_STATUS_DEFAULT_STATUS_HELPER_TYPES_H_
diff --git a/ios/chrome/browser/default_browser/model/default_status/default_status_helper_unittest.mm b/ios/chrome/browser/default_browser/model/default_status/default_status_helper_unittest.mm new file mode 100644 index 0000000..e18cc9d --- /dev/null +++ b/ios/chrome/browser/default_browser/model/default_status/default_status_helper_unittest.mm
@@ -0,0 +1,932 @@ +// 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 "ios/chrome/browser/default_browser/model/default_status/default_status_helper.h" + +#import <UIKit/UIKit.h> + +#import "base/test/metrics/histogram_tester.h" +#import "base/test/scoped_feature_list.h" +#import "base/time/time_override.h" +#import "components/prefs/testing_pref_service.h" +#import "ios/chrome/browser/default_browser/model/default_status/default_status_helper_constants.h" +#import "ios/chrome/browser/default_browser/model/default_status/default_status_helper_prefs.h" +#import "ios/chrome/browser/default_browser/model/default_status/default_status_helper_types.h" +#import "ios/chrome/browser/default_browser/model/utils.h" +#import "ios/chrome/browser/shared/model/utils/first_run_test_util.h" +#import "ios/chrome/browser/shared/public/features/features.h" +#import "testing/platform_test.h" + +namespace default_status { + +namespace { + +void ExpectSuccessHistogramsToHaveNoSamples( + const base::HistogramTester& histogram_tester) { + histogram_tester.ExpectTotalCount( + "IOS.DefaultStatusAPI.DaysSinceLastSuccessfulCall", 0); + histogram_tester.ExpectTotalCount("IOS.DefaultStatusAPI.IsDefaultBrowser", 0); + histogram_tester.ExpectTotalCount( + "IOS.DefaultStatusAPI.IsDefaultBrowser.Cohort1", 0); + histogram_tester.ExpectTotalCount( + "IOS.DefaultStatusAPI.IsDefaultBrowser.Cohort2", 0); + histogram_tester.ExpectTotalCount( + "IOS.DefaultStatusAPI.IsDefaultBrowser.Cohort3", 0); + histogram_tester.ExpectTotalCount( + "IOS.DefaultStatusAPI.IsDefaultBrowser.StrictCohorts", 0); + histogram_tester.ExpectTotalCount( + "IOS.DefaultStatusAPI.DefaultStatusRetention", 0); + histogram_tester.ExpectTotalCount("IOS.DefaultStatusAPI.HeuristicAssessment1", + 0); + histogram_tester.ExpectTotalCount("IOS.DefaultStatusAPI.HeuristicAssessment3", + 0); + histogram_tester.ExpectTotalCount("IOS.DefaultStatusAPI.HeuristicAssessment7", + 0); + histogram_tester.ExpectTotalCount( + "IOS.DefaultStatusAPI.HeuristicAssessment14", 0); + histogram_tester.ExpectTotalCount( + "IOS.DefaultStatusAPI.HeuristicAssessment21", 0); + histogram_tester.ExpectTotalCount( + "IOS.DefaultStatusAPI.HeuristicAssessment28", 0); + histogram_tester.ExpectTotalCount( + "IOS.DefaultStatusAPI.HeuristicAssessment35", 0); + histogram_tester.ExpectTotalCount( + "IOS.DefaultStatusAPI.HeuristicAssessment42", 0); +} + +} // namespace + +// Test fixture for testing the default status helper. +class DefaultStatusHelperTest : public PlatformTest { + public: + DefaultStatusHelperTest() { + scoped_feature_list_.InitWithFeatures( + /* enabled_features */ {kRunDefaultStatusCheck}, + /* disabled_features */ {}); + RegisterPrefs(); + } + + static base::Time NowOverride() { return now_override_; } + + static void SetNowOverride(base::Time now_override) { + now_override_ = now_override; + } + + protected: + void RegisterPrefs() { RegisterDefaultStatusPrefs(prefs()->registry()); } + + TestingPrefServiceSimple* prefs() { return &pref_service_; } + + base::test::ScopedFeatureList scoped_feature_list_; + TestingPrefServiceSimple pref_service_; + static base::Time now_override_; +}; + +base::Time DefaultStatusHelperTest::now_override_; + +// Tests IsNewClient. +TEST_F(DefaultStatusHelperTest, IsNewClient) { + ResetFirstRunSentinel(); + EXPECT_TRUE(internal::IsNewClient()); + ForceFirstRunRecency(27); + EXPECT_TRUE(internal::IsNewClient()); + ForceFirstRunRecency(29); + EXPECT_FALSE(internal::IsNewClient()); +} + +// Tests HasCohortAssigned. +TEST_F(DefaultStatusHelperTest, HasCohortAssigned) { + prefs()->ClearPref(kDefaultStatusAPICohort); + EXPECT_FALSE(internal::HasCohortAssigned(prefs())); + prefs()->SetInteger(kDefaultStatusAPICohort, 0); + EXPECT_FALSE(internal::HasCohortAssigned(prefs())); + prefs()->SetInteger(kDefaultStatusAPICohort, 1); + EXPECT_TRUE(internal::HasCohortAssigned(prefs())); +} + +// Tests GenerateRandomCohort. +TEST_F(DefaultStatusHelperTest, GenerateRandomCohort) { + for (int i = 0; i < 1000; ++i) { + int cohort = internal::GenerateRandomCohort(); + EXPECT_TRUE(cohort > 0 && cohort <= kCohortCount); + } +} + +// Tests GetCohortNextStartDate. +TEST_F(DefaultStatusHelperTest, GetCohortNextStartDate) { + base::subtle::ScopedTimeClockOverrides time_override( + &DefaultStatusHelperTest::NowOverride, nullptr, nullptr); + base::Time now_override; + base::Time expected; + + // January. + EXPECT_TRUE(base::Time::FromUTCString("2025-01-15", &now_override)); + SetNowOverride(now_override); + + EXPECT_TRUE(base::Time::FromUTCString("2025-01-01", &expected)); + EXPECT_EQ(internal::GetCohortNextStartDate(/*cohort_number=*/1), expected); + EXPECT_TRUE(base::Time::FromUTCString("2025-02-01", &expected)); + EXPECT_EQ(internal::GetCohortNextStartDate(/*cohort_number=*/2), expected); + EXPECT_TRUE(base::Time::FromUTCString("2025-03-01", &expected)); + EXPECT_EQ(internal::GetCohortNextStartDate(/*cohort_number=*/3), expected); + EXPECT_TRUE(base::Time::FromUTCString("2025-04-01", &expected)); + EXPECT_EQ(internal::GetCohortNextStartDate(/*cohort_number=*/4), expected); + + EXPECT_TRUE(base::Time::FromUTCString("2025-05-01", &expected)); + EXPECT_EQ(internal::GetCohortNextStartDate(/*cohort_number=*/1, + /*after_current_month=*/true), + expected); + EXPECT_TRUE(base::Time::FromUTCString("2025-02-01", &expected)); + EXPECT_EQ(internal::GetCohortNextStartDate(/*cohort_number=*/2, + /*after_current_month=*/true), + expected); + EXPECT_TRUE(base::Time::FromUTCString("2025-03-01", &expected)); + EXPECT_EQ(internal::GetCohortNextStartDate(/*cohort_number=*/3, + /*after_current_month=*/true), + expected); + EXPECT_TRUE(base::Time::FromUTCString("2025-04-01", &expected)); + EXPECT_EQ(internal::GetCohortNextStartDate(/*cohort_number=*/4, + /*after_current_month=*/true), + expected); + + // February. + EXPECT_TRUE(base::Time::FromUTCString("2025-02-15", &now_override)); + SetNowOverride(now_override); + + EXPECT_TRUE(base::Time::FromUTCString("2025-05-01", &expected)); + EXPECT_EQ(internal::GetCohortNextStartDate(/*cohort_number=*/1), expected); + EXPECT_TRUE(base::Time::FromUTCString("2025-02-01", &expected)); + EXPECT_EQ(internal::GetCohortNextStartDate(/*cohort_number=*/2), expected); + EXPECT_TRUE(base::Time::FromUTCString("2025-03-01", &expected)); + EXPECT_EQ(internal::GetCohortNextStartDate(/*cohort_number=*/3), expected); + EXPECT_TRUE(base::Time::FromUTCString("2025-04-01", &expected)); + EXPECT_EQ(internal::GetCohortNextStartDate(/*cohort_number=*/4), expected); + + EXPECT_TRUE(base::Time::FromUTCString("2025-05-01", &expected)); + EXPECT_EQ(internal::GetCohortNextStartDate(/*cohort_number=*/1, + /*after_current_month=*/true), + expected); + EXPECT_TRUE(base::Time::FromUTCString("2025-06-01", &expected)); + EXPECT_EQ(internal::GetCohortNextStartDate(/*cohort_number=*/2, + /*after_current_month=*/true), + expected); + EXPECT_TRUE(base::Time::FromUTCString("2025-03-01", &expected)); + EXPECT_EQ(internal::GetCohortNextStartDate(/*cohort_number=*/3, + /*after_current_month=*/true), + expected); + EXPECT_TRUE(base::Time::FromUTCString("2025-04-01", &expected)); + EXPECT_EQ(internal::GetCohortNextStartDate(/*cohort_number=*/4, + /*after_current_month=*/true), + expected); + + // March. + EXPECT_TRUE(base::Time::FromUTCString("2025-03-15", &now_override)); + SetNowOverride(now_override); + + EXPECT_TRUE(base::Time::FromUTCString("2025-05-01", &expected)); + EXPECT_EQ(internal::GetCohortNextStartDate(/*cohort_number=*/1), expected); + EXPECT_TRUE(base::Time::FromUTCString("2025-06-01", &expected)); + EXPECT_EQ(internal::GetCohortNextStartDate(/*cohort_number=*/2), expected); + EXPECT_TRUE(base::Time::FromUTCString("2025-03-01", &expected)); + EXPECT_EQ(internal::GetCohortNextStartDate(/*cohort_number=*/3), expected); + EXPECT_TRUE(base::Time::FromUTCString("2025-04-01", &expected)); + EXPECT_EQ(internal::GetCohortNextStartDate(/*cohort_number=*/4), expected); + + EXPECT_TRUE(base::Time::FromUTCString("2025-05-01", &expected)); + EXPECT_EQ(internal::GetCohortNextStartDate(/*cohort_number=*/1, + /*after_current_month=*/true), + expected); + EXPECT_TRUE(base::Time::FromUTCString("2025-06-01", &expected)); + EXPECT_EQ(internal::GetCohortNextStartDate(/*cohort_number=*/2, + /*after_current_month=*/true), + expected); + EXPECT_TRUE(base::Time::FromUTCString("2025-07-01", &expected)); + EXPECT_EQ(internal::GetCohortNextStartDate(/*cohort_number=*/3, + /*after_current_month=*/true), + expected); + EXPECT_TRUE(base::Time::FromUTCString("2025-04-01", &expected)); + EXPECT_EQ(internal::GetCohortNextStartDate(/*cohort_number=*/4, + /*after_current_month=*/true), + expected); + + // April. + EXPECT_TRUE(base::Time::FromUTCString("2025-04-15", &now_override)); + SetNowOverride(now_override); + + EXPECT_TRUE(base::Time::FromUTCString("2025-05-01", &expected)); + EXPECT_EQ(internal::GetCohortNextStartDate(/*cohort_number=*/1), expected); + EXPECT_TRUE(base::Time::FromUTCString("2025-06-01", &expected)); + EXPECT_EQ(internal::GetCohortNextStartDate(/*cohort_number=*/2), expected); + EXPECT_TRUE(base::Time::FromUTCString("2025-07-01", &expected)); + EXPECT_EQ(internal::GetCohortNextStartDate(/*cohort_number=*/3), expected); + EXPECT_TRUE(base::Time::FromUTCString("2025-04-01", &expected)); + EXPECT_EQ(internal::GetCohortNextStartDate(/*cohort_number=*/4), expected); + + EXPECT_TRUE(base::Time::FromUTCString("2025-05-01", &expected)); + EXPECT_EQ(internal::GetCohortNextStartDate(/*cohort_number=*/1, + /*after_current_month=*/true), + expected); + EXPECT_TRUE(base::Time::FromUTCString("2025-06-01", &expected)); + EXPECT_EQ(internal::GetCohortNextStartDate(/*cohort_number=*/2, + /*after_current_month=*/true), + expected); + EXPECT_TRUE(base::Time::FromUTCString("2025-07-01", &expected)); + EXPECT_EQ(internal::GetCohortNextStartDate(/*cohort_number=*/3, + /*after_current_month=*/true), + expected); + EXPECT_TRUE(base::Time::FromUTCString("2025-08-01", &expected)); + EXPECT_EQ(internal::GetCohortNextStartDate(/*cohort_number=*/4, + /*after_current_month=*/true), + expected); + + // May. + EXPECT_TRUE(base::Time::FromUTCString("2025-05-15", &now_override)); + SetNowOverride(now_override); + + EXPECT_TRUE(base::Time::FromUTCString("2025-05-01", &expected)); + EXPECT_EQ(internal::GetCohortNextStartDate(/*cohort_number=*/1), expected); + EXPECT_TRUE(base::Time::FromUTCString("2025-06-01", &expected)); + EXPECT_EQ(internal::GetCohortNextStartDate(/*cohort_number=*/2), expected); + EXPECT_TRUE(base::Time::FromUTCString("2025-07-01", &expected)); + EXPECT_EQ(internal::GetCohortNextStartDate(/*cohort_number=*/3), expected); + EXPECT_TRUE(base::Time::FromUTCString("2025-08-01", &expected)); + EXPECT_EQ(internal::GetCohortNextStartDate(/*cohort_number=*/4), expected); + + EXPECT_TRUE(base::Time::FromUTCString("2025-09-01", &expected)); + EXPECT_EQ(internal::GetCohortNextStartDate(/*cohort_number=*/1, + /*after_current_month=*/true), + expected); + EXPECT_TRUE(base::Time::FromUTCString("2025-06-01", &expected)); + EXPECT_EQ(internal::GetCohortNextStartDate(/*cohort_number=*/2, + /*after_current_month=*/true), + expected); + EXPECT_TRUE(base::Time::FromUTCString("2025-07-01", &expected)); + EXPECT_EQ(internal::GetCohortNextStartDate(/*cohort_number=*/3, + /*after_current_month=*/true), + expected); + EXPECT_TRUE(base::Time::FromUTCString("2025-08-01", &expected)); + EXPECT_EQ(internal::GetCohortNextStartDate(/*cohort_number=*/4, + /*after_current_month=*/true), + expected); + + // June. + EXPECT_TRUE(base::Time::FromUTCString("2025-06-15", &now_override)); + SetNowOverride(now_override); + + EXPECT_TRUE(base::Time::FromUTCString("2025-09-01", &expected)); + EXPECT_EQ(internal::GetCohortNextStartDate(/*cohort_number=*/1), expected); + EXPECT_TRUE(base::Time::FromUTCString("2025-06-01", &expected)); + EXPECT_EQ(internal::GetCohortNextStartDate(/*cohort_number=*/2), expected); + EXPECT_TRUE(base::Time::FromUTCString("2025-07-01", &expected)); + EXPECT_EQ(internal::GetCohortNextStartDate(/*cohort_number=*/3), expected); + EXPECT_TRUE(base::Time::FromUTCString("2025-08-01", &expected)); + EXPECT_EQ(internal::GetCohortNextStartDate(/*cohort_number=*/4), expected); + + EXPECT_TRUE(base::Time::FromUTCString("2025-09-01", &expected)); + EXPECT_EQ(internal::GetCohortNextStartDate(/*cohort_number=*/1, + /*after_current_month=*/true), + expected); + EXPECT_TRUE(base::Time::FromUTCString("2025-10-01", &expected)); + EXPECT_EQ(internal::GetCohortNextStartDate(/*cohort_number=*/2, + /*after_current_month=*/true), + expected); + EXPECT_TRUE(base::Time::FromUTCString("2025-07-01", &expected)); + EXPECT_EQ(internal::GetCohortNextStartDate(/*cohort_number=*/3, + /*after_current_month=*/true), + expected); + EXPECT_TRUE(base::Time::FromUTCString("2025-08-01", &expected)); + EXPECT_EQ(internal::GetCohortNextStartDate(/*cohort_number=*/4, + /*after_current_month=*/true), + expected); + + // July. + EXPECT_TRUE(base::Time::FromUTCString("2025-07-15", &now_override)); + SetNowOverride(now_override); + + EXPECT_TRUE(base::Time::FromUTCString("2025-09-01", &expected)); + EXPECT_EQ(internal::GetCohortNextStartDate(/*cohort_number=*/1), expected); + EXPECT_TRUE(base::Time::FromUTCString("2025-10-01", &expected)); + EXPECT_EQ(internal::GetCohortNextStartDate(/*cohort_number=*/2), expected); + EXPECT_TRUE(base::Time::FromUTCString("2025-07-01", &expected)); + EXPECT_EQ(internal::GetCohortNextStartDate(/*cohort_number=*/3), expected); + EXPECT_TRUE(base::Time::FromUTCString("2025-08-01", &expected)); + EXPECT_EQ(internal::GetCohortNextStartDate(/*cohort_number=*/4), expected); + + EXPECT_TRUE(base::Time::FromUTCString("2025-09-01", &expected)); + EXPECT_EQ(internal::GetCohortNextStartDate(/*cohort_number=*/1, + /*after_current_month=*/true), + expected); + EXPECT_TRUE(base::Time::FromUTCString("2025-10-01", &expected)); + EXPECT_EQ(internal::GetCohortNextStartDate(/*cohort_number=*/2, + /*after_current_month=*/true), + expected); + EXPECT_TRUE(base::Time::FromUTCString("2025-11-01", &expected)); + EXPECT_EQ(internal::GetCohortNextStartDate(/*cohort_number=*/3, + /*after_current_month=*/true), + expected); + EXPECT_TRUE(base::Time::FromUTCString("2025-08-01", &expected)); + EXPECT_EQ(internal::GetCohortNextStartDate(/*cohort_number=*/4, + /*after_current_month=*/true), + expected); + + // August. + EXPECT_TRUE(base::Time::FromUTCString("2025-08-15", &now_override)); + SetNowOverride(now_override); + + EXPECT_TRUE(base::Time::FromUTCString("2025-09-01", &expected)); + EXPECT_EQ(internal::GetCohortNextStartDate(/*cohort_number=*/1), expected); + EXPECT_TRUE(base::Time::FromUTCString("2025-10-01", &expected)); + EXPECT_EQ(internal::GetCohortNextStartDate(/*cohort_number=*/2), expected); + EXPECT_TRUE(base::Time::FromUTCString("2025-11-01", &expected)); + EXPECT_EQ(internal::GetCohortNextStartDate(/*cohort_number=*/3), expected); + EXPECT_TRUE(base::Time::FromUTCString("2025-08-01", &expected)); + EXPECT_EQ(internal::GetCohortNextStartDate(/*cohort_number=*/4), expected); + + EXPECT_TRUE(base::Time::FromUTCString("2025-09-01", &expected)); + EXPECT_EQ(internal::GetCohortNextStartDate(/*cohort_number=*/1, + /*after_current_month=*/true), + expected); + EXPECT_TRUE(base::Time::FromUTCString("2025-10-01", &expected)); + EXPECT_EQ(internal::GetCohortNextStartDate(/*cohort_number=*/2, + /*after_current_month=*/true), + expected); + EXPECT_TRUE(base::Time::FromUTCString("2025-11-01", &expected)); + EXPECT_EQ(internal::GetCohortNextStartDate(/*cohort_number=*/3, + /*after_current_month=*/true), + expected); + EXPECT_TRUE(base::Time::FromUTCString("2025-12-01", &expected)); + EXPECT_EQ(internal::GetCohortNextStartDate(/*cohort_number=*/4, + /*after_current_month=*/true), + expected); + + // September. + EXPECT_TRUE(base::Time::FromUTCString("2025-09-15", &now_override)); + SetNowOverride(now_override); + + EXPECT_TRUE(base::Time::FromUTCString("2025-09-01", &expected)); + EXPECT_EQ(internal::GetCohortNextStartDate(/*cohort_number=*/1), expected); + EXPECT_TRUE(base::Time::FromUTCString("2025-10-01", &expected)); + EXPECT_EQ(internal::GetCohortNextStartDate(/*cohort_number=*/2), expected); + EXPECT_TRUE(base::Time::FromUTCString("2025-11-01", &expected)); + EXPECT_EQ(internal::GetCohortNextStartDate(/*cohort_number=*/3), expected); + EXPECT_TRUE(base::Time::FromUTCString("2025-12-01", &expected)); + EXPECT_EQ(internal::GetCohortNextStartDate(/*cohort_number=*/4), expected); + + EXPECT_TRUE(base::Time::FromUTCString("2026-01-01", &expected)); + EXPECT_EQ(internal::GetCohortNextStartDate(/*cohort_number=*/1, + /*after_current_month=*/true), + expected); + EXPECT_TRUE(base::Time::FromUTCString("2025-10-01", &expected)); + EXPECT_EQ(internal::GetCohortNextStartDate(/*cohort_number=*/2, + /*after_current_month=*/true), + expected); + EXPECT_TRUE(base::Time::FromUTCString("2025-11-01", &expected)); + EXPECT_EQ(internal::GetCohortNextStartDate(/*cohort_number=*/3, + /*after_current_month=*/true), + expected); + EXPECT_TRUE(base::Time::FromUTCString("2025-12-01", &expected)); + EXPECT_EQ(internal::GetCohortNextStartDate(/*cohort_number=*/4, + /*after_current_month=*/true), + expected); + + // October. + EXPECT_TRUE(base::Time::FromUTCString("2025-10-15", &now_override)); + SetNowOverride(now_override); + + EXPECT_TRUE(base::Time::FromUTCString("2026-01-01", &expected)); + EXPECT_EQ(internal::GetCohortNextStartDate(/*cohort_number=*/1), expected); + EXPECT_TRUE(base::Time::FromUTCString("2025-10-01", &expected)); + EXPECT_EQ(internal::GetCohortNextStartDate(/*cohort_number=*/2), expected); + EXPECT_TRUE(base::Time::FromUTCString("2025-11-01", &expected)); + EXPECT_EQ(internal::GetCohortNextStartDate(/*cohort_number=*/3), expected); + EXPECT_TRUE(base::Time::FromUTCString("2025-12-01", &expected)); + EXPECT_EQ(internal::GetCohortNextStartDate(/*cohort_number=*/4), expected); + + EXPECT_TRUE(base::Time::FromUTCString("2026-01-01", &expected)); + EXPECT_EQ(internal::GetCohortNextStartDate(/*cohort_number=*/1, + /*after_current_month=*/true), + expected); + EXPECT_TRUE(base::Time::FromUTCString("2026-02-01", &expected)); + EXPECT_EQ(internal::GetCohortNextStartDate(/*cohort_number=*/2, + /*after_current_month=*/true), + expected); + EXPECT_TRUE(base::Time::FromUTCString("2025-11-01", &expected)); + EXPECT_EQ(internal::GetCohortNextStartDate(/*cohort_number=*/3, + /*after_current_month=*/true), + expected); + EXPECT_TRUE(base::Time::FromUTCString("2025-12-01", &expected)); + EXPECT_EQ(internal::GetCohortNextStartDate(/*cohort_number=*/4, + /*after_current_month=*/true), + expected); + + // November. + EXPECT_TRUE(base::Time::FromUTCString("2025-11-15", &now_override)); + SetNowOverride(now_override); + + EXPECT_TRUE(base::Time::FromUTCString("2026-01-01", &expected)); + EXPECT_EQ(internal::GetCohortNextStartDate(/*cohort_number=*/1), expected); + EXPECT_TRUE(base::Time::FromUTCString("2026-02-01", &expected)); + EXPECT_EQ(internal::GetCohortNextStartDate(/*cohort_number=*/2), expected); + EXPECT_TRUE(base::Time::FromUTCString("2025-11-01", &expected)); + EXPECT_EQ(internal::GetCohortNextStartDate(/*cohort_number=*/3), expected); + EXPECT_TRUE(base::Time::FromUTCString("2025-12-01", &expected)); + EXPECT_EQ(internal::GetCohortNextStartDate(/*cohort_number=*/4), expected); + + EXPECT_TRUE(base::Time::FromUTCString("2026-01-01", &expected)); + EXPECT_EQ(internal::GetCohortNextStartDate(/*cohort_number=*/1, + /*after_current_month=*/true), + expected); + EXPECT_TRUE(base::Time::FromUTCString("2026-02-01", &expected)); + EXPECT_EQ(internal::GetCohortNextStartDate(/*cohort_number=*/2, + /*after_current_month=*/true), + expected); + EXPECT_TRUE(base::Time::FromUTCString("2026-03-01", &expected)); + EXPECT_EQ(internal::GetCohortNextStartDate(/*cohort_number=*/3, + /*after_current_month=*/true), + expected); + EXPECT_TRUE(base::Time::FromUTCString("2025-12-01", &expected)); + EXPECT_EQ(internal::GetCohortNextStartDate(/*cohort_number=*/4, + /*after_current_month=*/true), + expected); + + // December. + EXPECT_TRUE(base::Time::FromUTCString("2025-12-15", &now_override)); + SetNowOverride(now_override); + + EXPECT_TRUE(base::Time::FromUTCString("2026-01-01", &expected)); + EXPECT_EQ(internal::GetCohortNextStartDate(/*cohort_number=*/1), expected); + EXPECT_TRUE(base::Time::FromUTCString("2026-02-01", &expected)); + EXPECT_EQ(internal::GetCohortNextStartDate(/*cohort_number=*/2), expected); + EXPECT_TRUE(base::Time::FromUTCString("2026-03-01", &expected)); + EXPECT_EQ(internal::GetCohortNextStartDate(/*cohort_number=*/3), expected); + EXPECT_TRUE(base::Time::FromUTCString("2025-12-01", &expected)); + EXPECT_EQ(internal::GetCohortNextStartDate(/*cohort_number=*/4), expected); + + EXPECT_TRUE(base::Time::FromUTCString("2026-01-01", &expected)); + EXPECT_EQ(internal::GetCohortNextStartDate(/*cohort_number=*/1, + /*after_current_month=*/true), + expected); + EXPECT_TRUE(base::Time::FromUTCString("2026-02-01", &expected)); + EXPECT_EQ(internal::GetCohortNextStartDate(/*cohort_number=*/2, + /*after_current_month=*/true), + expected); + EXPECT_TRUE(base::Time::FromUTCString("2026-03-01", &expected)); + EXPECT_EQ(internal::GetCohortNextStartDate(/*cohort_number=*/3, + /*after_current_month=*/true), + expected); + EXPECT_TRUE(base::Time::FromUTCString("2026-04-01", &expected)); + EXPECT_EQ(internal::GetCohortNextStartDate(/*cohort_number=*/4, + /*after_current_month=*/true), + expected); +} + +// Tests AssignNewCohortIfNeeded. +TEST_F(DefaultStatusHelperTest, AssignNewCohortIfNeeded) { + if (!@available(iOS 18.4, *)) { + // If the iOS version condition isn't met, no cohort should be assigned even + // if the other conditions are met. + prefs()->SetInteger(kDefaultStatusAPICohort, 0); + prefs()->SetTime(kDefaultStatusAPINextRetry, base::Time()); + ForceFirstRunRecency(100); + internal::AssignNewCohortIfNeeded(prefs()); + EXPECT_EQ(prefs()->GetInteger(kDefaultStatusAPICohort), 0); + EXPECT_EQ(prefs()->GetTime(kDefaultStatusAPINextRetry), base::Time()); + return; + } + + // If this is a new client, no cohort should be assigned even if the other + // conditions are met. + prefs()->SetInteger(kDefaultStatusAPICohort, 0); + prefs()->SetTime(kDefaultStatusAPINextRetry, base::Time()); + ForceFirstRunRecency(27); + internal::AssignNewCohortIfNeeded(prefs()); + EXPECT_EQ(prefs()->GetInteger(kDefaultStatusAPICohort), 0); + EXPECT_EQ(prefs()->GetTime(kDefaultStatusAPINextRetry), base::Time()); + + // If a cohort is already assigned, the cohort and next availability should + // not be changed, even if the other conditions are met. + prefs()->SetInteger(kDefaultStatusAPICohort, 1000); + base::Time time; + EXPECT_TRUE(base::Time::FromUTCString("2000-01-15", &time)); + prefs()->SetTime(kDefaultStatusAPINextRetry, time); + ForceFirstRunRecency(100); + internal::AssignNewCohortIfNeeded(prefs()); + EXPECT_EQ(prefs()->GetInteger(kDefaultStatusAPICohort), 1000); + EXPECT_EQ(prefs()->GetTime(kDefaultStatusAPINextRetry), time); + + // If all conditions are met, a new cohort should be assigned and the next + // availability should be set. + prefs()->SetInteger(kDefaultStatusAPICohort, 0); + prefs()->SetTime(kDefaultStatusAPINextRetry, base::Time()); + ForceFirstRunRecency(100); + internal::AssignNewCohortIfNeeded(prefs()); + EXPECT_NE(prefs()->GetInteger(kDefaultStatusAPICohort), 0); + EXPECT_NE(prefs()->GetTime(kDefaultStatusAPINextRetry), base::Time()); +} + +// Tests SystemToLocalEnum. +TEST_F(DefaultStatusHelperTest, SystemToLocalEnum) API_AVAILABLE(ios(18.4)) { + DefaultStatusAPIResult result = + internal::SystemToLocalEnum(UIApplicationCategoryDefaultStatusIsDefault); + EXPECT_EQ(result, DefaultStatusAPIResult::kIsDefault); + result = + internal::SystemToLocalEnum(UIApplicationCategoryDefaultStatusNotDefault); + EXPECT_EQ(result, DefaultStatusAPIResult::kIsNotDefault); + result = + internal::SystemToLocalEnum((UIApplicationCategoryDefaultStatus)1000); + EXPECT_EQ(result, DefaultStatusAPIResult::kUnknown); +} + +// Tests DetermineRetentionStatus. +TEST_F(DefaultStatusHelperTest, DetermineRetentionStatus) { + DefaultStatusRetention result = internal::DetermineRetentionStatus( + /*previous=*/DefaultStatusAPIResult::kIsNotDefault, + /*current=*/DefaultStatusAPIResult::kIsDefault); + EXPECT_EQ(result, DefaultStatusRetention::kBecameDefault); + result = internal::DetermineRetentionStatus( + /*previous=*/DefaultStatusAPIResult::kIsDefault, + /*current=*/DefaultStatusAPIResult::kIsNotDefault); + EXPECT_EQ(result, DefaultStatusRetention::kBecameNonDefault); + result = internal::DetermineRetentionStatus( + /*previous=*/DefaultStatusAPIResult::kIsDefault, + /*current=*/DefaultStatusAPIResult::kIsDefault); + EXPECT_EQ(result, DefaultStatusRetention::kRemainedDefault); + result = internal::DetermineRetentionStatus( + /*previous=*/DefaultStatusAPIResult::kIsNotDefault, + /*current=*/DefaultStatusAPIResult::kIsNotDefault); + EXPECT_EQ(result, DefaultStatusRetention::kRemainedNonDefault); +} + +// Tests that QueryDefaultStatusIfReadyAndLogResults doesn't do anything if the +// system isn't running on the minimum version. +TEST_F(DefaultStatusHelperTest, QueryDefaultStatusAPINotAvailable) +API_AVAILABLE(ios(18.4)) { + if (@available(iOS 18.4, *)) { + return; + } + + base::HistogramTester histogram_tester; + histogram_tester.ExpectTotalCount("IOS.DefaultStatusAPI.OutcomeType", 0); + prefs()->SetInteger(kDefaultStatusAPICohort, 1); + prefs()->SetTime(kDefaultStatusAPINextRetry, base::Time()); + internal::OverrideSystemCallForTesting(base::BindOnce([](NSError** error) { + // This should not be called. + EXPECT_TRUE(false); + return UIApplicationCategoryDefaultStatusUnavailable; + })); + + internal::QueryDefaultStatusIfReadyAndLogResults(prefs()); + + histogram_tester.ExpectTotalCount("IOS.DefaultStatusAPI.OutcomeType", 0); + histogram_tester.ExpectTotalCount( + "IOS.DefaultStatusAPI.CooldownError.DaysLeft", 0); + ExpectSuccessHistogramsToHaveNoSamples(histogram_tester); +} + +// Test that QueryDefaultStatusIfReadyAndLogResults does not do anything if +// the client is still on cooldown according to local prefs. +TEST_F(DefaultStatusHelperTest, QueryDefaultStatusAPIOnLocalCooldown) +API_AVAILABLE(ios(18.4)) { + if (!@available(iOS 18.4, *)) { + return; + } + + base::HistogramTester histogram_tester; + histogram_tester.ExpectTotalCount("IOS.DefaultStatusAPI.OutcomeType", 0); + prefs()->SetInteger(kDefaultStatusAPICohort, 1); + base::Time some_day_in_future = + base::subtle::TimeNowIgnoringOverride() + base::Days(100); + prefs()->SetTime(kDefaultStatusAPINextRetry, some_day_in_future); + internal::OverrideSystemCallForTesting(base::BindOnce([](NSError** error) { + // This should not be called. + EXPECT_TRUE(false); + return UIApplicationCategoryDefaultStatusUnavailable; + })); + + internal::QueryDefaultStatusIfReadyAndLogResults(prefs()); + + histogram_tester.ExpectTotalCount("IOS.DefaultStatusAPI.OutcomeType", 0); + histogram_tester.ExpectTotalCount( + "IOS.DefaultStatusAPI.CooldownError.DaysLeft", 0); + ExpectSuccessHistogramsToHaveNoSamples(histogram_tester); +} + +// Test that QueryDefaultStatusIfReadyAndLogResults correctly handles cooldown +// errors returned by the default status system API. +TEST_F(DefaultStatusHelperTest, QueryDefaultStatusAPISystemCooldownError) +API_AVAILABLE(ios(18.4)) { + if (!@available(iOS 18.4, *)) { + return; + } + + base::HistogramTester histogram_tester; + histogram_tester.ExpectTotalCount("IOS.DefaultStatusAPI.OutcomeType", 0); + prefs()->SetInteger(kDefaultStatusAPICohort, 1); + prefs()->SetTime(kDefaultStatusAPINextRetry, base::Time()); + base::Time next_retry = + base::subtle::TimeNowIgnoringOverride() + base::Days(100); + internal::OverrideSystemCallForTesting(base::BindOnce( + [](base::Time retry_date, NSError** error) { + NSDictionary* errorInfo = @{ + UIApplicationCategoryDefaultRetryAvailabilityDateErrorKey : + retry_date.ToNSDate() + }; + *error = [NSError + errorWithDomain:UIApplicationCategoryDefaultErrorDomain + code:UIApplicationCategoryDefaultErrorRateLimited + userInfo:errorInfo]; + return UIApplicationCategoryDefaultStatusUnavailable; + }, + next_retry)); + + internal::QueryDefaultStatusIfReadyAndLogResults(prefs()); + + histogram_tester.ExpectTotalCount("IOS.DefaultStatusAPI.OutcomeType", 1); + histogram_tester.ExpectBucketCount( + "IOS.DefaultStatusAPI.OutcomeType", + DefaultStatusAPIOutcomeType::kCooldownError, 1); + EXPECT_EQ(prefs()->GetTime(kDefaultStatusAPINextRetry), next_retry); + histogram_tester.ExpectTotalCount( + "IOS.DefaultStatusAPI.CooldownError.DaysLeft", 1); + histogram_tester.ExpectBucketCount( + "IOS.DefaultStatusAPI.CooldownError.DaysLeft", 100, 1); + ExpectSuccessHistogramsToHaveNoSamples(histogram_tester); +} + +// Test that QueryDefaultStatusIfReadyAndLogResults can handle unknown error +// types returned by the default status system API. +TEST_F(DefaultStatusHelperTest, QueryDefaultStatusAPISystemUnknownError) +API_AVAILABLE(ios(18.4)) { + if (!@available(iOS 18.4, *)) { + return; + } + + base::HistogramTester histogram_tester; + histogram_tester.ExpectTotalCount("IOS.DefaultStatusAPI.OutcomeType", 0); + prefs()->SetInteger(kDefaultStatusAPICohort, 1); + base::Time some_days_ago = + base::subtle::TimeNowIgnoringOverride() - base::Days(10); + prefs()->SetTime(kDefaultStatusAPINextRetry, some_days_ago); + internal::OverrideSystemCallForTesting(base::BindOnce([](NSError** error) { + NSDictionary* errorInfo = @{@"Fake" : @42}; + *error = [NSError errorWithDomain:UIApplicationCategoryDefaultErrorDomain + code:123456 + userInfo:errorInfo]; + return UIApplicationCategoryDefaultStatusUnavailable; + })); + + internal::QueryDefaultStatusIfReadyAndLogResults(prefs()); + + histogram_tester.ExpectTotalCount("IOS.DefaultStatusAPI.OutcomeType", 1); + histogram_tester.ExpectBucketCount("IOS.DefaultStatusAPI.OutcomeType", + DefaultStatusAPIOutcomeType::kOtherError, + 1); + EXPECT_EQ(prefs()->GetTime(kDefaultStatusAPINextRetry), some_days_ago); + histogram_tester.ExpectTotalCount( + "IOS.DefaultStatusAPI.CooldownError.DaysLeft", 0); + ExpectSuccessHistogramsToHaveNoSamples(histogram_tester); +} + +// Tests the success case of QueryDefaultStatusIfReadyAndLogResults for the +// following scenario: +// - Cohort 1 +// - Within cohort 1 reporting window +// - Is default +// - No previous default status API result +TEST_F(DefaultStatusHelperTest, + QueryDefaultStatusAPIStrictCohort1IsDefaultFirstTime) +API_AVAILABLE(ios(18.4)) { + if (!@available(iOS 18.4, *)) { + return; + } + + base::subtle::ScopedTimeClockOverrides time_override( + &DefaultStatusHelperTest::NowOverride, nullptr, nullptr); + base::Time now_override; + EXPECT_TRUE(base::Time::FromUTCString("2025-01-15", &now_override)); + SetNowOverride(now_override); + + base::HistogramTester histogram_tester; + histogram_tester.ExpectTotalCount("IOS.DefaultStatusAPI.OutcomeType", 0); + prefs()->SetInteger(kDefaultStatusAPICohort, 1); + prefs()->SetTime(kDefaultStatusAPILastSuccessfulCall, base::Time()); + prefs()->SetTime(kDefaultStatusAPINextRetry, base::Time()); + prefs()->SetInteger(kDefaultStatusAPIResult, + static_cast<int>(DefaultStatusAPIResult::kUnknown)); + internal::OverrideSystemCallForTesting(base::BindOnce([](NSError** error) { + return UIApplicationCategoryDefaultStatusIsDefault; + })); + + internal::QueryDefaultStatusIfReadyAndLogResults(prefs()); + + histogram_tester.ExpectTotalCount("IOS.DefaultStatusAPI.OutcomeType", 1); + histogram_tester.ExpectBucketCount("IOS.DefaultStatusAPI.OutcomeType", + DefaultStatusAPIOutcomeType::kSuccess, 1); + base::Time expected_next; + EXPECT_TRUE(base::Time::FromUTCString("2025-05-01", &expected_next)); + EXPECT_EQ(prefs()->GetTime(kDefaultStatusAPINextRetry), expected_next); + histogram_tester.ExpectTotalCount( + "IOS.DefaultStatusAPI.CooldownError.DaysLeft", 0); + histogram_tester.ExpectTotalCount( + "IOS.DefaultStatusAPI.DaysSinceLastSuccessfulCall", 0); + histogram_tester.ExpectTotalCount("IOS.DefaultStatusAPI.IsDefaultBrowser", 1); + histogram_tester.ExpectBucketCount("IOS.DefaultStatusAPI.IsDefaultBrowser", + true, 1); + histogram_tester.ExpectTotalCount( + "IOS.DefaultStatusAPI.IsDefaultBrowser.Cohort1", 1); + histogram_tester.ExpectBucketCount( + "IOS.DefaultStatusAPI.IsDefaultBrowser.Cohort1", true, 1); + histogram_tester.ExpectTotalCount( + "IOS.DefaultStatusAPI.IsDefaultBrowser.Cohort2", 0); + histogram_tester.ExpectTotalCount( + "IOS.DefaultStatusAPI.IsDefaultBrowser.Cohort3", 0); + histogram_tester.ExpectTotalCount( + "IOS.DefaultStatusAPI.IsDefaultBrowser.StrictCohorts", 1); + histogram_tester.ExpectBucketCount( + "IOS.DefaultStatusAPI.IsDefaultBrowser.StrictCohorts", true, 1); + histogram_tester.ExpectTotalCount( + "IOS.DefaultStatusAPI.DefaultStatusRetention", 0); + histogram_tester.ExpectTotalCount("IOS.DefaultStatusAPI.HeuristicAssessment1", + 1); + histogram_tester.ExpectTotalCount("IOS.DefaultStatusAPI.HeuristicAssessment3", + 1); + histogram_tester.ExpectTotalCount("IOS.DefaultStatusAPI.HeuristicAssessment7", + 1); + histogram_tester.ExpectTotalCount( + "IOS.DefaultStatusAPI.HeuristicAssessment14", 1); + histogram_tester.ExpectTotalCount( + "IOS.DefaultStatusAPI.HeuristicAssessment21", 1); + histogram_tester.ExpectTotalCount( + "IOS.DefaultStatusAPI.HeuristicAssessment28", 1); + histogram_tester.ExpectTotalCount( + "IOS.DefaultStatusAPI.HeuristicAssessment35", 1); + histogram_tester.ExpectTotalCount( + "IOS.DefaultStatusAPI.HeuristicAssessment42", 1); +} + +// Tests the success case of QueryDefaultStatusIfReadyAndLogResults for the +// following scenario: +// - Cohort 2 +// - Outside cohort 2 reporting window +// - Is not default +// - Has a previous default status API result of "is default" +TEST_F(DefaultStatusHelperTest, + QueryDefaultStatusAPICohort2IsNotDefaultRecurring) +API_AVAILABLE(ios(18.4)) { + if (!@available(iOS 18.4, *)) { + return; + } + + base::subtle::ScopedTimeClockOverrides time_override( + &DefaultStatusHelperTest::NowOverride, nullptr, nullptr); + base::Time now_override; + EXPECT_TRUE(base::Time::FromUTCString("2025-03-15", &now_override)); + SetNowOverride(now_override); + + base::HistogramTester histogram_tester; + histogram_tester.ExpectTotalCount("IOS.DefaultStatusAPI.OutcomeType", 0); + prefs()->SetInteger(kDefaultStatusAPICohort, 2); + base::Time last_successful_call = base::Time::Now() - base::Days(100); + prefs()->SetTime(kDefaultStatusAPILastSuccessfulCall, last_successful_call); + prefs()->SetTime(kDefaultStatusAPINextRetry, base::Time()); + prefs()->SetInteger(kDefaultStatusAPIResult, + static_cast<int>(DefaultStatusAPIResult::kIsDefault)); + internal::OverrideSystemCallForTesting(base::BindOnce([](NSError** error) { + return UIApplicationCategoryDefaultStatusNotDefault; + })); + + internal::QueryDefaultStatusIfReadyAndLogResults(prefs()); + + histogram_tester.ExpectTotalCount("IOS.DefaultStatusAPI.OutcomeType", 1); + histogram_tester.ExpectBucketCount("IOS.DefaultStatusAPI.OutcomeType", + DefaultStatusAPIOutcomeType::kSuccess, 1); + base::Time expected_next; + EXPECT_TRUE(base::Time::FromUTCString("2025-06-01", &expected_next)); + EXPECT_EQ(prefs()->GetTime(kDefaultStatusAPINextRetry), expected_next); + histogram_tester.ExpectTotalCount( + "IOS.DefaultStatusAPI.CooldownError.DaysLeft", 0); + histogram_tester.ExpectTotalCount( + "IOS.DefaultStatusAPI.DaysSinceLastSuccessfulCall", 1); + histogram_tester.ExpectBucketCount( + "IOS.DefaultStatusAPI.DaysSinceLastSuccessfulCall", 100, 1); + histogram_tester.ExpectTotalCount("IOS.DefaultStatusAPI.IsDefaultBrowser", 1); + histogram_tester.ExpectBucketCount("IOS.DefaultStatusAPI.IsDefaultBrowser", + false, 1); + histogram_tester.ExpectTotalCount( + "IOS.DefaultStatusAPI.IsDefaultBrowser.Cohort1", 0); + histogram_tester.ExpectTotalCount( + "IOS.DefaultStatusAPI.IsDefaultBrowser.Cohort2", 1); + histogram_tester.ExpectBucketCount( + "IOS.DefaultStatusAPI.IsDefaultBrowser.Cohort2", false, 1); + histogram_tester.ExpectTotalCount( + "IOS.DefaultStatusAPI.IsDefaultBrowser.Cohort3", 0); + histogram_tester.ExpectTotalCount( + "IOS.DefaultStatusAPI.IsDefaultBrowser.StrictCohorts", 0); + histogram_tester.ExpectTotalCount( + "IOS.DefaultStatusAPI.DefaultStatusRetention", 1); + histogram_tester.ExpectBucketCount( + "IOS.DefaultStatusAPI.DefaultStatusRetention", + DefaultStatusRetention::kBecameNonDefault, 1); + histogram_tester.ExpectTotalCount("IOS.DefaultStatusAPI.HeuristicAssessment1", + 1); + histogram_tester.ExpectTotalCount("IOS.DefaultStatusAPI.HeuristicAssessment3", + 1); + histogram_tester.ExpectTotalCount("IOS.DefaultStatusAPI.HeuristicAssessment7", + 1); + histogram_tester.ExpectTotalCount( + "IOS.DefaultStatusAPI.HeuristicAssessment14", 1); + histogram_tester.ExpectTotalCount( + "IOS.DefaultStatusAPI.HeuristicAssessment21", 1); + histogram_tester.ExpectTotalCount( + "IOS.DefaultStatusAPI.HeuristicAssessment28", 1); + histogram_tester.ExpectTotalCount( + "IOS.DefaultStatusAPI.HeuristicAssessment35", 1); + histogram_tester.ExpectTotalCount( + "IOS.DefaultStatusAPI.HeuristicAssessment42", 1); +} + +// Tests the success case of QueryDefaultStatusIfReadyAndLogResults for the +// following scenario: +// - Cohort 3 +// - Inside cohort 3 reporting window +// - Is default +// - Has a previous default status API result of "is default" +TEST_F(DefaultStatusHelperTest, + QueryDefaultStatusAPIStrictCohort3IsDefaultRecurring) +API_AVAILABLE(ios(18.4)) { + if (!@available(iOS 18.4, *)) { + return; + } + + base::subtle::ScopedTimeClockOverrides time_override( + &DefaultStatusHelperTest::NowOverride, nullptr, nullptr); + base::Time now_override; + EXPECT_TRUE(base::Time::FromUTCString("2025-07-15", &now_override)); + SetNowOverride(now_override); + + base::HistogramTester histogram_tester; + histogram_tester.ExpectTotalCount("IOS.DefaultStatusAPI.OutcomeType", 0); + prefs()->SetInteger(kDefaultStatusAPICohort, 3); + base::Time last_successful_call = base::Time::Now() - base::Days(150); + prefs()->SetTime(kDefaultStatusAPILastSuccessfulCall, last_successful_call); + prefs()->SetTime(kDefaultStatusAPINextRetry, base::Time()); + prefs()->SetInteger(kDefaultStatusAPIResult, + static_cast<int>(DefaultStatusAPIResult::kIsDefault)); + internal::OverrideSystemCallForTesting(base::BindOnce([](NSError** error) { + return UIApplicationCategoryDefaultStatusIsDefault; + })); + + internal::QueryDefaultStatusIfReadyAndLogResults(prefs()); + + histogram_tester.ExpectTotalCount("IOS.DefaultStatusAPI.OutcomeType", 1); + histogram_tester.ExpectBucketCount("IOS.DefaultStatusAPI.OutcomeType", + DefaultStatusAPIOutcomeType::kSuccess, 1); + base::Time expected_next; + EXPECT_TRUE(base::Time::FromUTCString("2025-11-01", &expected_next)); + EXPECT_EQ(prefs()->GetTime(kDefaultStatusAPINextRetry), expected_next); + histogram_tester.ExpectTotalCount( + "IOS.DefaultStatusAPI.CooldownError.DaysLeft", 0); + histogram_tester.ExpectTotalCount( + "IOS.DefaultStatusAPI.DaysSinceLastSuccessfulCall", 1); + histogram_tester.ExpectBucketCount( + "IOS.DefaultStatusAPI.DaysSinceLastSuccessfulCall", 150, 1); + histogram_tester.ExpectTotalCount("IOS.DefaultStatusAPI.IsDefaultBrowser", 1); + histogram_tester.ExpectBucketCount("IOS.DefaultStatusAPI.IsDefaultBrowser", + true, 1); + histogram_tester.ExpectTotalCount( + "IOS.DefaultStatusAPI.IsDefaultBrowser.Cohort1", 0); + histogram_tester.ExpectTotalCount( + "IOS.DefaultStatusAPI.IsDefaultBrowser.Cohort2", 0); + histogram_tester.ExpectTotalCount( + "IOS.DefaultStatusAPI.IsDefaultBrowser.Cohort3", 1); + histogram_tester.ExpectBucketCount( + "IOS.DefaultStatusAPI.IsDefaultBrowser.Cohort3", true, 1); + histogram_tester.ExpectTotalCount( + "IOS.DefaultStatusAPI.IsDefaultBrowser.StrictCohorts", 1); + histogram_tester.ExpectBucketCount( + "IOS.DefaultStatusAPI.IsDefaultBrowser.StrictCohorts", true, 1); + histogram_tester.ExpectTotalCount( + "IOS.DefaultStatusAPI.DefaultStatusRetention", 1); + histogram_tester.ExpectBucketCount( + "IOS.DefaultStatusAPI.DefaultStatusRetention", + DefaultStatusRetention::kRemainedDefault, 1); + histogram_tester.ExpectTotalCount("IOS.DefaultStatusAPI.HeuristicAssessment1", + 1); + histogram_tester.ExpectTotalCount("IOS.DefaultStatusAPI.HeuristicAssessment3", + 1); + histogram_tester.ExpectTotalCount("IOS.DefaultStatusAPI.HeuristicAssessment7", + 1); + histogram_tester.ExpectTotalCount( + "IOS.DefaultStatusAPI.HeuristicAssessment14", 1); + histogram_tester.ExpectTotalCount( + "IOS.DefaultStatusAPI.HeuristicAssessment21", 1); + histogram_tester.ExpectTotalCount( + "IOS.DefaultStatusAPI.HeuristicAssessment28", 1); + histogram_tester.ExpectTotalCount( + "IOS.DefaultStatusAPI.HeuristicAssessment35", 1); + histogram_tester.ExpectTotalCount( + "IOS.DefaultStatusAPI.HeuristicAssessment42", 1); +} + +} // namespace default_status
diff --git a/ios/chrome/browser/home_customization/utils/home_customization_helper.mm b/ios/chrome/browser/home_customization/utils/home_customization_helper.mm index ee36f6e..e16178c8 100644 --- a/ios/chrome/browser/home_customization/utils/home_customization_helper.mm +++ b/ios/chrome/browser/home_customization/utils/home_customization_helper.mm
@@ -47,7 +47,7 @@ } else if (commerce::kShopCardVariation.Get() == commerce::kShopCardArm2) { return l10n_util::GetNSString( - IDS_IOS_CONTENT_SUGGESTIONS_SHOPCARD_REVIEWS_ALT_TITLE); + IDS_IOS_CONTENT_SUGGESTIONS_SHOPCARD_REVIEWS_CUSTOMIZE_CARDS_ALT_2); } return @""; }
diff --git a/ios/chrome/browser/intelligence/enhanced_calendar/coordinator/BUILD.gn b/ios/chrome/browser/intelligence/enhanced_calendar/coordinator/BUILD.gn new file mode 100644 index 0000000..48c39167 --- /dev/null +++ b/ios/chrome/browser/intelligence/enhanced_calendar/coordinator/BUILD.gn
@@ -0,0 +1,22 @@ +# 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("coordinator") { + sources = [ + "enhanced_calendar_coordinator.h", + "enhanced_calendar_coordinator.mm", + "enhanced_calendar_mediator.h", + "enhanced_calendar_mediator.mm", + ] + + deps = [ + "//base", + "//ios/chrome/browser/intelligence/enhanced_calendar/ui", + "//ios/chrome/browser/shared/coordinator/chrome_coordinator", + "//ios/chrome/browser/shared/model/browser", + "//ios/chrome/browser/shared/model/web_state_list", + "//ios/public/provider/chrome/browser/add_to_calendar:add_to_calendar_api", + "//ios/web/public", + ] +}
diff --git a/ios/chrome/browser/intelligence/enhanced_calendar/coordinator/enhanced_calendar_coordinator.h b/ios/chrome/browser/intelligence/enhanced_calendar/coordinator/enhanced_calendar_coordinator.h new file mode 100644 index 0000000..64b8fd9 --- /dev/null +++ b/ios/chrome/browser/intelligence/enhanced_calendar/coordinator/enhanced_calendar_coordinator.h
@@ -0,0 +1,30 @@ +// Copyright 2025 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef IOS_CHROME_BROWSER_INTELLIGENCE_ENHANCED_CALENDAR_COORDINATOR_ENHANCED_CALENDAR_COORDINATOR_H_ +#define IOS_CHROME_BROWSER_INTELLIGENCE_ENHANCED_CALENDAR_COORDINATOR_ENHANCED_CALENDAR_COORDINATOR_H_ + +#import "ios/chrome/browser/shared/coordinator/chrome_coordinator/chrome_coordinator.h" + +namespace ios::provider { +enum class AddToCalendarIntegrationProvider; +} // namespace ios::provider + +// The coordinator for the Enhanced Calendar feature's UI. +@interface EnhancedCalendarCoordinator : ChromeCoordinator + +- (instancetype)initWithBaseViewController:(UIViewController*)viewController + browser:(Browser*)browser NS_UNAVAILABLE; + +// `integrationProvider` is the "add to calendar" integration provider to be +// shown to the user. +- (instancetype)initWithBaseViewController:(UIViewController*)viewController + browser:(Browser*)browser + integrationProvider: + (ios::provider::AddToCalendarIntegrationProvider) + integrationProvider NS_DESIGNATED_INITIALIZER; + +@end + +#endif // IOS_CHROME_BROWSER_INTELLIGENCE_ENHANCED_CALENDAR_COORDINATOR_ENHANCED_CALENDAR_COORDINATOR_H_
diff --git a/ios/chrome/browser/intelligence/enhanced_calendar/coordinator/enhanced_calendar_coordinator.mm b/ios/chrome/browser/intelligence/enhanced_calendar/coordinator/enhanced_calendar_coordinator.mm new file mode 100644 index 0000000..6a1625a --- /dev/null +++ b/ios/chrome/browser/intelligence/enhanced_calendar/coordinator/enhanced_calendar_coordinator.mm
@@ -0,0 +1,61 @@ +// 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 "ios/chrome/browser/intelligence/enhanced_calendar/coordinator/enhanced_calendar_coordinator.h" + +#import "ios/chrome/browser/intelligence/enhanced_calendar/coordinator/enhanced_calendar_mediator.h" +#import "ios/chrome/browser/intelligence/enhanced_calendar/ui/enhanced_calendar_view_controller.h" +#import "ios/chrome/browser/shared/model/browser/browser.h" +#import "ios/chrome/browser/shared/model/web_state_list/web_state_list.h" +#import "ios/public/provider/chrome/browser/add_to_calendar/add_to_calendar_api.h" + +@implementation EnhancedCalendarCoordinator { + // The integration provider for the "add to calendar" experience. + ios::provider::AddToCalendarIntegrationProvider _integrationProvider; + + // The mediator for handling the Enhanced Calendar service and its model + // request(s). + EnhancedCalendarMediator* _mediator; + + // The view controller presented as the Enhanced Calendar interstitial. + EnhancedCalendarViewController* _viewController; +} + +- (instancetype)initWithBaseViewController:(UIViewController*)viewController + browser:(Browser*)browser + integrationProvider: + (ios::provider::AddToCalendarIntegrationProvider) + integrationProvider { + self = [super initWithBaseViewController:viewController browser:browser]; + if (self) { + _integrationProvider = integrationProvider; + } + return self; +} + +#pragma mark - ChromeCoordinator + +- (void)start { + _viewController = [[EnhancedCalendarViewController alloc] init]; + + _mediator = [[EnhancedCalendarMediator alloc] + initWithWebState:self.browser->GetWebStateList()->GetActiveWebState() + integrationProvider:_integrationProvider]; + + _viewController.mutator = _mediator; + + [self.baseViewController presentViewController:_viewController + animated:YES + completion:nil]; +} + +- (void)stop { + [_mediator disconnect]; + [self.baseViewController dismissViewControllerAnimated:YES completion:nil]; + + _mediator = nil; + _viewController = nil; +} + +@end
diff --git a/ios/chrome/browser/intelligence/enhanced_calendar/coordinator/enhanced_calendar_mediator.h b/ios/chrome/browser/intelligence/enhanced_calendar/coordinator/enhanced_calendar_mediator.h new file mode 100644 index 0000000..60825694 --- /dev/null +++ b/ios/chrome/browser/intelligence/enhanced_calendar/coordinator/enhanced_calendar_mediator.h
@@ -0,0 +1,35 @@ +// 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 IOS_CHROME_BROWSER_INTELLIGENCE_ENHANCED_CALENDAR_COORDINATOR_ENHANCED_CALENDAR_MEDIATOR_H_ +#define IOS_CHROME_BROWSER_INTELLIGENCE_ENHANCED_CALENDAR_COORDINATOR_ENHANCED_CALENDAR_MEDIATOR_H_ + +#import <Foundation/Foundation.h> + +#import "ios/chrome/browser/intelligence/enhanced_calendar/ui/enhanced_calendar_mutator.h" + +namespace web { +class WebState; +} // namespace web + +namespace ios::provider { +enum class AddToCalendarIntegrationProvider; +} // namespace ios::provider + +// The mediator for the Enhanced Calendar feature's UI. +@interface EnhancedCalendarMediator : NSObject <EnhancedCalendarMutator> + +- (instancetype)initWithWebState:(web::WebState*)webState + integrationProvider: + (ios::provider::AddToCalendarIntegrationProvider) + integrationProvider NS_DESIGNATED_INITIALIZER; + +- (instancetype)init NS_UNAVAILABLE; + +// Disconnect the mediator. +- (void)disconnect; + +@end + +#endif // IOS_CHROME_BROWSER_INTELLIGENCE_ENHANCED_CALENDAR_COORDINATOR_ENHANCED_CALENDAR_MEDIATOR_H_
diff --git a/ios/chrome/browser/intelligence/enhanced_calendar/coordinator/enhanced_calendar_mediator.mm b/ios/chrome/browser/intelligence/enhanced_calendar/coordinator/enhanced_calendar_mediator.mm new file mode 100644 index 0000000..f9b4b9f --- /dev/null +++ b/ios/chrome/browser/intelligence/enhanced_calendar/coordinator/enhanced_calendar_mediator.mm
@@ -0,0 +1,43 @@ +// 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 "ios/chrome/browser/intelligence/enhanced_calendar/coordinator/enhanced_calendar_mediator.h" + +#import "base/memory/raw_ptr.h" +#import "ios/public/provider/chrome/browser/add_to_calendar/add_to_calendar_api.h" +#import "ios/web/public/web_state.h" + +@implementation EnhancedCalendarMediator { + // The web state that this mediator is associated with. + raw_ptr<web::WebState> _webState; + + // The integration provider for the "add to calendar" experience. + ios::provider::AddToCalendarIntegrationProvider _integrationProvider; +} + +- (instancetype)initWithWebState:(web::WebState*)webState + integrationProvider: + (ios::provider::AddToCalendarIntegrationProvider) + integrationProvider { + self = [super init]; + if (self) { + _webState = webState; + _integrationProvider = integrationProvider; + } + return self; +} + +- (void)disconnect { + _webState = nullptr; + // TODO(crbug.com/405195613): Cancel any in-flight requests. +} + +#pragma mark - EnhancedCalendarMutator + +- (void)cancelEnhancedCalendarRequestAndDismissBottomSheet { + // TODO(crbug.com/405195613): Cancel any in-flight requests, and dismiss the + // bottom sheet. +} + +@end
diff --git a/ios/chrome/browser/intelligence/enhanced_calendar/ui/BUILD.gn b/ios/chrome/browser/intelligence/enhanced_calendar/ui/BUILD.gn new file mode 100644 index 0000000..0044d3a6 --- /dev/null +++ b/ios/chrome/browser/intelligence/enhanced_calendar/ui/BUILD.gn
@@ -0,0 +1,20 @@ +# 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("ui") { + sources = [ + "enhanced_calendar_mutator.h", + "enhanced_calendar_view_controller.h", + "enhanced_calendar_view_controller.mm", + ] + + deps = [ + "//ios/chrome/app/strings:ios_strings_grit", + "//ios/chrome/browser/shared/ui/bottom_sheet:bottom_sheet_view_controller", + "//ios/chrome/browser/shared/ui/symbols", + "//ios/chrome/common/ui/confirmation_alert", + "//ios/public/provider/chrome/browser/add_to_calendar:add_to_calendar_api", + "//ui/base", + ] +}
diff --git a/ios/chrome/browser/intelligence/enhanced_calendar/ui/enhanced_calendar_mutator.h b/ios/chrome/browser/intelligence/enhanced_calendar/ui/enhanced_calendar_mutator.h new file mode 100644 index 0000000..928ce7a --- /dev/null +++ b/ios/chrome/browser/intelligence/enhanced_calendar/ui/enhanced_calendar_mutator.h
@@ -0,0 +1,18 @@ +// 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 IOS_CHROME_BROWSER_INTELLIGENCE_ENHANCED_CALENDAR_UI_ENHANCED_CALENDAR_MUTATOR_H_ +#define IOS_CHROME_BROWSER_INTELLIGENCE_ENHANCED_CALENDAR_UI_ENHANCED_CALENDAR_MUTATOR_H_ + +// Mutator protocol for the view controller to communicate with the +// EnhancedCalendarMediator. +@protocol EnhancedCalendarMutator + +// Cancels any in-flight EnhancedCalendar requests and dismisses the bottom +// sheet. User-triggered. +- (void)cancelEnhancedCalendarRequestAndDismissBottomSheet; + +@end + +#endif // IOS_CHROME_BROWSER_INTELLIGENCE_ENHANCED_CALENDAR_UI_ENHANCED_CALENDAR_MUTATOR_H_
diff --git a/ios/chrome/browser/intelligence/enhanced_calendar/ui/enhanced_calendar_view_controller.h b/ios/chrome/browser/intelligence/enhanced_calendar/ui/enhanced_calendar_view_controller.h new file mode 100644 index 0000000..3e2d83e3 --- /dev/null +++ b/ios/chrome/browser/intelligence/enhanced_calendar/ui/enhanced_calendar_view_controller.h
@@ -0,0 +1,25 @@ +// 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 IOS_CHROME_BROWSER_INTELLIGENCE_ENHANCED_CALENDAR_UI_ENHANCED_CALENDAR_VIEW_CONTROLLER_H_ +#define IOS_CHROME_BROWSER_INTELLIGENCE_ENHANCED_CALENDAR_UI_ENHANCED_CALENDAR_VIEW_CONTROLLER_H_ + +#import <UIKit/UIKit.h> + +#import "ios/chrome/browser/shared/ui/bottom_sheet/bottom_sheet_view_controller.h" +#import "ios/chrome/common/ui/confirmation_alert/confirmation_alert_action_handler.h" + +@protocol EnhancedCalendarMutator; + +// The view controller for the Enhanced Calendar feature's UI, presented as a +// bottom sheet. +@interface EnhancedCalendarViewController + : BottomSheetViewController <ConfirmationAlertActionHandler> + +// The mutator for this view controller to communicate to the mediator. +@property(nonatomic, weak) id<EnhancedCalendarMutator> mutator; + +@end + +#endif // IOS_CHROME_BROWSER_INTELLIGENCE_ENHANCED_CALENDAR_UI_ENHANCED_CALENDAR_VIEW_CONTROLLER_H_
diff --git a/ios/chrome/browser/intelligence/enhanced_calendar/ui/enhanced_calendar_view_controller.mm b/ios/chrome/browser/intelligence/enhanced_calendar/ui/enhanced_calendar_view_controller.mm new file mode 100644 index 0000000..e85102b --- /dev/null +++ b/ios/chrome/browser/intelligence/enhanced_calendar/ui/enhanced_calendar_view_controller.mm
@@ -0,0 +1,64 @@ +// Copyright 2025 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#import "ios/chrome/browser/intelligence/enhanced_calendar/ui/enhanced_calendar_view_controller.h" + +#import "ios/chrome/browser/intelligence/enhanced_calendar/ui/enhanced_calendar_mutator.h" +#import "ios/chrome/browser/shared/ui/symbols/symbols.h" +#import "ios/chrome/grit/ios_strings.h" +#import "ui/base/l10n/l10n_util_mac.h" + +namespace { + +// The header image SF Symbol name. +constexpr NSString* kHeaderImageName = @"wand.and.sparkles"; + +// The header image size. +const CGFloat kHeaderImageSize = 80.0; + +} // namespace + +@implementation EnhancedCalendarViewController + +- (void)viewDidLoad { + self.actionHandler = self; + + // Configuration. + self.scrollEnabled = NO; + self.showDismissBarButton = NO; + + // Titles. + self.titleString = + l10n_util::GetNSString(IDS_IOS_ENHANCED_CALENDAR_BOTTOM_SHEET_TITLE); + self.subtitleString = + l10n_util::GetNSString(IDS_IOS_ENHANCED_CALENDAR_BOTTOM_SHEET_SUBTITLE); + self.primaryActionString = l10n_util::GetNSString( + IDS_IOS_ENHANCED_CALENDAR_BOTTOM_SHEET_CANCEL_BUTTON); + + // Header image. + self.imageHasFixedSize = YES; + self.image = DefaultSymbolWithPointSize(kHeaderImageName, kHeaderImageSize); + + // Loading throbber. + UIActivityIndicatorView* loadingThrobber = [[UIActivityIndicatorView alloc] + initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleLarge]; + loadingThrobber.translatesAutoresizingMaskIntoConstraints = NO; + self.underTitleView = loadingThrobber; + [loadingThrobber startAnimating]; + + [super viewDidLoad]; +} + +- (void)viewDidDisappear:(BOOL)animated { + [self.mutator cancelEnhancedCalendarRequestAndDismissBottomSheet]; + [super viewDidDisappear:animated]; +} + +#pragma mark - ConfirmationAlertActionHandler + +- (void)confirmationAlertPrimaryAction { + [self.mutator cancelEnhancedCalendarRequestAndDismissBottomSheet]; +} + +@end
diff --git a/ios/chrome/browser/intelligence/proto_wrappers/page_context_wrapper.mm b/ios/chrome/browser/intelligence/proto_wrappers/page_context_wrapper.mm index 3935012..3d565deb 100644 --- a/ios/chrome/browser/intelligence/proto_wrappers/page_context_wrapper.mm +++ b/ios/chrome/browser/intelligence/proto_wrappers/page_context_wrapper.mm
@@ -159,7 +159,6 @@ // Retrieve WebState snapshot. The barrier's callback will be executed for all // codepaths in this method. - - (void)processSnapshotWithBarrier:(base::RepeatingClosure)barrier { __weak PageContextWrapper* weakSelf = self; auto callback = ^(UIImage* image) {
diff --git a/ios/chrome/browser/lens_overlay/coordinator/lens_overlay_coordinator.mm b/ios/chrome/browser/lens_overlay/coordinator/lens_overlay_coordinator.mm index 4136a11..1c6df444 100644 --- a/ios/chrome/browser/lens_overlay/coordinator/lens_overlay_coordinator.mm +++ b/ios/chrome/browser/lens_overlay/coordinator/lens_overlay_coordinator.mm
@@ -416,13 +416,13 @@ [_selectionViewController start]; if (self.isResultsBottomSheetCreated) { - // Only show the bottom sheet when in selection. For translate, build the - // necessary infrastructure but don't show it, effectively starting it - // hidden. [self buildResultsBottomSheetPresentation]; - if (!_selectionViewController.translateFilterActive) { - [self showResultsBottomSheet]; - } + [self showResultsBottomSheet]; + } else if (_selectionViewController.translateFilterActive) { + [self startResultPage]; + [_resultsPagePresenter + showInfoMessage:LensOverlayBottomSheetInfoMessageType:: + kImageTranslatedIndication]; } else { [self scheduleTooltipHintDisplayIfNecessary]; } @@ -736,6 +736,12 @@ } } +- (void)lensOverlayMediatorDidFailDetectingTranslatableText { + [self startResultPage]; + [_resultsPagePresenter showInfoMessage:LensOverlayBottomSheetInfoMessageType:: + kNoTranslatableTextWarning]; +} + - (void)prepareLensUIForBackgroundTabChange { if (!_associatedTabHelper || !self.isUICreated) { return; @@ -761,11 +767,7 @@ [_metricsRecorder recordResultLoadedWithTextSelection:_mediator.currentLensResult .isTextSelection]; - - if (!_resultMediator) { - [self startResultPage]; - } - + [self startResultPage]; [_resultMediator loadResultsURL:url]; } @@ -782,9 +784,7 @@ } - (void)handleSlowRequestHasStarted { - if (!_resultMediator) { - [self startResultPage]; - } + [self startResultPage]; [_resultMediator handleSlowRequestHasStarted]; } @@ -1029,6 +1029,10 @@ // Creates and displays the results bottom sheet. - (void)startResultPage { + if (_resultMediator) { + return; + } + Browser* browser = self.browser; ProfileIOS* profile = browser->GetProfile();
diff --git a/ios/chrome/browser/lens_overlay/coordinator/lens_overlay_mediator.mm b/ios/chrome/browser/lens_overlay/coordinator/lens_overlay_mediator.mm index f1ca8b6d..79d82d0 100644 --- a/ios/chrome/browser/lens_overlay/coordinator/lens_overlay_mediator.mm +++ b/ios/chrome/browser/lens_overlay/coordinator/lens_overlay_mediator.mm
@@ -239,15 +239,15 @@ // bottom sheet hidden, as no auto selection happens at this stage. [self.lensHandler resetSelectionAreaToInitialPosition:^{ }]; - [self.presentationDelegate hideBottomSheet]; + [self.presentationDelegate + showInfoMessage:LensOverlayBottomSheetInfoMessageType:: + kImageTranslatedIndication]; } else if (noSelectionInTranslate) { // A missing selection without a switch in modes indicates the user // intended to dismiss the current selection. - [self.presentationDelegate hideBottomSheet]; - } else if (switchToSelection || willUseTranslate) { - // When transitioning to selection the bottom sheet might be hidden. As - // auto selection might be on we need to restore it if hidden. - [self.presentationDelegate revealBottomSheetIfHidden]; + [self.presentationDelegate + showInfoMessage:LensOverlayBottomSheetInfoMessageType:: + kImageTranslatedIndication]; } } @@ -258,8 +258,22 @@ // The lens overlay search request produced an error. - (void)lensOverlayDidReceiveError:(id<ChromeLensOverlay>)lensOverlay { - [self.resultConsumer handleSearchRequestErrored]; + __weak id<LensOverlayResultConsumer> weakResultConsumer = self.resultConsumer; + auto completion = ^{ + [weakResultConsumer handleSearchRequestErrored]; + }; [self.toolbarConsumer setOmniboxEnabled:YES]; + // Make sure the bottom sheet is dismissed before triggering any alert. + if (self.presentationDelegate) { + [self.presentationDelegate hideBottomSheetWithCompletion:completion]; + } else { + completion(); + } +} + +- (void)lensOverlayDidFailDetectingTranslatableText: + (id<ChromeLensOverlay>)lensOverlay { + [_delegate lensOverlayMediatorDidFailDetectingTranslatableText]; } // The lens overlay search request produced a valid result.
diff --git a/ios/chrome/browser/lens_overlay/coordinator/lens_overlay_mediator_delegate.h b/ios/chrome/browser/lens_overlay/coordinator/lens_overlay_mediator_delegate.h index eb5a07b..84c47be 100644 --- a/ios/chrome/browser/lens_overlay/coordinator/lens_overlay_mediator_delegate.h +++ b/ios/chrome/browser/lens_overlay/coordinator/lens_overlay_mediator_delegate.h
@@ -19,6 +19,10 @@ /// Called when an URL needs to be opened in a new tab. - (void)lensOverlayMediatorOpenURLInNewTabRequsted:(GURL)url; +/// Called when a translation failed due to no or unprocessable text in the +/// given image. +- (void)lensOverlayMediatorDidFailDetectingTranslatableText; + @end #endif // IOS_CHROME_BROWSER_LENS_OVERLAY_COORDINATOR_LENS_OVERLAY_MEDIATOR_DELEGATE_H_
diff --git a/ios/chrome/browser/lens_overlay/model/lens_overlay_detents_manager.mm b/ios/chrome/browser/lens_overlay/model/lens_overlay_detents_manager.mm index 9968a38..e2886f9 100644 --- a/ios/chrome/browser/lens_overlay/model/lens_overlay_detents_manager.mm +++ b/ios/chrome/browser/lens_overlay/model/lens_overlay_detents_manager.mm
@@ -19,9 +19,16 @@ // The identifier for the peak detent. NSString* const kPeakSheetDetentIdentifier = @"kPeakSheetDetentIdentifier"; +// The identifier for the info message state. +NSString* const kInfoMessageSheetDetentIdentifier = + @"kInfoMessageSheetDetentIdentifier"; + // The detent height in points for the 'peak' state of the bottom sheet. const CGFloat kPeakDetentHeight = 100.0; +// The detent height in points for the error message state of the bottom sheet. +const CGFloat kinfoMessageDetentHeight = 160.0; + // The percentage of the screen that will be covered by the bottom sheet in // translate mode. const CGFloat kTranslateSheetHeightRatio = 0.33; @@ -130,16 +137,17 @@ return SheetDimensionState::kConsent; } + if ([identifier isEqualToString:kInfoMessageSheetDetentIdentifier]) { + return SheetDimensionState::kInfoMessage; + } + return SheetDimensionState::kHidden; } - (void)setPresentationStrategy: (SheetDetentPresentationStategy)presentationStrategy { _presentationStrategy = presentationStrategy; - if ([self isInMediumDetent] || [self isInLargeDetent]) { - // Refresh the detents presentation for the unrestricted state. - [self adjustDetentsForState:SheetDetentStateUnrestrictedMovement]; - } + [self adjustDetentsForState:SheetDetentStateUnrestrictedMovement]; } #pragma mark - Public methods @@ -226,6 +234,11 @@ _sheet.largestUndimmedDetentIdentifier = kConsentSheetDetentIdentifier; _sheet.selectedDetentIdentifier = kConsentSheetDetentIdentifier; break; + case SheetDetentStateInfoMessage: + _sheet.detents = @[ [self infoMessageDetent] ]; + _sheet.largestUndimmedDetentIdentifier = + kInfoMessageSheetDetentIdentifier; + _sheet.selectedDetentIdentifier = kInfoMessageSheetDetentIdentifier; } [self reportDimensionChangeIfNeeded:NO]; @@ -291,4 +304,14 @@ resolver:peakHeightResolver]; } +- (UISheetPresentationControllerDetent*)infoMessageDetent { + auto infoMessageHeightResolver = ^CGFloat( + id<UISheetPresentationControllerDetentResolutionContext> context) { + return kinfoMessageDetentHeight; + }; + return [UISheetPresentationControllerDetent + customDetentWithIdentifier:kInfoMessageSheetDetentIdentifier + resolver:infoMessageHeightResolver]; +} + @end
diff --git a/ios/chrome/browser/lens_overlay/model/lens_overlay_sheet_detent_state.h b/ios/chrome/browser/lens_overlay/model/lens_overlay_sheet_detent_state.h index ffc7175..1f58469 100644 --- a/ios/chrome/browser/lens_overlay/model/lens_overlay_sheet_detent_state.h +++ b/ios/chrome/browser/lens_overlay/model/lens_overlay_sheet_detent_state.h
@@ -13,6 +13,8 @@ SheetDetentStateConsentDialog, // The bottom sheet is in the peak state. SheetDetentStatePeakEnabled, + // The bottom sheet is showing an info message. + SheetDetentStateInfoMessage, }; // Indicates the state of the bottom sheet dimension. @@ -31,7 +33,9 @@ kPeaking = 3, // The bottom sheet is displayed with the fixed custom dimensions for consent. kConsent = 4, - kMaxValue = kConsent, + // The bottom sheet is showing an info message. + kInfoMessage = 5, + kMaxValue = kInfoMessage, }; // LINT.ThenChange(//tools/metrics/histograms/metadata/lens/enums.xml:SheetDimensionState)
diff --git a/ios/chrome/browser/lens_overlay/ui/BUILD.gn b/ios/chrome/browser/lens_overlay/ui/BUILD.gn index dc4b299e..f9f20a9a 100644 --- a/ios/chrome/browser/lens_overlay/ui/BUILD.gn +++ b/ios/chrome/browser/lens_overlay/ui/BUILD.gn
@@ -156,6 +156,7 @@ ":protocols", "//base", "//ios/chrome/app/strings:ios_strings_grit", + "//ios/chrome/browser/lens_overlay/model", "//ui/base", "//url", ]
diff --git a/ios/chrome/browser/lens_overlay/ui/lens_overlay_bottom_sheet_presentation_delegate.h b/ios/chrome/browser/lens_overlay/ui/lens_overlay_bottom_sheet_presentation_delegate.h index b70920e..a0fe1a8 100644 --- a/ios/chrome/browser/lens_overlay/ui/lens_overlay_bottom_sheet_presentation_delegate.h +++ b/ios/chrome/browser/lens_overlay/ui/lens_overlay_bottom_sheet_presentation_delegate.h
@@ -7,6 +7,14 @@ #import <Foundation/Foundation.h> +// Possible info messages to be shown in the bottom sheets. +enum class LensOverlayBottomSheetInfoMessageType { + // Informs the user that there was an error detecting the text in the image + kNoTranslatableTextWarning, + // Informs the user that a translation was executed on the given image. + kImageTranslatedIndication, +}; + // Presentation delegate for the bottom sheet. // Bottom sheet content may request the container to be maximized or minimized, // e.g. when the user selects a result that opens an image viewer. @@ -25,11 +33,14 @@ - (void)didLoadTranslateResult; // Hides the bottom sheet without destroying the presentation. -- (void)hideBottomSheet; +- (void)hideBottomSheetWithCompletion:(void (^)(void))completion; // Reveals the hidden bottom sheet. - (void)revealBottomSheetIfHidden; +// Hides the results UI and shows the given informational message. +- (void)showInfoMessage:(LensOverlayBottomSheetInfoMessageType)infoMessageType; + @end #endif // IOS_CHROME_BROWSER_LENS_OVERLAY_UI_LENS_OVERLAY_BOTTOM_SHEET_PRESENTATION_DELEGATE_H_
diff --git a/ios/chrome/browser/lens_overlay/ui/lens_overlay_results_page_presenter.mm b/ios/chrome/browser/lens_overlay/ui/lens_overlay_results_page_presenter.mm index a85b571c..0dac9a4d 100644 --- a/ios/chrome/browser/lens_overlay/ui/lens_overlay_results_page_presenter.mm +++ b/ios/chrome/browser/lens_overlay/ui/lens_overlay_results_page_presenter.mm
@@ -29,11 +29,24 @@ // by the bottom sheet presentation. const CGFloat kVisibleAreaMediumDetentThreshold = 100.0f; +// The duration of the opacity transition in the results page. +const CGFloat kOpacityAnimationDuration = 0.4; + } // namespace +// Animator responsible for handling the hiding and showing fading transition of +// the informational message views. +@interface InfoMessageAnimator + : NSObject <UIViewControllerAnimatedTransitioning> + +- (instancetype)initWithOperation:(UINavigationControllerOperation)operation; + +@end + @interface LensOverlayResultsPagePresenter () < LensOverlayPanTrackerDelegate, - LensOverlayDetentsManagerDelegate> + LensOverlayDetentsManagerDelegate, + UINavigationControllerDelegate> @end @implementation LensOverlayResultsPagePresenter { @@ -41,7 +54,7 @@ __weak UIViewController* _baseViewController; /// The results view controller to present. - __weak UIViewController* _resultViewController; + __weak LensResultPageViewController* _resultViewController; /// Orchestrates the change in detents of the associated bottom sheet. LensOverlayDetentsManager* _detentsManager; @@ -63,6 +76,13 @@ // The layout guide that defines the unobstructed area by the presentation. UILayoutGuide* _visibleAreaLayoutGuide; + + // TODO(crbug.com/405199044): Consider using a custom container for the UI + // orchestration. + // + // The navigation controller orchestrating the change between the results page + // and the informational messages. + UINavigationController* _presentationNavigationController; } - (instancetype)initWithBaseViewController:(UIViewController*)baseViewController @@ -72,6 +92,12 @@ if (self) { _baseViewController = baseViewController; _resultViewController = resultViewController; + _presentationNavigationController = [[UINavigationController alloc] + initWithRootViewController:resultViewController]; + _presentationNavigationController.toolbarHidden = YES; + _presentationNavigationController.navigationBarHidden = YES; + _presentationNavigationController.delegate = self; + _visibleAreaLayoutGuide = [[UILayoutGuide alloc] init]; } @@ -80,7 +106,8 @@ - (BOOL)isResultPageVisible { return _baseViewController.presentedViewController != nil && - _baseViewController.presentedViewController == _resultViewController; + _baseViewController.presentedViewController == + _presentationNavigationController; } - (SheetDimensionState)sheetDimension { @@ -140,7 +167,7 @@ } UISheetPresentationController* sheet = - _resultViewController.sheetPresentationController; + _presentationNavigationController.sheetPresentationController; sheet.prefersEdgeAttachedInCompactHeight = YES; sheet.prefersGrabberVisible = YES; sheet.preferredCornerRadius = kPreferredCornerRadius; @@ -191,7 +218,7 @@ __weak __typeof(self) weakSelf = self; [_baseViewController - presentViewController:_resultViewController + presentViewController:_presentationNavigationController animated:animated completion:^{ [weakSelf didFinishPresentingResultsPage]; @@ -222,7 +249,18 @@ } } -- (void)hideBottomSheet { +- (void)showInfoMessage:(LensOverlayBottomSheetInfoMessageType)infoMessageType { + [_detentsManager adjustDetentsForState:SheetDetentStateInfoMessage]; + + // TODO(crbug.com/405199044): Replace with the real UI once built. + UIViewController* infoMessageViewController = [[UIViewController alloc] init]; + infoMessageViewController.view.backgroundColor = [UIColor whiteColor]; + [_presentationNavigationController + pushViewController:infoMessageViewController + animated:YES]; +} + +- (void)hideBottomSheetWithCompletion:(void (^)(void))completion { [_displayLink invalidate]; [self sheetPresentationHeightChanged:0]; [_windowPanTracker stopTracking]; @@ -230,7 +268,7 @@ _detentsManager = nil; UIViewController* presentedVC = _baseViewController.presentedViewController; - [presentedVC dismissViewControllerAnimated:YES completion:nil]; + [presentedVC dismissViewControllerAnimated:YES completion:completion]; } - (void)dismissResultsPageAnimated:(BOOL)animated @@ -273,17 +311,17 @@ return; } + UIView* presentedView = _baseViewController.presentedViewController.view; + // Early return if the bottom sheet is not displayed yet. - CALayer* presentationLayer = - _resultViewController.view.layer.presentationLayer; + CALayer* presentationLayer = presentedView.layer.presentationLayer; if (!presentationLayer) { return; } - CGRect presentedFrame = _resultViewController.view.frame; - CGRect newFrame = - [_resultViewController.view convertRect:presentedFrame - toView:_baseViewController.view]; + CGRect presentedFrame = presentedView.frame; + CGRect newFrame = [presentedView convertRect:presentedFrame + toView:_baseViewController.view]; CGFloat containerHeight = _baseViewController.view.frame.size.height; CGFloat currentSheetHeight = containerHeight - newFrame.origin.y; @@ -306,9 +344,11 @@ currentSheetHeight <= kThresholdHeightForClosingSheet; BOOL userTouchesTheScreen = _windowPanTracker.isPanning; - BOOL shouldDestroyLensUI = sheetClosedThresholdReached && - !userTouchesTheScreen && - !_presentingAnimationInProgress; + BOOL infoMessageShown = + _detentsManager.sheetDimension == SheetDimensionState::kInfoMessage; + BOOL shouldDestroyLensUI = + sheetClosedThresholdReached && !userTouchesTheScreen && + !_presentingAnimationInProgress && !infoMessageShown; if (shouldDestroyLensUI) { [_displayLink invalidate]; [self sheetPresentationHeightChanged:0]; @@ -408,6 +448,7 @@ case SheetDimensionState::kHidden: return YES; case SheetDimensionState::kPeaking: + case SheetDimensionState::kInfoMessage: case SheetDimensionState::kLarge: return NO; case SheetDimensionState::kMedium: @@ -439,13 +480,86 @@ - (void)didLoadSelectionResult { _detentsManager.presentationStrategy = SheetDetentPresentationStategySelection; + [_presentationNavigationController popToRootViewControllerAnimated:YES]; [self adjustSelectionOcclusionInsets]; } - (void)didLoadTranslateResult { _detentsManager.presentationStrategy = SheetDetentPresentationStategyTranslate; + [_presentationNavigationController popToRootViewControllerAnimated:YES]; [self adjustSelectionOcclusionInsets]; } +#pragma mark - UINavigationControllerDelegate + +- (id<UIViewControllerAnimatedTransitioning>) + navigationController: + (UINavigationController*)navigationController + animationControllerForOperation:(UINavigationControllerOperation)operation + fromViewController:(UIViewController*)fromVC + toViewController:(UIViewController*)toVC { + return [[InfoMessageAnimator alloc] initWithOperation:operation]; +} + +@end + +@implementation InfoMessageAnimator { + // The navigation operation for the animator. + UINavigationControllerOperation _operation; +} + +- (instancetype)initWithOperation:(UINavigationControllerOperation)operation { + self = [super init]; + if (self) { + _operation = operation; + } + + return self; +} + +- (NSTimeInterval)transitionDuration: + (id<UIViewControllerContextTransitioning>)transitionContext { + return kOpacityAnimationDuration; +} + +- (void)animateTransition: + (id<UIViewControllerContextTransitioning>)transitionContext { + UIViewController* toViewController = [transitionContext + viewControllerForKey:UITransitionContextToViewControllerKey]; + UIViewController* fromViewController = [transitionContext + viewControllerForKey:UITransitionContextFromViewControllerKey]; + NSTimeInterval duration = [self transitionDuration:transitionContext]; + auto animationFinished = ^(BOOL finished) { + [transitionContext + completeTransition:![transitionContext transitionWasCancelled]]; + }; + + if (_operation == UINavigationControllerOperationPush) { + [[transitionContext containerView] addSubview:toViewController.view]; + toViewController.view.alpha = 0.0; + [UIView animateWithDuration:duration + animations:^{ + toViewController.view.alpha = 1.0; + } + completion:animationFinished]; + + return; + } + + if (_operation == UINavigationControllerOperationPop) { + [[transitionContext containerView] insertSubview:toViewController.view + belowSubview:fromViewController.view]; + + [UIView animateWithDuration:duration + animations:^{ + fromViewController.view.alpha = 0.0; + } + completion:animationFinished]; + return; + } + + animationFinished(YES); +} + @end
diff --git a/ios/chrome/browser/settings/ui_bundled/autofill/BUILD.gn b/ios/chrome/browser/settings/ui_bundled/autofill/BUILD.gn index b703b91..9c0f95d3 100644 --- a/ios/chrome/browser/settings/ui_bundled/autofill/BUILD.gn +++ b/ios/chrome/browser/settings/ui_bundled/autofill/BUILD.gn
@@ -161,6 +161,7 @@ "//ios/chrome/browser/authentication/ui_bundled:eg_test_support+eg2", "//ios/chrome/browser/autofill/ui_bundled:eg_test_support+eg2", "//ios/chrome/browser/autofill/ui_bundled/address_editor:constants", + "//ios/chrome/browser/autofill/ui_bundled/bottom_sheet:constants", "//ios/chrome/browser/metrics/model:eg_test_support+eg2", "//ios/chrome/browser/policy/model:eg_test_support+eg2", "//ios/chrome/browser/settings/ui_bundled:settings_root_constants",
diff --git a/ios/chrome/browser/settings/ui_bundled/autofill/autofill_add_address_manually_egtest.mm b/ios/chrome/browser/settings/ui_bundled/autofill/autofill_add_address_manually_egtest.mm index 044a310..2cc694a 100644 --- a/ios/chrome/browser/settings/ui_bundled/autofill/autofill_add_address_manually_egtest.mm +++ b/ios/chrome/browser/settings/ui_bundled/autofill/autofill_add_address_manually_egtest.mm
@@ -9,6 +9,7 @@ #import "ios/chrome/browser/authentication/ui_bundled/signin_earl_grey.h" #import "ios/chrome/browser/authentication/ui_bundled/signin_earl_grey_ui_test_util.h" #import "ios/chrome/browser/autofill/ui_bundled/autofill_app_interface.h" +#import "ios/chrome/browser/autofill/ui_bundled/bottom_sheet/bottom_sheet_constants.h" #import "ios/chrome/browser/signin/model/fake_system_identity.h" #import "ios/chrome/grit/ios_strings.h" #import "ios/chrome/test/earl_grey/chrome_earl_grey.h" @@ -22,6 +23,7 @@ using chrome_test_util::ButtonWithAccessibilityLabelId; using chrome_test_util::SettingsDoneButton; using chrome_test_util::SettingsMenuBackButton; +using chrome_test_util::SettingsProfileMatcher; using chrome_test_util::SettingsToolbarAddButton; namespace { @@ -56,6 +58,11 @@ grey_kindOfClass([UITextField class]), nil); } +// Matcher for the "add address" bottom sheet. +id<GREYMatcher> EditProfileBottomSheet() { + return grey_accessibilityID(kEditProfileBottomSheetViewIdentfier); +} + // Helper to open the address settings page. void OpenAddressSettings() { [ChromeEarlGreyUI openSettingsMenu]; @@ -117,7 +124,7 @@ performAction:grey_replaceText(@"H3H 1H1")]; } -// Helper to open the add address view. +// Helper to open the "add address" bottom sheet. - (void)openAddAddressView:(BOOL)signIn { if (signIn) { [SigninEarlGreyUI @@ -131,6 +138,10 @@ // Tap the "Add" button. [[EarlGrey selectElementWithMatcher:SettingsToolbarAddButton()] performAction:grey_tap()]; + + // Verify the "add address" bottom sheet is visible. + [[EarlGrey selectElementWithMatcher:EditProfileBottomSheet()] + assertWithMatcher:grey_sufficientlyVisible()]; } #pragma mark - Tests @@ -182,11 +193,23 @@ [SigninEarlGreyUI signOut]; } -// Tests that tapping the `Cancel` button triggers the dismissal of the add -// address bottom sheet. +// Tests that tapping the `Cancel` button triggers the dismissal of the "add +// address" bottom sheet. - (void)testCancelButton { - // TODO(crbug.com/406799169): EGTest for checking if the `Cancel` button works - // as expected. + [self openAddAddressView:NO]; + + // Tap "Cancel". + [[EarlGrey + selectElementWithMatcher:grey_allOf( + grey_accessibilityID( + kEditProfileBottomSheetCancelButton), + grey_accessibilityTrait( + UIAccessibilityTraitButton), + nil)] performAction:grey_tap()]; + + // Verify the address settings opened. + [[EarlGrey selectElementWithMatcher:SettingsProfileMatcher()] + assertWithMatcher:grey_sufficientlyVisible()]; } // Tests the 'Save' button enabled state when manually adding an address to the
diff --git a/ios/chrome/browser/shared/model/prefs/BUILD.gn b/ios/chrome/browser/shared/model/prefs/BUILD.gn index 85837ed7..70a59c0 100644 --- a/ios/chrome/browser/shared/model/prefs/BUILD.gn +++ b/ios/chrome/browser/shared/model/prefs/BUILD.gn
@@ -140,6 +140,10 @@ "//ios/web/common:features", "//ui/base", ] + + if (target_environment != "catalyst") { + deps += [ "//ios/chrome/browser/default_browser/model/default_status" ] + } } source_set("unit_tests") {
diff --git a/ios/chrome/browser/shared/model/prefs/browser_prefs.mm b/ios/chrome/browser/shared/model/prefs/browser_prefs.mm index ab5d0cf6e..1055b7f 100644 --- a/ios/chrome/browser/shared/model/prefs/browser_prefs.mm +++ b/ios/chrome/browser/shared/model/prefs/browser_prefs.mm
@@ -126,6 +126,10 @@ #import "ios/web/common/features.h" #import "ui/base/l10n/l10n_util.h" +#if !BUILDFLAG(IS_IOS_MACCATALYST) +#import "ios/chrome/browser/default_browser/model/default_status/default_status_helper_prefs.h" +#endif // !BUILDFLAG(IS_IOS_MACCATALYST) + namespace { // Deprecated 05/2024. @@ -404,6 +408,10 @@ auto_deletion::AutoDeletionService::RegisterLocalStatePrefs(registry); push_notification_prefs::RegisterLocalStatePrefs(registry); +#if !BUILDFLAG(IS_IOS_MACCATALYST) + default_status::RegisterDefaultStatusPrefs(registry); +#endif // !BUILDFLAG(IS_IOS_MACCATALYST) + // Preferences related to the profile manager. registry->RegisterStringPref(prefs::kLastUsedProfile, std::string()); registry->RegisterBooleanPref(prefs::kLegacyProfileHidden, false);
diff --git a/ios/chrome/browser/shared/public/commands/BUILD.gn b/ios/chrome/browser/shared/public/commands/BUILD.gn index 5f2806d..a4956b4 100644 --- a/ios/chrome/browser/shared/public/commands/BUILD.gn +++ b/ios/chrome/browser/shared/public/commands/BUILD.gn
@@ -25,6 +25,7 @@ "credential_provider_promo_commands.h", "docking_promo_commands.h", "drive_file_picker_commands.h", + "enhanced_calendar_commands.h", "feed_commands.h", "find_in_page_commands.h", "generate_qr_code_command.h",
diff --git a/ios/chrome/browser/shared/public/commands/enhanced_calendar_commands.h b/ios/chrome/browser/shared/public/commands/enhanced_calendar_commands.h new file mode 100644 index 0000000..2cc7c50 --- /dev/null +++ b/ios/chrome/browser/shared/public/commands/enhanced_calendar_commands.h
@@ -0,0 +1,25 @@ +// 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 IOS_CHROME_BROWSER_SHARED_PUBLIC_COMMANDS_ENHANCED_CALENDAR_COMMANDS_H_ +#define IOS_CHROME_BROWSER_SHARED_PUBLIC_COMMANDS_ENHANCED_CALENDAR_COMMANDS_H_ + +namespace ios::provider { +enum class AddToCalendarIntegrationProvider; +} // namespace ios::provider + +// Commands to show/hide the Enhanced Calendar bottom sheet. +@protocol EnhancedCalendarCommands <NSObject> + +// Shows the Enhanced Calendar bottom sheet for the current WebState. +- (void)showEnhancedCalendarBottomSheetWithIntegrationProvider: + (ios::provider::AddToCalendarIntegrationProvider)integrationProvider; + +// Hides the Enhanced Calendar bottom sheet. Will also cancel any in-flight +// Enhanced Calendar model requests +- (void)hideEnhancedCalendarBottomSheet; + +@end + +#endif // IOS_CHROME_BROWSER_SHARED_PUBLIC_COMMANDS_ENHANCED_CALENDAR_COMMANDS_H_
diff --git a/ios/chrome/browser/shared/public/features/features.h b/ios/chrome/browser/shared/public/features/features.h index e2560d7..9a33ad0 100644 --- a/ios/chrome/browser/shared/public/features/features.h +++ b/ios/chrome/browser/shared/public/features/features.h
@@ -966,7 +966,7 @@ extern const std::string_view kFRESignInSecondaryActionLabelUpdateParamStaySignedOut; -// Returns whether 'kFRESignInSecondaryActionLabelUpdate' is enabled +// Returns whether 'kFRESignInSecondaryActionLabelUpdate' is enabled. bool FRESignInSecondaryActionLabelUpdate(); // Enables passkey syncing follow-up features. @@ -1094,4 +1094,11 @@ // Checks if background customization is enabled on the NTP. bool IsNTPBackgroundCustomizationEnabled(); +// Feature flag to control whether default status API check and reporting are +// enabled. +BASE_DECLARE_FEATURE(kRunDefaultStatusCheck); + +// Returns whether `kRunDefaultStatusCheck` is enabled. +bool IsRunDefaultStatusCheckEnabled(); + #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 90810282..9f114bd 100644 --- a/ios/chrome/browser/shared/public/features/features.mm +++ b/ios/chrome/browser/shared/public/features/features.mm
@@ -1345,3 +1345,11 @@ bool IsNTPBackgroundCustomizationEnabled() { return base::FeatureList::IsEnabled(kNTPBackgroundCustomization); } + +BASE_FEATURE(kRunDefaultStatusCheck, + "RunDefaultStatusCheck", + base::FEATURE_DISABLED_BY_DEFAULT); + +bool IsRunDefaultStatusCheckEnabled() { + return base::FeatureList::IsEnabled(kRunDefaultStatusCheck); +}
diff --git a/ios/chrome/credential_provider_extension/BUILD.gn b/ios/chrome/credential_provider_extension/BUILD.gn index a655b34..bb276da 100644 --- a/ios/chrome/credential_provider_extension/BUILD.gn +++ b/ios/chrome/credential_provider_extension/BUILD.gn
@@ -168,6 +168,18 @@ frameworks = [ "Foundation.framework" ] } +source_set("favicon_util") { + sources = [ + "favicon_util.h", + "favicon_util.mm", + ] + deps = [ + "//ios/chrome/common/app_group", + "//ios/chrome/common/ui/favicon", + ] + frameworks = [ "Foundation.framework" ] +} + source_set("passkey_request_details") { sources = [ "passkey_request_details.h",
diff --git a/ios/chrome/credential_provider_extension/credential_provider_view_controller.mm b/ios/chrome/credential_provider_extension/credential_provider_view_controller.mm index e9304c6..82bbb5c 100644 --- a/ios/chrome/credential_provider_extension/credential_provider_view_controller.mm +++ b/ios/chrome/credential_provider_extension/credential_provider_view_controller.mm
@@ -1017,16 +1017,34 @@ completion:nil]; } +// Returns the favicon associated with the rpId if it exists. +// Returns nil otherwise. +- (NSString*)faviconForRpId:(NSString*)rpId { + // Verify if a favicon already exists for the provided rpId. + NSArray<id<Credential>>* credentials = self.credentialStore.credentials; + NSUInteger credentialIndex = + [credentials indexOfObjectPassingTest:^BOOL(id<Credential> credential, + NSUInteger idx, BOOL* stop) { + return [credential.rpId isEqualToString:rpId] && + credential.favicon.length > 0; + }]; + return credentialIndex != NSNotFound ? credentials[credentialIndex].favicon + : nil; +} + // Shows a confirmation dialog to the user before performing passkey creation. - (void)showMultiProfilePasskeyCreationDialogWithDetails: (PasskeyRequestDetails*)passkeyRequestDetails gaia:(NSString*)gaia { + NSString* favicon = + [self faviconForRpId:passkeyRequestDetails.relyingPartyIdentifier]; MultiProfilePasskeyCreationViewController* multiProfilePasskeyCreationViewController = [[MultiProfilePasskeyCreationViewController alloc] initWithDetails:passkeyRequestDetails gaia:gaia userEmail:[self userEmail] + favicon:favicon navigationItemTitleView:self.passkeyNavigationItemTitleView delegate:self];
diff --git a/ios/chrome/credential_provider_extension/favicon_util.h b/ios/chrome/credential_provider_extension/favicon_util.h new file mode 100644 index 0000000..1283f6ed --- /dev/null +++ b/ios/chrome/credential_provider_extension/favicon_util.h
@@ -0,0 +1,19 @@ +// 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 IOS_CHROME_CREDENTIAL_PROVIDER_EXTENSION_FAVICON_UTIL_H_ +#define IOS_CHROME_CREDENTIAL_PROVIDER_EXTENSION_FAVICON_UTIL_H_ + +#import <Foundation/Foundation.h> + +@class FaviconAttributes; + +typedef void (^BlockWithFaviconAttributes)(FaviconAttributes*); + +// Fetches the favicon attributes of the provided favicon and calls the +// completion block with the favicon attributes. +void FetchFaviconAsync(NSString* favicon, + BlockWithFaviconAttributes completion); + +#endif // IOS_CHROME_CREDENTIAL_PROVIDER_EXTENSION_FAVICON_UTIL_H_
diff --git a/ios/chrome/credential_provider_extension/favicon_util.mm b/ios/chrome/credential_provider_extension/favicon_util.mm new file mode 100644 index 0000000..4983140b --- /dev/null +++ b/ios/chrome/credential_provider_extension/favicon_util.mm
@@ -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. + +#import "ios/chrome/credential_provider_extension/favicon_util.h" + +#import "ios/chrome/common/app_group/app_group_constants.h" +#import "ios/chrome/common/ui/favicon/favicon_attributes.h" + +void FetchFaviconAsync(NSString* favicon, + BlockWithFaviconAttributes completion) { + dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0), ^{ + NSURL* filePath = [app_group::SharedFaviconAttributesFolder() + URLByAppendingPathComponent:favicon + isDirectory:NO]; + NSError* error = nil; + NSData* data = [NSData dataWithContentsOfURL:filePath + options:0 + error:&error]; + FaviconAttributes* attributes = nil; + if (data && !error) { + NSKeyedUnarchiver* unarchiver = + [[NSKeyedUnarchiver alloc] initForReadingFromData:data error:nil]; + unarchiver.requiresSecureCoding = NO; + attributes = [unarchiver decodeObjectForKey:NSKeyedArchiveRootObjectKey]; + } + completion(attributes); + }); +}
diff --git a/ios/chrome/credential_provider_extension/ui/BUILD.gn b/ios/chrome/credential_provider_extension/ui/BUILD.gn index e8f4f4ac..41c9f74 100644 --- a/ios/chrome/credential_provider_extension/ui/BUILD.gn +++ b/ios/chrome/credential_provider_extension/ui/BUILD.gn
@@ -82,6 +82,7 @@ "//ios/chrome/common/ui/table_view", "//ios/chrome/common/ui/table_view:cells_constants", "//ios/chrome/common/ui/util", + "//ios/chrome/credential_provider_extension:favicon_util", "//ios/chrome/credential_provider_extension:metrics_util", "//ios/chrome/credential_provider_extension:passkey_request_details", "//ios/chrome/credential_provider_extension:passkey_util",
diff --git a/ios/chrome/credential_provider_extension/ui/credential_list_view_controller.mm b/ios/chrome/credential_provider_extension/ui/credential_list_view_controller.mm index 6b5f0e1a..8c2c4c7 100644 --- a/ios/chrome/credential_provider_extension/ui/credential_list_view_controller.mm +++ b/ios/chrome/credential_provider_extension/ui/credential_list_view_controller.mm
@@ -15,6 +15,7 @@ #import "ios/chrome/common/ui/favicon/favicon_view.h" #import "ios/chrome/common/ui/table_view/favicon_table_view_cell.h" #import "ios/chrome/common/ui/util/pointer_interaction_util.h" +#import "ios/chrome/credential_provider_extension/favicon_util.h" #import "ios/chrome/credential_provider_extension/metrics_util.h" #import "ios/chrome/credential_provider_extension/ui/credential_list_global_header_view.h" #import "ios/chrome/credential_provider_extension/ui/credential_list_header_view.h" @@ -237,33 +238,19 @@ id<Credential> credential = [self credentialForIndexPath:indexPath]; DCHECK(credential); DCHECK(cell); - CredentialListCell* credentialCell = + __weak CredentialListCell* credentialCell = base::apple::ObjCCastStrict<CredentialListCell>(cell); NSString* serviceIdentifier = credential.serviceIdentifier; - dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0), ^{ - NSURL* filePath = [app_group::SharedFaviconAttributesFolder() - URLByAppendingPathComponent:credential.favicon - isDirectory:NO]; - NSError* error = nil; - NSData* data = [NSData dataWithContentsOfURL:filePath - options:0 - error:&error]; - if (data && !error) { - NSKeyedUnarchiver* unarchiver = - [[NSKeyedUnarchiver alloc] initForReadingFromData:data error:nil]; - unarchiver.requiresSecureCoding = NO; - FaviconAttributes* attributes = - [unarchiver decodeObjectForKey:NSKeyedArchiveRootObjectKey]; - // Only set favicon if the cell hasn't been reused. - if ([credentialCell.uniqueIdentifier isEqualToString:serviceIdentifier]) { - // Update the UI on the main thread. - dispatch_async(dispatch_get_main_queue(), ^{ - if (attributes) { - [credentialCell.faviconView configureWithAttributes:attributes]; - } - }); - } + FetchFaviconAsync(credential.favicon, ^(FaviconAttributes* attributes) { + // Only set favicon if the cell hasn't been reused. + if ([credentialCell.uniqueIdentifier isEqualToString:serviceIdentifier]) { + // Update the UI on the main thread. + dispatch_async(dispatch_get_main_queue(), ^{ + if (attributes) { + [credentialCell.faviconView configureWithAttributes:attributes]; + } + }); } }); }
diff --git a/ios/chrome/credential_provider_extension/ui/multi_profile_passkey_creation_view_controller.h b/ios/chrome/credential_provider_extension/ui/multi_profile_passkey_creation_view_controller.h index 64ee356..bdcf5ee3 100644 --- a/ios/chrome/credential_provider_extension/ui/multi_profile_passkey_creation_view_controller.h +++ b/ios/chrome/credential_provider_extension/ui/multi_profile_passkey_creation_view_controller.h
@@ -38,6 +38,7 @@ initWithDetails:(PasskeyRequestDetails*)passkeyRequestDetails gaia:(NSString*)gaia userEmail:(NSString*)userEmail + favicon:(NSString*)favicon navigationItemTitleView:(UIView*)navigationItemTitleView delegate: (id<MultiProfilePasskeyCreationViewControllerDelegate>)
diff --git a/ios/chrome/credential_provider_extension/ui/multi_profile_passkey_creation_view_controller.mm b/ios/chrome/credential_provider_extension/ui/multi_profile_passkey_creation_view_controller.mm index d4e814f9..a1a828d 100644 --- a/ios/chrome/credential_provider_extension/ui/multi_profile_passkey_creation_view_controller.mm +++ b/ios/chrome/credential_provider_extension/ui/multi_profile_passkey_creation_view_controller.mm
@@ -8,6 +8,7 @@ #import "ios/chrome/common/ui/favicon/favicon_container_view.h" #import "ios/chrome/common/ui/favicon/favicon_view.h" #import "ios/chrome/common/ui/util/constraints_ui_util.h" +#import "ios/chrome/credential_provider_extension/favicon_util.h" #import "ios/chrome/credential_provider_extension/passkey_request_details.h" namespace { @@ -83,6 +84,12 @@ // Information about a passkey credential request. PasskeyRequestDetails* _passkeyRequestDetails; + // The favicon view shown by this view controller's view. + FaviconView* _faviconView; + + // The favicon attributes, if a favicon is available. Nil otherwise. + FaviconAttributes* _faviconAttributes; + // The gaia ID associated with the current account NSString* _gaia; @@ -95,6 +102,7 @@ initWithDetails:(PasskeyRequestDetails*)passkeyRequestDetails gaia:(NSString*)gaia userEmail:(NSString*)userEmail + favicon:(NSString*)favicon navigationItemTitleView:(UIView*)navigationItemTitleView delegate: (id<MultiProfilePasskeyCreationViewControllerDelegate>) @@ -106,6 +114,14 @@ _gaia = gaia; _multiProfilePasskeyCreationViewControllerDelegate = delegate; _navigationItemTitleView = navigationItemTitleView; + + // Attempt to fetch the favicon. + if (favicon) { + __weak __typeof(self) weakSelf = self; + FetchFaviconAsync(favicon, ^(FaviconAttributes* attributes) { + [weakSelf setFaviconAttributes:attributes]; + }); + } } return self; } @@ -134,6 +150,8 @@ self.view.backgroundColor = GetBackgroundColor(); self.navigationItem.titleView = _navigationItemTitleView; + + [_faviconView configureWithAttributes:_faviconAttributes]; } #pragma mark - PromoStyleViewController @@ -187,6 +205,11 @@ return specificContentView; } +// Sets the favicon attributes. +- (void)setFaviconAttributes:(FaviconAttributes*)attributes { + _faviconAttributes = attributes; +} + // Creates the favicon view for the passkey information view. - (UIView*)iconView { FaviconContainerView* faviconContainerView = @@ -197,10 +220,10 @@ imageWithTintColor:[UIColor colorNamed:kTextQuaternaryColor] renderingMode:UIImageRenderingModeAlwaysOriginal]]; - // TODO(crbug.com/382479915): Verify if the favicon for the current URL is - // available before using the default icon. - [faviconContainerView.faviconView - configureWithAttributes:defaultWorldIconAttributes]; + // Use the default world icon as the default favicon. + _faviconView = faviconContainerView.faviconView; + [_faviconView configureWithAttributes:defaultWorldIconAttributes]; + [faviconContainerView setFaviconBackgroundColor:GetBackgroundColor()]; [faviconContainerView setFaviconBorderColor:[UIColor clearColor]]; return faviconContainerView;
diff --git a/ios/chrome/credential_provider_extension/ui/multi_profile_passkey_creation_view_controller_unittest.mm b/ios/chrome/credential_provider_extension/ui/multi_profile_passkey_creation_view_controller_unittest.mm index 044eb9c..db9674b 100644 --- a/ios/chrome/credential_provider_extension/ui/multi_profile_passkey_creation_view_controller_unittest.mm +++ b/ios/chrome/credential_provider_extension/ui/multi_profile_passkey_creation_view_controller_unittest.mm
@@ -25,6 +25,7 @@ initWithDetails:details gaia:nil userEmail:@"peter.parker@gmail.com" + favicon:nil navigationItemTitleView:navigationView delegate:nil]; }
diff --git a/ios/chrome/test/BUILD.gn b/ios/chrome/test/BUILD.gn index 1eeb5d2..119055d 100644 --- a/ios/chrome/test/BUILD.gn +++ b/ios/chrome/test/BUILD.gn
@@ -537,6 +537,12 @@ "//ios/testing/earl_grey:unit_tests", ] + if (target_environment != "catalyst") { + deps += [ + "//ios/chrome/browser/default_browser/model/default_status:unit_tests", + ] + } + if (ios_enable_widget_kit_extension) { deps += [ "//ios/chrome/browser/widget_kit/model:unit_tests" ] }
diff --git a/ios/google_internal/frameworks/ChromeExtensionKeychainInternal.framework.dSYM.ios.zip.sha1 b/ios/google_internal/frameworks/ChromeExtensionKeychainInternal.framework.dSYM.ios.zip.sha1 index 0b7b8a2..2bb78d30 100644 --- a/ios/google_internal/frameworks/ChromeExtensionKeychainInternal.framework.dSYM.ios.zip.sha1 +++ b/ios/google_internal/frameworks/ChromeExtensionKeychainInternal.framework.dSYM.ios.zip.sha1
@@ -1 +1 @@ -807569f612f36a60d65fe69c01dbb19cd4c29957 \ No newline at end of file +a73e832bf0cff9d086dc49787ded093e85b2037c \ No newline at end of file
diff --git a/ios/google_internal/frameworks/ChromeExtensionKeychainInternal.framework.dSYM.ios_asan.zip.sha1 b/ios/google_internal/frameworks/ChromeExtensionKeychainInternal.framework.dSYM.ios_asan.zip.sha1 index 8b4f6cae..3eae749 100644 --- a/ios/google_internal/frameworks/ChromeExtensionKeychainInternal.framework.dSYM.ios_asan.zip.sha1 +++ b/ios/google_internal/frameworks/ChromeExtensionKeychainInternal.framework.dSYM.ios_asan.zip.sha1
@@ -1 +1 @@ -6f17a6acf2304bfb1c3f0f602029b14c1dd2e1a2 \ No newline at end of file +2ee40abcb11ae7a9ee14934cde0b01e00166d989 \ No newline at end of file
diff --git a/ios/google_internal/frameworks/ChromeInternal.framework.dSYM.ios.zip.sha1 b/ios/google_internal/frameworks/ChromeInternal.framework.dSYM.ios.zip.sha1 index 125925bb..0c5b2bc5 100644 --- a/ios/google_internal/frameworks/ChromeInternal.framework.dSYM.ios.zip.sha1 +++ b/ios/google_internal/frameworks/ChromeInternal.framework.dSYM.ios.zip.sha1
@@ -1 +1 @@ -83272d501d27dcecac07e220d79c13062471aa13 \ No newline at end of file +88fb9435476c1919e18a8267dd8c119524d0602f \ No newline at end of file
diff --git a/ios/google_internal/frameworks/ChromeInternal.framework.dSYM.ios_asan.zip.sha1 b/ios/google_internal/frameworks/ChromeInternal.framework.dSYM.ios_asan.zip.sha1 index 059e1bdd..688cd3d 100644 --- a/ios/google_internal/frameworks/ChromeInternal.framework.dSYM.ios_asan.zip.sha1 +++ b/ios/google_internal/frameworks/ChromeInternal.framework.dSYM.ios_asan.zip.sha1
@@ -1 +1 @@ -090e4778dd96187ce2f338b4e6f494ce1cab0978 \ No newline at end of file +7f525bd96fba6aa69d2f0ae69119dc4cb27f561d \ No newline at end of file
diff --git a/ios/google_internal/frameworks/ChromeSSOInternal.framework.dSYM.ios.zip.sha1 b/ios/google_internal/frameworks/ChromeSSOInternal.framework.dSYM.ios.zip.sha1 index 554d260..8a269a8 100644 --- a/ios/google_internal/frameworks/ChromeSSOInternal.framework.dSYM.ios.zip.sha1 +++ b/ios/google_internal/frameworks/ChromeSSOInternal.framework.dSYM.ios.zip.sha1
@@ -1 +1 @@ -5ac896cff40a70b67786d9f77ee48c8bd34c9488 \ No newline at end of file +40226924cc1e43a85336a4d791a3b4b7611db449 \ No newline at end of file
diff --git a/ios/google_internal/frameworks/ChromeSSOInternal.framework.dSYM.ios_asan.zip.sha1 b/ios/google_internal/frameworks/ChromeSSOInternal.framework.dSYM.ios_asan.zip.sha1 index e940e9dc..d1d629c 100644 --- a/ios/google_internal/frameworks/ChromeSSOInternal.framework.dSYM.ios_asan.zip.sha1 +++ b/ios/google_internal/frameworks/ChromeSSOInternal.framework.dSYM.ios_asan.zip.sha1
@@ -1 +1 @@ -2cfe04a6c81c2a8e6e6ce85e3d3d57b73aa479ac \ No newline at end of file +018107616cc0ebe23fc03e6664f576376c214a15 \ No newline at end of file
diff --git a/ios/google_internal/frameworks/chrome_extension_keychain_internal_dynamic_framework.ios.zip.sha1 b/ios/google_internal/frameworks/chrome_extension_keychain_internal_dynamic_framework.ios.zip.sha1 index ee46a45e..5a11586f 100644 --- a/ios/google_internal/frameworks/chrome_extension_keychain_internal_dynamic_framework.ios.zip.sha1 +++ b/ios/google_internal/frameworks/chrome_extension_keychain_internal_dynamic_framework.ios.zip.sha1
@@ -1 +1 @@ -b953b39f06630981601349fac27d79f61902e377 \ No newline at end of file +7389d5af2f632249076839617cf18d276a99defa \ No newline at end of file
diff --git a/ios/google_internal/frameworks/chrome_extension_keychain_internal_dynamic_framework.ios_asan.zip.sha1 b/ios/google_internal/frameworks/chrome_extension_keychain_internal_dynamic_framework.ios_asan.zip.sha1 index ddee2bb..591a0464 100644 --- a/ios/google_internal/frameworks/chrome_extension_keychain_internal_dynamic_framework.ios_asan.zip.sha1 +++ b/ios/google_internal/frameworks/chrome_extension_keychain_internal_dynamic_framework.ios_asan.zip.sha1
@@ -1 +1 @@ -9cca9b246e48548e1d467fa149c8d29aca49ccc1 \ No newline at end of file +11b6735e4997a291acb8b07cf3a934bb4a46ac8d \ No newline at end of file
diff --git a/ios/google_internal/frameworks/chrome_extension_keychain_internal_dynamic_framework.iossimulator.zip.sha1 b/ios/google_internal/frameworks/chrome_extension_keychain_internal_dynamic_framework.iossimulator.zip.sha1 index 2b716820..d13708f67 100644 --- a/ios/google_internal/frameworks/chrome_extension_keychain_internal_dynamic_framework.iossimulator.zip.sha1 +++ b/ios/google_internal/frameworks/chrome_extension_keychain_internal_dynamic_framework.iossimulator.zip.sha1
@@ -1 +1 @@ -2cdae6f09473b3572b58534e9ea05f11e8b92e77 \ No newline at end of file +93f9e18ce7fe172f1115902ceaecaff769ca3ed5 \ No newline at end of file
diff --git a/ios/google_internal/frameworks/chrome_extension_keychain_internal_dynamic_framework.iossimulator_asan.zip.sha1 b/ios/google_internal/frameworks/chrome_extension_keychain_internal_dynamic_framework.iossimulator_asan.zip.sha1 index 9724d6f..b98cb482 100644 --- a/ios/google_internal/frameworks/chrome_extension_keychain_internal_dynamic_framework.iossimulator_asan.zip.sha1 +++ b/ios/google_internal/frameworks/chrome_extension_keychain_internal_dynamic_framework.iossimulator_asan.zip.sha1
@@ -1 +1 @@ -513d9a59436279e89252b4407dadbd209608b38e \ No newline at end of file +7c2fe928108858d551b345d6e0bfc5db50e4a69c \ No newline at end of file
diff --git a/ios/google_internal/frameworks/chrome_internal_dynamic_framework.ios.zip.sha1 b/ios/google_internal/frameworks/chrome_internal_dynamic_framework.ios.zip.sha1 index daa8e07..e0b9a7a76 100644 --- a/ios/google_internal/frameworks/chrome_internal_dynamic_framework.ios.zip.sha1 +++ b/ios/google_internal/frameworks/chrome_internal_dynamic_framework.ios.zip.sha1
@@ -1 +1 @@ -4563fffb11431871a48d3f18a1913d4847150825 \ No newline at end of file +421ae1af3163e208c4762169bb339aacbef622da \ No newline at end of file
diff --git a/ios/google_internal/frameworks/chrome_internal_dynamic_framework.ios_asan.zip.sha1 b/ios/google_internal/frameworks/chrome_internal_dynamic_framework.ios_asan.zip.sha1 index cbded24..0b1c955 100644 --- a/ios/google_internal/frameworks/chrome_internal_dynamic_framework.ios_asan.zip.sha1 +++ b/ios/google_internal/frameworks/chrome_internal_dynamic_framework.ios_asan.zip.sha1
@@ -1 +1 @@ -5ddf3ca6d77629e80c274f20ec57e1ac2b2985ea \ No newline at end of file +32b7e7c416b5c408abd4b0eae914b6ccc82b2f31 \ No newline at end of file
diff --git a/ios/google_internal/frameworks/chrome_internal_dynamic_framework.iossimulator.zip.sha1 b/ios/google_internal/frameworks/chrome_internal_dynamic_framework.iossimulator.zip.sha1 index 15263b4..8f7ad26 100644 --- a/ios/google_internal/frameworks/chrome_internal_dynamic_framework.iossimulator.zip.sha1 +++ b/ios/google_internal/frameworks/chrome_internal_dynamic_framework.iossimulator.zip.sha1
@@ -1 +1 @@ -8031381d3b201929057be5979cfcc4056357d6fb \ No newline at end of file +d52760af6895c48662fcae97f98dc0eecaff64b9 \ No newline at end of file
diff --git a/ios/google_internal/frameworks/chrome_internal_dynamic_framework.iossimulator_asan.zip.sha1 b/ios/google_internal/frameworks/chrome_internal_dynamic_framework.iossimulator_asan.zip.sha1 index 8d15ada2..87225b14c 100644 --- a/ios/google_internal/frameworks/chrome_internal_dynamic_framework.iossimulator_asan.zip.sha1 +++ b/ios/google_internal/frameworks/chrome_internal_dynamic_framework.iossimulator_asan.zip.sha1
@@ -1 +1 @@ -0531c619cf3122db3498c26476e3c16d461a5fe2 \ No newline at end of file +1a47a9666faa95931e8390f1d1abec9a1a31cc1f \ No newline at end of file
diff --git a/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.ios.zip.sha1 b/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.ios.zip.sha1 index 5d062283..e3bf9c5 100644 --- a/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.ios.zip.sha1 +++ b/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.ios.zip.sha1
@@ -1 +1 @@ -00bd025e2852879efcf0e9241570cd0a91d046e7 \ No newline at end of file +8f869a375ba890c2eea187ec00c2523a6d9ae768 \ No newline at end of file
diff --git a/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.ios_asan.zip.sha1 b/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.ios_asan.zip.sha1 index 215549784..d788c31c 100644 --- a/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.ios_asan.zip.sha1 +++ b/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.ios_asan.zip.sha1
@@ -1 +1 @@ -da4d79cdf3a7ae2f8d1fef4bb3ee3b67ef527ff4 \ No newline at end of file +d8e6b10f36cac3bc8021e30af1d198426140dd02 \ No newline at end of file
diff --git a/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.iossimulator.zip.sha1 b/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.iossimulator.zip.sha1 index 74f7d53..327ef68 100644 --- a/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.iossimulator.zip.sha1 +++ b/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.iossimulator.zip.sha1
@@ -1 +1 @@ -bb6ab974ff12942145fb7bb3cabe4283267662a0 \ No newline at end of file +53f920d21fa33b396809716bb7d4d97c19db724a \ No newline at end of file
diff --git a/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.iossimulator_asan.zip.sha1 b/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.iossimulator_asan.zip.sha1 index 92d8636..d110983 100644 --- a/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.iossimulator_asan.zip.sha1 +++ b/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.iossimulator_asan.zip.sha1
@@ -1 +1 @@ -92160005ddfd341cdad494d3fd462173083fbdb8 \ No newline at end of file +ccaf4c4c8506f89ab7b50888cf9b3afc9da693e3 \ No newline at end of file
diff --git a/ios/google_internal/frameworks/chrome_test_internal_dynamic_framework.ios.zip.sha1 b/ios/google_internal/frameworks/chrome_test_internal_dynamic_framework.ios.zip.sha1 index ef748f1..c2a032f 100644 --- a/ios/google_internal/frameworks/chrome_test_internal_dynamic_framework.ios.zip.sha1 +++ b/ios/google_internal/frameworks/chrome_test_internal_dynamic_framework.ios.zip.sha1
@@ -1 +1 @@ -93711d8d82de0703ab7b79aee8df1a2b776375d8 \ No newline at end of file +f605232993eaec81fbaba751dd20a3acb3a70ff0 \ No newline at end of file
diff --git a/ios/google_internal/frameworks/chrome_test_internal_dynamic_framework.iossimulator.zip.sha1 b/ios/google_internal/frameworks/chrome_test_internal_dynamic_framework.iossimulator.zip.sha1 index d7c417e..ae1a5be 100644 --- a/ios/google_internal/frameworks/chrome_test_internal_dynamic_framework.iossimulator.zip.sha1 +++ b/ios/google_internal/frameworks/chrome_test_internal_dynamic_framework.iossimulator.zip.sha1
@@ -1 +1 @@ -631c8f8ee1b61d5cef25ff04dd1801be9c4b1578 \ No newline at end of file +3c55549fc3a25f66c2cb9da948ba7678d0e45686 \ No newline at end of file
diff --git a/ios/google_internal/frameworks/remoting_internal_dynamic_framework.ios.zip.sha1 b/ios/google_internal/frameworks/remoting_internal_dynamic_framework.ios.zip.sha1 index b4e9d62..7f0bb13 100644 --- a/ios/google_internal/frameworks/remoting_internal_dynamic_framework.ios.zip.sha1 +++ b/ios/google_internal/frameworks/remoting_internal_dynamic_framework.ios.zip.sha1
@@ -1 +1 @@ -d26b40a34a2a02bad885cf7f2c9f10edb4b5e29e \ No newline at end of file +fc89cb25a2ce7caca3d4d16f4e2ad64edb82a1f3 \ No newline at end of file
diff --git a/ios/google_internal/frameworks/remoting_internal_dynamic_framework.ios_asan.zip.sha1 b/ios/google_internal/frameworks/remoting_internal_dynamic_framework.ios_asan.zip.sha1 index cb6d44a..d62d619 100644 --- a/ios/google_internal/frameworks/remoting_internal_dynamic_framework.ios_asan.zip.sha1 +++ b/ios/google_internal/frameworks/remoting_internal_dynamic_framework.ios_asan.zip.sha1
@@ -1 +1 @@ -1ddbf42e592cdd2de2b42df8fd963b90a584d39f \ No newline at end of file +b6abea6d6e78fb9f523e3fc61f1d799707c42576 \ No newline at end of file
diff --git a/ios/google_internal/frameworks/remoting_internal_dynamic_framework.iossimulator.zip.sha1 b/ios/google_internal/frameworks/remoting_internal_dynamic_framework.iossimulator.zip.sha1 index b9505a4b..8a3b82c 100644 --- a/ios/google_internal/frameworks/remoting_internal_dynamic_framework.iossimulator.zip.sha1 +++ b/ios/google_internal/frameworks/remoting_internal_dynamic_framework.iossimulator.zip.sha1
@@ -1 +1 @@ -219b07096aab2fa0cd908f6bc11d713a593fbaf2 \ No newline at end of file +1c1c75508a7114c03003bc0eaa55ddc8cbcf5dff \ No newline at end of file
diff --git a/ios/google_internal/frameworks/remoting_internal_dynamic_framework.iossimulator_asan.zip.sha1 b/ios/google_internal/frameworks/remoting_internal_dynamic_framework.iossimulator_asan.zip.sha1 index 72223dc..94613503 100644 --- a/ios/google_internal/frameworks/remoting_internal_dynamic_framework.iossimulator_asan.zip.sha1 +++ b/ios/google_internal/frameworks/remoting_internal_dynamic_framework.iossimulator_asan.zip.sha1
@@ -1 +1 @@ -af794b1e13244cb58f09fc027e36a81efcae62e2 \ No newline at end of file +c42011166bce12d7ebb6eb1926511e9779811ea3 \ No newline at end of file
diff --git a/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.ios.zip.sha1 b/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.ios.zip.sha1 index a06c9a3..d0968db 100644 --- a/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.ios.zip.sha1 +++ b/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.ios.zip.sha1
@@ -1 +1 @@ -cfc73db3a3cca4a940fea73b1aea9012ba07829a \ No newline at end of file +1e26110643582e1439de807927063ee70b73e1e4 \ No newline at end of file
diff --git a/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.ios_asan.zip.sha1 b/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.ios_asan.zip.sha1 index 1bea27c..3edbee1 100644 --- a/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.ios_asan.zip.sha1 +++ b/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.ios_asan.zip.sha1
@@ -1 +1 @@ -268a96e855e20604a7717aa7a4adf0c32158fc61 \ No newline at end of file +375af3fefaf6f73dc78b2eb215af45de78203feb \ No newline at end of file
diff --git a/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.iossimulator.zip.sha1 b/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.iossimulator.zip.sha1 index 66d6a41..f08dbe58 100644 --- a/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.iossimulator.zip.sha1 +++ b/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.iossimulator.zip.sha1
@@ -1 +1 @@ -2da352799973738d3f2f16afb15aee28b9719084 \ No newline at end of file +c410fa282c6a056aca7a590075ac0cf7469f909d \ No newline at end of file
diff --git a/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.iossimulator_asan.zip.sha1 b/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.iossimulator_asan.zip.sha1 index 2a802c7c..a55d33f 100644 --- a/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.iossimulator_asan.zip.sha1 +++ b/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.iossimulator_asan.zip.sha1
@@ -1 +1 @@ -189e83b800dbfb72704658ce28930591cc62bcdc \ No newline at end of file +cb553149bbc91abcc362a666447aa9d4ce106577 \ No newline at end of file
diff --git a/ios/public/provider/chrome/browser/lens/lens_overlay_api.h b/ios/public/provider/chrome/browser/lens/lens_overlay_api.h index c047328..c6afebdb 100644 --- a/ios/public/provider/chrome/browser/lens/lens_overlay_api.h +++ b/ios/public/provider/chrome/browser/lens/lens_overlay_api.h
@@ -45,6 +45,11 @@ // The lens overlay has deferred a gesture. - (void)lensOverlayDidDeferGesture:(id<ChromeLensOverlay>)lensOverlay; +@optional +// The lens overlay failed to detect translatable text. +- (void)lensOverlayDidFailDetectingTranslatableText: + (id<ChromeLensOverlay>)lensOverlay; + @end // Defines the interface for interacting with a Chrome Lens Overlay.
diff --git a/ios/web_view/internal/cwv_user_content_controller.mm b/ios/web_view/internal/cwv_user_content_controller.mm index 9dbecbe..61deb30 100644 --- a/ios/web_view/internal/cwv_user_content_controller.mm +++ b/ios/web_view/internal/cwv_user_content_controller.mm
@@ -55,12 +55,12 @@ - (void)addUserScript:(nonnull CWVUserScript*)userScript { [_userScripts addObject:userScript]; - [self updateEarlyPageScript]; + [self updatePageScripts]; } - (void)removeAllUserScripts { [_userScripts removeAllObjects]; - [self updateEarlyPageScript]; + [self updatePageScripts]; } - (nonnull NSArray<CWVUserScript*>*)userScripts { @@ -69,13 +69,23 @@ // Updates the early page script associated with the BrowserState with the // content of _userScripts. -- (void)updateEarlyPageScript { +- (void)updatePageScripts { NSMutableString* joinedAllFramesScript = [[NSMutableString alloc] init]; NSMutableString* joinedMainFrameScript = [[NSMutableString alloc] init]; + NSMutableString* joinedAllFramesDocEndScript = [[NSMutableString alloc] init]; + NSMutableString* joinedMainFrameDocEndScript = [[NSMutableString alloc] init]; for (CWVUserScript* script in _userScripts) { // Inserts "\n" between scripts to make it safer to join multiple scripts, // in case the first script doesn't end with ";" or "\n". - if (script.isForMainFrameOnly) { + if (script.injectionTime == CWVUserScriptInjectionTimeAtDocumentEnd) { + if (script.isForMainFrameOnly) { + [joinedMainFrameDocEndScript appendString:script.source]; + [joinedMainFrameDocEndScript appendString:@"\n"]; + } else { + [joinedAllFramesDocEndScript appendString:script.source]; + [joinedAllFramesDocEndScript appendString:@"\n"]; + } + } else if (script.isForMainFrameOnly) { [joinedMainFrameScript appendString:script.source]; [joinedMainFrameScript appendString:@"\n"]; } else { @@ -85,7 +95,9 @@ } WebViewScriptsJavaScriptFeature::FromBrowserState(_configuration.browserState) ->SetScripts(base::SysNSStringToUTF8(joinedAllFramesScript), - base::SysNSStringToUTF8(joinedMainFrameScript)); + base::SysNSStringToUTF8(joinedMainFrameScript), + base::SysNSStringToUTF8(joinedAllFramesDocEndScript), + base::SysNSStringToUTF8(joinedMainFrameDocEndScript)); } - (void)addMessageHandler:(void (^)(NSDictionary* payload))handler
diff --git a/ios/web_view/internal/cwv_user_script.mm b/ios/web_view/internal/cwv_user_script.mm index 6dbf30d..60555f9 100644 --- a/ios/web_view/internal/cwv_user_script.mm +++ b/ios/web_view/internal/cwv_user_script.mm
@@ -7,8 +7,8 @@ @implementation CWVUserScript @synthesize source = _source; - @synthesize forMainFrameOnly = _forMainFrameOnly; +@synthesize injectionTime = _injectionTime; - (nonnull instancetype)initWithSource:(nonnull NSString*)source { return [self initWithSource:source forMainFrameOnly:true]; @@ -16,10 +16,19 @@ - (nonnull instancetype)initWithSource:(nonnull NSString*)source forMainFrameOnly:(BOOL)forMainFrameOnly { + return [self initWithSource:source + forMainFrameOnly:forMainFrameOnly + injectionTime:CWVUserScriptInjectionTimeAtDocumentStart]; +} + +- (instancetype)initWithSource:(NSString*)source + forMainFrameOnly:(BOOL)forMainFrameOnly + injectionTime:(CWVUserScriptInjectionTime)injectionTime { self = [super init]; if (self) { _source = [source copy]; _forMainFrameOnly = forMainFrameOnly; + _injectionTime = injectionTime; } return self; }
diff --git a/ios/web_view/internal/js_messaging/web_view_scripts_java_script_feature.h b/ios/web_view/internal/js_messaging/web_view_scripts_java_script_feature.h index 5993464..1c5b7c73 100644 --- a/ios/web_view/internal/js_messaging/web_view_scripts_java_script_feature.h +++ b/ios/web_view/internal/js_messaging/web_view_scripts_java_script_feature.h
@@ -32,7 +32,9 @@ web::BrowserState* browser_state); void SetScripts(std::optional<std::string> all_frames_script, - std::optional<std::string> main_frame_script); + std::optional<std::string> main_frame_script, + std::optional<std::string> all_frames_script_doc_end, + std::optional<std::string> main_frame_script_doc_end); private: std::vector<FeatureScript> GetScripts() const override; @@ -42,6 +44,8 @@ std::optional<std::string> all_frames_script_; std::optional<std::string> main_frame_script_; + std::optional<std::string> all_frames_script_doc_end_; + std::optional<std::string> main_frame_script_doc_end_; }; #endif // IOS_WEB_VIEW_INTERNAL_JS_MESSAGING_WEB_VIEW_SCRIPTS_JAVA_SCRIPT_FEATURE_H_
diff --git a/ios/web_view/internal/js_messaging/web_view_scripts_java_script_feature.mm b/ios/web_view/internal/js_messaging/web_view_scripts_java_script_feature.mm index 4c9bae7..94dca32 100644 --- a/ios/web_view/internal/js_messaging/web_view_scripts_java_script_feature.mm +++ b/ios/web_view/internal/js_messaging/web_view_scripts_java_script_feature.mm
@@ -40,9 +40,13 @@ void WebViewScriptsJavaScriptFeature::SetScripts( std::optional<std::string> all_frames_script, - std::optional<std::string> main_frame_script) { + std::optional<std::string> main_frame_script, + std::optional<std::string> all_frames_script_doc_end, + std::optional<std::string> main_frame_script_doc_end) { all_frames_script_ = all_frames_script; main_frame_script_ = main_frame_script; + all_frames_script_doc_end_ = all_frames_script_doc_end; + main_frame_script_doc_end_ = main_frame_script_doc_end; // Feature scripts must be explicitly updated after they change. web::WKWebViewConfigurationProvider& config_provider = @@ -67,5 +71,19 @@ JavaScriptFeature::FeatureScript::InjectionTime::kDocumentStart, JavaScriptFeature::FeatureScript::TargetFrames::kMainFrame)); } + if (all_frames_script_doc_end_) { + feature_scripts.push_back( + JavaScriptFeature::FeatureScript::CreateWithString( + all_frames_script_doc_end_.value(), + JavaScriptFeature::FeatureScript::InjectionTime::kDocumentEnd, + JavaScriptFeature::FeatureScript::TargetFrames::kAllFrames)); + } + if (main_frame_script_doc_end_) { + feature_scripts.push_back( + JavaScriptFeature::FeatureScript::CreateWithString( + main_frame_script_doc_end_.value(), + JavaScriptFeature::FeatureScript::InjectionTime::kDocumentEnd, + JavaScriptFeature::FeatureScript::TargetFrames::kMainFrame)); + } return feature_scripts; }
diff --git a/ios/web_view/public/cwv_user_script.h b/ios/web_view/public/cwv_user_script.h index 92ed22cf..e37c230 100644 --- a/ios/web_view/public/cwv_user_script.h +++ b/ios/web_view/public/cwv_user_script.h
@@ -11,9 +11,12 @@ NS_ASSUME_NONNULL_BEGIN -// User Script to be injected into a webpage after window.document is created, -// but before other content is loaded (i.e., at the same timing as -// WKUserScriptInjectionTimeAtDocumentStart). +typedef NS_ENUM(NSInteger, CWVUserScriptInjectionTime) { + CWVUserScriptInjectionTimeAtDocumentStart, + CWVUserScriptInjectionTimeAtDocumentEnd +}; + +// User Script to be injected into a webpage. CWV_EXPORT @interface CWVUserScript : NSObject @@ -23,16 +26,26 @@ // Whether the script should be injected into all frames or just the main frame. @property(nonatomic, readonly, getter=isForMainFrameOnly) BOOL forMainFrameOnly; +// The time at which the script should be injected: at the start or end of the +// document load. +@property(nonatomic, readonly) CWVUserScriptInjectionTime injectionTime; + - (instancetype)init NS_UNAVAILABLE; -// Creates a user script which should be injected into the main frame only. +// Creates a user script which should be injected into the main frame only at document start time. - (instancetype)initWithSource:(NSString*)source; // Creates a user script which should be injected into all frames or just the -// main frame. +// main frame at document start time. - (instancetype)initWithSource:(NSString*)source forMainFrameOnly:(BOOL)forMainFrameOnly; +// Creates a user script which will be injected into all frames or just the +// main frame, and at the specified injection time. +- (instancetype)initWithSource:(NSString*)source + forMainFrameOnly:(BOOL)forMainFrameOnly + injectionTime:(CWVUserScriptInjectionTime)injectionTime; + @end NS_ASSUME_NONNULL_END
diff --git a/ios_internal b/ios_internal index ab1e6c4..a4b8373 160000 --- a/ios_internal +++ b/ios_internal
@@ -1 +1 @@ -Subproject commit ab1e6c45e8b0ceb3f070f0a39c5271766fc7aa12 +Subproject commit a4b8373a27c133f175d81ed8044474c23cfb164b
diff --git a/ipc/BUILD.gn b/ipc/BUILD.gn index eef379f1..c9262e8 100644 --- a/ipc/BUILD.gn +++ b/ipc/BUILD.gn
@@ -30,6 +30,8 @@ "param_traits_read_macros.h", "param_traits_write_macros.h", "struct_constructor_macros.h", + "tracing_helpers.h", + "tracing_helpers_internal.h", ] if (use_blink) { @@ -299,6 +301,7 @@ "ipc_sync_message_unittest.cc", "ipc_sync_message_unittest.h", "sync_socket_unittest.cc", + "tracing_helpers_unittest.cc", ] if (is_posix || is_fuchsia) {
diff --git a/ipc/ipc_message_macros.h b/ipc/ipc_message_macros.h index 3f14a993..81e4c4b 100644 --- a/ipc/ipc_message_macros.h +++ b/ipc/ipc_message_macros.h
@@ -199,11 +199,11 @@ #include <tuple> #include "base/export_template.h" -#include "base/hash/md5_constexpr.h" #include "base/task/common/task_annotator.h" #include "ipc/ipc_message_templates.h" #include "ipc/ipc_message_utils.h" #include "ipc/param_traits_macros.h" +#include "ipc/tracing_helpers.h" // Convenience macro for defining structs without inheritance. Should not need // to be subsequently redefined. @@ -324,7 +324,7 @@ // associated with the incoming IPC message that caused them to be posted. #define IPC_TASK_ANNOTATOR_CONTEXT(msg_class) \ static constexpr uint32_t kMessageHash = \ - base::MD5Hash32Constexpr(IPC_TASK_ANNOTATOR_STRINGIFY(msg_class)); \ + ipc::GetLegacyIpcTraceId(IPC_TASK_ANNOTATOR_STRINGIFY(msg_class)); \ base::TaskAnnotator::ScopedSetIpcHash scoped_ipc_hash(kMessageHash); #define IPC_BEGIN_MESSAGE_MAP(class_name, msg) \
diff --git a/ipc/tracing_helpers.h b/ipc/tracing_helpers.h new file mode 100644 index 0000000..e40ec79 --- /dev/null +++ b/ipc/tracing_helpers.h
@@ -0,0 +1,21 @@ +// Copyright 2019 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef IPC_TRACING_HELPERS_H_ +#define IPC_TRACING_HELPERS_H_ + +#include <string_view> + +#include "ipc/tracing_helpers_internal.h" + +namespace ipc { + +// Calculates the first 32 bits of the MD5 digest of the provided data. Used for +// calculating an ID used for tracing legacy IPC messages. Do not use this in +// new code. +constexpr uint32_t GetLegacyIpcTraceId(std::string_view string); + +} // namespace ipc + +#endif // IPC_TRACING_HELPERS_H_
diff --git a/base/hash/md5_constexpr_internal.h b/ipc/tracing_helpers_internal.h similarity index 92% rename from base/hash/md5_constexpr_internal.h rename to ipc/tracing_helpers_internal.h index cf9534e..1993ff97 100644 --- a/base/hash/md5_constexpr_internal.h +++ b/ipc/tracing_helpers_internal.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 BASE_HASH_MD5_CONSTEXPR_INTERNAL_H_ -#define BASE_HASH_MD5_CONSTEXPR_INTERNAL_H_ +#ifndef IPC_TRACING_HELPERS_INTERNAL_H_ +#define IPC_TRACING_HELPERS_INTERNAL_H_ #include <stdint.h> @@ -13,7 +13,7 @@ #include "base/check_op.h" #include "base/numerics/safe_conversions.h" -namespace base { +namespace ipc { namespace internal { // The implementation here is based on the pseudocode provided by Wikipedia: @@ -217,7 +217,7 @@ // Processes an entire message. static constexpr IntermediateData ProcessMessage(std::string_view message) { const uint32_t m = - GetPaddedMessageLength(checked_cast<uint32_t>(message.size())); + GetPaddedMessageLength(base::checked_cast<uint32_t>(message.size())); IntermediateData intermediate0 = kInitialIntermediateData; for (uint32_t offset = 0; offset < m; offset += 64) { RoundData data = GetRoundData(message, m, offset); @@ -241,12 +241,6 @@ ////////////////////////////////////////////////////////////////////////////// // WRAPPER FUNCTIONS - static constexpr uint64_t Hash64(std::string_view data) { - IntermediateData intermediate = ProcessMessage(data); - return (static_cast<uint64_t>(SwapEndian(intermediate.a)) << 32) | - static_cast<uint64_t>(SwapEndian(intermediate.b)); - } - static constexpr uint32_t Hash32(std::string_view data) { IntermediateData intermediate = ProcessMessage(data); return SwapEndian(intermediate.a); @@ -257,14 +251,10 @@ // Implementations of the functions exposed in the public header. -constexpr uint64_t MD5Hash64Constexpr(std::string_view string) { - return internal::MD5CE::Hash64(string); -} - -constexpr uint32_t MD5Hash32Constexpr(std::string_view string) { +constexpr uint32_t GetLegacyIpcTraceId(std::string_view string) { return internal::MD5CE::Hash32(string); } -} // namespace base +} // namespace ipc -#endif // BASE_HASH_MD5_CONSTEXPR_INTERNAL_H_ +#endif // IPC_TRACING_HELPERS_INTERNAL_H_
diff --git a/ipc/tracing_helpers_unittest.cc b/ipc/tracing_helpers_unittest.cc new file mode 100644 index 0000000..1ff8dffb --- /dev/null +++ b/ipc/tracing_helpers_unittest.cc
@@ -0,0 +1,21 @@ +// Copyright 2019 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "ipc/tracing_helpers.h" + +namespace ipc { + +// Ensure that everything works at compile-time by comparing to a few +// reference hashes. +constexpr char kMessage0[] = "message digest"; + +static_assert(GetLegacyIpcTraceId(kMessage0) == 0xF96B697Dul, + "incorrect MD5Hash32 implementation"); + +constexpr char kMessage1[] = "The quick brown fox jumps over the lazy dog"; + +static_assert(GetLegacyIpcTraceId(kMessage1) == 0x9E107D9Dul, + "incorrect MD5Hash32 implementation"); + +} // namespace ipc
diff --git a/mojo/public/cpp/bindings/interface_endpoint_client.h b/mojo/public/cpp/bindings/interface_endpoint_client.h index 2d796ce..f683fea 100644 --- a/mojo/public/cpp/bindings/interface_endpoint_client.h +++ b/mojo/public/cpp/bindings/interface_endpoint_client.h
@@ -102,7 +102,8 @@ AssociatedGroup* associated_group(); scoped_refptr<ThreadSafeProxy> CreateThreadSafeProxy( - scoped_refptr<ThreadSafeProxy::Target> target); + scoped_refptr<ThreadSafeProxy::Target> target, + const base::Location& location); // Sets a MessageFilter which can filter a message after validation but // before dispatch.
diff --git a/mojo/public/cpp/bindings/lib/associated_interface_ptr_state.h b/mojo/public/cpp/bindings/lib/associated_interface_ptr_state.h index dbb4a86..c76a345 100644 --- a/mojo/public/cpp/bindings/lib/associated_interface_ptr_state.h +++ b/mojo/public/cpp/bindings/lib/associated_interface_ptr_state.h
@@ -15,6 +15,7 @@ #include "base/component_export.h" #include "base/functional/bind.h" #include "base/functional/callback_forward.h" +#include "base/location.h" #include "base/memory/ptr_util.h" #include "base/memory/scoped_refptr.h" #include "base/task/sequenced_task_runner.h" @@ -76,8 +77,9 @@ } scoped_refptr<ThreadSafeProxy> CreateThreadSafeProxy( - scoped_refptr<ThreadSafeProxy::Target> target) { - return endpoint_client_->CreateThreadSafeProxy(std::move(target)); + scoped_refptr<ThreadSafeProxy::Target> target, + const base::Location& location) { + return endpoint_client_->CreateThreadSafeProxy(std::move(target), location); } protected:
diff --git a/mojo/public/cpp/bindings/lib/interface_endpoint_client.cc b/mojo/public/cpp/bindings/lib/interface_endpoint_client.cc index 9615abc7..0ecfee48 100644 --- a/mojo/public/cpp/bindings/lib/interface_endpoint_client.cc +++ b/mojo/public/cpp/bindings/lib/interface_endpoint_client.cc
@@ -59,11 +59,13 @@ base::WeakPtr<InterfaceEndpointClient> endpoint, scoped_refptr<ThreadSafeProxy::Target> target, const AssociatedGroup& associated_group, - scoped_refptr<base::SequencedTaskRunner> task_runner) + scoped_refptr<base::SequencedTaskRunner> task_runner, + const base::Location& location) : endpoint_(std::move(endpoint)), target_(std::move(target)), associated_group_(associated_group), - task_runner_(std::move(task_runner)) {} + task_runner_(std::move(task_runner)), + location_(location) {} ThreadSafeInterfaceEndpointClientProxy( const ThreadSafeInterfaceEndpointClientProxy&) = delete; @@ -154,12 +156,14 @@ class ForwardToCallingThread : public MessageReceiver { public: - explicit ForwardToCallingThread(std::unique_ptr<MessageReceiver> responder) + explicit ForwardToCallingThread(std::unique_ptr<MessageReceiver> responder, + const base::Location& location) : responder_(std::move(responder)), - caller_task_runner_(base::SequencedTaskRunner::GetCurrentDefault()) {} + caller_task_runner_(base::SequencedTaskRunner::GetCurrentDefault()), + location_(location) {} ~ForwardToCallingThread() override { - caller_task_runner_->DeleteSoon(FROM_HERE, std::move(responder_)); + caller_task_runner_->DeleteSoon(location_, std::move(responder_)); } private: @@ -167,7 +171,7 @@ // `this` will be deleted immediately after this method returns. We must // relinquish ownership of `responder_` so it doesn't get deleted. caller_task_runner_->PostTask( - FROM_HERE, + location_, base::BindOnce(&ForwardToCallingThread::CallAcceptAndDeleteResponder, std::move(responder_), std::move(*message))); return true; @@ -181,6 +185,7 @@ std::unique_ptr<MessageReceiver> responder_; scoped_refptr<base::SequencedTaskRunner> caller_task_runner_; + const base::Location location_; }; class ForwardSameThreadResponder : public MessageReceiver { @@ -231,6 +236,7 @@ const scoped_refptr<base::SequencedTaskRunner> task_runner_; const scoped_refptr<InProgressSyncCalls> sync_calls_{ base::MakeRefCounted<InProgressSyncCalls>()}; + const base::Location location_; }; void DetermineIfEndpointIsConnected( @@ -381,8 +387,8 @@ // Async messages are always posted (even if `task_runner_` runs tasks on // this sequence) to guarantee that two async calls can't be reordered. if (!message.has_flag(Message::kFlagIsSync)) { - auto reply_forwarder = - std::make_unique<ForwardToCallingThread>(std::move(responder)); + auto reply_forwarder = std::make_unique<ForwardToCallingThread>( + std::move(responder), location_); task_runner_->PostTask( FROM_HERE, base::BindOnce(&ThreadSafeInterfaceEndpointClientProxy :: @@ -510,10 +516,11 @@ } scoped_refptr<ThreadSafeProxy> InterfaceEndpointClient::CreateThreadSafeProxy( - scoped_refptr<ThreadSafeProxy::Target> target) { + scoped_refptr<ThreadSafeProxy::Target> target, + const base::Location& location) { return base::MakeRefCounted<ThreadSafeInterfaceEndpointClientProxy>( weak_ptr_factory_.GetWeakPtr(), std::move(target), *associated_group_, - task_runner_); + task_runner_, location); } ScopedInterfaceEndpointHandle InterfaceEndpointClient::PassHandle() {
diff --git a/mojo/public/cpp/bindings/lib/interface_ptr_state.h b/mojo/public/cpp/bindings/lib/interface_ptr_state.h index b6b88ee..860de777 100644 --- a/mojo/public/cpp/bindings/lib/interface_ptr_state.h +++ b/mojo/public/cpp/bindings/lib/interface_ptr_state.h
@@ -17,6 +17,7 @@ #include "base/dcheck_is_on.h" #include "base/functional/bind.h" #include "base/functional/callback.h" +#include "base/location.h" #include "base/memory/scoped_refptr.h" #include "base/task/sequenced_task_runner.h" #include "base/time/time.h" @@ -72,8 +73,9 @@ } scoped_refptr<ThreadSafeProxy> CreateThreadSafeProxy( - scoped_refptr<ThreadSafeProxy::Target> target) { - return endpoint_client_->CreateThreadSafeProxy(std::move(target)); + scoped_refptr<ThreadSafeProxy::Target> target, + const base::Location& location) { + return endpoint_client_->CreateThreadSafeProxy(std::move(target), location); } #if DCHECK_IS_ON()
diff --git a/mojo/public/cpp/bindings/receiver_set.h b/mojo/public/cpp/bindings/receiver_set.h index 41b31247..8d3062f4 100644 --- a/mojo/public/cpp/bindings/receiver_set.h +++ b/mojo/public/cpp/bindings/receiver_set.h
@@ -340,6 +340,24 @@ return pending_receivers; } + // Similar to the method above, but it also includes the receiver's context. + std::vector<std::pair<PendingType, Context>> TakeReceiversWithContext() { + static_assert(ContextTraits::SupportsContext(), + "TakeReceiversWithContext() requires non-void context type."); + + ReceiverSetState::EntryMap entries; + std::swap(state_.entries(), entries); + std::vector<std::pair<PendingType, Context>> pending_receivers; + for (auto& entry : entries) { + ReceiverEntry& receiver = + static_cast<ReceiverEntry&>(entry.second->receiver()); + pending_receivers.emplace_back( + receiver.Unbind(), + std::move(*static_cast<Context*>(receiver.GetContext()))); + } + return pending_receivers; + } + // Removes all receivers from the set, effectively closing all of them. This // ReceiverSet will not schedule or execute any further method invocations or // disconnection notifications until a new receiver is added to the set.
diff --git a/mojo/public/cpp/bindings/shared_associated_remote.h b/mojo/public/cpp/bindings/shared_associated_remote.h index 5a72c19f..e9f5833f 100644 --- a/mojo/public/cpp/bindings/shared_associated_remote.h +++ b/mojo/public/cpp/bindings/shared_associated_remote.h
@@ -7,6 +7,7 @@ #include <tuple> +#include "base/location.h" #include "base/memory/scoped_refptr.h" #include "base/task/sequenced_task_runner.h" #include "mojo/public/cpp/bindings/associated_remote.h" @@ -47,9 +48,10 @@ explicit SharedAssociatedRemote( PendingAssociatedRemote<Interface> pending_remote, scoped_refptr<base::SequencedTaskRunner> bind_task_runner = - base::SequencedTaskRunner::GetCurrentDefault()) { + base::SequencedTaskRunner::GetCurrentDefault(), + const base::Location& location = base::Location::Current()) { if (pending_remote.is_valid()) - Bind(std::move(pending_remote), std::move(bind_task_runner)); + Bind(std::move(pending_remote), std::move(bind_task_runner), location); } bool is_bound() const { return remote_ != nullptr; } @@ -83,20 +85,22 @@ // one of them, on `task_runner`. The other is returned as a receiver. mojo::PendingAssociatedReceiver<Interface> BindNewEndpointAndPassReceiver( scoped_refptr<base::SequencedTaskRunner> bind_task_runner = - base::SequencedTaskRunner::GetCurrentDefault()) { + base::SequencedTaskRunner::GetCurrentDefault(), + const base::Location& location = base::Location::Current()) { if (!internal::GetRuntimeFeature_ExpectEnabled<Interface>()) { return PendingAssociatedReceiver<Interface>(); } mojo::PendingAssociatedRemote<Interface> remote; auto receiver = remote.InitWithNewEndpointAndPassReceiver(); - Bind(std::move(remote), std::move(bind_task_runner)); + Bind(std::move(remote), std::move(bind_task_runner), location); return receiver; } // Binds to `pending_remote` on `bind_task_runner`. void Bind(PendingAssociatedRemote<Interface> pending_remote, scoped_refptr<base::SequencedTaskRunner> bind_task_runner = - base::SequencedTaskRunner::GetCurrentDefault()) { + base::SequencedTaskRunner::GetCurrentDefault(), + const base::Location& location = base::Location::Current()) { DCHECK(!remote_); DCHECK(pending_remote.is_valid()); if (!internal::GetRuntimeFeature_ExpectEnabled<Interface>()) { @@ -104,7 +108,7 @@ return; } remote_ = SharedRemoteBase<AssociatedRemote<Interface>>::Create( - std::move(pending_remote), std::move(bind_task_runner)); + std::move(pending_remote), std::move(bind_task_runner), location); } private:
diff --git a/mojo/public/cpp/bindings/shared_remote.h b/mojo/public/cpp/bindings/shared_remote.h index ded9752..a1fef2e 100644 --- a/mojo/public/cpp/bindings/shared_remote.h +++ b/mojo/public/cpp/bindings/shared_remote.h
@@ -9,6 +9,7 @@ #include <tuple> #include "base/functional/bind.h" +#include "base/location.h" #include "base/memory/ptr_util.h" #include "base/memory/ref_counted.h" #include "base/synchronization/waitable_event.h" @@ -116,10 +117,11 @@ RemoteWrapper(const RemoteWrapper&) = delete; RemoteWrapper& operator=(const RemoteWrapper&) = delete; - std::unique_ptr<ThreadSafeForwarder<InterfaceType>> CreateForwarder() { + std::unique_ptr<ThreadSafeForwarder<InterfaceType>> CreateForwarder( + const base::Location& location) { return std::make_unique<ThreadSafeForwarder<InterfaceType>>( remote_.internal_state()->CreateThreadSafeProxy( - base::MakeRefCounted<ProxyTarget>(this))); + base::MakeRefCounted<ProxyTarget>(this), location)); } void set_disconnect_handler( @@ -204,16 +206,21 @@ } }; - explicit SharedRemoteBase(scoped_refptr<RemoteWrapper> wrapper) - : wrapper_(std::move(wrapper)), forwarder_(wrapper_->CreateForwarder()) {} + explicit SharedRemoteBase(scoped_refptr<RemoteWrapper> wrapper, + const base::Location& location) + : wrapper_(std::move(wrapper)), + forwarder_(wrapper_->CreateForwarder(location)) {} // Creates a SharedRemoteBase bound to `pending_remote`. All messages sent // through the SharedRemote will first bounce through `task_runner`. static scoped_refptr<SharedRemoteBase> Create( PendingType pending_remote, - scoped_refptr<base::SequencedTaskRunner> task_runner) { - return new SharedRemoteBase(base::MakeRefCounted<RemoteWrapper>( - std::move(pending_remote), std::move(task_runner))); + scoped_refptr<base::SequencedTaskRunner> task_runner, + const base::Location& location) { + return new SharedRemoteBase( + base::MakeRefCounted<RemoteWrapper>(std::move(pending_remote), + std::move(task_runner)), + location); } ~SharedRemoteBase() = default; @@ -248,15 +255,18 @@ // Constructs a SharedRemote bound to `pending_remote` on the calling // sequence. See `Bind()` below for more details. - explicit SharedRemote(PendingRemote<Interface> pending_remote) { - Bind(std::move(pending_remote), nullptr); + explicit SharedRemote( + PendingRemote<Interface> pending_remote, + const base::Location& location = base::Location::Current()) { + Bind(std::move(pending_remote), nullptr, location); } // Constructs a SharedRemote bound to `pending_remote` on the sequence given // by `bind_task_runner`. See `Bind()` below for more details. SharedRemote(PendingRemote<Interface> pending_remote, - scoped_refptr<base::SequencedTaskRunner> bind_task_runner) { - Bind(std::move(pending_remote), std::move(bind_task_runner)); + scoped_refptr<base::SequencedTaskRunner> bind_task_runner, + const base::Location& location = base::Location::Current()) { + Bind(std::move(pending_remote), std::move(bind_task_runner), location); } // SharedRemote supports both copy and move construction and assignment. These @@ -311,31 +321,33 @@ // this call and re-bound to `pending_remote`. Any prior copies made are NOT // affected and will retain their reference to the original Remote. void Bind(PendingRemote<Interface> pending_remote, - scoped_refptr<base::SequencedTaskRunner> bind_task_runner) { + scoped_refptr<base::SequencedTaskRunner> bind_task_runner, + const base::Location& location = base::Location::Current()) { if (!internal::GetRuntimeFeature_ExpectEnabled<Interface>()) { remote_.reset(); return; } if (bind_task_runner && pending_remote) { remote_ = SharedRemoteBase<Remote<Interface>>::Create( - std::move(pending_remote), std::move(bind_task_runner)); + std::move(pending_remote), std::move(bind_task_runner), location); } else if (pending_remote) { remote_ = SharedRemoteBase<Remote<Interface>>::Create( std::move(pending_remote), - base::SequencedTaskRunner::GetCurrentDefault()); + base::SequencedTaskRunner::GetCurrentDefault(), location); } } // Creates a new pipe, binding this SharedRemote to one end on // `bind_task_runner` and returning the other end as a PendingReceiver. PendingReceiver<Interface> BindNewPipeAndPassReceiver( - scoped_refptr<base::SequencedTaskRunner> bind_task_runner = nullptr) { + scoped_refptr<base::SequencedTaskRunner> bind_task_runner = nullptr, + const base::Location& location = base::Location::Current()) { if (!internal::GetRuntimeFeature_ExpectEnabled<Interface>()) { return PendingReceiver<Interface>(); } PendingRemote<Interface> remote; auto receiver = remote.InitWithNewPipeAndPassReceiver(); - Bind(std::move(remote), std::move(bind_task_runner)); + Bind(std::move(remote), std::move(bind_task_runner), location); return receiver; }
diff --git a/mojo/public/cpp/bindings/tests/BUILD.gn b/mojo/public/cpp/bindings/tests/BUILD.gn index f99e76a..9b53648 100644 --- a/mojo/public/cpp/bindings/tests/BUILD.gn +++ b/mojo/public/cpp/bindings/tests/BUILD.gn
@@ -49,6 +49,7 @@ "report_bad_message_unittest.cc", "request_response_unittest.cc", "result_response_unittest.cc", + "result_response_unittest_mojom_traits.h", "router_test_util.cc", "router_test_util.h", "sample_service_unittest.cc", @@ -213,6 +214,19 @@ ] traits_headers = [ "default_construct_unittest_mojom_traits.h" ] }, + { + types = [ + { + mojom = "mojo.test.mojom.ResultValue" + cpp = "::mojo::test::MappedResultValue" + }, + { + mojom = "mojo.test.mojom.ResultError" + cpp = "::mojo::test::MappedResultError" + }, + ] + traits_headers = [ "result_response_unittest_mojom_traits.h" ] + }, ] }
diff --git a/mojo/public/cpp/bindings/tests/result_response.test-mojom b/mojo/public/cpp/bindings/tests/result_response.test-mojom index 1a118648..4d4cdd7 100644 --- a/mojo/public/cpp/bindings/tests/result_response.test-mojom +++ b/mojo/public/cpp/bindings/tests/result_response.test-mojom
@@ -5,9 +5,26 @@ module mojo.test.mojom; interface TestResultInterface { - // Echos the input value with success. - TestSuccess(int32 value) => result<int32, bool>; + // Echos the input value with success. + TestSuccess(int32 value) => result<int32, bool>; - // Echos the failure message with failure. - TestFailure(string failure) => result<bool, string>; + // Echos the failure message with failure. + TestFailure(string failure) => result<bool, string>; +}; + +struct ResultValue { + int32 value; +}; + +struct ResultError { + bool is_catastrophic; + string msg; +}; + +interface TestResultInterfaceWithTrait { + // Returns a success with a value of '1'. + TestSuccess() => result<ResultValue, ResultError>; + + // Returns a failure with a a nuclear metldown. + TestFailure() => result<ResultValue, ResultError>; };
diff --git a/mojo/public/cpp/bindings/tests/result_response_unittest.cc b/mojo/public/cpp/bindings/tests/result_response_unittest.cc index 36897e8..e1e43ea 100644 --- a/mojo/public/cpp/bindings/tests/result_response_unittest.cc +++ b/mojo/public/cpp/bindings/tests/result_response_unittest.cc
@@ -10,6 +10,7 @@ #include "mojo/public/cpp/bindings/remote.h" #include "mojo/public/cpp/bindings/tests/bindings_test_base.h" #include "mojo/public/cpp/bindings/tests/result_response.test-mojom.h" +#include "result_response_unittest_mojom_traits.h" #include "testing/gtest/include/gtest/gtest.h" namespace mojo::test { @@ -37,6 +38,31 @@ mojo::Receiver<mojom::TestResultInterface> receiver_; }; +class TraitInterfaceImpl : public mojom::TestResultInterfaceWithTrait { + public: + explicit TraitInterfaceImpl( + mojo::PendingReceiver<mojom::TestResultInterfaceWithTrait> receiver) + : receiver_(this, std::move(receiver)) {} + TraitInterfaceImpl(const TraitInterfaceImpl&) = delete; + TraitInterfaceImpl& operator=(const TraitInterfaceImpl&) = delete; + ~TraitInterfaceImpl() override = default; + + // mojom::TestResultTraitInterface + void TestSuccess(TestSuccessCallback cb) override { + std::move(cb).Run(base::ok(MappedResultValue{1})); + } + + void TestFailure(TestFailureCallback cb) override { + MappedResultError err; + err.is_game_over_ = true; + err.reason_ = "meltdown!"; + std::move(cb).Run(base::unexpected(err)); + } + + private: + mojo::Receiver<mojom::TestResultInterfaceWithTrait> receiver_; +}; + using ResultResponseTest = BindingsTestBase; TEST_P(ResultResponseTest, TestResult) { @@ -66,6 +92,33 @@ loop.Run(); } +TEST_P(ResultResponseTest, TestSuccessTrait) { + mojo::Remote<mojom::TestResultInterfaceWithTrait> remote; + TraitInterfaceImpl impl(remote.BindNewPipeAndPassReceiver()); + + base::RunLoop loop; + remote->TestSuccess(base::BindLambdaForTesting( + [&](base::expected<MappedResultValue, MappedResultError> result) { + ASSERT_EQ(1, result.value().magic_value); + loop.Quit(); + })); + loop.Run(); +} + +TEST_P(ResultResponseTest, TestFailureTrait) { + mojo::Remote<mojom::TestResultInterfaceWithTrait> remote; + TraitInterfaceImpl impl(remote.BindNewPipeAndPassReceiver()); + + base::RunLoop loop; + remote->TestFailure(base::BindLambdaForTesting( + [&](base::expected<MappedResultValue, MappedResultError> result) { + ASSERT_TRUE(result.error().is_game_over_); + ASSERT_EQ(result.error().reason_, "meltdown!"); + loop.Quit(); + })); + loop.Run(); +} + INSTANTIATE_MOJO_BINDINGS_TEST_SUITE_P(ResultResponseTest); } // namespace
diff --git a/mojo/public/cpp/bindings/tests/result_response_unittest_mojom_traits.h b/mojo/public/cpp/bindings/tests/result_response_unittest_mojom_traits.h new file mode 100644 index 0000000..d633f32c --- /dev/null +++ b/mojo/public/cpp/bindings/tests/result_response_unittest_mojom_traits.h
@@ -0,0 +1,68 @@ +// 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_PUBLIC_CPP_BINDINGS_TESTS_RESULT_RESPONSE_UNITTEST_MOJOM_TRAITS_H_ +#define MOJO_PUBLIC_CPP_BINDINGS_TESTS_RESULT_RESPONSE_UNITTEST_MOJOM_TRAITS_H_ + +#include <string> + +#include "mojo/public/cpp/bindings/string_data_view.h" +#include "mojo/public/cpp/bindings/struct_traits.h" +#include "mojo/public/cpp/bindings/tests/result_response.test-mojom-shared.h" + +namespace mojo { +namespace test { + +struct MappedResultValue { + int magic_value; +}; + +class MappedResultError { + public: + MappedResultError() = default; + virtual ~MappedResultError() = default; + MappedResultError(const MappedResultError&) = default; + MappedResultError& operator=(const MappedResultError&) = default; + + bool is_game_over_; + std::string reason_; +}; + +} // namespace test + +template <> +struct StructTraits<test::mojom::ResultValueDataView, test::MappedResultValue> { + static int value(const test::MappedResultValue& in) { return in.magic_value; } + + static bool Read(test::mojom::ResultValueDataView in, + test::MappedResultValue* out) { + out->magic_value = in.value(); + return true; + } +}; + +template <> +struct StructTraits<test::mojom::ResultErrorDataView, test::MappedResultError> { + static bool is_catastrophic(const test::MappedResultError& in) { + return in.is_game_over_; + } + + static std::string_view msg(const test::MappedResultError& in) { + return in.reason_; + } + + static bool Read(test::mojom::ResultErrorDataView in, + test::MappedResultError* out) { + out->is_game_over_ = in.is_catastrophic(); + + mojo::StringDataView s_view; + in.GetMsgDataView(&s_view); + out->reason_ = s_view.value(); + return true; + } +}; + +} // namespace mojo + +#endif // MOJO_PUBLIC_CPP_BINDINGS_TESTS_RESULT_RESPONSE_UNITTEST_MOJOM_TRAITS_H_
diff --git a/mojo/public/tools/bindings/generators/cpp_templates/module.cc.tmpl b/mojo/public/tools/bindings/generators/cpp_templates/module.cc.tmpl index 3e55f4a..a8e46ca 100644 --- a/mojo/public/tools/bindings/generators/cpp_templates/module.cc.tmpl +++ b/mojo/public/tools/bindings/generators/cpp_templates/module.cc.tmpl
@@ -17,7 +17,6 @@ #include <utility> #include "base/debug/alias.h" -#include "base/hash/md5_constexpr.h" #include "base/run_loop.h" #include "base/strings/string_number_conversions.h" #include "base/task/thread_pool/thread_pool_instance.h"
diff --git a/net/http/http_network_transaction.cc b/net/http/http_network_transaction.cc index 15ff8c7..1de7d7f 100644 --- a/net/http/http_network_transaction.cc +++ b/net/http/http_network_transaction.cc
@@ -219,6 +219,9 @@ int HttpNetworkTransaction::Start(const HttpRequestInfo* request_info, CompletionOnceCallback callback, const NetLogWithSource& net_log) { + TRACE_EVENT("net", "HttpNetworkTransaction::Start", + NetLogWithSourceToFlow(net_log), "url", request_info->url); + if (request_info->load_flags & LOAD_ONLY_FROM_CACHE) return ERR_CACHE_MISS; @@ -278,6 +281,9 @@ DCHECK(!stream_request_.get()); DCHECK_EQ(STATE_NONE, next_state_); + TRACE_EVENT("net", "HttpNetworkTransaction::RestartIgnoringLastError", + NetLogWithSourceToFlow(net_log_)); + if (!CheckMaxRestarts()) return ERR_TOO_MANY_RETRIES; @@ -304,6 +310,9 @@ DCHECK(!stream_.get()); DCHECK_EQ(STATE_NONE, next_state_); + TRACE_EVENT("net", "HttpNetworkTransaction::RestartWithCertificate", + NetLogWithSourceToFlow(net_log_)); + if (!CheckMaxRestarts()) return ERR_TOO_MANY_RETRIES; @@ -333,6 +342,9 @@ int HttpNetworkTransaction::RestartWithAuth(const AuthCredentials& credentials, CompletionOnceCallback callback) { + TRACE_EVENT("net", "HttpNetworkTransaction::RestartWithAuth", + NetLogWithSourceToFlow(net_log_)); + if (!CheckMaxRestarts()) return ERR_TOO_MANY_RETRIES; @@ -429,6 +441,8 @@ // Close the stream and mark it as not_reusable. Even in the // keep_alive case, we've determined that the stream_ is not // reusable if new_stream is NULL. + TRACE_EVENT("net", "HttpNetworkTransaction::DidDrainBodyForAuthRestart", + NetLogWithSourceToFlow(net_log_)); stream_->Close(true); next_state_ = STATE_CREATE_STREAM; } else { @@ -649,6 +663,8 @@ } int HttpNetworkTransaction::ResumeNetworkStart() { + TRACE_EVENT("net", "HttpNetworkTransaction::ResumeNetworkStart", + NetLogWithSourceToFlow(net_log_)); DCHECK_EQ(next_state_, STATE_CREATE_STREAM); return DoLoop(OK); } @@ -946,6 +962,8 @@ } int HttpNetworkTransaction::DoNotifyBeforeCreateStream() { + TRACE_EVENT("net", "HttpNetworkTransaction::NotifyBeforeCreateStream", + NetLogWithSourceToFlow(net_log_)); next_state_ = STATE_CREATE_STREAM; bool defer = false; if (!before_network_start_callback_.is_null()) @@ -957,7 +975,8 @@ int HttpNetworkTransaction::DoCreateStream() { TRACE_EVENT("net", "HttpNetworkTransaction::CreateStream", - NetLogWithSourceToFlow(net_log_)); + NetLogWithSourceToFlow(net_log_), "retry_attempts", + retry_attempts_, "num_restarts", num_restarts_); response_.network_accessed = true; next_state_ = STATE_CREATE_STREAM_COMPLETE; @@ -1382,7 +1401,9 @@ int HttpNetworkTransaction::DoReadHeadersComplete(int result) { TRACE_EVENT("net", "HttpNetworkTransaction::ReadHeadersComplete", - NetLogWithSourceToFlow(net_log_), "result", result); + NetLogWithSourceToFlow(net_log_), "result", result, + "response_code", + response_.headers ? response_.headers->response_code() : -1); // We can get a ERR_SSL_CLIENT_AUTH_CERT_NEEDED here due to SSL renegotiation. // Server certificate errors are impossible. Rather than reverify the new // server certificate, BoringSSL forbids server certificates from changing. @@ -2099,6 +2120,10 @@ void HttpNetworkTransaction::ResetConnectionAndRequestForResend( RetryReason retry_reason) { + TRACE_EVENT("net", + "HttpNetworkTransaction::ResetConnectionAndRequestForResend", + NetLogWithSourceToFlow(net_log_), "retry_reason", retry_reason); + reset_connection_and_request_for_resend_start_time_ = base::TimeTicks::Now(); // TODO:(crbug.com/1495705): Remove this CHECK after fixing the bug.
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 6a8b9f0..6ab0c489 100644 --- a/services/on_device_model/public/mojom/on_device_model.mojom +++ b/services/on_device_model/public/mojom/on_device_model.mojom
@@ -207,6 +207,10 @@ uint32? top_k; // Deprecated: use SessionParams.temperature instead. float? temperature; + + // A JSON schema defining structured output requirements for the response. + // Passed as an opaque JSON blob to llguidance. + [MinVersion=1] string? response_json_schema; }; // A session for a model that allows adding context and then executing an input
diff --git a/testing/buildbot/chromium.perf.json b/testing/buildbot/chromium.perf.json index 727f446..38ddac9f 100644 --- a/testing/buildbot/chromium.perf.json +++ b/testing/buildbot/chromium.perf.json
@@ -651,6 +651,141 @@ } ] }, + "android-pixel9-perf": { + "isolated_scripts": [ + { + "args": [ + "-v", + "-v", + "--browser=android-trichrome-chrome-google-64-32-bundle", + "--upload-results", + "--test-shard-map-filename=android-pixel9-perf_map.json", + "--ignore-benchmark-exit-code" + ], + "merge": { + "script": "//tools/perf/process_perf_results.py" + }, + "name": "performance_test_suite_android_trichrome_chrome_google_64_32_bundle", + "resultdb": { + "enable": true + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimensions": { + "device_os": "B", + "device_os_flavor": "google", + "device_type": "tokay", + "os": "Android", + "pool": "chrome.tests.perf" + }, + "expiration": 7200, + "hard_timeout": 14400, + "io_timeout": 21600, + "service_account": "chrome-tester@chops-service-accounts.iam.gserviceaccount.com", + "shards": 4 + }, + "test": "performance_test_suite_android_trichrome_chrome_google_64_32_bundle", + "trigger_script": { + "args": [ + "--multiple-dimension-script-verbose", + "True" + ], + "requires_simultaneous_shard_dispatch": true, + "script": "//testing/trigger_scripts/perf_device_trigger.py" + } + } + ] + }, + "android-pixel9-pro-perf": { + "isolated_scripts": [ + { + "args": [ + "-v", + "-v", + "--browser=android-trichrome-chrome-google-64-32-bundle", + "--upload-results", + "--test-shard-map-filename=android-pixel9-pro-perf_map.json", + "--ignore-benchmark-exit-code" + ], + "merge": { + "script": "//tools/perf/process_perf_results.py" + }, + "name": "performance_test_suite_android_trichrome_chrome_google_64_32_bundle", + "resultdb": { + "enable": true + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimensions": { + "device_os": "B", + "device_os_flavor": "google", + "device_type": "caiman", + "os": "Android", + "pool": "chrome.tests.perf" + }, + "expiration": 7200, + "hard_timeout": 14400, + "io_timeout": 21600, + "service_account": "chrome-tester@chops-service-accounts.iam.gserviceaccount.com", + "shards": 4 + }, + "test": "performance_test_suite_android_trichrome_chrome_google_64_32_bundle", + "trigger_script": { + "args": [ + "--multiple-dimension-script-verbose", + "True" + ], + "requires_simultaneous_shard_dispatch": true, + "script": "//testing/trigger_scripts/perf_device_trigger.py" + } + } + ] + }, + "android-pixel9-pro-xl-perf": { + "isolated_scripts": [ + { + "args": [ + "-v", + "-v", + "--browser=android-trichrome-chrome-google-64-32-bundle", + "--upload-results", + "--test-shard-map-filename=android-pixel9-pro-xl-perf_map.json", + "--ignore-benchmark-exit-code" + ], + "merge": { + "script": "//tools/perf/process_perf_results.py" + }, + "name": "performance_test_suite_android_trichrome_chrome_google_64_32_bundle", + "resultdb": { + "enable": true + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimensions": { + "device_os": "B", + "device_os_flavor": "google", + "device_type": "komodo", + "os": "Android", + "pool": "chrome.tests.perf" + }, + "expiration": 7200, + "hard_timeout": 14400, + "io_timeout": 21600, + "service_account": "chrome-tester@chops-service-accounts.iam.gserviceaccount.com", + "shards": 4 + }, + "test": "performance_test_suite_android_trichrome_chrome_google_64_32_bundle", + "trigger_script": { + "args": [ + "--multiple-dimension-script-verbose", + "True" + ], + "requires_simultaneous_shard_dispatch": true, + "script": "//testing/trigger_scripts/perf_device_trigger.py" + } + } + ] + }, "android_arm64-builder-perf": { "isolated_scripts": [ {
diff --git a/testing/buildbot/chromium.perf.pinpoint.json b/testing/buildbot/chromium.perf.pinpoint.json index 2571f87..b1d0900ef 100644 --- a/testing/buildbot/chromium.perf.pinpoint.json +++ b/testing/buildbot/chromium.perf.pinpoint.json
@@ -570,6 +570,141 @@ } ] }, + "android-pixel9-perf": { + "isolated_scripts": [ + { + "args": [ + "-v", + "-v", + "--browser=android-trichrome-chrome-google-64-32-bundle", + "--upload-results", + "--test-shard-map-filename=android-pixel9-perf_map.json", + "--ignore-benchmark-exit-code" + ], + "merge": { + "script": "//tools/perf/process_perf_results.py" + }, + "name": "performance_test_suite_android_trichrome_chrome_google_64_32_bundle", + "resultdb": { + "enable": true + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimensions": { + "device_os": "B", + "device_os_flavor": "google", + "device_type": "tokay", + "os": "Android", + "pool": "chrome.tests.perf" + }, + "expiration": 7200, + "hard_timeout": 14400, + "io_timeout": 21600, + "service_account": "chrome-tester@chops-service-accounts.iam.gserviceaccount.com", + "shards": 4 + }, + "test": "performance_test_suite_android_trichrome_chrome_google_64_32_bundle", + "trigger_script": { + "args": [ + "--multiple-dimension-script-verbose", + "True" + ], + "requires_simultaneous_shard_dispatch": true, + "script": "//testing/trigger_scripts/perf_device_trigger.py" + } + } + ] + }, + "android-pixel9-pro-perf": { + "isolated_scripts": [ + { + "args": [ + "-v", + "-v", + "--browser=android-trichrome-chrome-google-64-32-bundle", + "--upload-results", + "--test-shard-map-filename=android-pixel9-pro-perf_map.json", + "--ignore-benchmark-exit-code" + ], + "merge": { + "script": "//tools/perf/process_perf_results.py" + }, + "name": "performance_test_suite_android_trichrome_chrome_google_64_32_bundle", + "resultdb": { + "enable": true + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimensions": { + "device_os": "B", + "device_os_flavor": "google", + "device_type": "caiman", + "os": "Android", + "pool": "chrome.tests.perf" + }, + "expiration": 7200, + "hard_timeout": 14400, + "io_timeout": 21600, + "service_account": "chrome-tester@chops-service-accounts.iam.gserviceaccount.com", + "shards": 4 + }, + "test": "performance_test_suite_android_trichrome_chrome_google_64_32_bundle", + "trigger_script": { + "args": [ + "--multiple-dimension-script-verbose", + "True" + ], + "requires_simultaneous_shard_dispatch": true, + "script": "//testing/trigger_scripts/perf_device_trigger.py" + } + } + ] + }, + "android-pixel9-pro-xl-perf": { + "isolated_scripts": [ + { + "args": [ + "-v", + "-v", + "--browser=android-trichrome-chrome-google-64-32-bundle", + "--upload-results", + "--test-shard-map-filename=android-pixel9-pro-xl-perf_map.json", + "--ignore-benchmark-exit-code" + ], + "merge": { + "script": "//tools/perf/process_perf_results.py" + }, + "name": "performance_test_suite_android_trichrome_chrome_google_64_32_bundle", + "resultdb": { + "enable": true + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimensions": { + "device_os": "B", + "device_os_flavor": "google", + "device_type": "komodo", + "os": "Android", + "pool": "chrome.tests.perf" + }, + "expiration": 7200, + "hard_timeout": 14400, + "io_timeout": 21600, + "service_account": "chrome-tester@chops-service-accounts.iam.gserviceaccount.com", + "shards": 4 + }, + "test": "performance_test_suite_android_trichrome_chrome_google_64_32_bundle", + "trigger_script": { + "args": [ + "--multiple-dimension-script-verbose", + "True" + ], + "requires_simultaneous_shard_dispatch": true, + "script": "//testing/trigger_scripts/perf_device_trigger.py" + } + } + ] + }, "android_arm64-builder-perf": {}, "android_arm64-builder-perf-pgo": {}, "linux-builder-perf": {},
diff --git a/testing/variations/fieldtrial_testing_config.json b/testing/variations/fieldtrial_testing_config.json index 62deb259..fdad623f 100644 --- a/testing/variations/fieldtrial_testing_config.json +++ b/testing/variations/fieldtrial_testing_config.json
@@ -2222,7 +2222,6 @@ { "name": "Enabled", "enable_features": [ - "AutofillFixFormTracking", "AutofillPreferSavedFormAsSubmittedForm", "AutofillUseSubmittedFormInHtmlSubmission" ] @@ -7923,6 +7922,29 @@ ] } ], + "DesktopOmniboxRichAutocompletionMinChar": [ + { + "platforms": [ + "chromeos", + "chromeos_lacros", + "linux", + "mac", + "windows" + ], + "experiments": [ + { + "name": "Enabled_1_v1", + "params": { + "RichAutocompletionAutocompleteShortcutTextMinChar": "1", + "RichAutocompletionAutocompleteTitlesMinChar": "1" + }, + "enable_features": [ + "OmniboxRichAutocompletion" + ] + } + ] + } + ], "DesktopOmniboxShortcutBoost": [ { "platforms": [ @@ -17763,7 +17785,6 @@ "name": "Enabled", "enable_features": [ "DedicatedWorkerAblationStudyEnabled", - "PlzDedicatedWorker", "ServiceWorkerClientIdAlignedWithSpec" ] } @@ -22717,6 +22738,28 @@ ] } ], + "SharedTabGroupsTeamfood": [ + { + "platforms": [ + "android" + ], + "experiments": [ + { + "name": "Enabled", + "params": { + "show_send_feedback": "true" + }, + "enable_features": [ + "DataSharing", + "DeferMediaLoadInBackgroundTab", + "EnableTabTitleSanitization", + "EnableUrlRestriction", + "SyncSharedTabGroupDataInTransportMode" + ] + } + ] + } + ], "SharedWorkerBlobURLFix": [ { "platforms": [
diff --git a/third_party/android_deps/autorolled/VERSION.txt b/third_party/android_deps/autorolled/VERSION.txt index 74d3103..a555e61 100644 --- a/third_party/android_deps/autorolled/VERSION.txt +++ b/third_party/android_deps/autorolled/VERSION.txt
@@ -1 +1 @@ -88efd2a99f52198 \ No newline at end of file +151a6dedf2f706a \ No newline at end of file
diff --git a/third_party/android_deps/autorolled/bill_of_materials.json b/third_party/android_deps/autorolled/bill_of_materials.json index a0ba705..8bb3305 100644 --- a/third_party/android_deps/autorolled/bill_of_materials.json +++ b/third_party/android_deps/autorolled/bill_of_materials.json
@@ -1647,12 +1647,12 @@ { "name": "kotlinx-serialization-bom", "group": "org.jetbrains.kotlinx", - "version": "1.7.2" + "version": "1.7.3" }, { "name": "kotlinx-serialization-core", "group": "org.jetbrains.kotlinx", - "version": "1.6.3" + "version": "1.7.3" }, { "name": "kotlinx-serialization-core-jvm",
diff --git a/third_party/android_deps/autorolled/build.gradle b/third_party/android_deps/autorolled/build.gradle index 2511eda..a41e020 100644 --- a/third_party/android_deps/autorolled/build.gradle +++ b/third_party/android_deps/autorolled/build.gradle
@@ -343,8 +343,8 @@ versionCache['org.jetbrains.kotlinx:kotlinx-coroutines-rx3'] = '1.7.3' versionCache['org.jetbrains.kotlinx:kotlinx-coroutines-test'] = '1.7.3' versionCache['org.jetbrains.kotlinx:kotlinx-coroutines-test-jvm'] = '1.7.3' -versionCache['org.jetbrains.kotlinx:kotlinx-serialization-bom'] = '1.7.2' -versionCache['org.jetbrains.kotlinx:kotlinx-serialization-core'] = '1.6.3' +versionCache['org.jetbrains.kotlinx:kotlinx-serialization-bom'] = '1.7.3' +versionCache['org.jetbrains.kotlinx:kotlinx-serialization-core'] = '1.7.3' versionCache['org.jetbrains.kotlinx:kotlinx-serialization-core-jvm'] = '1.7.2' versionCache['org.jetbrains:annotations'] = '23.0.0' versionCache['org.jsoup:jsoup'] = '1.15.1'
diff --git a/third_party/android_sdk/cipd/system_images/android-34/android-automotive/x86_64.yaml b/third_party/android_sdk/cipd/system_images/android-34/android-automotive/x86_64.yaml new file mode 100644 index 0000000..8850f69 --- /dev/null +++ b/third_party/android_sdk/cipd/system_images/android-34/android-automotive/x86_64.yaml
@@ -0,0 +1,9 @@ +# Copyright 2025 The Chromium Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +package: chromium/third_party/android_sdk/public/system-images/android-34/android-automotive/x86_64 +description: system_images;android-34-ext9;android-automotive;x86_64 +root: ../../../../public/ +data: + - dir: system-images/android-34-ext9/android-automotive/x86_64
diff --git a/third_party/angle b/third_party/angle index b64aa6d..f56c8e0 160000 --- a/third_party/angle +++ b/third_party/angle
@@ -1 +1 @@ -Subproject commit b64aa6d5c5bdf4acf7de56dcb52594027f29a3ec +Subproject commit f56c8e02ce8b75bba76782d1b13200f0bde6cc63
diff --git a/third_party/blink/common/features.cc b/third_party/blink/common/features.cc index 5922af74..785722d 100644 --- a/third_party/blink/common/features.cc +++ b/third_party/blink/common/features.cc
@@ -2011,12 +2011,6 @@ "Path2DPaintCache", base::FEATURE_DISABLED_BY_DEFAULT); -// Enable browser-initiated dedicated worker script loading -// (PlzDedicatedWorker). https://crbug.com/906991 -BASE_FEATURE(kPlzDedicatedWorker, - "PlzDedicatedWorker", - base::FEATURE_ENABLED_BY_DEFAULT); - BASE_FEATURE(kDedicatedWorkerAblationStudyEnabled, "DedicatedWorkerAblationStudyEnabled", base::FEATURE_DISABLED_BY_DEFAULT); @@ -2395,7 +2389,7 @@ base::FEATURE_DISABLED_BY_DEFAULT); // If disabled, client_id and resultingClientId behavior keeps the old -// Chromium behavior even after the PlzDedicatedWorker is enabled. +// Chromium behavior. // This is workaround for crbug.com/1520512 until the fix gets ready. BASE_FEATURE(kServiceWorkerClientIdAlignedWithSpec, "ServiceWorkerClientIdAlignedWithSpec",
diff --git a/third_party/blink/common/service_worker/service_worker_loader_helpers.cc b/third_party/blink/common/service_worker/service_worker_loader_helpers.cc index 33a4baff..4632bd0b 100644 --- a/third_party/blink/common/service_worker/service_worker_loader_helpers.cc +++ b/third_party/blink/common/service_worker/service_worker_loader_helpers.cc
@@ -10,7 +10,6 @@ #include <utility> #include <vector> -#include "base/feature_list.h" #include "base/strings/stringprintf.h" #include "mojo/public/cpp/bindings/self_owned_receiver.h" #include "net/http/http_util.h" @@ -21,7 +20,6 @@ #include "services/network/public/mojom/fetch_api.mojom-shared.h" #include "services/network/public/mojom/url_response_head.mojom.h" #include "third_party/blink/public/common/blob/blob_utils.h" -#include "third_party/blink/public/common/features.h" #include "third_party/blink/public/common/loader/resource_type_util.h" #include "ui/base/page_transition_types.h" @@ -194,10 +192,10 @@ // static bool ServiceWorkerLoaderHelpers::IsMainRequestDestination( network::mojom::RequestDestination destination) { - // When PlzDedicatedWorker is enabled, a dedicated worker script is considered - // to be a main resource. - if (destination == network::mojom::RequestDestination::kWorker) - return base::FeatureList::IsEnabled(features::kPlzDedicatedWorker); + // A dedicated worker script is considered to be a main resource. + if (destination == network::mojom::RequestDestination::kWorker) { + return true; + } return IsRequestDestinationFrame(destination) || destination == network::mojom::RequestDestination::kSharedWorker; }
diff --git a/third_party/blink/public/common/features.h b/third_party/blink/public/common/features.h index bf446497..4db920f2 100644 --- a/third_party/blink/public/common/features.h +++ b/third_party/blink/public/common/features.h
@@ -1334,8 +1334,6 @@ BLINK_COMMON_EXPORT BASE_DECLARE_FEATURE(kPartitionVisitedLinkDatabase); -BLINK_COMMON_EXPORT BASE_DECLARE_FEATURE(kPlzDedicatedWorker); - BLINK_COMMON_EXPORT BASE_DECLARE_FEATURE(kDedicatedWorkerAblationStudyEnabled); BLINK_COMMON_EXPORT BASE_DECLARE_FEATURE_PARAM(int, kDedicatedWorkerStartDelayInMs); @@ -1865,8 +1863,6 @@ BLINK_COMMON_EXPORT bool IsParkableImagesToDiskEnabled(); -BLINK_COMMON_EXPORT bool IsPlzDedicatedWorkerEnabled(); - BLINK_COMMON_EXPORT bool IsSetIntervalWithoutClampEnabled(); // Returns if unload handlers are considered as a blocklisted reason for
diff --git a/third_party/blink/public/mojom/ai/ai_language_model.mojom b/third_party/blink/public/mojom/ai/ai_language_model.mojom index 83194bed..ed8f29d5 100644 --- a/third_party/blink/public/mojom/ai/ai_language_model.mojom +++ b/third_party/blink/public/mojom/ai/ai_language_model.mojom
@@ -122,8 +122,11 @@ // A session for prompting a model with inputs to obtain streaming responses. interface AILanguageModel { // Prompts the model with arbitrary content provided via the JavaScript API. + // A JSON schema can be provided to define structured output requirements + // for the response. The schema is optional, and is expected to be valid JSON. Prompt( array<AILanguageModelPrompt> prompts, + string? response_json_schema, pending_remote<ModelStreamingResponder> pending_responder ); // Creates a new session with the same configuration and existing context
diff --git a/third_party/blink/public/platform/web_dedicated_or_shared_worker_fetch_context.h b/third_party/blink/public/platform/web_dedicated_or_shared_worker_fetch_context.h index b7424bbe..0f9d13c4 100644 --- a/third_party/blink/public/platform/web_dedicated_or_shared_worker_fetch_context.h +++ b/third_party/blink/public/platform/web_dedicated_or_shared_worker_fetch_context.h
@@ -63,16 +63,9 @@ mojo::PendingRemote<mojom::ResourceLoadInfoNotifier> pending_resource_load_info_notifier); - // Clones this fetch context for a nested worker. - // For non-PlzDedicatedWorker. This will be removed once PlzDedicatedWorker is - // enabled by default. - virtual scoped_refptr<WebDedicatedOrSharedWorkerFetchContext> - CloneForNestedWorkerDeprecated( - scoped_refptr<base::SingleThreadTaskRunner> task_runner) = 0; - - // For PlzDedicatedWorker. The cloned fetch context does not inherit some - // fields (e.g., blink::WebServiceWorkerProviderContext) from this fetch - // context, and instead that takes values passed from the browser process. + // The cloned fetch context does not inherit some fields (e.g., + // blink::WebServiceWorkerProviderContext) from this fetch context, and + // instead that takes values passed from the browser process. virtual scoped_refptr<WebDedicatedOrSharedWorkerFetchContext> CloneForNestedWorker( WebServiceWorkerProviderContext* service_worker_provider_context,
diff --git a/third_party/blink/renderer/core/editing/finder/find_buffer.cc b/third_party/blink/renderer/core/editing/finder/find_buffer.cc index 4a98e6f..a5ef6356 100644 --- a/third_party/blink/renderer/core/editing/finder/find_buffer.cc +++ b/third_party/blink/renderer/core/editing/finder/find_buffer.cc
@@ -132,21 +132,33 @@ return !style->IsInert(); } -// Returns the next/previous node after |start_node| (including start node) that -// is a text node and is searchable and visible. +// Returns the next/previous node after |start_node| (including start node and +// in the search range) that is a text node and is searchable and visible. template <class Direction> -Node* GetVisibleTextNode(Node& start_node) { +Node* GetVisibleTextNode(Node& start_node, Node* past_last_node = nullptr) { Node* node = &start_node; + + // Move the end node to a visible subtree. Since we'll be testing node against + // it, it must be searchable otherwise node might skip past it. + if (past_last_node) { + while (Node* ancestor = + GetOutermostNonSearchableAncestor(*past_last_node)) { + past_last_node = Direction::NextSkippingSubtree(*ancestor); + if (!past_last_node) { + break; + } + } + } + // Move to outside display none subtree if we're inside one. while (Node* ancestor = GetOutermostNonSearchableAncestor(*node)) { - if (!ancestor) - return nullptr; node = Direction::NextSkippingSubtree(*ancestor); if (!node) return nullptr; } + // Move to first text node that's visible. - while (node) { + while (node && node != past_last_node) { const ComputedStyle* style = EnsureComputedStyleForFind(*node); if (FindBuffer::ShouldIgnoreContents(*node) || (style && style->Display() == EDisplay::kNone)) { @@ -179,6 +191,9 @@ while (node && !node->isSameNode(&end)) { node = FlatTreeTraversal::Next(*node); } + if (!node) { + return false; + } return node->isSameNode(&end); } @@ -341,7 +356,8 @@ return true; } -Node* FindBuffer::ForwardVisibleTextNode(Node& start_node) { +Node* FindBuffer::ForwardVisibleTextNode(Node& start_node, + Node* past_last_node) { struct ForwardDirection { static Node* Next(const Node& node) { return FlatTreeTraversal::Next(node); @@ -350,7 +366,7 @@ return FlatTreeTraversal::NextSkippingChildren(node); } }; - return GetVisibleTextNode<ForwardDirection>(start_node); + return GetVisibleTextNode<ForwardDirection>(start_node, past_last_node); } Node* FindBuffer::BackwardVisibleTextNode(Node& start_node) { @@ -394,9 +410,12 @@ const Node* const first_node = range.StartPosition().NodeAsRangeFirstNode(); if (!first_node) return; + // Get first visible text node from |start_position|. - Node* node = - ForwardVisibleTextNode(*range.StartPosition().NodeAsRangeFirstNode()); + // Make sure the node stays within the search range. + Node* past_last_node = range.EndPosition().NodeAsRangePastLastNode(); + Node* node = ForwardVisibleTextNode( + *range.StartPosition().NodeAsRangeFirstNode(), past_last_node); if (!node || !node->isConnected()) return; @@ -412,8 +431,6 @@ // Used for checking if we reached a new block. Node* last_added_text_node = nullptr; - - // We will also stop if we encountered/passed |end_node|. Node* end_node = range.EndPosition().NodeAsRangeLastNode(); if (ruby_support == RubySupport::kEnabledForcefully ||
diff --git a/third_party/blink/renderer/core/editing/finder/find_buffer.h b/third_party/blink/renderer/core/editing/finder/find_buffer.h index 382d73c..66c8610 100644 --- a/third_party/blink/renderer/core/editing/finder/find_buffer.h +++ b/third_party/blink/renderer/core/editing/finder/find_buffer.h
@@ -57,7 +57,8 @@ const Node& end_node); // See |GetVisibleTextNode|. - static Node* ForwardVisibleTextNode(Node& start_node); + static Node* ForwardVisibleTextNode(Node& start_node, + Node* past_last_node = nullptr); static Node* BackwardVisibleTextNode(Node& start_node); static bool ShouldIgnoreContents(const Node& node);
diff --git a/third_party/blink/renderer/core/editing/selection_modifier_line.cc b/third_party/blink/renderer/core/editing/selection_modifier_line.cc index 33c6de0..90cd29c 100644 --- a/third_party/blink/renderer/core/editing/selection_modifier_line.cc +++ b/third_party/blink/renderer/core/editing/selection_modifier_line.cc
@@ -61,8 +61,8 @@ if (cursor_.Current().IsEmptyLineBox()) return false; const PhysicalSize physical_size = cursor_.Current().Size(); - const LogicalSize logical_size = physical_size.ConvertToLogical( - cursor_.Current().Style().GetWritingMode()); + const LogicalSize logical_size = ToLogicalSize( + physical_size, cursor_.Current().Style().GetWritingMode()); if (!logical_size.block_size) return false; for (InlineCursor cursor(cursor_); cursor; cursor.MoveToNext()) {
diff --git a/third_party/blink/renderer/core/fragment_directive/text_fragment_finder.cc b/third_party/blink/renderer/core/fragment_directive/text_fragment_finder.cc index 5d77869..2235045 100644 --- a/third_party/blink/renderer/core/fragment_directive/text_fragment_finder.cc +++ b/third_party/blink/renderer/core/fragment_directive/text_fragment_finder.cc
@@ -375,7 +375,20 @@ const TextFragmentSelector& selector, Document* document, FindBufferRunnerType runner_type) - : client_(client), selector_(selector), document_(document) { + : TextFragmentFinder(client, + selector, + document->createRange(), + runner_type) { + if (document->body()) { + range_->selectNode(document->body()); + } +} + +TextFragmentFinder::TextFragmentFinder(Client& client, + const TextFragmentSelector& selector, + Range* range, + FindBufferRunnerType runner_type) + : client_(client), selector_(selector), range_(range) { DCHECK(!selector_.Start().empty()); DCHECK(selector_.Type() != TextFragmentSelector::SelectorType::kInvalid); if (runner_type == TextFragmentFinder::FindBufferRunnerType::kAsynchronous) { @@ -393,24 +406,22 @@ void TextFragmentFinder::FindMatch() { Cancel(); - auto forced_lock_scope = - document_->GetDisplayLockDocumentState().GetScopedForceActivatableLocks(); - document_->UpdateStyleAndLayout(DocumentUpdateReason::kFindInPage); + auto forced_lock_scope = range_->OwnerDocument() + .GetDisplayLockDocumentState() + .GetScopedForceActivatableLocks(); + range_->OwnerDocument().UpdateStyleAndLayout( + DocumentUpdateReason::kFindInPage); first_match_.Clear(); - FindMatchFromPosition(PositionInFlatTree::FirstPositionInNode(*document_)); + + PositionInFlatTree search_start = + ToPositionInFlatTree(range_->StartPosition()); + FindMatchFromPosition(search_start); } void TextFragmentFinder::FindMatchFromPosition( PositionInFlatTree search_start) { - PositionInFlatTree search_end; - if (document_->documentElement() && - document_->documentElement()->lastChild()) { - search_end = PositionInFlatTree::AfterNode( - *document_->documentElement()->lastChild()); - } else { - search_end = PositionInFlatTree::LastPositionInNode(*document_); - } + PositionInFlatTree search_end = ToPositionInFlatTree(range_->EndPosition()); search_range_ = MakeGarbageCollected<RangeInFlatTree>(search_start, search_end); match_range_ = @@ -436,7 +447,7 @@ } void TextFragmentFinder::Trace(Visitor* visitor) const { - visitor->Trace(document_); + visitor->Trace(range_); visitor->Trace(range_end_search_start_); visitor->Trace(potential_match_); visitor->Trace(prefix_match_);
diff --git a/third_party/blink/renderer/core/fragment_directive/text_fragment_finder.h b/third_party/blink/renderer/core/fragment_directive/text_fragment_finder.h index ca3b48c..7d0970e 100644 --- a/third_party/blink/renderer/core/fragment_directive/text_fragment_finder.h +++ b/third_party/blink/renderer/core/fragment_directive/text_fragment_finder.h
@@ -56,6 +56,10 @@ const TextFragmentSelector& selector, Document* document, FindBufferRunnerType runner_type); + TextFragmentFinder(Client& client, + const TextFragmentSelector& selector, + Range* range, + FindBufferRunnerType runner_type); virtual ~TextFragmentFinder() = default; // Begins searching in the given top-level document. @@ -70,8 +74,6 @@ const TextFragmentSelector& GetSelector() const { return selector_; } protected: - friend class TextFragmentFinderTest; - FRIEND_TEST_ALL_PREFIXES(TextFragmentFinderTest, DOMMutation); void FindPrefix(); void FindTextStart(); void FindTextEnd(); @@ -88,6 +90,10 @@ SelectorMatchStep step_ = kMatchPrefix; private: + friend class MockTextFragmentFinder; + friend class TextFragmentFinderTest; + FRIEND_TEST_ALL_PREFIXES(TextFragmentFinderTest, DOMMutation); + void FindMatchFromPosition(PositionInFlatTree search_start); void OnFindMatchInRangeComplete(String search_text, @@ -117,7 +123,7 @@ Client& client_; const TextFragmentSelector selector_; - Member<Document> document_; + Member<Range> range_; // Start positions for the next text end |FindTask|, this is separate as the // search for end might move the position, which should be discarded.
diff --git a/third_party/blink/renderer/core/fragment_directive/text_fragment_finder_test.cc b/third_party/blink/renderer/core/fragment_directive/text_fragment_finder_test.cc index 9ad7fd2e..e94b2aa 100644 --- a/third_party/blink/renderer/core/fragment_directive/text_fragment_finder_test.cc +++ b/third_party/blink/renderer/core/fragment_directive/text_fragment_finder_test.cc
@@ -16,11 +16,29 @@ MockTextFragmentFinder(Client& client, const TextFragmentSelector& selector, Document* document, - FindBufferRunnerType runner_type) - : TextFragmentFinder(client, selector, document, runner_type) {} + FindBufferRunnerType runner_type, + bool manual_step_through = false) + : TextFragmentFinder(client, selector, document, runner_type) { + manual_step_through_ = manual_step_through; + } + + MockTextFragmentFinder(Client& client, + const TextFragmentSelector& selector, + Range* range, + FindBufferRunnerType runner_type, + bool manual_step_through = false) + : TextFragmentFinder(client, selector, range, runner_type) { + manual_step_through_ = manual_step_through; + } private: - void GoToStep(SelectorMatchStep step) override { step_ = step; } + bool manual_step_through_; + void GoToStep(SelectorMatchStep step) override { + step_ = step; + if (!manual_step_through_) { + TextFragmentFinder::GoToStep(step); + } + } }; class MockTextFragmentFinderClient : public TextFragmentFinder::Client { @@ -40,6 +58,13 @@ } }; +MATCHER_P(RangeContainedBy, element, "") { + return FlatTreeTraversal::Contains( + *element, *arg.StartPosition().ComputeContainerNode()) && + FlatTreeTraversal::Contains(*element, + *arg.EndPosition().ComputeContainerNode()); +} + // Tests that Find tasks will fail gracefully when DOM mutations invalidate the // Find task properties. TEST_F(TextFragmentFinderTest, DOMMutation) { @@ -59,7 +84,8 @@ MockTextFragmentFinder* finder = MakeGarbageCollected<MockTextFragmentFinder>( client, selector, &GetDocument(), - TextFragmentFinder::FindBufferRunnerType::kSynchronous); + TextFragmentFinder::FindBufferRunnerType::kSynchronous, + /*manual_step_through=*/true); EXPECT_CALL(client, DidFindMatch(_, _)).Times(0); { @@ -86,4 +112,358 @@ } } +// Tests that a text match is found in the document. +TEST_F(TextFragmentFinderTest, TextMatchInDocument) { + SimRequest request("https://example.com/test.html", "text/html"); + LoadURL("https://example.com/test.html"); + request.Complete(R"HTML( + <!DOCTYPE html> + <p id='first'>First paragraph start text to unique snippet of text.</p> + )HTML"); + + TextFragmentSelector selector(TextFragmentSelector::SelectorType::kExact, + "First paragraph", "", "", ""); + + MockTextFragmentFinderClient client; + + MockTextFragmentFinder* finder = MakeGarbageCollected<MockTextFragmentFinder>( + client, selector, &GetDocument(), + TextFragmentFinder::FindBufferRunnerType::kSynchronous); + + EXPECT_CALL(client, NoMatchFound()).Times(0); + EXPECT_CALL(client, DidFindMatch(_, true)).Times(1); + finder->FindMatch(); + Mock::VerifyAndClearExpectations(&client); +} + +// Tests that a text match is found in the given range. +TEST_F(TextFragmentFinderTest, TextMatchInRange) { + SimRequest request("https://example.com/test.html", "text/html"); + LoadURL("https://example.com/test.html"); + request.Complete(R"HTML( + <!DOCTYPE html> + <div> + <p id='p1'>First paragraph start text to unique snippet of text.</p> + <p id='p2'>Second paragraph start text to unique snippet of text.</p> + <p id='p3'>Third paragraph start text to unique snippet of text.</p> + </div> + )HTML"); + + Element* const p1 = GetDocument().getElementById(AtomicString("p1")); + Element* const p3 = GetDocument().getElementById(AtomicString("p3")); + + auto* new_range = MakeGarbageCollected<Range>(GetDocument(), Position(p1, 0), + Position(p3, 0)); + EXPECT_EQ( + "First paragraph start text to unique snippet of text.\n\n" + "Second paragraph start text to unique snippet of text.\n\n", + new_range->GetText()); + + TextFragmentSelector selector(TextFragmentSelector::SelectorType::kExact, + "First paragraph start text", + "Second paragraph", "", ""); + MockTextFragmentFinderClient client; + + MockTextFragmentFinder* finder = MakeGarbageCollected<MockTextFragmentFinder>( + client, selector, new_range, + TextFragmentFinder::FindBufferRunnerType::kSynchronous); + + EXPECT_CALL(client, NoMatchFound()).Times(0); + EXPECT_CALL(client, DidFindMatch(_, true)).Times(1); + finder->FindMatch(); + Mock::VerifyAndClearExpectations(&client); +} + +// Tests that a selector whose start term is in the search range but end term +// is not does not match. +TEST_F(TextFragmentFinderTest, TextEndMatchNotInRange) { + SimRequest request("https://example.com/test.html", "text/html"); + LoadURL("https://example.com/test.html"); + request.Complete(R"HTML( + <!DOCTYPE html> + <div> + <p id='p1'>First paragraph start text to unique snippet of text.</p> + <p id='p2'>Second paragraph start text to unique snippet of text.</p> + <p id='p3'>Third paragraph start text to unique snippet of text.</p> + </div> + )HTML"); + + Element* const p2 = GetDocument().getElementById(AtomicString("p2")); + Element* const p3 = GetDocument().getElementById(AtomicString("p3")); + + auto* new_range = MakeGarbageCollected<Range>(GetDocument(), Position(p2, 0), + Position(p3, 0)); + EXPECT_EQ("Second paragraph start text to unique snippet of text.\n\n", + new_range->GetText()); + + TextFragmentSelector selector(TextFragmentSelector::SelectorType::kExact, + "Second paragraph", "Third paragraph", "", ""); + MockTextFragmentFinderClient client; + + MockTextFragmentFinder* finder = MakeGarbageCollected<MockTextFragmentFinder>( + client, selector, new_range, + TextFragmentFinder::FindBufferRunnerType::kSynchronous); + + EXPECT_CALL(client, NoMatchFound()).Times(1); + EXPECT_CALL(client, DidFindMatch(_, _)).Times(0); + finder->FindMatch(); + Mock::VerifyAndClearExpectations(&client); +} + +// Tests that a text match is not found in the given range even though it is in +// the document, but it is before the range. +TEST_F(TextFragmentFinderTest, TextMatchNotFoundBeforeRange) { + SimRequest request("https://example.com/test.html", "text/html"); + LoadURL("https://example.com/test.html"); + request.Complete(R"HTML( + <!DOCTYPE html> + <div> + <p id='p1'>First paragraph start text to unique snippet of text.</p> + <p id='p2'>Second paragraph start text to unique snippet of text.</p> + <p id='p3'>Third paragraph start text to unique snippet of text.</p> + </div> + )HTML"); + + Element* const p2 = GetDocument().getElementById(AtomicString("p2")); + Element* const p3 = GetDocument().getElementById(AtomicString("p3")); + + auto* range = MakeGarbageCollected<Range>(GetDocument(), Position(p2, 0), + Position(p3, 0)); + EXPECT_EQ("Second paragraph start text to unique snippet of text.\n\n", + range->GetText()); + + TextFragmentSelector selector(TextFragmentSelector::SelectorType::kExact, + "First paragraph start text", "", "", ""); + MockTextFragmentFinderClient client; + + MockTextFragmentFinder* finder = MakeGarbageCollected<MockTextFragmentFinder>( + client, selector, range, + TextFragmentFinder::FindBufferRunnerType::kSynchronous); + + EXPECT_CALL(client, NoMatchFound()).Times(1); + EXPECT_CALL(client, DidFindMatch(_, _)).Times(0); + finder->FindMatch(); + Mock::VerifyAndClearExpectations(&client); +} + +// Tests that a text match is not found in the given range even though it is in +// the document, but it is after the range. +TEST_F(TextFragmentFinderTest, TextMatchNotFoundAfterRange) { + SimRequest request("https://example.com/test.html", "text/html"); + LoadURL("https://example.com/test.html"); + request.Complete(R"HTML( + <!DOCTYPE html> + <div> + <p id='p1'>First paragraph start text to unique snippet of text.</p> + <p id='p2'>Second paragraph start text to unique snippet of text.</p> + <p id='p3'>Third paragraph start text to unique snippet of text.</p> + </div> + )HTML"); + + Element* const p2 = GetDocument().getElementById(AtomicString("p2")); + Element* const p3 = GetDocument().getElementById(AtomicString("p3")); + + auto* range = MakeGarbageCollected<Range>(GetDocument(), Position(p2, 0), + Position(p3, 0)); + EXPECT_EQ("Second paragraph start text to unique snippet of text.\n\n", + range->GetText()); + + TextFragmentSelector selector(TextFragmentSelector::SelectorType::kExact, + "Third paragraph start text", "", "", ""); + MockTextFragmentFinderClient client; + + MockTextFragmentFinder* finder = MakeGarbageCollected<MockTextFragmentFinder>( + client, selector, range, + TextFragmentFinder::FindBufferRunnerType::kSynchronous); + + EXPECT_CALL(client, NoMatchFound()).Times(1); + EXPECT_CALL(client, DidFindMatch(_, _)).Times(0); + finder->FindMatch(); + Mock::VerifyAndClearExpectations(&client); +} + +// Tests that when there are multiple text matches in the page, and the first +// match is returned in the given range. +TEST_F(TextFragmentFinderTest, TextDidFindMatchInRange) { + SimRequest request("https://example.com/test.html", "text/html"); + LoadURL("https://example.com/test.html"); + request.Complete(R"HTML( + <!DOCTYPE html> + <div> + <p id='p1'>Paragraph start text 1 to unique snippet of text.</p> + <p id='p2'>Paragraph start text 2 to unique snippet of text.</p> + <p id='p3'>Paragraph start text 3 to unique snippet of text.</p> + </div> + )HTML"); + + Element* const p1 = GetDocument().getElementById(AtomicString("p1")); + Element* const p3 = GetDocument().getElementById(AtomicString("p3")); + + auto* range = MakeGarbageCollected<Range>(GetDocument(), Position(p1, 0), + Position(p3, 0)); + EXPECT_EQ( + "Paragraph start text 1 to unique snippet of text.\n\n" + "Paragraph start text 2 to unique snippet of text.\n\n", + range->GetText()); + + TextFragmentSelector selector(TextFragmentSelector::SelectorType::kExact, + "Paragraph start text", "", "", ""); + MockTextFragmentFinderClient client; + + MockTextFragmentFinder* finder = MakeGarbageCollected<MockTextFragmentFinder>( + client, selector, range, + TextFragmentFinder::FindBufferRunnerType::kSynchronous); + + EXPECT_CALL(client, NoMatchFound()).Times(0); + bool is_unique_expected = false; + EXPECT_CALL(client, DidFindMatch(RangeContainedBy(p1), is_unique_expected)) + .Times(1); + finder->FindMatch(); + Mock::VerifyAndClearExpectations(&client); +} + +// Tests that a text match is found within a defined suffix in the given range. +TEST_F(TextFragmentFinderTest, TextMatchWithSuffixInRange) { + SimRequest request("https://example.com/test.html", "text/html"); + LoadURL("https://example.com/test.html"); + request.Complete(R"HTML( + <!DOCTYPE html> + <div> + <p id='p1'>First paragraph start text to unique snippet of text.</p> + <p id='p2'>Second paragraph start text to unique snippet of text.</p> + <p id='p3'>Third paragraph start text to unique snippet of text.</p> + </div> + )HTML"); + + Element* const p1 = GetDocument().getElementById(AtomicString("p1")); + Element* const p2 = GetDocument().getElementById(AtomicString("p2")); + + auto* new_range = MakeGarbageCollected<Range>(GetDocument(), Position(p1, 0), + Position(p2, 0)); + EXPECT_EQ("First paragraph start text to unique snippet of text.\n\n", + new_range->GetText()); + + TextFragmentSelector selector(TextFragmentSelector::SelectorType::kExact, + "First paragraph", "", "", "start text"); + MockTextFragmentFinderClient client; + + MockTextFragmentFinder* finder = MakeGarbageCollected<MockTextFragmentFinder>( + client, selector, new_range, + TextFragmentFinder::FindBufferRunnerType::kSynchronous); + + EXPECT_CALL(client, NoMatchFound()).Times(0); + EXPECT_CALL(client, DidFindMatch(_, true)).Times(1); + finder->FindMatch(); + Mock::VerifyAndClearExpectations(&client); +} + +// Tests that a text match is not found when the defined prefix is not in the +// given range. +TEST_F(TextFragmentFinderTest, TextMatchWithPrefixNotInRange) { + SimRequest request("https://example.com/test.html", "text/html"); + LoadURL("https://example.com/test.html"); + request.Complete(R"HTML( + <!DOCTYPE html> + <div> + <p id='p1'>First paragraph start text to unique snippet of text.</p> + <p id='p2'>Second paragraph start text to unique snippet of text.</p> + <p id='p3'>Third paragraph start text to unique snippet of text.</p> + </div> + )HTML"); + + Element* const p2 = GetDocument().getElementById(AtomicString("p2")); + Element* const p3 = GetDocument().getElementById(AtomicString("p3")); + + auto* new_range = MakeGarbageCollected<Range>(GetDocument(), Position(p2, 0), + Position(p3, 0)); + EXPECT_EQ("Second paragraph start text to unique snippet of text.\n\n", + new_range->GetText()); + + TextFragmentSelector selector(TextFragmentSelector::SelectorType::kExact, + "Second paragraph", "", "snippet of text", + "start text"); + MockTextFragmentFinderClient client; + + MockTextFragmentFinder* finder = MakeGarbageCollected<MockTextFragmentFinder>( + client, selector, new_range, + TextFragmentFinder::FindBufferRunnerType::kSynchronous); + + EXPECT_CALL(client, NoMatchFound()).Times(1); + EXPECT_CALL(client, DidFindMatch(_, _)).Times(0); + finder->FindMatch(); + Mock::VerifyAndClearExpectations(&client); +} + +// Tests that a text match is not found before the defined surfix which is not +// in the given range. +TEST_F(TextFragmentFinderTest, TextMatchWithSuffixNotInRange) { + SimRequest request("https://example.com/test.html", "text/html"); + LoadURL("https://example.com/test.html"); + request.Complete(R"HTML( + <!DOCTYPE html> + <div> + <p id='p1'>First paragraph start text to unique snippet of text.</p> + <p id='p2'>Second paragraph start text to unique snippet of text.</p> + <p id='p3'>Third paragraph start text to unique snippet of text.</p> + </div> + )HTML"); + + Element* const p1 = GetDocument().getElementById(AtomicString("p1")); + Element* const p2 = GetDocument().getElementById(AtomicString("p2")); + + auto* new_range = MakeGarbageCollected<Range>(GetDocument(), Position(p1, 0), + Position(p2, 0)); + EXPECT_EQ("First paragraph start text to unique snippet of text.\n\n", + new_range->GetText()); + + TextFragmentSelector selector(TextFragmentSelector::SelectorType::kExact, + "snippet of text", "", "", "Second paragraph"); + MockTextFragmentFinderClient client; + + MockTextFragmentFinder* finder = MakeGarbageCollected<MockTextFragmentFinder>( + client, selector, new_range, + TextFragmentFinder::FindBufferRunnerType::kSynchronous); + + EXPECT_CALL(client, NoMatchFound()).Times(1); + EXPECT_CALL(client, DidFindMatch(_, _)).Times(0); + finder->FindMatch(); + Mock::VerifyAndClearExpectations(&client); +} + +// Test that if the range contains partial text of a node, the text should match +// in the range not the full text of the node. +TEST_F(TextFragmentFinderTest, TextMatchPartialNodeNotInRange) { + SimRequest request("https://example.com/test.html", "text/html"); + LoadURL("https://example.com/test.html"); + request.Complete(R"HTML( + <!DOCTYPE html> + <div> + <span id='p1'>First paragraph start text to unique snippet of text 1.</span> + <span id='p2'>Second paragraph start text to unique snippet of text 2.</span> + <span id='p3'>Third paragraph start text to unique snippet of text 3.</span> + </div> + )HTML"); + + Element* const p1 = GetDocument().getElementById(AtomicString("p1")); + Element* const p2 = GetDocument().getElementById(AtomicString("p2")); + + auto* new_range = MakeGarbageCollected<Range>( + GetDocument(), Position(*p1->firstChild(), 6), Position(p2, 0)); + EXPECT_EQ("paragraph start text to unique snippet of text 1. ", + new_range->GetText()); + + TextFragmentSelector selector(TextFragmentSelector::SelectorType::kExact, + "First paragraph", "", "", ""); + MockTextFragmentFinderClient client; + + MockTextFragmentFinder* finder = MakeGarbageCollected<MockTextFragmentFinder>( + client, selector, new_range, + TextFragmentFinder::FindBufferRunnerType::kSynchronous); + + EXPECT_CALL(client, NoMatchFound()).Times(1); + EXPECT_CALL(client, DidFindMatch(_, _)).Times(0); + finder->FindMatch(); + Mock::VerifyAndClearExpectations(&client); +} + } // namespace blink
diff --git a/third_party/blink/renderer/core/html/resources/html.css b/third_party/blink/renderer/core/html/resources/html.css index 0d8dbdf..6138a60e 100644 --- a/third_party/blink/renderer/core/html/resources/html.css +++ b/third_party/blink/renderer/core/html/resources/html.css
@@ -727,13 +727,6 @@ color: ButtonText; } -@supports not blink-feature(ButtonNoAlignItems) { - input[type="button" i], input[type="submit" i], input[type="reset" i], - input[type="file" i]::-webkit-file-upload-button, button { - align-items: flex-start; - } -} - input[type="checkbox" i]:disabled, input[type="file" i]:disabled, input[type="hidden" i]:disabled,
diff --git a/third_party/blink/renderer/core/layout/anchor_evaluator_impl.cc b/third_party/blink/renderer/core/layout/anchor_evaluator_impl.cc index 529209d..85509ba 100644 --- a/third_party/blink/renderer/core/layout/anchor_evaluator_impl.cc +++ b/third_party/blink/renderer/core/layout/anchor_evaluator_impl.cc
@@ -371,7 +371,7 @@ WritingMode self_writing_mode) const { const PhysicalSize& physical_size = reference.rect.size; LogicalSize logical_size = - physical_size.ConvertToLogical(container_writing_mode); + ToLogicalSize(physical_size, container_writing_mode); switch (anchor_size_value) { case CSSAnchorSizeValue::kInline:
diff --git a/third_party/blink/renderer/core/layout/block_node.cc b/third_party/blink/renderer/core/layout/block_node.cc index b070652..9ac6041 100644 --- a/third_party/blink/renderer/core/layout/block_node.cc +++ b/third_party/blink/renderer/core/layout/block_node.cc
@@ -1549,8 +1549,8 @@ const PhysicalNaturalSizingInfo legacy_sizing_info = To<LayoutReplaced>(*box_).ComputeNaturalSizingInfo(); if (!legacy_sizing_info.aspect_ratio.IsEmpty()) { - return legacy_sizing_info.aspect_ratio.ConvertToLogical( - Style().GetWritingMode()); + return ToLogicalSize(legacy_sizing_info.aspect_ratio, + Style().GetWritingMode()); } } @@ -1794,7 +1794,7 @@ BoxStrut margins = ComputePhysicalMargins(constraint_space, Style()) .ConvertToLogical({writing_mode, TextDirection::kLtr}); shape_outside->SetReferenceBoxLogicalSize( - box_size.ConvertToLogical(writing_mode), + ToLogicalSize(box_size, writing_mode), LogicalSize(margins.InlineSum(), margins.BlockSum())); shape_outside->SetPercentageResolutionInlineSize( constraint_space.PercentageResolutionInlineSize());
diff --git a/third_party/blink/renderer/core/layout/column_layout_algorithm.cc b/third_party/blink/renderer/core/layout/column_layout_algorithm.cc index 19838d16f..191dbe6 100644 --- a/third_party/blink/renderer/core/layout/column_layout_algorithm.cc +++ b/third_party/blink/renderer/core/layout/column_layout_algorithm.cc
@@ -434,8 +434,7 @@ WritingMode writing_mode = parent_space.GetWritingMode(); DCHECK(previous_column.IsColumnBox()); const BlockBreakToken* break_token = previous_column.GetBreakToken(); - LogicalSize column_size = - previous_column.Size().ConvertToLogical(writing_mode); + LogicalSize column_size = ToLogicalSize(previous_column.Size(), writing_mode); ConstraintSpace child_space = CreateConstraintSpaceForFragmentainer( parent_space, kFragmentColumn, column_size, /*percentage_resolution_size=*/column_size, /*balance_columns=*/false, @@ -1660,7 +1659,7 @@ for (auto& child : container_builder_.Children()) { if (child.fragment->IsFragmentainerBox()) { LayoutUnit fragmentainer_block_size = - child.fragment->Size().ConvertToLogical(writing_mode).block_size; + ToLogicalSize(child.fragment->Size(), writing_mode).block_size; total_block_size += ClampedToValidFragmentainerCapacity(fragmentainer_block_size); }
diff --git a/third_party/blink/renderer/core/layout/fragment_builder.cc b/third_party/blink/renderer/core/layout/fragment_builder.cc index 2798ae7..19f7804 100644 --- a/third_party/blink/renderer/core/layout/fragment_builder.cc +++ b/third_party/blink/renderer/core/layout/fragment_builder.cc
@@ -211,7 +211,7 @@ // Set the child's `anchor-name` before propagating its descendants', so // that ancestors have precedence over their descendants. LogicalRect logical_rect(child_offset, - child.Size().ConvertToLogical(GetWritingMode())); + ToLogicalSize(child.Size(), GetWritingMode())); const WritingModeConverter converter(GetWritingDirection(), Size()); PhysicalRect rect = converter.ToPhysical(logical_rect); options = AnchorQuerySetOptions(
diff --git a/third_party/blink/renderer/core/layout/fragmentation_utils.cc b/third_party/blink/renderer/core/layout/fragmentation_utils.cc index 619269b8..d94eaa98 100644 --- a/third_party/blink/renderer/core/layout/fragmentation_utils.cc +++ b/third_party/blink/renderer/core/layout/fragmentation_utils.cc
@@ -1587,7 +1587,7 @@ // other kind of monolithic content. WritingMode writing_mode = container_writing_direction.GetWritingMode(); LogicalSize logical_size = - result.GetPhysicalFragment().Size().ConvertToLogical(writing_mode); + ToLogicalSize(result.GetPhysicalFragment().Size(), writing_mode); block_size = logical_size.block_size; // Then remove any block-end trimming, since it shouldn't take up space in
diff --git a/third_party/blink/renderer/core/layout/geometry/physical_size.h b/third_party/blink/renderer/core/layout/geometry/physical_size.h index dfdb0183..a0a08460 100644 --- a/third_party/blink/renderer/core/layout/geometry/physical_size.h +++ b/third_party/blink/renderer/core/layout/geometry/physical_size.h
@@ -43,11 +43,6 @@ LayoutUnit width; LayoutUnit height; - LogicalSize ConvertToLogical(WritingMode mode) const { - return mode == WritingMode::kHorizontalTb ? LogicalSize(width, height) - : LogicalSize(height, width); - } - PhysicalSize operator+(const PhysicalSize& other) const { return PhysicalSize{this->width + other.width, this->height + other.height}; } @@ -140,8 +135,13 @@ CORE_EXPORT std::ostream& operator<<(std::ostream&, const PhysicalSize&); +inline LogicalSize ToLogicalSize(const PhysicalSize& size, WritingMode mode) { + return IsHorizontalWritingMode(mode) ? LogicalSize(size.width, size.height) + : LogicalSize(size.height, size.width); +} + inline PhysicalSize ToPhysicalSize(const LogicalSize& other, WritingMode mode) { - return mode == WritingMode::kHorizontalTb + return IsHorizontalWritingMode(mode) ? PhysicalSize(other.inline_size, other.block_size) : PhysicalSize(other.block_size, other.inline_size); }
diff --git a/third_party/blink/renderer/core/layout/geometry/writing_mode_converter.h b/third_party/blink/renderer/core/layout/geometry/writing_mode_converter.h index 95f153c1..1578cb8 100644 --- a/third_party/blink/renderer/core/layout/geometry/writing_mode_converter.h +++ b/third_party/blink/renderer/core/layout/geometry/writing_mode_converter.h
@@ -111,7 +111,7 @@ inline LogicalSize WritingModeConverter::ToLogical( const PhysicalSize& size) const { - return size.ConvertToLogical(GetWritingMode()); + return ToLogicalSize(size, GetWritingMode()); } inline PhysicalSize WritingModeConverter::ToPhysical(
diff --git a/third_party/blink/renderer/core/layout/inline/fragment_items_builder.cc b/third_party/blink/renderer/core/layout/inline/fragment_items_builder.cc index f5540fa7..1a98e700 100644 --- a/third_party/blink/renderer/core/layout/inline/fragment_items_builder.cc +++ b/third_party/blink/renderer/core/layout/inline/fragment_items_builder.cc
@@ -315,7 +315,7 @@ last_break_token = break_token; container_builder->AddChild(*line_fragment, item_offset); - used_block_size += item.Size().ConvertToLogical(writing_mode).block_size; + used_block_size += ToLogicalSize(item.Size(), writing_mode).block_size; items_.emplace_back(item_offset, item); const PhysicalRect line_box_bounds = item.RectInContainerFragment();
diff --git a/third_party/blink/renderer/core/layout/inline/inline_cursor.cc b/third_party/blink/renderer/core/layout/inline/inline_cursor.cc index 3d7da36..9f10c70 100644 --- a/third_party/blink/renderer/core/layout/inline/inline_cursor.cc +++ b/third_party/blink/renderer/core/layout/inline/inline_cursor.cc
@@ -640,8 +640,7 @@ // Hitting on line bottom doesn't count, to match legacy behavior. const LayoutUnit child_block_end_offset = child_block_offset + - child_item->Size() - .ConvertToLogical(writing_direction.GetWritingMode()) + ToLogicalSize(child_item->Size(), writing_direction.GetWritingMode()) .block_size; if (point_block_offset >= child_block_end_offset) { if (child_block_end_offset > closest_line_after_block_offset) { @@ -765,8 +764,7 @@ } const LayoutUnit child_inline_end_offset = child_inline_offset + - child_item->Size() - .ConvertToLogical(writing_direction.GetWritingMode()) + ToLogicalSize(child_item->Size(), writing_direction.GetWritingMode()) .inline_size; if (point_inline_offset >= child_inline_end_offset) { if (child_item->IsFloating())
diff --git a/third_party/blink/renderer/core/layout/layout_box.cc b/third_party/blink/renderer/core/layout/layout_box.cc index 3d253197..b3f58dcb 100644 --- a/third_party/blink/renderer/core/layout/layout_box.cc +++ b/third_party/blink/renderer/core/layout/layout_box.cc
@@ -299,7 +299,7 @@ PhysicalSize size( WebThemeEngineHelper::GetNativeThemeEngine()->GetSize(part)); size.Scale(style.EffectiveZoom()); - return size.ConvertToLogical(style.GetWritingMode()); + return ToLogicalSize(size, style.GetWritingMode()); } LayoutUnit ListBoxDefaultItemHeight(const LayoutBox& box) {
diff --git a/third_party/blink/renderer/core/layout/layout_box_utils.cc b/third_party/blink/renderer/core/layout/layout_box_utils.cc index 6b79152a..08baf5a 100644 --- a/third_party/blink/renderer/core/layout/layout_box_utils.cc +++ b/third_party/blink/renderer/core/layout/layout_box_utils.cc
@@ -23,9 +23,8 @@ // TODO(almaher): We can't assume all fragments will have the same inline // size. - return box.GetPhysicalFragment(0u) - ->Size() - .ConvertToLogical(box.StyleRef().GetWritingMode()) + return ToLogicalSize(box.GetPhysicalFragment(0u)->Size(), + box.StyleRef().GetWritingMode()) .inline_size; } @@ -38,9 +37,8 @@ LayoutUnit total_block_size; while (num_fragments > 0) { LayoutUnit block_size = - box.GetPhysicalFragment(num_fragments - 1) - ->Size() - .ConvertToLogical(box.StyleRef().GetWritingMode()) + ToLogicalSize(box.GetPhysicalFragment(num_fragments - 1)->Size(), + box.StyleRef().GetWritingMode()) .block_size; if (block_size > LayoutUnit()) { total_block_size += block_size;
diff --git a/third_party/blink/renderer/core/layout/layout_multi_column_set.cc b/third_party/blink/renderer/core/layout/layout_multi_column_set.cc index 3421389..11f21797 100644 --- a/third_party/blink/renderer/core/layout/layout_multi_column_set.cc +++ b/third_party/blink/renderer/core/layout/layout_multi_column_set.cc
@@ -396,7 +396,7 @@ border_padding_scrollbar.VerticalSum()); LogicalSize logical_size; logical_size.inline_size = - content_size.ConvertToLogical(writing_mode).inline_size; + ToLogicalSize(content_size, writing_mode).inline_size; // TODO(layout-dev): Ideally we should not depend on the layout tree structure // because it may be different from the tree for the physical fragments.
diff --git a/third_party/blink/renderer/core/layout/length_utils.cc b/third_party/blink/renderer/core/layout/length_utils.cc index 93ce044..8bbd239 100644 --- a/third_party/blink/renderer/core/layout/length_utils.cc +++ b/third_party/blink/renderer/core/layout/length_utils.cc
@@ -959,7 +959,7 @@ const auto& style = node.Style(); PhysicalSize natural_size(LayoutUnit(300), LayoutUnit(150)); natural_size.Scale(style.EffectiveZoom()); - return natural_size.ConvertToLogical(style.GetWritingMode()); + return ToLogicalSize(natural_size, style.GetWritingMode()); } // This takes the aspect-ratio, and natural-sizes and normalizes them returning @@ -1293,7 +1293,7 @@ PhysicalSize container_size(svg_root->GetContainerSize()); if (!container_size.IsEmpty()) { LogicalSize size = - container_size.ConvertToLogical(node.Style().GetWritingMode()); + ToLogicalSize(container_size, node.Style().GetWritingMode()); size.inline_size += border_padding.InlineSum(); size.block_size += border_padding.BlockSum(); return size; @@ -1672,7 +1672,7 @@ } const auto size = node.InitialContainingBlockSize(); - return {size.ConvertToLogical(style.GetWritingMode()), {}, {}, {}}; + return {ToLogicalSize(size, style.GetWritingMode()), {}, {}, {}}; } const auto border = ComputeBorders(space, node);
diff --git a/third_party/blink/renderer/core/layout/list/unpositioned_list_marker.cc b/third_party/blink/renderer/core/layout/list/unpositioned_list_marker.cc index 4d4327d..f22409f 100644 --- a/third_party/blink/renderer/core/layout/list/unpositioned_list_marker.cc +++ b/third_party/blink/renderer/core/layout/list/unpositioned_list_marker.cc
@@ -134,7 +134,7 @@ // When there are no line boxes, marker is top-aligned to the list item. // https://github.com/w3c/csswg-drafts/issues/2417 LogicalSize marker_size = - marker_physical_fragment.Size().ConvertToLogical(space.GetWritingMode()); + ToLogicalSize(marker_physical_fragment.Size(), space.GetWritingMode()); LogicalOffset offset(InlineOffset(marker_size.inline_size), LayoutUnit()); DCHECK(container_builder);
diff --git a/third_party/blink/renderer/core/layout/logical_fragment.h b/third_party/blink/renderer/core/layout/logical_fragment.h index 9bcb640..ca464f7 100644 --- a/third_party/blink/renderer/core/layout/logical_fragment.h +++ b/third_party/blink/renderer/core/layout/logical_fragment.h
@@ -33,8 +33,8 @@ : physical_fragment_.Size().width; } LogicalSize Size() const { - return physical_fragment_.Size().ConvertToLogical( - writing_direction_.GetWritingMode()); + return ToLogicalSize(physical_fragment_.Size(), + writing_direction_.GetWritingMode()); } WritingDirectionMode GetWritingDirection() const { return writing_direction_;
diff --git a/third_party/blink/renderer/core/layout/out_of_flow_layout_part.cc b/third_party/blink/renderer/core/layout/out_of_flow_layout_part.cc index 152820e..f717556 100644 --- a/third_party/blink/renderer/core/layout/out_of_flow_layout_part.cc +++ b/third_party/blink/renderer/core/layout/out_of_flow_layout_part.cc
@@ -471,7 +471,7 @@ ? PhysicalSize(frame_view->Size()) : PhysicalSize(frame_view->LayoutViewport()->ExcludeScrollbars( frame_view->Size())); - return size.ConvertToLogical(container.Style().GetWritingMode()); + return ToLogicalSize(size, container.Style().GetWritingMode()); } OutOfFlowLayoutPart::OutOfFlowLayoutPart(BoxFragmentBuilder* container_builder) @@ -825,8 +825,8 @@ const auto writing_direction = containing_block->StyleRef().GetWritingDirection(); - LogicalSize size = containing_block_fragment->Size().ConvertToLogical( - writing_direction.GetWritingMode()); + LogicalSize size = ToLogicalSize(containing_block_fragment->Size(), + writing_direction.GetWritingMode()); size.block_size = BoxTotalBlockSize(*To<LayoutBox>(containing_block)); // TODO(1079031): This should eventually include scrollbar and border. @@ -1060,8 +1060,8 @@ container_converter.ToLogical(end_rect.offset, end_rect.size); // Add in the size of the fragment to get the logical end of the fragment. - end_offset += end_rect.size.ConvertToLogical( - container_writing_direction.GetWritingMode()); + end_offset += ToLogicalSize(end_rect.size, + container_writing_direction.GetWritingMode()); // Make sure we subtract the inline borders, we don't need to do this in the // inline direction if the blocks are in opposite directions. @@ -1273,8 +1273,8 @@ multicol_box_fragment->Borders().ConvertToLogical(writing_direction) + multicol_box_fragment->Padding().ConvertToLogical(writing_direction); LayoutUnit available_inline_size = - multicol_box_fragment->Size() - .ConvertToLogical(writing_direction.GetWritingMode()) + ToLogicalSize(multicol_box_fragment->Size(), + writing_direction.GetWritingMode()) .inline_size - border_padding.InlineSum(); column_inline_progression = @@ -1666,9 +1666,8 @@ if (!column_balancing_info_) { fragment = &GetChildFragment(index); fragmentainer_consumed_block_size_ += - fragment->Size() - .ConvertToLogical( - container_builder_->Style().GetWritingMode()) + ToLogicalSize(fragment->Size(), + container_builder_->Style().GetWritingMode()) .block_size; } } @@ -2482,8 +2481,8 @@ offset_info.block_estimate = node_dimensions.size.block_size; offset_info.container_content_size = - container_physical_content_size.ConvertToLogical( - candidate_writing_direction.GetWritingMode()); + ToLogicalSize(container_physical_content_size, + candidate_writing_direction.GetWritingMode()); // Calculate the offsets. const BoxStrut inset = @@ -2633,7 +2632,7 @@ PhysicalSize physical_size = ToPhysicalSize(logical_size, style.GetWritingMode()); LogicalSize available_size = - physical_size.ConvertToLogical(GetConstraintSpace().GetWritingMode()); + ToLogicalSize(physical_size, GetConstraintSpace().GetWritingMode()); bool is_repeatable = false; ConstraintSpaceBuilder builder(GetConstraintSpace(), @@ -2999,7 +2998,7 @@ const WritingMode container_writing_mode = container_builder_->Style().GetWritingMode(); LogicalSize fragmentainer_size = - fragment.Size().ConvertToLogical(container_writing_mode); + ToLogicalSize(fragment.Size(), container_writing_mode); LogicalSize percentage_resolution_size = LogicalSize(fragmentainer_size.inline_size, container_builder_->ChildAvailableSize().block_size); @@ -3054,9 +3053,8 @@ for (; child_index < ChildCount(); child_index++) { const PhysicalBoxFragment& child_fragment = GetChildFragment(child_index); if (child_fragment.IsFragmentainerBox()) { - fragmentainer_block_size = child_fragment.Size() - .ConvertToLogical(default_writing_mode) - .block_size; + fragmentainer_block_size = + ToLogicalSize(child_fragment.Size(), default_writing_mode).block_size; fragmentainer_block_size = ClampedToValidFragmentainerCapacity(fragmentainer_block_size); current_max_block_size += fragmentainer_block_size;
diff --git a/third_party/blink/renderer/core/layout/page_container_layout_algorithm.cc b/third_party/blink/renderer/core/layout/page_container_layout_algorithm.cc index 64ad876..9d4cff2 100644 --- a/third_party/blink/renderer/core/layout/page_container_layout_algorithm.cc +++ b/third_party/blink/renderer/core/layout/page_container_layout_algorithm.cc
@@ -505,8 +505,8 @@ // Need to lay out for block-sizes. main_axis_is_auto = child.Style().LogicalHeight().IsAuto(); const LayoutResult* result = child.Layout(child_space); - LogicalSize size = result->GetPhysicalFragment().Size().ConvertToLogical( - child_space.GetWritingMode()); + LogicalSize size = ToLogicalSize(result->GetPhysicalFragment().Size(), + child_space.GetWritingMode()); minmax.min_size = size.block_size; minmax.max_size = size.block_size; margin_sum = margins.BlockSum(); @@ -528,7 +528,7 @@ } LogicalSize available_logical_size = - available_physical_size.ConvertToLogical(Style().GetWritingMode()); + ToLogicalSize(available_physical_size, Style().GetWritingMode()); std::array<PreferredSizeInfo, 3> preferred_main_axis_sizes; LayoutUnit total_max_size_for_auto; bool has_auto_sized_box = false; @@ -724,7 +724,7 @@ child.Style().GetWritingDirection(), /*is_new_fc=*/true); LogicalSize available_size = - edge_rect.size.ConvertToLogical(Style().GetWritingMode()); + ToLogicalSize(edge_rect.size, Style().GetWritingMode()); bool main_axis_is_inline = IsHorizontal(dir) == Style().IsHorizontalWritingMode(); if (main_axis_is_inline) {
diff --git a/third_party/blink/renderer/core/layout/pagination_utils.cc b/third_party/blink/renderer/core/layout/pagination_utils.cc index a9e1dedf..a439062d 100644 --- a/third_party/blink/renderer/core/layout/pagination_utils.cc +++ b/third_party/blink/renderer/core/layout/pagination_utils.cc
@@ -46,7 +46,7 @@ LogicalSize layout_size) { DCHECK(ShouldCenterPageOnPaper(document.GetFrame()->GetPrintParams())); LogicalSize target_size = - PageBoxDefaultSize(document).ConvertToLogical(style.GetWritingMode()); + ToLogicalSize(PageBoxDefaultSize(document), style.GetWritingMode()); if (layout_size.inline_size != layout_size.block_size && (target_size.inline_size > target_size.block_size) != (layout_size.inline_size > layout_size.block_size)) { @@ -121,7 +121,7 @@ } } - return layout_size.ConvertToLogical(style.GetWritingMode()); + return ToLogicalSize(layout_size, style.GetWritingMode()); } void ResolvePageBoxGeometry(const BlockNode& page_box, @@ -228,7 +228,7 @@ &geometry, &margins); LogicalSize source_size = geometry.border_box_size + margins; LogicalSize target_size = - page_container.Size().ConvertToLogical(style.GetWritingMode()); + ToLogicalSize(page_container.Size(), style.GetWritingMode()); return layout_scale * TargetShrinkScaleFactor(target_size, source_size); }
diff --git a/third_party/blink/renderer/core/layout/physical_box_fragment.cc b/third_party/blink/renderer/core/layout/physical_box_fragment.cc index 3bc10d6..d460a1c4 100644 --- a/third_party/blink/renderer/core/layout/physical_box_fragment.cc +++ b/third_party/blink/renderer/core/layout/physical_box_fragment.cc
@@ -669,7 +669,7 @@ stitched_offset.block_offset = incoming_break_token->ConsumedBlockSize(); LogicalRect logical_fragment_rect( stitched_offset, - Size().ConvertToLogical(writing_direction.GetWritingMode())); + ToLogicalSize(Size(), writing_direction.GetWritingMode())); PhysicalRect physical_fragment_rect = converter.ToPhysical(logical_fragment_rect); @@ -1509,9 +1509,8 @@ bool check_no_fragmentation) const { DCHECK_EQ(layout_object_, other.layout_object_); - LogicalSize size = size_.ConvertToLogical(Style().GetWritingMode()); - LogicalSize other_size = - other.size_.ConvertToLogical(Style().GetWritingMode()); + LogicalSize size = ToLogicalSize(size_, Style().GetWritingMode()); + LogicalSize other_size = ToLogicalSize(other.size_, Style().GetWritingMode()); DCHECK_EQ(size.inline_size, other_size.inline_size); if (check_same_block_size) DCHECK_EQ(size.block_size, other_size.block_size);
diff --git a/third_party/blink/renderer/core/layout/svg/layout_svg_foreign_object.cc b/third_party/blink/renderer/core/layout/svg/layout_svg_foreign_object.cc index 2429fe7..9f49efb 100644 --- a/third_party/blink/renderer/core/layout/svg/layout_svg_foreign_object.cc +++ b/third_party/blink/renderer/core/layout/svg/layout_svg_foreign_object.cc
@@ -121,9 +121,10 @@ // This is necessary for external/wpt/inert/inert-on-non-html.html. // See FullyClipsContents() in fully_clipped_state_stack.cc. const float zoom = style.EffectiveZoom(); - LogicalSize zoomed_size = PhysicalSize(LayoutUnit(viewport_.width() * zoom), - LayoutUnit(viewport_.height() * zoom)) - .ConvertToLogical(style.GetWritingMode()); + LogicalSize zoomed_size = + ToLogicalSize(PhysicalSize(LayoutUnit(viewport_.width() * zoom), + LayoutUnit(viewport_.height() * zoom)), + style.GetWritingMode()); // Use the zoomed version of the viewport as the location, because we will // interpose a transform that "unzooms" the effective zoom to let the children
diff --git a/third_party/blink/renderer/core/loader/document_load_timing.h b/third_party/blink/renderer/core/loader/document_load_timing.h index 02ef4c31..ad707da2 100644 --- a/third_party/blink/renderer/core/loader/document_load_timing.h +++ b/third_party/blink/renderer/core/loader/document_load_timing.h
@@ -34,8 +34,7 @@ #include "third_party/blink/renderer/core/core_export.h" #include "third_party/blink/renderer/platform/heap/garbage_collected.h" #include "third_party/blink/renderer/platform/heap/member.h" -#include "third_party/blink/renderer/platform/instrumentation/tracing/traced_value.h" -#include "third_party/perfetto/include/perfetto/tracing/traced_value_forward.h" +#include "third_party/perfetto/include/perfetto/tracing/traced_value.h" namespace base { class Clock;
diff --git a/third_party/blink/renderer/core/paint/box_fragment_painter.cc b/third_party/blink/renderer/core/paint/box_fragment_painter.cc index 7e63aa9..0f3773c 100644 --- a/third_party/blink/renderer/core/paint/box_fragment_painter.cc +++ b/third_party/blink/renderer/core/paint/box_fragment_painter.cc
@@ -1349,10 +1349,10 @@ BoxSide box_side = BoxSideFromGridDirection(style, track_direction); Color rule_color; - EBorderStyle rule_style; - LayoutUnit rule_thickness; RuleBreak rule_break; Length rule_outset; + GapDataList<EBorderStyle> rule_styles; + GapDataList<int> rule_widths; // TODO(crbug.com/357648037): We are currently only painting gaps with a // single color, but we should update this to paint with all values @@ -1360,21 +1360,22 @@ if (track_direction == kForColumns) { rule_color = LayoutObject::ResolveColor(style, GetCSSPropertyColumnRuleColor()); - rule_style = ComputedStyle::CollapsedBorderStyle( - style.ColumnRuleStyle().GetLegacyValue()); - rule_thickness = LayoutUnit(style.ColumnRuleWidth().GetLegacyValue()); + rule_styles = style.ColumnRuleStyle(); + rule_widths = style.ColumnRuleWidth(); rule_break = style.ColumnRuleBreak(); rule_outset = style.ColumnRuleOutset(); } else { rule_color = LayoutObject::ResolveColor(style, GetCSSPropertyRowRuleColor()); - rule_style = ComputedStyle::CollapsedBorderStyle( - style.RowRuleStyle().GetLegacyValue()); - rule_thickness = LayoutUnit(style.RowRuleWidth().GetLegacyValue()); + rule_styles = style.RowRuleStyle(); + rule_widths = style.RowRuleWidth(); rule_break = style.RowRuleBreak(); rule_outset = style.RowRuleOutset(); } + rule_styles.ExpandValues(); + rule_widths.ExpandValues(); + // Determines if the `end_index` should advance when determining pairs for gap // decorations. For `kSpanningItem` rule break, decorations break only at "T" // intersections, so we simply check that the intersection isn't blocked @@ -1516,6 +1517,10 @@ LayoutUnit decoration_end_offset = LayoutUnit(end_width / 2.0f) - end_outset; + EBorderStyle rule_style = + rule_styles.GetGapDecorationForGapIndex(gap_index, gaps.size()); + LayoutUnit rule_thickness = LayoutUnit( + rule_widths.GetGapDecorationForGapIndex(gap_index, gaps.size())); if (track_direction == kForColumns) { // For columns, paint a vertical strip at the center of the gap. const LayoutUnit center = gap[start].inline_offset;
diff --git a/third_party/blink/renderer/core/paint/fieldset_painter.cc b/third_party/blink/renderer/core/paint/fieldset_painter.cc index d7c8c62b..988adea6 100644 --- a/third_party/blink/renderer/core/paint/fieldset_painter.cc +++ b/third_party/blink/renderer/core/paint/fieldset_painter.cc
@@ -50,12 +50,12 @@ // However we don't need them. const WritingDirectionMode writing_direction = style.GetWritingDirection(); const LogicalSize logical_fieldset_content_size = - (fieldset_size - - PhysicalSize(fieldset_borders.HorizontalSum(), - fieldset_borders.VerticalSum()) - - PhysicalSize(fragment.Padding().HorizontalSum(), - fragment.Padding().VerticalSum())) - .ConvertToLogical(writing_direction.GetWritingMode()); + ToLogicalSize(fieldset_size - + PhysicalSize(fieldset_borders.HorizontalSum(), + fieldset_borders.VerticalSum()) - + PhysicalSize(fragment.Padding().HorizontalSum(), + fragment.Padding().VerticalSum()), + writing_direction.GetWritingMode()); LogicalOffset relative_offset = ComputeRelativeOffset( (*legend)->Style(), writing_direction, logical_fieldset_content_size); LogicalOffset legend_logical_offset =
diff --git a/third_party/blink/renderer/core/style/computed_style.h b/third_party/blink/renderer/core/style/computed_style.h index 635406ed..30b5ff0 100644 --- a/third_party/blink/renderer/core/style/computed_style.h +++ b/third_party/blink/renderer/core/style/computed_style.h
@@ -585,8 +585,7 @@ // column-rule-width GapDataList<int> ColumnRuleWidth() const { - if (ColumnRuleStyle().GetLegacyValue() == EBorderStyle::kNone || - ColumnRuleStyle().GetLegacyValue() == EBorderStyle::kHidden) { + if (!BorderStyleIsVisible(ColumnRuleStyle())) { return GapDataList<int>(0); } return ColumnRuleWidthInternal(); @@ -1000,8 +999,8 @@ Display() != EDisplay::kFlex)) [[likely]] { return false; } - return ColumnRuleWidth().GetLegacyValue() && !ColumnRuleIsTransparent() && - BorderStyleIsVisible(ColumnRuleStyle().GetLegacyValue()); + return HasRuleWidth(ColumnRuleWidth()) && !ColumnRuleIsTransparent() && + BorderStyleIsVisible(ColumnRuleStyle()); } bool RowRuleIsTransparent() const { @@ -1011,8 +1010,8 @@ .IsFullyTransparent(); } bool HasRowRule() const { - return RowRuleWidth().GetLegacyValue() && !RowRuleIsTransparent() && - BorderStyleIsVisible(RowRuleStyle().GetLegacyValue()); + return HasRuleWidth(RowRuleWidth()) && !RowRuleIsTransparent() && + BorderStyleIsVisible(RowRuleStyle()); } bool HasGapRule() const { return HasColumnRule() || HasRowRule(); } @@ -2248,6 +2247,49 @@ static bool BorderStyleIsVisible(EBorderStyle style) { return style != EBorderStyle::kNone && style != EBorderStyle::kHidden; } + + static bool BorderStyleIsVisible(GapDataList<EBorderStyle> styles) { + for (const auto& style : styles.GetGapDataList()) { + if (!style.IsRepeaterData()) { + // Simple single value, check directly. + if (BorderStyleIsVisible(style.GetValue())) { + return true; + } + } else { + // Repeater value, check each repeated value. + for (const auto& repeated_style : + style.GetValueRepeater()->RepeatedValues()) { + if (BorderStyleIsVisible(repeated_style)) { + return true; + } + } + } + } + + return false; + } + + static bool HasRuleWidth(GapDataList<int> widths) { + for (const auto& width : widths.GetGapDataList()) { + if (!width.IsRepeaterData()) { + // Simple single value, check directly. + if (width.GetValue() != 0) { + return true; + } + } else { + // Repeater value, check each repeated value. + for (const auto& repeated_width : + width.GetValueRepeater()->RepeatedValues()) { + if (repeated_width != 0) { + return true; + } + } + } + } + + return false; + } + bool BorderObscuresBackground() const; void GetBorderEdgeInfo( BorderEdgeArray& edges, @@ -2423,7 +2465,7 @@ LogicalSize LogicalAspectRatio() const { DCHECK_NE(AspectRatio().GetType(), EAspectRatioType::kAuto); - return AspectRatio().GetLayoutRatio().ConvertToLogical(GetWritingMode()); + return ToLogicalSize(AspectRatio().GetLayoutRatio(), GetWritingMode()); } EBoxSizing BoxSizingForAspectRatio() const {
diff --git a/third_party/blink/renderer/core/style/gap_data_list.h b/third_party/blink/renderer/core/style/gap_data_list.h index 176dcb5..93635e6 100644 --- a/third_party/blink/renderer/core/style/gap_data_list.h +++ b/third_party/blink/renderer/core/style/gap_data_list.h
@@ -19,6 +19,8 @@ class CORE_EXPORT GapDataList { DISALLOW_NEW(); + using VectorType = ValueRepeater<T>::VectorType; + public: using GapDataVector = HeapVector<GapData<T>, 1>; GapDataList() = default; @@ -45,7 +47,10 @@ gap_data_list_.emplace_back(GapData<T>(value)); } - void Trace(Visitor* visitor) const { visitor->Trace(gap_data_list_); } + void Trace(Visitor* visitor) const { + visitor->Trace(gap_data_list_); + TraceIfNeeded<VectorType>::Trace(visitor, expanded_values_); + } const GapDataVector& GetGapDataList() const { return gap_data_list_; } @@ -57,10 +62,100 @@ } const T GetLegacyValue() const { - CHECK_EQ(gap_data_list_.size(), 1U); return gap_data_list_[0].GetValue(); } + // TODO(samomekarajr): Potential optimization. We might not need to expand + // values. Instead, we can adopt a method similar to the grid's implementation + // by using sets. This way, when given a particular gap index, we can + // translate it to a specific item in the set. This approach allows us to + // avoid expanding values and storing them in a vector, especially when an + // integer repeater has a very large count. For this to be worthwhie, we need + // to be able to get a given decoration value for a gap index in less than + // O(n) time. + + // Expands `gap_data_list_` into `expanded_values_` by evaluating the + // repeaters and storing the values in the order they are specified. + // When an auto repeater is present, it stores the range of the auto + // repeated values as [`auto_repeat_start_`, `auto_repeat_end_`). + void ExpandValues() { + expanded_values_.clear(); + auto_repeat_start_ = kNotFound; + auto_repeat_end_ = kNotFound; + + for (const auto& gap_data : gap_data_list_) { + if (!gap_data.IsRepeaterData()) { + // Simple single value, add to `expanded_values_`. + expanded_values_.push_back(gap_data.GetValue()); + } else { + const ValueRepeater<T>* repeater = gap_data.GetValueRepeater(); + + if (repeater->IsAutoRepeater()) { + // Only one auto repeater is allowed, so store the range of the auto + // repeated values. + CHECK_EQ(auto_repeat_start_, kNotFound); + CHECK_EQ(auto_repeat_end_, kNotFound); + auto_repeat_start_ = expanded_values_.size(); + for (const auto& value : repeater->RepeatedValues()) { + expanded_values_.push_back(value); + } + auto_repeat_end_ = expanded_values_.size(); + } else { + // Integer repeater, add values `count` times. + wtf_size_t count = repeater->RepeatCount(); + + for (size_t i = 0; i < count; ++i) { + for (const auto& value : repeater->RepeatedValues()) { + expanded_values_.push_back(value); + } + } + } + } + } + } + + // Returns the gap decoration value for a given gap index. It uses the + // `auto_repeat_start_` and `auto_repeat_end_` to partition the + // `expanded_values_` into leading, auto repeat, and trailing sections. The + // value at the given index is then determined based on the section it falls + // into. + T GetGapDecorationForGapIndex(wtf_size_t gap_index, + wtf_size_t total_gaps) const { + if (auto_repeat_start_ == kNotFound) { + // No auto repeaters are present, so we need to return the value at the + // valid index. The size of `expanded_values_` might be less than the + // `total_gaps`, so we apply values cyclically. + return expanded_values_[gap_index % expanded_values_.size()]; + } + + // Leading section ends at the start of the auto repeat section. + const wtf_size_t leading_section_end = auto_repeat_start_; + + // Trailing section starts after the auto repeat section so it'll be the + // number of gaps minus the number of trailing values. + const wtf_size_t trailing_section_start = + total_gaps - (expanded_values_.size() - auto_repeat_end_); + + if (gap_index < leading_section_end) { + // Leading values can be indexed directly. + return expanded_values_[gap_index]; + } else if (gap_index >= trailing_section_start) { + // Get the index of the gap relative to the trailing section and return + // expanded value at that index. Note: `trailing_index` indicates the + // index of the gap relative to trailing values, hence we need to add + // `auto_repeat_end_` to get the actual index in the expanded values. + wtf_size_t trailing_index = gap_index - trailing_section_start; + return expanded_values_[auto_repeat_end_ + trailing_index]; + } else { + // For auto repeat values, we get the index of the gap relative to the + // auto section and return the expanded value at that index. + wtf_size_t auto_section_length = auto_repeat_end_ - auto_repeat_start_; + wtf_size_t gap_index_in_auto_section = + (gap_index - leading_section_end) % auto_section_length; + return expanded_values_[auto_repeat_start_ + gap_index_in_auto_section]; + } + } + bool operator==(const GapDataList& o) const { return gap_data_list_ == o.gap_data_list_; } @@ -69,6 +164,16 @@ private: GapDataVector gap_data_list_; + + // Holds the expanded values of `gap_data_list_` in the order they are + // specified. This is used to get the gap decoration value for a given gap + // index. If the `gap_data_list_` contains an auto repeater, the repeated + // values are stored in the order they are specified, with + // `auto_repeat_start_` and `auto_repeat_end_` indicating the range of the + // auto repeated values. + VectorType expanded_values_; + wtf_size_t auto_repeat_start_ = kNotFound; + wtf_size_t auto_repeat_end_ = kNotFound; }; } // namespace blink
diff --git a/third_party/blink/renderer/core/workers/dedicated_worker.cc b/third_party/blink/renderer/core/workers/dedicated_worker.cc index b499db4b..a0c372a 100644 --- a/third_party/blink/renderer/core/workers/dedicated_worker.cc +++ b/third_party/blink/renderer/core/workers/dedicated_worker.cc
@@ -15,10 +15,8 @@ #include "base/trace_event/typed_macros.h" #include "base/unguessable_token.h" #include "mojo/public/cpp/bindings/pending_remote.h" -#include "services/network/public/cpp/cross_origin_embedder_policy.h" #include "services/network/public/mojom/fetch_api.mojom-blink.h" #include "third_party/blink/public/common/blob/blob_utils.h" -#include "third_party/blink/public/common/features.h" #include "third_party/blink/public/mojom/browser_interface_broker.mojom-blink.h" #include "third_party/blink/public/mojom/fetch/fetch_api_request.mojom-blink.h" #include "third_party/blink/public/mojom/script/script_type.mojom-blink.h" @@ -301,50 +299,6 @@ // Continue in OnScriptLoadStarted() or OnScriptLoadStartFailed(). } -void DedicatedWorker::OnHostCreated( - mojo::PendingRemote<network::mojom::blink::URLLoaderFactory> - blob_url_loader_factory, - const network::CrossOriginEmbedderPolicy& parent_coep, - CrossVariantMojoRemote< - mojom::blink::BackForwardCacheControllerHostInterfaceBase> - back_forward_cache_controller_host) { - DCHECK(!base::FeatureList::IsEnabled(features::kPlzDedicatedWorker)); - const RejectCoepUnsafeNone reject_coep_unsafe_none( - network::CompatibleWithCrossOriginIsolated(parent_coep)); - if (options_->type() == script_type_names::kClassic) { - // Legacy code path (to be deprecated, see https://crbug.com/835717): - // A worker thread will start after scripts are fetched on the current - // thread. - classic_script_loader_ = MakeGarbageCollected<WorkerClassicScriptLoader>(); - classic_script_loader_->LoadTopLevelScriptAsynchronously( - *GetExecutionContext(), GetExecutionContext()->Fetcher(), - script_request_url_, nullptr /* worker_main_script_load_params */, - mojom::blink::RequestContextType::WORKER, - network::mojom::RequestDestination::kWorker, - network::mojom::RequestMode::kSameOrigin, - network::mojom::CredentialsMode::kSameOrigin, - WTF::BindOnce(&DedicatedWorker::OnResponse, WrapPersistent(this)), - WTF::BindOnce(&DedicatedWorker::OnFinished, WrapPersistent(this), - std::move(back_forward_cache_controller_host)), - reject_coep_unsafe_none, std::move(blob_url_loader_factory)); - return; - } - if (options_->type() == script_type_names::kModule) { - // Specify empty source code etc. here because scripts will be fetched on - // the worker thread. - ContinueStart(script_request_url_, - nullptr /* worker_main_script_load_params */, - network::mojom::ReferrerPolicy::kDefault, - Vector<network::mojom::blink::ContentSecurityPolicyPtr>(), - String() /* source_code */, reject_coep_unsafe_none, - std::move(back_forward_cache_controller_host), - /*coep_reporting_observer=*/mojo::NullReceiver(), - /*dip_reporting_observer=*/mojo::NullReceiver()); - return; - } - NOTREACHED() << "Invalid type: " << IDLEnumAsString(options_->type()); -} - void DedicatedWorker::terminate() { DCHECK(!GetExecutionContext() || GetExecutionContext()->IsContextThread()); context_proxy_->TerminateGlobalScope(); @@ -390,10 +344,6 @@ coep_reporting_observer, CrossVariantMojoReceiver<mojom::blink::ReportingObserverInterfaceBase> dip_reporting_observer) { - DCHECK(base::FeatureList::IsEnabled(features::kPlzDedicatedWorker)); - TRACE_EVENT_NESTABLE_ASYNC_END0("blink.worker", - "PlzDedicatedWorker Specific Setup", - TRACE_ID_LOCAL(this)); TRACE_EVENT("blink.worker", "DedicatedWorker::OnScriptLoadStarted"); // Specify empty source code here because scripts will be fetched on the // worker thread. @@ -407,10 +357,6 @@ } void DedicatedWorker::OnScriptLoadStartFailed() { - DCHECK(base::FeatureList::IsEnabled(features::kPlzDedicatedWorker)); - TRACE_EVENT_NESTABLE_ASYNC_END0("blink.worker", - "PlzDedicatedWorker Specific Setup", - TRACE_ID_LOCAL(this)); TRACE_EVENT("blink.worker", "DedicatedWorker::OnScriptLoadStartFailed"); // Specify empty source code here because scripts will be fetched on the context_proxy_->DidFailToFetchScript(); @@ -677,13 +623,9 @@ if (auto* window = DynamicTo<LocalDOMWindow>(GetExecutionContext())) { scoped_refptr<WebWorkerFetchContext> web_worker_fetch_context; LocalFrame* frame = window->GetFrame(); - if (base::FeatureList::IsEnabled(features::kPlzDedicatedWorker)) { - web_worker_fetch_context = - frame->Client()->CreateWorkerFetchContextForPlzDedicatedWorker( - factory_client_.get()); - } else { - web_worker_fetch_context = frame->Client()->CreateWorkerFetchContext(); - } + web_worker_fetch_context = + frame->Client()->CreateWorkerFetchContextForPlzDedicatedWorker( + factory_client_.get()); web_worker_fetch_context->SetIsOnSubframe(!frame->IsOutermostMainFrame()); return web_worker_fetch_context; }
diff --git a/third_party/blink/renderer/core/workers/dedicated_worker.h b/third_party/blink/renderer/core/workers/dedicated_worker.h index 080ae5a..db3e50f 100644 --- a/third_party/blink/renderer/core/workers/dedicated_worker.h +++ b/third_party/blink/renderer/core/workers/dedicated_worker.h
@@ -12,7 +12,6 @@ #include "base/memory/scoped_refptr.h" #include "mojo/public/cpp/bindings/pending_remote.h" #include "services/network/public/mojom/content_security_policy.mojom-blink-forward.h" -#include "services/network/public/mojom/url_loader_factory.mojom-blink.h" #include "third_party/blink/public/common/loader/worker_main_script_load_parameters.h" #include "third_party/blink/public/common/tokens/tokens.h" #include "third_party/blink/public/mojom/browser_interface_broker.mojom-blink-forward.h" @@ -194,14 +193,6 @@ // May return nullptr. std::unique_ptr<WebContentSettingsClient> CreateWebContentSettingsClient(); - void OnHostCreated( - mojo::PendingRemote<network::mojom::blink::URLLoaderFactory> - blob_url_loader_factory, - const network::CrossOriginEmbedderPolicy& parent_coep, - CrossVariantMojoRemote< - mojom::blink::BackForwardCacheControllerHostInterfaceBase> - back_forward_cache_controller_host); - // Callbacks for |classic_script_loader_|. void OnResponse(); void OnFinished(
diff --git a/third_party/blink/renderer/core/workers/dedicated_worker_global_scope.cc b/third_party/blink/renderer/core/workers/dedicated_worker_global_scope.cc index f4c5a07..c07697b 100644 --- a/third_party/blink/renderer/core/workers/dedicated_worker_global_scope.cc +++ b/third_party/blink/renderer/core/workers/dedicated_worker_global_scope.cc
@@ -40,7 +40,6 @@ #include "base/trace_event/typed_macros.h" #include "base/types/pass_key.h" #include "net/storage_access_api/status.h" -#include "third_party/blink/public/common/features.h" #include "third_party/blink/public/common/loader/worker_main_script_load_parameters.h" #include "third_party/blink/public/mojom/fetch/fetch_api_request.mojom-blink.h" #include "third_party/blink/public/mojom/frame/back_forward_cache_controller.mojom-blink.h" @@ -302,7 +301,6 @@ const FetchClientSettingsObjectSnapshot& outside_settings_object, WorkerResourceTimingNotifier& outside_resource_timing_notifier, const v8_inspector::V8StackTraceId& stack_id) { - DCHECK(base::FeatureList::IsEnabled(features::kPlzDedicatedWorker)); DCHECK(!IsContextPaused()); TRACE_EVENT("blink.worker", "DedicatedWorkerGlobalScope::FetchAndRunClassicScript", @@ -385,13 +383,9 @@ } bool DedicatedWorkerGlobalScope::IsOffMainThreadScriptFetchDisabled() { - // The top-level dedicated worker script is loaded on the main thread when the - // script type is classic and PlzDedicatedWorker (off-the-main-thread script - // fetch) is disabled. - // TODO(https://crbug.com/835717): Remove this function after dedicated + // TODO(https://crbug.com/835717): Remove this function now that dedicated // workers support off-the-main-thread script fetch by default. - return GetScriptType() == mojom::blink::ScriptType::kClassic && - !base::FeatureList::IsEnabled(features::kPlzDedicatedWorker); + return false; } const String DedicatedWorkerGlobalScope::name() const { @@ -452,7 +446,6 @@ void DedicatedWorkerGlobalScope::DidReceiveResponseForClassicScript( WorkerClassicScriptLoader* classic_script_loader) { DCHECK(IsContextThread()); - DCHECK(base::FeatureList::IsEnabled(features::kPlzDedicatedWorker)); probe::DidReceiveScriptResponse(this, classic_script_loader->Identifier()); } @@ -461,7 +454,6 @@ WorkerClassicScriptLoader* classic_script_loader, const v8_inspector::V8StackTraceId& stack_id) { DCHECK(IsContextThread()); - DCHECK(base::FeatureList::IsEnabled(features::kPlzDedicatedWorker)); TRACE_EVENT("blink.worker", "DedicatedWorkerGlobalScope::DidFetchClassicScript"); TRACE_EVENT_NESTABLE_ASYNC_END0( @@ -545,8 +537,8 @@ // `back_forward_cache_controller_host_` might not be bound when non- // PlzDedicatedWorker is used. Non-PlzDedicatedWorker will be removed in near // future. - // TODO(hajimehoshi): Remove this 'if' branch after non-PlzDedicatedWorker is - // removed. + // TODO(crbug.com/40093136): Remove this 'if' branch now that + // PlzDedicatedWorker has been removed. if (!back_forward_cache_controller_host_.is_bound()) { return; }
diff --git a/third_party/blink/renderer/core/workers/dedicated_worker_global_scope.h b/third_party/blink/renderer/core/workers/dedicated_worker_global_scope.h index 2ecf753b..550ab33 100644 --- a/third_party/blink/renderer/core/workers/dedicated_worker_global_scope.h +++ b/third_party/blink/renderer/core/workers/dedicated_worker_global_scope.h
@@ -229,6 +229,8 @@ bool cross_origin_isolated_capability_; bool is_isolated_context_; Member<WorkerAnimationFrameProvider> animation_frame_provider_; + // TODO(crbug.com/40093136): Remove this now that PlzDedicatedWorker has + // launched. RejectCoepUnsafeNone reject_coep_unsafe_none_ = RejectCoepUnsafeNone(false); HeapMojoRemote<mojom::blink::DedicatedWorkerHost> dedicated_worker_host_{
diff --git a/third_party/blink/renderer/core/workers/dedicated_worker_messaging_proxy.cc b/third_party/blink/renderer/core/workers/dedicated_worker_messaging_proxy.cc index 04e31222..034948b 100644 --- a/third_party/blink/renderer/core/workers/dedicated_worker_messaging_proxy.cc +++ b/third_party/blink/renderer/core/workers/dedicated_worker_messaging_proxy.cc
@@ -5,10 +5,8 @@ #include "third_party/blink/renderer/core/workers/dedicated_worker_messaging_proxy.h" #include <memory> -#include "base/feature_list.h" #include "base/trace_event/typed_macros.h" #include "services/network/public/mojom/fetch_api.mojom-blink.h" -#include "third_party/blink/public/common/features.h" #include "third_party/blink/public/common/tokens/tokens.h" #include "third_party/blink/public/mojom/v8_cache_options.mojom-blink.h" #include "third_party/blink/public/mojom/worker/dedicated_worker_host.mojom-blink-forward.h" @@ -81,6 +79,8 @@ const KURL& script_url, const FetchClientSettingsObjectSnapshot& outside_settings_object, const v8_inspector::V8StackTraceId& stack_id, + // TODO(crbug.com/40093136): Remove this now that PlzDedicatedWorker has + // launched. const String& source_code, RejectCoepUnsafeNone reject_coep_unsafe_none, const blink::DedicatedWorkerToken& token, @@ -112,20 +112,14 @@ // destination, and inside settings." UseCounter::Count(GetExecutionContext(), WebFeature::kClassicDedicatedWorker); - if (base::FeatureList::IsEnabled(features::kPlzDedicatedWorker)) { - auto* resource_timing_notifier = - WorkerResourceTimingNotifierImpl::CreateForOutsideResourceFetcher( - *GetExecutionContext()); - // TODO(crbug.com/1177199): pass a proper policy container - GetWorkerThread()->FetchAndRunClassicScript( - script_url, std::move(worker_main_script_load_params), - /*policy_container=*/nullptr, outside_settings_object.CopyData(), - resource_timing_notifier, stack_id); - } else { - // Legacy code path (to be deprecated, see https://crbug.com/835717): - GetWorkerThread()->EvaluateClassicScript( - script_url, source_code, nullptr /* cached_meta_data */, stack_id); - } + auto* resource_timing_notifier = + WorkerResourceTimingNotifierImpl::CreateForOutsideResourceFetcher( + *GetExecutionContext()); + // TODO(crbug.com/1177199): pass a proper policy container + GetWorkerThread()->FetchAndRunClassicScript( + script_url, std::move(worker_main_script_load_params), + /*policy_container=*/nullptr, outside_settings_object.CopyData(), + resource_timing_notifier, stack_id); } else if (options->type() == script_type_names::kModule) { // "module: Fetch a module worker script graph given url, outside settings, // destination, the value of the credentials member of options, and inside
diff --git a/third_party/blink/renderer/core/workers/dedicated_worker_test.cc b/third_party/blink/renderer/core/workers/dedicated_worker_test.cc index 6666696..3c7217f 100644 --- a/third_party/blink/renderer/core/workers/dedicated_worker_test.cc +++ b/third_party/blink/renderer/core/workers/dedicated_worker_test.cc
@@ -15,7 +15,6 @@ #include "base/test/bind.h" #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" -#include "third_party/blink/public/common/features.h" #include "third_party/blink/public/mojom/v8_cache_options.mojom-blink.h" #include "third_party/blink/public/mojom/worker/dedicated_worker_host.mojom-blink.h" #include "third_party/blink/public/platform/task_type.h" @@ -313,14 +312,13 @@ WorkerBackingThreadStartupData::AtomicsWaitMode::kAllow), WorkerObjectProxy().token()); - if (base::FeatureList::IsEnabled(features::kPlzDedicatedWorker)) { - PostCrossThreadTask( - *GetDedicatedWorkerThread()->GetTaskRunner(TaskType::kInternalTest), - FROM_HERE, - CrossThreadBindOnce( - &DedicatedWorkerThreadForTest::InitializeGlobalScope, - CrossThreadUnretained(GetDedicatedWorkerThread()), script_url_)); - } + PostCrossThreadTask( + *GetDedicatedWorkerThread()->GetTaskRunner(TaskType::kInternalTest), + FROM_HERE, + CrossThreadBindOnce( + &DedicatedWorkerThreadForTest::InitializeGlobalScope, + CrossThreadUnretained(GetDedicatedWorkerThread()), script_url_)); + } void EvaluateClassicScript(const String& source) {
diff --git a/third_party/blink/renderer/core/workers/worker_or_worklet_global_scope.cc b/third_party/blink/renderer/core/workers/worker_or_worklet_global_scope.cc index 0d9c84b..78bcbea 100644 --- a/third_party/blink/renderer/core/workers/worker_or_worklet_global_scope.cc +++ b/third_party/blink/renderer/core/workers/worker_or_worklet_global_scope.cc
@@ -586,13 +586,6 @@ // parser metadata is "not-parser-inserted, ParserDisposition parser_state = kNotParserInserted; - RejectCoepUnsafeNone reject_coep_unsafe_none(false); - if (ShouldRejectCoepUnsafeNoneTopModuleScript() && - destination == network::mojom::RequestDestination::kWorker) { - DCHECK(!base::FeatureList::IsEnabled(features::kPlzDedicatedWorker)); - reject_coep_unsafe_none = RejectCoepUnsafeNone(true); - } - // credentials mode is credentials mode, and referrer policy is the empty // string. // Module worker scripts are fetched with fetchpriority kAuto. @@ -600,7 +593,7 @@ nonce, IntegrityMetadataSet(), integrity_attribute, parser_state, credentials_mode, network::mojom::ReferrerPolicy::kDefault, mojom::blink::FetchPriorityHint::kAuto, - RenderBlockingBehavior::kNonBlocking, reject_coep_unsafe_none); + RenderBlockingBehavior::kNonBlocking); Modulator* modulator = Modulator::From(ScriptController()->GetScriptState()); // Step 3. "Perform the internal module script graph fetching procedure ..."
diff --git a/third_party/blink/renderer/core/workers/worker_or_worklet_global_scope.h b/third_party/blink/renderer/core/workers/worker_or_worklet_global_scope.h index cfe2005..cfa740b 100644 --- a/third_party/blink/renderer/core/workers/worker_or_worklet_global_scope.h +++ b/third_party/blink/renderer/core/workers/worker_or_worklet_global_scope.h
@@ -124,7 +124,8 @@ // Returns true when we should reject a response without // cross-origin-embedder-policy: require-corp. - // TODO(crbug.com/1064920): Remove this once PlzDedicatedWorker ships. + // TODO(crbug.com/1064920): Remove this now that PlzDedicatedWorker has + // shipped. virtual RejectCoepUnsafeNone ShouldRejectCoepUnsafeNoneTopModuleScript() const { return RejectCoepUnsafeNone(false);
diff --git a/third_party/blink/renderer/modules/ai/exception_helpers.cc b/third_party/blink/renderer/modules/ai/exception_helpers.cc index bf4d4c7..ff8ad711 100644 --- a/third_party/blink/renderer/modules/ai/exception_helpers.cc +++ b/third_party/blink/renderer/modules/ai/exception_helpers.cc
@@ -60,6 +60,9 @@ "initialPrompts."; const char kExceptionMessageUnsupportedLanguages[] = "The specified languages are not supported."; +const char kExceptionMessageInvalidResponseJsonSchema[] = + "Response json schema is invalid - it should be an object that can be " + "stringified into a JSON string."; void ThrowInvalidContextException(ExceptionState& exception_state) { exception_state.ThrowDOMException(DOMExceptionCode::kInvalidStateError, @@ -105,6 +108,23 @@ return false; } +String ValidateAndStringifyObject(const ScriptValue& input, + ScriptState* script_state, + ExceptionState& exception_state) { + v8::Local<v8::String> value; + if (!input.V8Value()->IsObject() || + !v8::JSON::Stringify(script_state->GetContext(), + input.V8Value().As<v8::Object>()) + .ToLocal(&value)) { + exception_state.ThrowDOMException( + DOMExceptionCode::kNotSupportedError, + kExceptionMessageInvalidResponseJsonSchema); + return WTF::String(); + } + return ToBlinkString<String>(script_state->GetIsolate(), value, + kDoNotExternalize); +} + namespace { // Create an UnknownError exception, include `error` in the exception // message. This is intended for handling values of
diff --git a/third_party/blink/renderer/modules/ai/exception_helpers.h b/third_party/blink/renderer/modules/ai/exception_helpers.h index b16251f..3363ae2 100644 --- a/third_party/blink/renderer/modules/ai/exception_helpers.h +++ b/third_party/blink/renderer/modules/ai/exception_helpers.h
@@ -27,6 +27,7 @@ extern const char kExceptionMessageSystemPromptIsDefinedMultipleTimes[]; extern const char kExceptionMessageSystemPromptIsNotTheFirst[]; extern const char kExceptionMessageUnsupportedLanguages[]; +extern const char kExceptionMessageInvalidResponseJsonSchema[]; void ThrowInvalidContextException(ExceptionState& exception_state); void ThrowSessionDestroyedException(ExceptionState& exception_state); @@ -49,6 +50,12 @@ ScriptState* script_state, ExceptionState& exception_state); +// Validates and stringifies the responseJSONSchema option if provided. +// Throws an exception if an unsupported schema is detected. +String ValidateAndStringifyObject(const ScriptValue& input, + ScriptState* script_state, + ExceptionState& exception_state); + } // namespace blink #endif // THIRD_PARTY_BLINK_RENDERER_MODULES_AI_EXCEPTION_HELPERS_H_
diff --git a/third_party/blink/renderer/modules/ai/language_model.cc b/third_party/blink/renderer/modules/ai/language_model.cc index e5957af..68bf245 100644 --- a/third_party/blink/renderer/modules/ai/language_model.cc +++ b/third_party/blink/renderer/modules/ai/language_model.cc
@@ -37,6 +37,7 @@ #include "third_party/blink/renderer/modules/webaudio/audio_buffer.h" #include "third_party/blink/renderer/platform/audio/audio_bus.h" #include "third_party/blink/renderer/platform/bindings/script_state.h" +#include "third_party/blink/renderer/platform/bindings/to_blink_string.h" #include "third_party/blink/renderer/platform/blob/blob_data.h" #include "third_party/blink/renderer/platform/heap/garbage_collected.h" #include "third_party/blink/renderer/platform/heap/persistent.h" @@ -476,6 +477,19 @@ return promise; } + String stringified_schema; + if (RuntimeEnabledFeatures::AIPromptAPIStructuredOutputEnabled()) { + if (options->hasResponseJSONSchema()) { + stringified_schema = ValidateAndStringifyObject( + options->responseJSONSchema(), script_state, exception_state); + if (!stringified_schema) { + // ValidateAndStringifyObject throws an exception, which automatically + // rejects the promise, when it returns a null string. + return promise; + } + } + } + auto pending_remote = CreateModelExecutionResponder( script_state, signal, resolver, task_runner_, AIMetrics::AISessionType::kLanguageModel, @@ -484,6 +498,7 @@ WTF::BindRepeating(&LanguageModel::OnQuotaOverflow, WrapWeakPersistent(this))); language_model_remote_->Prompt(std::move(prompts).value(), + std::move(stringified_schema), std::move(pending_remote)); return promise; } @@ -537,6 +552,19 @@ return nullptr; } + String stringified_schema; + if (RuntimeEnabledFeatures::AIPromptAPIStructuredOutputEnabled()) { + if (options->hasResponseJSONSchema()) { + stringified_schema = ValidateAndStringifyObject( + options->responseJSONSchema(), script_state, exception_state); + if (!stringified_schema) { + // ValidateAndStringifyObject throws an exception when it returns + // nullopt. + return nullptr; + } + } + } + auto [readable_stream, pending_remote] = CreateModelExecutionStreamingResponder( script_state, signal, task_runner_, @@ -547,6 +575,7 @@ WrapWeakPersistent(this))); language_model_remote_->Prompt(std::move(prompts).value(), + std::move(stringified_schema), std::move(pending_remote)); return readable_stream; }
diff --git a/third_party/blink/renderer/modules/ai/language_model.idl b/third_party/blink/renderer/modules/ai/language_model.idl index a85be3d5..e6f55677 100644 --- a/third_party/blink/renderer/modules/ai/language_model.idl +++ b/third_party/blink/renderer/modules/ai/language_model.idl
@@ -9,6 +9,7 @@ }; dictionary LanguageModelPromptOptions { + object responseJSONSchema; AbortSignal signal; };
diff --git a/third_party/blink/renderer/modules/canvas/canvas2d/base_rendering_context_2d.cc b/third_party/blink/renderer/modules/canvas/canvas2d/base_rendering_context_2d.cc index 65631e206..834e4b45 100644 --- a/third_party/blink/renderer/modules/canvas/canvas2d/base_rendering_context_2d.cc +++ b/third_party/blink/renderer/modules/canvas/canvas2d/base_rendering_context_2d.cc
@@ -1465,7 +1465,7 @@ // If that did not work (e.g., the canvas host does not yet exist), we can // return the preferred canvas format. - return FromDawnEnum(GPU::preferred_canvas_format()); + return FromDawnEnum(GPU::GetPreferredCanvasFormat()); } GPUTexture* BaseRenderingContext2D::transferToGPUTexture( @@ -1651,7 +1651,7 @@ // If the caller explicitly destroyed the WebGPU access texture, there is // nothing to transfer. - if (webgpu_access_texture_->Destroyed()) { + if (webgpu_access_texture_->IsDestroyed()) { exception_state.ThrowDOMException(DOMExceptionCode::kInvalidStateError, "The texture has been destroyed."); webgpu_access_texture_ = nullptr;
diff --git a/third_party/blink/renderer/modules/credentialmanagement/authentication_credentials_container.cc b/third_party/blink/renderer/modules/credentialmanagement/authentication_credentials_container.cc index 75189b5..05bd35e 100644 --- a/third_party/blink/renderer/modules/credentialmanagement/authentication_credentials_container.cc +++ b/third_party/blink/renderer/modules/credentialmanagement/authentication_credentials_container.cc
@@ -2267,20 +2267,6 @@ mojom::blink::RpMode rp_mode = mojom::blink::RpMode::kPassive; auto v8_rp_mode = identity_options.mode(); - // TODO(crbug.com/372198646): remove the debugging aid enums after shipping - // active mode. - if (v8_rp_mode == - blink::V8IdentityCredentialRequestOptionsMode::Enum::kWidget || - v8_rp_mode == - blink::V8IdentityCredentialRequestOptionsMode::Enum::kButton) { - resolver->GetExecutionContext()->AddConsoleMessage( - MakeGarbageCollected<ConsoleMessage>( - mojom::blink::ConsoleMessageSource::kJavaScript, - mojom::blink::ConsoleMessageLevel::kWarning, - "The mode button/widget are renamed to active/passive " - "respectively and will be deprecated soon.")); - } - rp_mode = mojo::ConvertTo<mojom::blink::RpMode>(v8_rp_mode); if (rp_mode == mojom::blink::RpMode::kActive) { if (identity_provider_ptrs.size() > 1u) {
diff --git a/third_party/blink/renderer/modules/credentialmanagement/credential_manager_type_converters.cc b/third_party/blink/renderer/modules/credentialmanagement/credential_manager_type_converters.cc index dfd1369..fc0e601 100644 --- a/third_party/blink/renderer/modules/credentialmanagement/credential_manager_type_converters.cc +++ b/third_party/blink/renderer/modules/credentialmanagement/credential_manager_type_converters.cc
@@ -1041,10 +1041,6 @@ return RpMode::kPassive; case blink::V8IdentityCredentialRequestOptionsMode::Enum::kActive: return RpMode::kActive; - case blink::V8IdentityCredentialRequestOptionsMode::Enum::kWidget: - return RpMode::kPassive; - case blink::V8IdentityCredentialRequestOptionsMode::Enum::kButton: - return RpMode::kActive; } }
diff --git a/third_party/blink/renderer/modules/credentialmanagement/identity_credential_request_options.idl b/third_party/blink/renderer/modules/credentialmanagement/identity_credential_request_options.idl index 1a61d26..d0942cd 100644 --- a/third_party/blink/renderer/modules/credentialmanagement/identity_credential_request_options.idl +++ b/third_party/blink/renderer/modules/credentialmanagement/identity_credential_request_options.idl
@@ -12,10 +12,7 @@ enum IdentityCredentialRequestOptionsMode { "active", - "passive", - // TODO(crbug.com/372198646): remove the debugging aid enums after shipping active mode. - "button", - "widget" + "passive" }; // https://fedidcg.github.io/FedCM/#dictdef-identitycredentialrequestoptions
diff --git a/third_party/blink/renderer/modules/service_worker/service_worker_global_scope.cc b/third_party/blink/renderer/modules/service_worker/service_worker_global_scope.cc index 311a1b3..3547d82 100644 --- a/third_party/blink/renderer/modules/service_worker/service_worker_global_scope.cc +++ b/third_party/blink/renderer/modules/service_worker/service_worker_global_scope.cc
@@ -48,7 +48,6 @@ #include "services/network/public/mojom/cookie_manager.mojom-blink.h" #include "services/network/public/mojom/cross_origin_embedder_policy.mojom.h" #include "services/network/public/mojom/url_loader_factory.mojom-blink.h" -#include "third_party/blink/public/common/features.h" #include "third_party/blink/public/mojom/fetch/fetch_api_request.mojom-blink.h" #include "third_party/blink/public/mojom/notifications/notification.mojom-blink.h" #include "third_party/blink/public/mojom/push_messaging/push_messaging.mojom-blink.h" @@ -1551,8 +1550,8 @@ // main resource load -> only resultingClientId. // sub resource load -> only clientId. // worker script load -> only clientId. (treated as subresource) - // * PlzDecicatedWorker makes this as main resource load. - // We should fix this. + // * TODO(crbug.com/1064920): PlzDedicatedWorker makes this as main resource + // load. We should fix this. // // Expected behavior: // main resource load -> clientId and resultingClientId. @@ -1572,7 +1571,6 @@ if (is_main_resource_load && params->request->destination == network::mojom::RequestDestination::kWorker) { - CHECK(base::FeatureList::IsEnabled(features::kPlzDedicatedWorker)); is_main_resource_load = false; } event_init->setClientId(is_main_resource_load ? String()
diff --git a/third_party/blink/renderer/modules/webgl/webgl_rendering_context_base.cc b/third_party/blink/renderer/modules/webgl/webgl_rendering_context_base.cc index ee7c32c..c439247 100644 --- a/third_party/blink/renderer/modules/webgl/webgl_rendering_context_base.cc +++ b/third_party/blink/renderer/modules/webgl/webgl_rendering_context_base.cc
@@ -293,7 +293,7 @@ } gfx::Rect WebGLRenderingContextBase::TexImageParams::GetSourceRect( - gfx::Size source_size) { + gfx::Size source_size) const { gfx::Rect rect(unpack_skip_pixels, unpack_skip_rows, width.value_or(source_size.width()), height.value_or(source_size.height())); @@ -318,10 +318,15 @@ } GrSurfaceOrigin -WebGLRenderingContextBase::TexImageParams::GetDestinationOrigin() { +WebGLRenderingContextBase::TexImageParams::GetDestinationOrigin() const { return unpack_flip_y ? kBottomLeft_GrSurfaceOrigin : kTopLeft_GrSurfaceOrigin; } +SkAlphaType WebGLRenderingContextBase::TexImageParams::GetDestinationAlphaType() + const { + return unpack_premultiply_alpha ? kPremul_SkAlphaType : kUnpremul_SkAlphaType; +} + void WebGLRenderingContextBase::InitializeWebGLContextLimits( WebGraphicsContext3DProvider* context_provider) { base::AutoLock locker(WebGLContextLimitLock()); @@ -5350,9 +5355,8 @@ // during readPixels, if needed. If the input is opaque, do not change it // (readPixels fails if the source is opaque and the destination is not). if (converted_info.alphaType() != kOpaque_SkAlphaType) { - converted_info = converted_info.makeAlphaType( - params.unpack_premultiply_alpha ? kPremul_SkAlphaType - : kUnpremul_SkAlphaType); + converted_info = + converted_info.makeAlphaType(params.GetDestinationAlphaType()); } // Set the color type to perform pixel format conversion during readPixels, @@ -5393,11 +5397,11 @@ // the source and the requested premultiplication format. WebGLImageConversion::AlphaOp alpha_op = WebGLImageConversion::kAlphaDoNothing; - if (params.unpack_premultiply_alpha && + if (params.GetDestinationAlphaType() == kPremul_SkAlphaType && pixmap.alphaType() == kUnpremul_SkAlphaType) { alpha_op = WebGLImageConversion::kAlphaDoPremultiply; } - if (!params.unpack_premultiply_alpha && + if (params.GetDestinationAlphaType() == kUnpremul_SkAlphaType && pixmap.alphaType() == kPremul_SkAlphaType) { alpha_op = WebGLImageConversion::kAlphaDoUnmultiply; } @@ -5842,7 +5846,7 @@ } ImageExtractor image_extractor( - image_for_render.get(), params.unpack_premultiply_alpha, + image_for_render.get(), params.GetDestinationAlphaType(), params.unpack_colorspace_conversion ? PredefinedColorSpaceToSkColorSpace(unpack_color_space_) : nullptr); @@ -5984,14 +5988,14 @@ if (source_image) { source_image->CopyToTexture( ContextGL(), params.target, target_texture, params.level, - params.unpack_premultiply_alpha, params.GetDestinationOrigin(), + params.GetDestinationAlphaType(), params.GetDestinationOrigin(), gfx::Point(params.xoffset, params.yoffset), source_sub_rectangle); } else { WebGLRenderingContextBase* gl = source_canvas_webgl_context; ScopedTexture2DRestorer inner_restorer(gl); if (!gl->GetDrawingBuffer()->CopyToPlatformTexture( ContextGL(), params.target, target_texture, params.level, - params.unpack_premultiply_alpha, params.GetDestinationOrigin(), + params.GetDestinationAlphaType(), params.GetDestinationOrigin(), gfx::Point(params.xoffset, params.yoffset), source_sub_rectangle, kBackBuffer)) { NOTREACHED();
diff --git a/third_party/blink/renderer/modules/webgl/webgl_rendering_context_base.h b/third_party/blink/renderer/modules/webgl/webgl_rendering_context_base.h index d2ee763a..9d3e103 100644 --- a/third_party/blink/renderer/modules/webgl/webgl_rendering_context_base.h +++ b/third_party/blink/renderer/modules/webgl/webgl_rendering_context_base.h
@@ -1256,14 +1256,17 @@ // Returns sub rect of the source based on `unpack_*` params above. This // rect is always in top-left coordinate space. - gfx::Rect GetSourceRect(gfx::Size source_size); + gfx::Rect GetSourceRect(gfx::Size source_size) const; // Returns GrSurfaceOrigin of the destination texture for this operation. // Note, that textures don't have persistent orientation (e.g unlike // SharedImages), so this value doesn't affect the data in the texture // before the operation, only operation itself. This is helper because // everything else operates in terms of GrSurfaceOrigin. - GrSurfaceOrigin GetDestinationOrigin(); + GrSurfaceOrigin GetDestinationOrigin() const; + + // Returns desired alpha type of the uploaded data. + SkAlphaType GetDestinationAlphaType() const; // The source's height for 3D copies. If we are doing a 3D copy, then we // interpret the 2D source as 3D by treating it as vertical sequence of
diff --git a/third_party/blink/renderer/modules/webgpu/dawn_object.cc b/third_party/blink/renderer/modules/webgpu/dawn_object.cc index 0879b77a..bdc6a43 100644 --- a/third_party/blink/renderer/modules/webgpu/dawn_object.cc +++ b/third_party/blink/renderer/modules/webgpu/dawn_object.cc
@@ -24,7 +24,7 @@ void DawnObjectBase::setLabel(const String& value) { label_ = value; - setLabelImpl(value); + SetLabelImpl(value); } void DawnObjectBase::EnsureFlush(scheduler::EventLoop& event_loop) {
diff --git a/third_party/blink/renderer/modules/webgpu/dawn_object.h b/third_party/blink/renderer/modules/webgpu/dawn_object.h index 5106afcc..b9fdd478 100644 --- a/third_party/blink/renderer/modules/webgpu/dawn_object.h +++ b/third_party/blink/renderer/modules/webgpu/dawn_object.h
@@ -57,7 +57,7 @@ const String& label() const { return label_; } void setLabel(const String& value); - virtual void setLabelImpl(const String& value) = 0; + virtual void SetLabelImpl(const String& value) = 0; private: scoped_refptr<DawnControlClientHolder> dawn_control_client_;
diff --git a/third_party/blink/renderer/modules/webgpu/gpu.cc b/third_party/blink/renderer/modules/webgpu/gpu.cc index 5a5fcde..16956221 100644 --- a/third_party/blink/renderer/modules/webgpu/gpu.cc +++ b/third_party/blink/renderer/modules/webgpu/gpu.cc
@@ -449,10 +449,10 @@ } V8GPUTextureFormat GPU::getPreferredCanvasFormat() { - return FromDawnEnum(preferred_canvas_format()); + return FromDawnEnum(GetPreferredCanvasFormat()); } -wgpu::TextureFormat GPU::preferred_canvas_format() { +wgpu::TextureFormat GPU::GetPreferredCanvasFormat() { #if BUILDFLAG(IS_ANDROID) return wgpu::TextureFormat::RGBA8Unorm; #else
diff --git a/third_party/blink/renderer/modules/webgpu/gpu.h b/third_party/blink/renderer/modules/webgpu/gpu.h index dbee3cc..e953b76 100644 --- a/third_party/blink/renderer/modules/webgpu/gpu.h +++ b/third_party/blink/renderer/modules/webgpu/gpu.h
@@ -81,14 +81,15 @@ // ExecutionContextLifecycleObserver overrides void ContextDestroyed() override; - // gpu.idl + // gpu.idl {{{ ScriptPromise<IDLNullable<GPUAdapter>> requestAdapter( ScriptState* script_state, const GPURequestAdapterOptions* options); V8GPUTextureFormat getPreferredCanvasFormat(); WGSLLanguageFeatures* wgslLanguageFeatures() const; + // }}} End of WebIDL binding implementation. - static wgpu::TextureFormat preferred_canvas_format(); + static wgpu::TextureFormat GetPreferredCanvasFormat(); // Store the buffer in a weak hash set so we can destroy it when the // context is destroyed. @@ -97,7 +98,7 @@ // destroyed. void UntrackMappableBuffer(GPUBuffer* buffer); - BoxedMappableWGPUBufferHandles* mappable_buffer_handles() const { + BoxedMappableWGPUBufferHandles* GetMappableBufferHandles() const { return mappable_buffer_handles_.get(); }
diff --git a/third_party/blink/renderer/modules/webgpu/gpu_adapter.cc b/third_party/blink/renderer/modules/webgpu/gpu_adapter.cc index 0e5a9862..b0b2ff1 100644 --- a/third_party/blink/renderer/modules/webgpu/gpu_adapter.cc +++ b/third_party/blink/renderer/modules/webgpu/gpu_adapter.cc
@@ -283,7 +283,7 @@ HashSet<wgpu::FeatureName> required_features_set; for (const V8GPUFeatureName& f : descriptor->requiredFeatures()) { // If the feature is not a valid feature reject with a type error. - if (!features_->has(f.AsEnum())) { + if (!features_->Has(f.AsEnum())) { resolver->RejectWithTypeError( String::Format("Unsupported feature: %s", f.AsCStr())); return promise;
diff --git a/third_party/blink/renderer/modules/webgpu/gpu_adapter.h b/third_party/blink/renderer/modules/webgpu/gpu_adapter.h index 21fe98f..aaf3570 100644 --- a/third_party/blink/renderer/modules/webgpu/gpu_adapter.h +++ b/third_party/blink/renderer/modules/webgpu/gpu_adapter.h
@@ -59,7 +59,7 @@ GPUAdapterInfo* CreateAdapterInfoForAdapter(); - bool isXRCompatible() const { return is_xr_compatible_; } + bool IsXRCompatible() const { return is_xr_compatible_; } private: void OnRequestDeviceCallback(GPUDevice* device, @@ -69,7 +69,7 @@ wgpu::Device dawn_device, wgpu::StringView error_message); - void setLabelImpl(const String&) override { + void SetLabelImpl(const String&) override { // There isn't a wgpu::Adapter::SetLabel, just skip. }
diff --git a/third_party/blink/renderer/modules/webgpu/gpu_adapter_info.h b/third_party/blink/renderer/modules/webgpu/gpu_adapter_info.h index 78ebedce..2d76a57 100644 --- a/third_party/blink/renderer/modules/webgpu/gpu_adapter_info.h +++ b/third_party/blink/renderer/modules/webgpu/gpu_adapter_info.h
@@ -38,7 +38,7 @@ void AppendMemoryHeapInfo(GPUMemoryHeapInfo*); - // gpu_adapter_info.idl + // gpu_adapter_info.idl {{{ const String& vendor() const; const String& architecture() const; const String& device() const; @@ -52,6 +52,7 @@ const HeapVector<Member<GPUMemoryHeapInfo>>& memoryHeaps() const; const std::optional<uint32_t>& d3dShaderModel() const; const std::optional<uint32_t>& vkDriverVersion() const; + // }}} End of WebIDL binding implementation. void Trace(Visitor*) const override;
diff --git a/third_party/blink/renderer/modules/webgpu/gpu_bind_group.h b/third_party/blink/renderer/modules/webgpu/gpu_bind_group.h index 3de0b6c2..bef615b 100644 --- a/third_party/blink/renderer/modules/webgpu/gpu_bind_group.h +++ b/third_party/blink/renderer/modules/webgpu/gpu_bind_group.h
@@ -26,7 +26,7 @@ GPUBindGroup(const GPUBindGroup&) = delete; GPUBindGroup& operator=(const GPUBindGroup&) = delete; - void setLabelImpl(const String& value) override { + void SetLabelImpl(const String& value) override { std::string utf8_label = value.Utf8(); GetHandle().SetLabel(utf8_label.c_str()); }
diff --git a/third_party/blink/renderer/modules/webgpu/gpu_bind_group_layout.h b/third_party/blink/renderer/modules/webgpu/gpu_bind_group_layout.h index 641683a6..b260a71 100644 --- a/third_party/blink/renderer/modules/webgpu/gpu_bind_group_layout.h +++ b/third_party/blink/renderer/modules/webgpu/gpu_bind_group_layout.h
@@ -27,7 +27,7 @@ GPUBindGroupLayout(const GPUBindGroupLayout&) = delete; GPUBindGroupLayout& operator=(const GPUBindGroupLayout&) = delete; - void setLabelImpl(const String& value) override { + void SetLabelImpl(const String& value) override { std::string utf8_label = value.Utf8(); GetHandle().SetLabel(utf8_label.c_str()); }
diff --git a/third_party/blink/renderer/modules/webgpu/gpu_buffer.cc b/third_party/blink/renderer/modules/webgpu/gpu_buffer.cc index efca1fa..a3d1ef2 100644 --- a/third_party/blink/renderer/modules/webgpu/gpu_buffer.cc +++ b/third_party/blink/renderer/modules/webgpu/gpu_buffer.cc
@@ -159,7 +159,7 @@ GPU* gpu = device->adapter()->gpu(); gpu->TrackMappableBuffer(buffer); device->TrackMappableBuffer(buffer); - buffer->mappable_buffer_handles_ = gpu->mappable_buffer_handles(); + buffer->mappable_buffer_handles_ = gpu->GetMappableBufferHandles(); } return buffer;
diff --git a/third_party/blink/renderer/modules/webgpu/gpu_buffer.h b/third_party/blink/renderer/modules/webgpu/gpu_buffer.h index 374dfed..7766688 100644 --- a/third_party/blink/renderer/modules/webgpu/gpu_buffer.h +++ b/third_party/blink/renderer/modules/webgpu/gpu_buffer.h
@@ -40,7 +40,7 @@ void Trace(Visitor* visitor) const override; - // gpu_buffer.idl + // gpu_buffer.idl {{{ ScriptPromise<IDLUndefined> mapAsync(ScriptState* script_state, uint32_t mode, uint64_t offset, @@ -62,6 +62,7 @@ uint64_t size() const; uint32_t usage() const; V8GPUBufferMapState mapState() const; + // }}} End of WebIDL binding implementation. void DetachMappedArrayBuffers(v8::Isolate* isolate); @@ -85,7 +86,7 @@ size_t data_length); void ResetMappingState(v8::Isolate* isolate); - void setLabelImpl(const String& value) override { + void SetLabelImpl(const String& value) override { std::string utf8_label = value.Utf8(); GetHandle().SetLabel(utf8_label.c_str()); }
diff --git a/third_party/blink/renderer/modules/webgpu/gpu_buffer_usage.h b/third_party/blink/renderer/modules/webgpu/gpu_buffer_usage.h index 67d1dec..8be1d50d 100644 --- a/third_party/blink/renderer/modules/webgpu/gpu_buffer_usage.h +++ b/third_party/blink/renderer/modules/webgpu/gpu_buffer_usage.h
@@ -14,19 +14,6 @@ DEFINE_WRAPPERTYPEINFO(); public: - // gpu_buffer_usage.idl - static constexpr uint32_t kMapRead = V8GPUBufferUsage::Constant::kMapRead; - static constexpr uint32_t kMapWrite = V8GPUBufferUsage::Constant::kMapWrite; - static constexpr uint32_t kCopySrc = V8GPUBufferUsage::Constant::kCopySrc; - static constexpr uint32_t kCopyDst = V8GPUBufferUsage::Constant::kCopyDst; - static constexpr uint32_t kIndex = V8GPUBufferUsage::Constant::kIndex; - static constexpr uint32_t kVertex = V8GPUBufferUsage::Constant::kVertex; - static constexpr uint32_t kUniform = V8GPUBufferUsage::Constant::kUniform; - static constexpr uint32_t kStorage = V8GPUBufferUsage::Constant::kStorage; - static constexpr uint32_t kIndirect = V8GPUBufferUsage::Constant::kIndirect; - static constexpr uint32_t kQueryResolve = - V8GPUBufferUsage::Constant::kQueryResolve; - GPUBufferUsage(const GPUBufferUsage&) = delete; GPUBufferUsage& operator=(const GPUBufferUsage&) = delete; };
diff --git a/third_party/blink/renderer/modules/webgpu/gpu_canvas_context.cc b/third_party/blink/renderer/modules/webgpu/gpu_canvas_context.cc index 76ff610..2cf7fac 100644 --- a/third_party/blink/renderer/modules/webgpu/gpu_canvas_context.cc +++ b/third_party/blink/renderer/modules/webgpu/gpu_canvas_context.cc
@@ -167,12 +167,12 @@ return nullptr; } - if (device_->destroyed()) { + if (device_->IsDestroyed()) { return MakeFallbackStaticBitmapImage(alpha_mode_); } // If there is a current texture, create a snapshot from it. - if (texture_ && !texture_->Destroyed()) { + if (texture_ && !texture_->IsDestroyed()) { return SnapshotInternal(texture_->GetHandle(), swap_buffers_->Size()); } else if (swap_texture_) { return SnapshotInternal(swap_texture_->GetHandle(), swap_buffers_->Size()); @@ -208,7 +208,7 @@ return false; } - if (device_->destroyed()) { + if (device_->IsDestroyed()) { SkColor4f color = alpha_mode_ == V8GPUCanvasAlphaMode::Enum::kOpaque ? SkColors::kBlack : SkColors::kTransparent; @@ -336,7 +336,6 @@ std::move(release_callback))); } -// gpu_presentation_context.idl V8UnionHTMLCanvasElementOrOffscreenCanvas* GPUCanvasContext::getHTMLOrOffscreenCanvas() const { if (Host()->IsOffscreenCanvas()) { @@ -442,9 +441,9 @@ // about not using the preferred format. suppress_preferred_format_warning_ = false; if (copy_to_swap_texture_required_ && - GPU::preferred_canvas_format() == wgpu::TextureFormat::BGRA8Unorm && + GPU::GetPreferredCanvasFormat() == wgpu::TextureFormat::BGRA8Unorm && texture_descriptor_.usage & wgpu::TextureUsage::StorageBinding && - !device_->adapter()->features()->has( + !device_->adapter()->features()->Has( V8GPUFeatureName::Enum::kBgra8UnormStorage)) { suppress_preferred_format_warning_ = true; } @@ -508,7 +507,7 @@ // In cases where a copy is necessary the swap buffers will always use the // preferred canvas format. - swap_texture_descriptor_.format = GPU::preferred_canvas_format(); + swap_texture_descriptor_.format = GPU::GetPreferredCanvasFormat(); // The swap buffer texture doesn't need any view formats. swap_texture_descriptor_.viewFormats = nullptr; @@ -758,7 +757,7 @@ DCHECK(texture_); DCHECK(swap_texture_); - if (copy_to_swap_texture_required_ && texture_ && !texture_->Destroyed()) { + if (copy_to_swap_texture_required_ && texture_ && !texture_->IsDestroyed()) { CopyToSwapTexture(); texture_->ClearBeforeDestroyCallback(); texture_->destroy(); @@ -780,7 +779,7 @@ } bool GPUCanvasContext::IsGPUDeviceDestroyed() { - return device_->destroyed(); + return device_->IsDestroyed(); } void GPUCanvasContext::CopyToSwapTexture() {
diff --git a/third_party/blink/renderer/modules/webgpu/gpu_canvas_context.h b/third_party/blink/renderer/modules/webgpu/gpu_canvas_context.h index 2656ada..37cae74 100644 --- a/third_party/blink/renderer/modules/webgpu/gpu_canvas_context.h +++ b/third_party/blink/renderer/modules/webgpu/gpu_canvas_context.h
@@ -98,13 +98,13 @@ return false; } - // gpu_presentation_context.idl + // gpu_canvas_context.idl {{{ V8UnionHTMLCanvasElementOrOffscreenCanvas* getHTMLOrOffscreenCanvas() const; - void configure(const GPUCanvasConfiguration* descriptor, ExceptionState&); void unconfigure(); GPUCanvasConfiguration* getConfiguration(); GPUTexture* getCurrentTexture(ScriptState*, ExceptionState&); + // }}} End of WebIDL binding implementation. // WebGPUSwapBufferProvider::Client implementation void OnTextureTransferred() override;
diff --git a/third_party/blink/renderer/modules/webgpu/gpu_color_write.h b/third_party/blink/renderer/modules/webgpu/gpu_color_write.h index 43d2f9e3..33ae70f1 100644 --- a/third_party/blink/renderer/modules/webgpu/gpu_color_write.h +++ b/third_party/blink/renderer/modules/webgpu/gpu_color_write.h
@@ -14,13 +14,6 @@ DEFINE_WRAPPERTYPEINFO(); public: - // gpu_color_write.idl - static constexpr uint32_t kRed = V8GPUColorWrite::Constant::kRed; - static constexpr uint32_t kGreen = V8GPUColorWrite::Constant::kGreen; - static constexpr uint32_t kBlue = V8GPUColorWrite::Constant::kBlue; - static constexpr uint32_t kAlpha = V8GPUColorWrite::Constant::kAlpha; - static constexpr uint32_t kAll = V8GPUColorWrite::Constant::kAll; - GPUColorWrite(const GPUColorWrite&) = delete; GPUColorWrite& operator=(const GPUColorWrite&) = delete; };
diff --git a/third_party/blink/renderer/modules/webgpu/gpu_command_buffer.h b/third_party/blink/renderer/modules/webgpu/gpu_command_buffer.h index a12d0237..bc6c704 100644 --- a/third_party/blink/renderer/modules/webgpu/gpu_command_buffer.h +++ b/third_party/blink/renderer/modules/webgpu/gpu_command_buffer.h
@@ -21,7 +21,7 @@ GPUCommandBuffer& operator=(const GPUCommandBuffer&) = delete; private: - void setLabelImpl(const String& value) override { + void SetLabelImpl(const String& value) override { std::string utf8_label = value.Utf8(); GetHandle().SetLabel(utf8_label.c_str()); }
diff --git a/third_party/blink/renderer/modules/webgpu/gpu_command_encoder.cc b/third_party/blink/renderer/modules/webgpu/gpu_command_encoder.cc index 06be734..55452b3 100644 --- a/third_party/blink/renderer/modules/webgpu/gpu_command_encoder.cc +++ b/third_party/blink/renderer/modules/webgpu/gpu_command_encoder.cc
@@ -396,12 +396,12 @@ ExceptionState& exception_state) { V8GPUFeatureName::Enum requiredFeatureEnum = V8GPUFeatureName::Enum::kTimestampQuery; - if (!device_->features()->has(requiredFeatureEnum)) { + if (!device_->features()->Has(requiredFeatureEnum)) { exception_state.ThrowTypeError( String::Format("Use of the writeTimestamp() method requires the '%s' " "feature to be enabled on %s.", V8GPUFeatureName(requiredFeatureEnum).AsCStr(), - device_->formattedLabel().c_str())); + device_->GetFormattedLabel().c_str())); return; } GetHandle().WriteTimestamp(querySet->GetHandle(), queryIndex);
diff --git a/third_party/blink/renderer/modules/webgpu/gpu_command_encoder.h b/third_party/blink/renderer/modules/webgpu/gpu_command_encoder.h index 71ad9bc..5a52373 100644 --- a/third_party/blink/renderer/modules/webgpu/gpu_command_encoder.h +++ b/third_party/blink/renderer/modules/webgpu/gpu_command_encoder.h
@@ -37,7 +37,7 @@ GPUCommandEncoder(const GPUCommandEncoder&) = delete; GPUCommandEncoder& operator=(const GPUCommandEncoder&) = delete; - // gpu_command_encoder.idl + // gpu_command_encoder.idl {{{ GPURenderPassEncoder* beginRenderPass( const GPURenderPassDescriptor* descriptor, ExceptionState& exception_state); @@ -134,8 +134,9 @@ GetHandle().ClearBuffer(buffer->GetHandle(), offset, size); } GPUCommandBuffer* finish(const GPUCommandBufferDescriptor* descriptor); + // }}} End of WebIDL binding implementation. - void setLabelImpl(const String& value) override { + void SetLabelImpl(const String& value) override { std::string utf8_label = value.Utf8(); GetHandle().SetLabel(utf8_label.c_str()); }
diff --git a/third_party/blink/renderer/modules/webgpu/gpu_compute_pass_encoder.cc b/third_party/blink/renderer/modules/webgpu/gpu_compute_pass_encoder.cc index 5afccf10..54ae405 100644 --- a/third_party/blink/renderer/modules/webgpu/gpu_compute_pass_encoder.cc +++ b/third_party/blink/renderer/modules/webgpu/gpu_compute_pass_encoder.cc
@@ -58,12 +58,12 @@ ExceptionState& exception_state) { V8GPUFeatureName::Enum requiredFeatureEnum = V8GPUFeatureName::Enum::kChromiumExperimentalTimestampQueryInsidePasses; - if (!device_->features()->has(requiredFeatureEnum)) { + if (!device_->features()->Has(requiredFeatureEnum)) { exception_state.ThrowTypeError(String::Format( "Use of the writeTimestamp() method on compute pass requires the '%s' " "feature to be enabled on %s.", V8GPUFeatureName(requiredFeatureEnum).AsCStr(), - device_->formattedLabel().c_str())); + device_->GetFormattedLabel().c_str())); return; } GetHandle().WriteTimestamp(querySet->GetHandle(), queryIndex);
diff --git a/third_party/blink/renderer/modules/webgpu/gpu_compute_pass_encoder.h b/third_party/blink/renderer/modules/webgpu/gpu_compute_pass_encoder.h index d41a99b..5dfdf89 100644 --- a/third_party/blink/renderer/modules/webgpu/gpu_compute_pass_encoder.h +++ b/third_party/blink/renderer/modules/webgpu/gpu_compute_pass_encoder.h
@@ -25,7 +25,7 @@ GPUComputePassEncoder(const GPUComputePassEncoder&) = delete; GPUComputePassEncoder& operator=(const GPUComputePassEncoder&) = delete; - // gpu_compute_pass_encoder.idl + // gpu_compute_pass_encoder.idl {{{ void setBindGroup(uint32_t index, const DawnObject<wgpu::BindGroup>* bindGroup) { GetHandle().SetBindGroup( @@ -69,8 +69,9 @@ uint32_t queryIndex, ExceptionState& exception_state); void end() { GetHandle().End(); } + // }}} End of WebIDL binding implementation. - void setLabelImpl(const String& value) override { + void SetLabelImpl(const String& value) override { std::string utf8_label = value.Utf8(); GetHandle().SetLabel(utf8_label.c_str()); }
diff --git a/third_party/blink/renderer/modules/webgpu/gpu_compute_pipeline.h b/third_party/blink/renderer/modules/webgpu/gpu_compute_pipeline.h index 7b361b1f..354b25f2 100644 --- a/third_party/blink/renderer/modules/webgpu/gpu_compute_pipeline.h +++ b/third_party/blink/renderer/modules/webgpu/gpu_compute_pipeline.h
@@ -36,7 +36,7 @@ GPUBindGroupLayout* getBindGroupLayout(uint32_t index); - void setLabelImpl(const String& value) override { + void SetLabelImpl(const String& value) override { std::string utf8_label = value.Utf8(); GetHandle().SetLabel(utf8_label.c_str()); }
diff --git a/third_party/blink/renderer/modules/webgpu/gpu_device.cc b/third_party/blink/renderer/modules/webgpu/gpu_device.cc index fff0854..32a55cc 100644 --- a/third_party/blink/renderer/modules/webgpu/gpu_device.cc +++ b/third_party/blink/renderer/modules/webgpu/gpu_device.cc
@@ -196,6 +196,17 @@ } } +bool GPUDevice::IsDestroyed() const { + return destroyed_; +} + +std::string GPUDevice::GetFormattedLabel() const { + std::string deviceLabel = + label().empty() ? "[Device]" : "[Device \"" + label().Utf8() + "\"]"; + + return deviceLabel; +} + void GPUDevice::InjectError(wgpu::ErrorType type, const char* message) { GetHandle().InjectError(type, message); } @@ -203,9 +214,11 @@ void GPUDevice::AddConsoleWarning(wgpu::StringView message) { AddConsoleWarning(StringFromASCIIAndUTF8(message)); } + void GPUDevice::AddConsoleWarning(const char* message) { AddConsoleWarning(StringFromASCIIAndUTF8(message)); } + void GPUDevice::AddConsoleWarning(const String& message) { ExecutionContext* execution_context = GetExecutionContext(); if (execution_context && allowed_console_warnings_remaining_ > 0) { @@ -237,7 +250,7 @@ message = "WebGPU canvas configured with a different format than is " "preferred by this device (\"" + - FromDawnEnum(GPU::preferred_canvas_format()).AsString() + + FromDawnEnum(GPU::GetPreferredCanvasFormat()).AsString() + "\"). This requires an extra copy, which may impact performance."; break; case GPUSingletonWarning::kDepthKey: @@ -274,7 +287,7 @@ V8GPUFeatureName::Enum requiredFeatureEnum = requiredFeatureOptional.value(); - if (features_->has(requiredFeatureEnum)) { + if (features_->Has(requiredFeatureEnum)) { return true; } @@ -283,17 +296,10 @@ exception_state.ThrowTypeError(String::Format( "Use of the '%s' texture format requires the '%s' feature " "to be enabled on %s.", - format.AsCStr(), requiredFeature.AsCStr(), formattedLabel().c_str())); + format.AsCStr(), requiredFeature.AsCStr(), GetFormattedLabel().c_str())); return false; } -std::string GPUDevice::formattedLabel() const { - std::string deviceLabel = - label().empty() ? "[Device]" : "[Device \"" + label().Utf8() + "\"]"; - - return deviceLabel; -} - // Validates that any features required for the given blend factor are enabled // for this device. If not, throw a TypeError to ensure consistency with // browsers that haven't yet implemented the feature. @@ -308,7 +314,7 @@ V8GPUFeatureName::Enum requiredFeatureEnum = requiredFeatureOptional.value(); - if (features_->has(requiredFeatureEnum)) { + if (features_->Has(requiredFeatureEnum)) { return true; } @@ -318,7 +324,7 @@ String::Format("Use of the '%s' blend factor requires the '%s' feature " "to be enabled on %s.", blend_factor.AsCStr(), requiredFeature.AsCStr(), - formattedLabel().c_str())); + GetFormattedLabel().c_str())); return false; } @@ -496,10 +502,6 @@ return queue_.Get(); } -bool GPUDevice::destroyed() const { - return destroyed_; -} - void GPUDevice::destroy(v8::Isolate* isolate) { destroyed_ = true; external_texture_cache_->Destroy(); @@ -638,14 +640,14 @@ const V8GPUFeatureName::Enum kTimestampQueryInsidePasses = V8GPUFeatureName::Enum::kChromiumExperimentalTimestampQueryInsidePasses; if (descriptor->type() == V8GPUQueryType::Enum::kTimestamp && - !features_->has(kTimestampQuery) && - !features_->has(kTimestampQueryInsidePasses)) { + !features_->Has(kTimestampQuery) && + !features_->Has(kTimestampQueryInsidePasses)) { exception_state.ThrowTypeError( String::Format("Use of timestamp queries requires the '%s' or '%s' " "feature to be enabled on %s.", V8GPUFeatureName(kTimestampQuery).AsCStr(), V8GPUFeatureName(kTimestampQueryInsidePasses).AsCStr(), - formattedLabel().c_str())); + GetFormattedLabel().c_str())); return nullptr; } return GPUQuerySet::Create(this, descriptor);
diff --git a/third_party/blink/renderer/modules/webgpu/gpu_device.h b/third_party/blink/renderer/modules/webgpu/gpu_device.h index 3931c28..c88db32 100644 --- a/third_party/blink/renderer/modules/webgpu/gpu_device.h +++ b/third_party/blink/renderer/modules/webgpu/gpu_device.h
@@ -100,15 +100,15 @@ void Trace(Visitor* visitor) const override; - // gpu_device.idl + // gpu_device.idl {{{ GPUAdapter* adapter() const; GPUSupportedFeatures* features() const; GPUSupportedLimits* limits() const { return limits_.Get(); } GPUAdapterInfo* adapterInfo() const; ScriptPromise<GPUDeviceLostInfo> lost(ScriptState* script_state); + // }}} End of WebIDL binding implementation. GPUQueue* queue(); - bool destroyed() const; void destroy(v8::Isolate* isolate); @@ -164,6 +164,8 @@ const AtomicString& InterfaceName() const override; ExecutionContext* GetExecutionContext() const override; + bool IsDestroyed() const; + std::string GetFormattedLabel() const; void InjectError(wgpu::ErrorType type, const char* message); void AddConsoleWarning(const String& message); void AddConsoleWarning(const char* message); @@ -178,8 +180,6 @@ bool ValidateBlendFactor(V8GPUBlendFactor blend_factor, ExceptionState& exception_state); - std::string formattedLabel() const; - // Store the buffer in a weak hash set so we can unmap it when the // device is destroyed. void TrackMappableBuffer(GPUBuffer* buffer); @@ -230,7 +230,7 @@ wgpu::ComputePipeline compute_pipeline, wgpu::StringView message); - void setLabelImpl(const String& value) override { + void SetLabelImpl(const String& value) override { std::string utf8_label = value.Utf8(); GetHandle().SetLabel(utf8_label.c_str()); }
diff --git a/third_party/blink/renderer/modules/webgpu/gpu_device_lost_info.h b/third_party/blink/renderer/modules/webgpu/gpu_device_lost_info.h index 312d096..72976029 100644 --- a/third_party/blink/renderer/modules/webgpu/gpu_device_lost_info.h +++ b/third_party/blink/renderer/modules/webgpu/gpu_device_lost_info.h
@@ -25,9 +25,10 @@ GPUDeviceLostInfo(const GPUDeviceLostInfo&) = delete; GPUDeviceLostInfo& operator=(const GPUDeviceLostInfo&) = delete; - // gpu_device_lost_info.idl + // gpu_device_lost_info.idl {{{ V8GPUDeviceLostReason reason() const; const String& message() const; + // }}} End of WebIDL binding implementation. private: V8GPUDeviceLostReason::Enum reason_;
diff --git a/third_party/blink/renderer/modules/webgpu/gpu_error.h b/third_party/blink/renderer/modules/webgpu/gpu_error.h index 93ad03cc..d5c4437f 100644 --- a/third_party/blink/renderer/modules/webgpu/gpu_error.h +++ b/third_party/blink/renderer/modules/webgpu/gpu_error.h
@@ -20,8 +20,9 @@ GPUError(const GPUError&) = delete; GPUError& operator=(const GPUError&) = delete; - // gpu_error.idl + // gpu_error.idl {{{ const String& message() const; + // }}} End of WebIDL binding implementation. private: String message_;
diff --git a/third_party/blink/renderer/modules/webgpu/gpu_external_texture.cc b/third_party/blink/renderer/modules/webgpu/gpu_external_texture.cc index 149014e1..e06bc88 100644 --- a/third_party/blink/renderer/modules/webgpu/gpu_external_texture.cc +++ b/third_party/blink/renderer/modules/webgpu/gpu_external_texture.cc
@@ -28,7 +28,7 @@ ExceptionState& exception_state) { // Ensure the GPUExternalTexture created from a destroyed GPUDevice will be // expired immediately. - if (device()->destroyed()) { + if (device()->IsDestroyed()) { return GPUExternalTexture::CreateExpired(this, descriptor, exception_state); } @@ -347,7 +347,7 @@ void GPUExternalTexture::Refresh() { CHECK(status_ != Status::Destroyed); - if (active()) { + if (IsActive()) { return; } @@ -356,7 +356,7 @@ } void GPUExternalTexture::Expire() { - if (expired() || destroyed()) { + if (IsExpired() || IsDestroyed()) { return; } @@ -365,13 +365,13 @@ } void GPUExternalTexture::Destroy() { - DCHECK(!destroyed()); + DCHECK(!IsDestroyed()); DCHECK(mailbox_texture_); // One copy path finished video frame access after GPUExternalTexture // construction. Zero copy path needs to ensure all gpu commands // execution finished before destroy. - if (isZeroCopy() && isReadLockFenceEnabled()) { + if (isZeroCopy() && IsReadLockFenceEnabled()) { cache_->ReferenceUntilGPUIsFinished(std::move(mailbox_texture_)); } @@ -398,7 +398,7 @@ // If GPUExternalTexture is used in current task scope, don't do // reimport until current task scope finished. - if (active()) { + if (IsActive()) { return false; } @@ -480,8 +480,9 @@ void GPUExternalTexture::OnVideoFrameClosed() { CHECK(task_runner_); - if (destroyed()) + if (IsDestroyed()) { return; + } // Expire the GPUExternalTexture here in the main thread to prevent it from // being used again (because WebGPU runs on the main thread). Expiring the @@ -502,11 +503,11 @@ WrapCrossThreadWeakPersistent(this)))); } -bool GPUExternalTexture::active() const { +bool GPUExternalTexture::IsActive() const { return status_ == Status::Active; } -bool GPUExternalTexture::expired() const { +bool GPUExternalTexture::IsExpired() const { return status_ == Status::Expired; } @@ -514,11 +515,11 @@ return is_zero_copy_; } -bool GPUExternalTexture::isReadLockFenceEnabled() const { +bool GPUExternalTexture::IsReadLockFenceEnabled() const { return read_lock_fences_enabled_; } -bool GPUExternalTexture::destroyed() const { +bool GPUExternalTexture::IsDestroyed() const { return status_ == Status::Destroyed; }
diff --git a/third_party/blink/renderer/modules/webgpu/gpu_external_texture.h b/third_party/blink/renderer/modules/webgpu/gpu_external_texture.h index 1350fd7..ddfb18e 100644 --- a/third_party/blink/renderer/modules/webgpu/gpu_external_texture.h +++ b/third_party/blink/renderer/modules/webgpu/gpu_external_texture.h
@@ -114,9 +114,11 @@ GPUExternalTexture(const GPUExternalTexture&) = delete; GPUExternalTexture& operator=(const GPUExternalTexture&) = delete; + // gpu_external_texture.idl {{{ bool isZeroCopy() const; - bool isReadLockFenceEnabled() const; + // }}} End of WebIDL binding implementation. + bool IsReadLockFenceEnabled() const; void Destroy(); void Expire(); void Refresh(); @@ -160,7 +162,7 @@ std::optional<media::VideoFrame::ID> media_video_frame_unique_id, ExceptionState& exception_state); - void setLabelImpl(const String& value) override { + void SetLabelImpl(const String& value) override { std::string utf8_label = value.Utf8(); GetHandle().SetLabel(utf8_label.c_str()); } @@ -177,9 +179,9 @@ // frame multiple time cases. void RemoveFromCache(); - bool active() const; - bool expired() const; - bool destroyed() const; + bool IsActive() const; + bool IsExpired() const; + bool IsDestroyed() const; scoped_refptr<WebGPUMailboxTexture> mailbox_texture_; bool is_zero_copy_ = false;
diff --git a/third_party/blink/renderer/modules/webgpu/gpu_heap_property.h b/third_party/blink/renderer/modules/webgpu/gpu_heap_property.h index d13a073..202aa498 100644 --- a/third_party/blink/renderer/modules/webgpu/gpu_heap_property.h +++ b/third_party/blink/renderer/modules/webgpu/gpu_heap_property.h
@@ -14,18 +14,6 @@ DEFINE_WRAPPERTYPEINFO(); public: - // gpu_heap_property.idl - static constexpr uint32_t kDeviceLocal = - V8GPUHeapProperty::Constant::kDeviceLocal; - static constexpr uint32_t kHostVisible = - V8GPUHeapProperty::Constant::kHostVisible; - static constexpr uint32_t kHostCoherent = - V8GPUHeapProperty::Constant::kHostCoherent; - static constexpr uint32_t kHostUncached = - V8GPUHeapProperty::Constant::kHostUncached; - static constexpr uint32_t kHostCached = - V8GPUHeapProperty::Constant::kHostCached; - GPUHeapProperty(const GPUHeapProperty&) = delete; GPUHeapProperty& operator=(const GPUHeapProperty&) = delete; };
diff --git a/third_party/blink/renderer/modules/webgpu/gpu_map_mode.h b/third_party/blink/renderer/modules/webgpu/gpu_map_mode.h index fea5bf1d..f2c2b76 100644 --- a/third_party/blink/renderer/modules/webgpu/gpu_map_mode.h +++ b/third_party/blink/renderer/modules/webgpu/gpu_map_mode.h
@@ -14,10 +14,6 @@ DEFINE_WRAPPERTYPEINFO(); public: - // gpu_map_mode.idl - static constexpr uint32_t kRead = V8GPUMapMode::Constant::kRead; - static constexpr uint32_t kWrite = V8GPUMapMode::Constant::kWrite; - GPUMapMode(const GPUMapMode&) = delete; GPUMapMode& operator=(const GPUMapMode&) = delete; };
diff --git a/third_party/blink/renderer/modules/webgpu/gpu_memory_heap_info.h b/third_party/blink/renderer/modules/webgpu/gpu_memory_heap_info.h index 90665a0..6828882 100644 --- a/third_party/blink/renderer/modules/webgpu/gpu_memory_heap_info.h +++ b/third_party/blink/renderer/modules/webgpu/gpu_memory_heap_info.h
@@ -19,9 +19,10 @@ GPUMemoryHeapInfo(const GPUMemoryHeapInfo&) = delete; GPUMemoryHeapInfo& operator=(const GPUMemoryHeapInfo&) = delete; - // gpu_memory_heap_info.idl + // gpu_memory_heap_info.idl {{{ uint64_t size() const; uint32_t properties() const; + // }}} End of WebIDL binding implementation. private: wgpu::MemoryHeapInfo info_;
diff --git a/third_party/blink/renderer/modules/webgpu/gpu_pipeline_layout.h b/third_party/blink/renderer/modules/webgpu/gpu_pipeline_layout.h index 9a68f8d..76444a6c 100644 --- a/third_party/blink/renderer/modules/webgpu/gpu_pipeline_layout.h +++ b/third_party/blink/renderer/modules/webgpu/gpu_pipeline_layout.h
@@ -26,7 +26,7 @@ GPUPipelineLayout& operator=(const GPUPipelineLayout&) = delete; private: - void setLabelImpl(const String& value) override { + void SetLabelImpl(const String& value) override { std::string utf8_label = value.Utf8(); GetHandle().SetLabel(utf8_label.c_str()); }
diff --git a/third_party/blink/renderer/modules/webgpu/gpu_query_set.h b/third_party/blink/renderer/modules/webgpu/gpu_query_set.h index 371f1d6c..92b911e 100644 --- a/third_party/blink/renderer/modules/webgpu/gpu_query_set.h +++ b/third_party/blink/renderer/modules/webgpu/gpu_query_set.h
@@ -26,13 +26,14 @@ GPUQuerySet(const GPUQuerySet&) = delete; GPUQuerySet& operator=(const GPUQuerySet&) = delete; - // gpu_queryset.idl + // gpu_queryset.idl {{{ void destroy(); V8GPUQueryType type() const; uint32_t count() const; + // }}} End of WebIDL binding implementation. private: - void setLabelImpl(const String& value) override { + void SetLabelImpl(const String& value) override { std::string utf8_label = value.Utf8(); GetHandle().SetLabel(utf8_label.c_str()); }
diff --git a/third_party/blink/renderer/modules/webgpu/gpu_queue.cc b/third_party/blink/renderer/modules/webgpu/gpu_queue.cc index 4310102..66485c40 100644 --- a/third_party/blink/renderer/modules/webgpu/gpu_queue.cc +++ b/third_party/blink/renderer/modules/webgpu/gpu_queue.cc
@@ -282,7 +282,9 @@ // generate // required results. ImageExtractor image_extractor(image_for_canvas.get(), - external_image_dst_info.premultiplied_alpha, + external_image_dst_info.premultiplied_alpha + ? kPremul_SkAlphaType + : kUnpremul_SkAlphaType, PredefinedColorSpaceToSkColorSpace( external_image_dst_info.color_space)); sk_image = image_extractor.GetSkImage();
diff --git a/third_party/blink/renderer/modules/webgpu/gpu_queue.h b/third_party/blink/renderer/modules/webgpu/gpu_queue.h index 4f9c3b4..e6e75af 100644 --- a/third_party/blink/renderer/modules/webgpu/gpu_queue.h +++ b/third_party/blink/renderer/modules/webgpu/gpu_queue.h
@@ -38,7 +38,7 @@ GPUQueue(const GPUQueue&) = delete; GPUQueue& operator=(const GPUQueue&) = delete; - // gpu_queue.idl + // gpu_queue.idl {{{ void submit(ScriptState* script_state, const HeapVector<Member<GPUCommandBuffer>>& buffers); ScriptPromise<IDLUndefined> onSubmittedWorkDone(ScriptState* script_state); @@ -84,6 +84,7 @@ GPUImageCopyTextureTagged* destination, const V8GPUExtent3D* copySize, ExceptionState& exception_state); + // }}} End of WebIDL binding implementation. private: void CopyFromVideoElement(const ExternalTextureSource source, @@ -116,7 +117,7 @@ const V8GPUExtent3D* write_size, ExceptionState& exception_state); - void setLabelImpl(const String& value) override { + void SetLabelImpl(const String& value) override { std::string utf8_label = value.Utf8(); GetHandle().SetLabel(utf8_label.c_str()); }
diff --git a/third_party/blink/renderer/modules/webgpu/gpu_render_bundle.h b/third_party/blink/renderer/modules/webgpu/gpu_render_bundle.h index 3383c7ed..ce05443 100644 --- a/third_party/blink/renderer/modules/webgpu/gpu_render_bundle.h +++ b/third_party/blink/renderer/modules/webgpu/gpu_render_bundle.h
@@ -23,7 +23,7 @@ GPURenderBundle& operator=(const GPURenderBundle&) = delete; private: - void setLabelImpl(const String& value) override { + void SetLabelImpl(const String& value) override { std::string utf8_label = value.Utf8(); GetHandle().SetLabel(utf8_label.c_str()); }
diff --git a/third_party/blink/renderer/modules/webgpu/gpu_render_bundle_encoder.h b/third_party/blink/renderer/modules/webgpu/gpu_render_bundle_encoder.h index a4752294..79bfad1 100644 --- a/third_party/blink/renderer/modules/webgpu/gpu_render_bundle_encoder.h +++ b/third_party/blink/renderer/modules/webgpu/gpu_render_bundle_encoder.h
@@ -35,7 +35,7 @@ GPURenderBundleEncoder(const GPURenderBundleEncoder&) = delete; GPURenderBundleEncoder& operator=(const GPURenderBundleEncoder&) = delete; - // gpu_render_bundle_encoder.idl + // gpu_render_bundle_encoder.idl {{{ void setBindGroup(uint32_t index, DawnObject<wgpu::BindGroup>* bindGroup) { GetHandle().SetBindGroup( index, bindGroup ? bindGroup->GetHandle() : wgpu::BindGroup(nullptr), 0, @@ -62,7 +62,6 @@ void setPipeline(const DawnObject<wgpu::RenderPipeline>* pipeline) { GetHandle().SetPipeline(pipeline->GetHandle()); } - void setIndexBuffer(const DawnObject<wgpu::Buffer>* buffer, const V8GPUIndexFormat& format, uint64_t offset) { @@ -112,10 +111,10 @@ GetHandle().DrawIndexedIndirect(indirectBuffer->GetHandle(), indirectOffset); } - GPURenderBundle* finish(const GPURenderBundleDescriptor* webgpu_desc); + // }}} End of WebIDL binding implementation. - void setLabelImpl(const String& value) override { + void SetLabelImpl(const String& value) override { std::string utf8_label = value.Utf8(); GetHandle().SetLabel(utf8_label.c_str()); }
diff --git a/third_party/blink/renderer/modules/webgpu/gpu_render_pass_encoder.cc b/third_party/blink/renderer/modules/webgpu/gpu_render_pass_encoder.cc index b9af52f..1d2773e5 100644 --- a/third_party/blink/renderer/modules/webgpu/gpu_render_pass_encoder.cc +++ b/third_party/blink/renderer/modules/webgpu/gpu_render_pass_encoder.cc
@@ -93,13 +93,13 @@ V8GPUFeatureName::Enum requiredFeatureEnum = V8GPUFeatureName::Enum::kChromiumExperimentalMultiDrawIndirect; - if (!device_->features()->has(requiredFeatureEnum)) { + if (!device_->features()->Has(requiredFeatureEnum)) { exception_state.ThrowTypeError( String::Format("Use of the multiDrawIndirect() method on render pass " "requires the '%s' " "feature to be enabled on %s.", V8GPUFeatureName(requiredFeatureEnum).AsCStr(), - device_->formattedLabel().c_str())); + device_->GetFormattedLabel().c_str())); return; } GetHandle().MultiDrawIndirect( @@ -137,13 +137,13 @@ V8GPUFeatureName::Enum requiredFeatureEnum = V8GPUFeatureName::Enum::kChromiumExperimentalMultiDrawIndirect; - if (!device_->features()->has(requiredFeatureEnum)) { + if (!device_->features()->Has(requiredFeatureEnum)) { exception_state.ThrowTypeError(String::Format( "Use of the multiDrawIndexedIndirect() method on render pass " "requires the '%s' " "feature to be enabled on %s.", V8GPUFeatureName(requiredFeatureEnum).AsCStr(), - device_->formattedLabel().c_str())); + device_->GetFormattedLabel().c_str())); return; } GetHandle().MultiDrawIndexedIndirect( @@ -166,12 +166,12 @@ V8GPUFeatureName::Enum requiredFeatureEnum = V8GPUFeatureName::Enum::kChromiumExperimentalTimestampQueryInsidePasses; - if (!device_->features()->has(requiredFeatureEnum)) { + if (!device_->features()->Has(requiredFeatureEnum)) { exception_state.ThrowTypeError(String::Format( "Use of the writeTimestamp() method on render pass requires the '%s' " "feature to be enabled on %s.", V8GPUFeatureName(requiredFeatureEnum).AsCStr(), - device_->formattedLabel().c_str())); + device_->GetFormattedLabel().c_str())); return; } GetHandle().WriteTimestamp(querySet->GetHandle(), queryIndex);
diff --git a/third_party/blink/renderer/modules/webgpu/gpu_render_pass_encoder.h b/third_party/blink/renderer/modules/webgpu/gpu_render_pass_encoder.h index 6be7b682..5183b23 100644 --- a/third_party/blink/renderer/modules/webgpu/gpu_render_pass_encoder.h +++ b/third_party/blink/renderer/modules/webgpu/gpu_render_pass_encoder.h
@@ -29,7 +29,7 @@ GPURenderPassEncoder(const GPURenderPassEncoder&) = delete; GPURenderPassEncoder& operator=(const GPURenderPassEncoder&) = delete; - // gpu_render_pass_encoder.idl + // gpu_render_pass_encoder.idl {{{ void setBindGroup(uint32_t index, DawnObject<wgpu::BindGroup>* bindGroup) { GetHandle().SetBindGroup( index, bindGroup ? bindGroup->GetHandle() : wgpu::BindGroup(nullptr)); @@ -55,7 +55,6 @@ void setPipeline(const DawnObject<wgpu::RenderPipeline>* pipeline) { GetHandle().SetPipeline(pipeline->GetHandle()); } - void setBlendConstant(const V8GPUColor* color, ExceptionState& exception_state); void setStencilReference(uint32_t reference) { @@ -160,8 +159,9 @@ uint32_t queryIndex, ExceptionState& exception_state); void end() { GetHandle().End(); } + // }}} End of WebIDL binding implementation. - void setLabelImpl(const String& value) override { + void SetLabelImpl(const String& value) override { std::string utf8_label = value.Utf8(); GetHandle().SetLabel(utf8_label.c_str()); }
diff --git a/third_party/blink/renderer/modules/webgpu/gpu_render_pipeline.h b/third_party/blink/renderer/modules/webgpu/gpu_render_pipeline.h index ee856da..158ad26 100644 --- a/third_party/blink/renderer/modules/webgpu/gpu_render_pipeline.h +++ b/third_party/blink/renderer/modules/webgpu/gpu_render_pipeline.h
@@ -92,7 +92,7 @@ GPUBindGroupLayout* getBindGroupLayout(uint32_t index); private: - void setLabelImpl(const String& value) override { + void SetLabelImpl(const String& value) override { std::string utf8_label = value.Utf8(); GetHandle().SetLabel(utf8_label.c_str()); }
diff --git a/third_party/blink/renderer/modules/webgpu/gpu_sampler.h b/third_party/blink/renderer/modules/webgpu/gpu_sampler.h index e4d2653..1d925db 100644 --- a/third_party/blink/renderer/modules/webgpu/gpu_sampler.h +++ b/third_party/blink/renderer/modules/webgpu/gpu_sampler.h
@@ -25,7 +25,7 @@ GPUSampler& operator=(const GPUSampler&) = delete; private: - void setLabelImpl(const String& value) override { + void SetLabelImpl(const String& value) override { std::string utf8_label = value.Utf8(); GetHandle().SetLabel(utf8_label.c_str()); }
diff --git a/third_party/blink/renderer/modules/webgpu/gpu_shader_module.h b/third_party/blink/renderer/modules/webgpu/gpu_shader_module.h index 25353c47..0d98e34 100644 --- a/third_party/blink/renderer/modules/webgpu/gpu_shader_module.h +++ b/third_party/blink/renderer/modules/webgpu/gpu_shader_module.h
@@ -38,7 +38,7 @@ wgpu::CompilationInfoRequestStatus status, const wgpu::CompilationInfo* info); - void setLabelImpl(const String& value) override { + void SetLabelImpl(const String& value) override { std::string utf8_label = value.Utf8(); GetHandle().SetLabel(utf8_label.c_str()); }
diff --git a/third_party/blink/renderer/modules/webgpu/gpu_shader_stage.h b/third_party/blink/renderer/modules/webgpu/gpu_shader_stage.h index 06a0252..6e94a40 100644 --- a/third_party/blink/renderer/modules/webgpu/gpu_shader_stage.h +++ b/third_party/blink/renderer/modules/webgpu/gpu_shader_stage.h
@@ -14,11 +14,6 @@ DEFINE_WRAPPERTYPEINFO(); public: - // gpu_shader_stage.idl - static constexpr uint32_t kVertex = V8GPUShaderStage::Constant::kVertex; - static constexpr uint32_t kFragment = V8GPUShaderStage::Constant::kFragment; - static constexpr uint32_t kCompute = V8GPUShaderStage::Constant::kCompute; - GPUShaderStage(const GPUShaderStage&) = delete; GPUShaderStage& operator=(const GPUShaderStage&) = delete; };
diff --git a/third_party/blink/renderer/modules/webgpu/gpu_supported_features.cc b/third_party/blink/renderer/modules/webgpu/gpu_supported_features.cc index d66fb8ed..d7b1e739 100644 --- a/third_party/blink/renderer/modules/webgpu/gpu_supported_features.cc +++ b/third_party/blink/renderer/modules/webgpu/gpu_supported_features.cc
@@ -78,19 +78,15 @@ features_bitset_.set(static_cast<size_t>(feature_name.AsEnum())); } -bool GPUSupportedFeatures::has(const V8GPUFeatureName::Enum feature) const { +bool GPUSupportedFeatures::Has(const V8GPUFeatureName::Enum feature) const { return features_bitset_.test(static_cast<size_t>(feature)); } -bool GPUSupportedFeatures::has(const String& feature) const { - return features_.Contains(feature); -} - bool GPUSupportedFeatures::hasForBinding( ScriptState* script_state, const String& feature, ExceptionState& exception_state) const { - return has(feature); + return features_.Contains(feature); } GPUSupportedFeatures::IterationSource::IterationSource(
diff --git a/third_party/blink/renderer/modules/webgpu/gpu_supported_features.h b/third_party/blink/renderer/modules/webgpu/gpu_supported_features.h index f2e7bfc1..bfaf95c 100644 --- a/third_party/blink/renderer/modules/webgpu/gpu_supported_features.h +++ b/third_party/blink/renderer/modules/webgpu/gpu_supported_features.h
@@ -30,18 +30,17 @@ void AddFeatureName(const V8GPUFeatureName feature_name); - bool has(const String& feature) const; + const HashSet<String>& FeatureNameSet() const { return features_; } + // Fast path, it allows to avoid hash computation from string for + // checking features. + bool Has(const V8GPUFeatureName::Enum feature) const; + + // gpu_supported_features.idl {{{ bool hasForBinding(ScriptState* script_state, const String& feature, ExceptionState& exception_state) const; - - // Fast path, it allows to avoid hash computation from string for - // checking features. - bool has(const V8GPUFeatureName::Enum feature) const; - unsigned size() const { return features_.size(); } - - const HashSet<String>& FeatureNameSet() const { return features_; } + // }}} End of WebIDL binding implementation. private: HashSet<String> features_;
diff --git a/third_party/blink/renderer/modules/webgpu/gpu_texture.h b/third_party/blink/renderer/modules/webgpu/gpu_texture.h index d1178df..29ad245 100644 --- a/third_party/blink/renderer/modules/webgpu/gpu_texture.h +++ b/third_party/blink/renderer/modules/webgpu/gpu_texture.h
@@ -43,7 +43,7 @@ GPUTexture(const GPUTexture&) = delete; GPUTexture& operator=(const GPUTexture&) = delete; - // gpu_texture.idl + // gpu_texture.idl {{{ GPUTextureView* createView(const GPUTextureViewDescriptor* webgpu_desc, ExceptionState& exception_state); void destroy(); @@ -55,11 +55,12 @@ V8GPUTextureDimension dimension() const; V8GPUTextureFormat format() const; uint32_t usage() const; + // }}} End of WebIDL binding implementation. wgpu::TextureDimension Dimension() { return dimension_; } wgpu::TextureFormat Format() { return format_; } wgpu::TextureUsage Usage() { return usage_; } - bool Destroyed() { return destroyed_; } + bool IsDestroyed() { return destroyed_; } void DissociateMailbox(); @@ -73,7 +74,7 @@ void ClearBeforeDestroyCallback(); private: - void setLabelImpl(const String& value) override { + void SetLabelImpl(const String& value) override { std::string utf8_label = value.Utf8(); GetHandle().SetLabel(utf8_label.c_str()); }
diff --git a/third_party/blink/renderer/modules/webgpu/gpu_texture_usage.h b/third_party/blink/renderer/modules/webgpu/gpu_texture_usage.h index 2c211e9..bb9319c 100644 --- a/third_party/blink/renderer/modules/webgpu/gpu_texture_usage.h +++ b/third_party/blink/renderer/modules/webgpu/gpu_texture_usage.h
@@ -15,16 +15,6 @@ DEFINE_WRAPPERTYPEINFO(); public: - // gpu_texture_usage.idl - static constexpr uint32_t kCopySrc = V8GPUTextureUsage::Constant::kCopySrc; - static constexpr uint32_t kCopyDst = V8GPUTextureUsage::Constant::kCopyDst; - static constexpr uint32_t kTextureBinding = - V8GPUTextureUsage::Constant::kTextureBinding; - static constexpr uint32_t kStorageBinding = - V8GPUTextureUsage::Constant::kStorageBinding; - static constexpr uint32_t kRenderAttachment = - V8GPUTextureUsage::Constant::kRenderAttachment; - GPUTextureUsage(const GPUTextureUsage&) = delete; GPUTextureUsage& operator=(const GPUTextureUsage&) = delete; };
diff --git a/third_party/blink/renderer/modules/webgpu/gpu_texture_view.h b/third_party/blink/renderer/modules/webgpu/gpu_texture_view.h index 3fa27eaf..3eefba6 100644 --- a/third_party/blink/renderer/modules/webgpu/gpu_texture_view.h +++ b/third_party/blink/renderer/modules/webgpu/gpu_texture_view.h
@@ -21,7 +21,7 @@ GPUTextureView& operator=(const GPUTextureView&) = delete; private: - void setLabelImpl(const String& value) override { + void SetLabelImpl(const String& value) override { std::string utf8_label = value.Utf8(); GetHandle().SetLabel(utf8_label.c_str()); }
diff --git a/third_party/blink/renderer/modules/webgpu/gpu_uncaptured_error_event.h b/third_party/blink/renderer/modules/webgpu/gpu_uncaptured_error_event.h index 3269e5bb..8582744 100644 --- a/third_party/blink/renderer/modules/webgpu/gpu_uncaptured_error_event.h +++ b/third_party/blink/renderer/modules/webgpu/gpu_uncaptured_error_event.h
@@ -26,8 +26,9 @@ void Trace(Visitor*) const override; - // gpu_uncaptured_error_event.idl + // gpu_uncaptured_error_event.idl {{{ GPUError* error(); + // }}} End of WebIDL binding implementation. private: Member<GPUError> error_;
diff --git a/third_party/blink/renderer/modules/webgpu/wgsl_language_features.cc b/third_party/blink/renderer/modules/webgpu/wgsl_language_features.cc index 272c0b3..b47d7778a 100644 --- a/third_party/blink/renderer/modules/webgpu/wgsl_language_features.cc +++ b/third_party/blink/renderer/modules/webgpu/wgsl_language_features.cc
@@ -17,15 +17,11 @@ } } -bool WGSLLanguageFeatures::has(const String& feature) const { - return features_.Contains(feature); -} - bool WGSLLanguageFeatures::hasForBinding( ScriptState* script_state, const String& feature, ExceptionState& exception_state) const { - return has(feature); + return features_.Contains(feature); } WGSLLanguageFeatures::IterationSource::IterationSource(
diff --git a/third_party/blink/renderer/modules/webgpu/wgsl_language_features.h b/third_party/blink/renderer/modules/webgpu/wgsl_language_features.h index f2a161ccb..e164a83 100644 --- a/third_party/blink/renderer/modules/webgpu/wgsl_language_features.h +++ b/third_party/blink/renderer/modules/webgpu/wgsl_language_features.h
@@ -23,14 +23,14 @@ explicit WGSLLanguageFeatures( const std::vector<wgpu::WGSLLanguageFeatureName>& features); - bool has(const String& feature) const; + const HashSet<String>& FeatureNameSet() const { return features_; } + + // wgsl_language_features.idl {{{ bool hasForBinding(ScriptState* script_state, const String& feature, ExceptionState& exception_state) const; - unsigned size() const { return features_.size(); } - - const HashSet<String>& FeatureNameSet() const { return features_; } + // }}} End of WebIDL binding implementation. private: HashSet<String> features_;
diff --git a/third_party/blink/renderer/modules/xr/xr_gpu_binding.cc b/third_party/blink/renderer/modules/xr/xr_gpu_binding.cc index 13078601..0e5be45 100644 --- a/third_party/blink/renderer/modules/xr/xr_gpu_binding.cc +++ b/third_party/blink/renderer/modules/xr/xr_gpu_binding.cc
@@ -54,14 +54,14 @@ return nullptr; } - if (device->destroyed()) { + if (device->IsDestroyed()) { exception_state.ThrowDOMException(DOMExceptionCode::kInvalidStateError, "Cannot create an XRGPUBinding with a " "destroyed WebGPU device."); return nullptr; } - if (!device->adapter()->isXRCompatible()) { + if (!device->adapter()->IsXRCompatible()) { exception_state.ThrowDOMException( DOMExceptionCode::kInvalidStateError, "WebGPU device must be created by an XR compatible adapter in order to " @@ -216,7 +216,7 @@ V8GPUTextureFormat XRGPUBinding::getPreferredColorFormat() { // TODO(crbug.com/5818595): Ensure the backend swap chain format matches this. // Till then the copy between formats is done in XRGPUTextureArraySwapChain. - return FromDawnEnum(GPU::preferred_canvas_format()); + return FromDawnEnum(GPU::GetPreferredCanvasFormat()); } bool XRGPUBinding::CanCreateLayer(ExceptionState& exception_state) { @@ -227,7 +227,7 @@ return false; } - if (device_->destroyed()) { + if (device_->IsDestroyed()) { exception_state.ThrowDOMException(DOMExceptionCode::kInvalidStateError, "Cannot create a new layer with a " "destroyed WebGPU device.");
diff --git a/third_party/blink/renderer/platform/graphics/accelerated_static_bitmap_image.cc b/third_party/blink/renderer/platform/graphics/accelerated_static_bitmap_image.cc index f4cf0643..f6c996b 100644 --- a/third_party/blink/renderer/platform/graphics/accelerated_static_bitmap_image.cc +++ b/third_party/blink/renderer/platform/graphics/accelerated_static_bitmap_image.cc
@@ -170,7 +170,7 @@ GLenum dest_target, GLuint dest_texture_id, GLint dest_level, - bool unpack_premultiply_alpha, + SkAlphaType dest_alpha_type, GrSurfaceOrigin destination_origin, const gfx::Point& dest_point, const gfx::Rect& src_rect) { @@ -188,9 +188,9 @@ auto source_scoped_si_access = source_si_texture->BeginAccess( mailbox_ref_->sync_token(), /*readonly=*/true); const bool do_alpha_multiply = GetAlphaType() == kUnpremul_SkAlphaType && - unpack_premultiply_alpha == true; + dest_alpha_type == kPremul_SkAlphaType; const bool do_alpha_unmultiply = GetAlphaType() == kPremul_SkAlphaType && - unpack_premultiply_alpha == false; + dest_alpha_type == kUnpremul_SkAlphaType; // `src_rect` here is always in top-left coordinate space, but // CopySubTextureCHROMIUM source rect is in texture coordinate space, so we
diff --git a/third_party/blink/renderer/platform/graphics/accelerated_static_bitmap_image.h b/third_party/blink/renderer/platform/graphics/accelerated_static_bitmap_image.h index b483dff..494df22 100644 --- a/third_party/blink/renderer/platform/graphics/accelerated_static_bitmap_image.h +++ b/third_party/blink/renderer/platform/graphics/accelerated_static_bitmap_image.h
@@ -97,7 +97,7 @@ GLenum dest_target, GLuint dest_texture_id, GLint dest_level, - bool unpack_premultiply_alpha, + SkAlphaType dest_alpha_type, GrSurfaceOrigin destination_origin, const gfx::Point& dest_point, const gfx::Rect& src_rect) override;
diff --git a/third_party/blink/renderer/platform/graphics/accelerated_static_bitmap_image_test.cc b/third_party/blink/renderer/platform/graphics/accelerated_static_bitmap_image_test.cc index 2490fbe..1237f67 100644 --- a/third_party/blink/renderer/platform/graphics/accelerated_static_bitmap_image_test.cc +++ b/third_party/blink/renderer/platform/graphics/accelerated_static_bitmap_image_test.cc
@@ -121,8 +121,8 @@ gfx::Rect source_sub_rectangle(0, 0, 10, 10); ASSERT_TRUE(bitmap->CopyToTexture( &destination_gl, GL_TEXTURE_2D, /*dest_texture_id=*/1, - /*dest_texture_level=*/0, /*unpack_premultiply_alpha=*/false, - kTopLeft_GrSurfaceOrigin, dest_point, source_sub_rectangle)); + /*dest_level=*/0, kUnpremul_SkAlphaType, kTopLeft_GrSurfaceOrigin, + dest_point, source_sub_rectangle)); testing::Mock::VerifyAndClearExpectations(&gl_); testing::Mock::VerifyAndClearExpectations(&destination_gl);
diff --git a/third_party/blink/renderer/platform/graphics/gpu/drawing_buffer.cc b/third_party/blink/renderer/platform/graphics/gpu/drawing_buffer.cc index e26102b..7031793 100644 --- a/third_party/blink/renderer/platform/graphics/gpu/drawing_buffer.cc +++ b/third_party/blink/renderer/platform/graphics/gpu/drawing_buffer.cc
@@ -1099,8 +1099,8 @@ GLenum dst_texture_target, GLuint dst_texture, GLint dst_level, - bool premultiply_alpha, - GrSurfaceOrigin destination_origin, + SkAlphaType dst_alpha_type, + GrSurfaceOrigin dst_origin, const gfx::Point& dst_texture_offset, const gfx::Rect& src_rect, SourceDrawingBuffer src_buffer) { @@ -1112,7 +1112,7 @@ const gpu::SyncToken& produce_sync_token, SkAlphaType src_alpha_type, const gfx::Size&) -> std::optional<gpu::SyncToken> { // If origin doesn't match, we need to flip. - bool do_flip_y = src_shared_image->surface_origin() != destination_origin; + bool do_flip_y = src_shared_image->surface_origin() != dst_origin; // `src_rect` here is always in top-left coordinate space, but // CopySubTextureCHROMIUM source rect is in texture coordinate space, so we @@ -1124,9 +1124,11 @@ GLboolean unpack_premultiply_alpha_needed = GL_FALSE; GLboolean unpack_unpremultiply_alpha_needed = GL_FALSE; - if (src_alpha_type == kPremul_SkAlphaType && !premultiply_alpha) { + if (src_alpha_type == kPremul_SkAlphaType && + dst_alpha_type == kUnpremul_SkAlphaType) { unpack_unpremultiply_alpha_needed = GL_TRUE; - } else if (src_alpha_type == kUnpremul_SkAlphaType && premultiply_alpha) { + } else if (src_alpha_type == kUnpremul_SkAlphaType && + dst_alpha_type == kPremul_SkAlphaType) { unpack_premultiply_alpha_needed = GL_TRUE; } @@ -1144,8 +1146,8 @@ src_si_texture.reset(); return sync_token; }; - return CopyToPlatformInternal(dst_gl, !premultiply_alpha, src_buffer, - copy_function); + return CopyToPlatformInternal(dst_gl, dst_alpha_type == kUnpremul_SkAlphaType, + src_buffer, copy_function); } bool DrawingBuffer::CopyToPlatformMailbox(
diff --git a/third_party/blink/renderer/platform/graphics/gpu/drawing_buffer.h b/third_party/blink/renderer/platform/graphics/gpu/drawing_buffer.h index c59430ab..54278e5 100644 --- a/third_party/blink/renderer/platform/graphics/gpu/drawing_buffer.h +++ b/third_party/blink/renderer/platform/graphics/gpu/drawing_buffer.h
@@ -273,8 +273,8 @@ GLenum dst_target, GLuint dst_texture, GLint dst_level, - bool premultiply_alpha, - GrSurfaceOrigin destination_origin, + SkAlphaType dst_alpha_type, + GrSurfaceOrigin dst_origin, const gfx::Point& dst_texture_offset, const gfx::Rect& src_rect, SourceDrawingBuffer);
diff --git a/third_party/blink/renderer/platform/graphics/gpu/image_extractor.cc b/third_party/blink/renderer/platform/graphics/gpu/image_extractor.cc index c2adaee..5690356 100644 --- a/third_party/blink/renderer/platform/graphics/gpu/image_extractor.cc +++ b/third_party/blink/renderer/platform/graphics/gpu/image_extractor.cc
@@ -28,7 +28,7 @@ } // anonymous namespace ImageExtractor::ImageExtractor(Image* image, - bool premultiply_alpha, + SkAlphaType target_alpha_type, sk_sp<SkColorSpace> target_color_space) { if (!image) { return; @@ -80,7 +80,7 @@ // bother re-decoding if premultiply alpha was requested, because we will // do that lossy conversion later. if (skia_image->alphaType() == kPremul_SkAlphaType && - !premultiply_alpha) { + target_alpha_type != kPremul_SkAlphaType) { needs_redecode = true; }
diff --git a/third_party/blink/renderer/platform/graphics/gpu/image_extractor.h b/third_party/blink/renderer/platform/graphics/gpu/image_extractor.h index d4507cc..420b464 100644 --- a/third_party/blink/renderer/platform/graphics/gpu/image_extractor.h +++ b/third_party/blink/renderer/platform/graphics/gpu/image_extractor.h
@@ -22,7 +22,7 @@ // nullptr. Otherwise, `target_color_space` should be set to the color space // that the image will ultimately be converted to. ImageExtractor(Image*, - bool premultiply_alpha, + SkAlphaType target_alpha_type, sk_sp<SkColorSpace> target_color_space); ImageExtractor(const ImageExtractor&) = delete; ImageExtractor& operator=(const ImageExtractor&) = delete;
diff --git a/third_party/blink/renderer/platform/graphics/static_bitmap_image.h b/third_party/blink/renderer/platform/graphics/static_bitmap_image.h index 0e957e3..9f1323d 100644 --- a/third_party/blink/renderer/platform/graphics/static_bitmap_image.h +++ b/third_party/blink/renderer/platform/graphics/static_bitmap_image.h
@@ -66,7 +66,7 @@ GLenum dest_target, GLuint dest_texture_id, GLint dest_level, - bool unpack_premultiply_alpha, + SkAlphaType dest_alpha_type, GrSurfaceOrigin destination_origin, const gfx::Point& dest_point, const gfx::Rect& src_rect) {
diff --git a/third_party/blink/renderer/platform/loader/fetch/resource_loader.cc b/third_party/blink/renderer/platform/loader/fetch/resource_loader.cc index 8a85767d..d325448 100644 --- a/third_party/blink/renderer/platform/loader/fetch/resource_loader.cc +++ b/third_party/blink/renderer/platform/loader/fetch/resource_loader.cc
@@ -934,20 +934,6 @@ return; } - // https://wicg.github.io/cross-origin-embedder-policy/#integration-html - // TODO(crbug.com/1064920): Remove this once PlzDedicatedWorker ships. - if (options.reject_coep_unsafe_none && - !network::CompatibleWithCrossOriginIsolated( - response.GetCrossOriginEmbedderPolicy()) && - !response.CurrentRequestUrl().ProtocolIsData() && - !response.CurrentRequestUrl().ProtocolIs("blob")) { - DCHECK(!base::FeatureList::IsEnabled(features::kPlzDedicatedWorker)); - HandleError(ResourceError::BlockedByResponse( - response.CurrentRequestUrl(), network::mojom::BlockedByResponseReason:: - kCoepFrameResourceNeedsCoepHeader)); - return; - } - // Redirect information for possible post-request checks below. const std::optional<ResourceRequest::RedirectInfo>& previous_redirect_info = request.GetRedirectInfo();
diff --git a/third_party/blink/renderer/platform/loader/fetch/url_loader/dedicated_or_shared_worker_fetch_context_impl.cc b/third_party/blink/renderer/platform/loader/fetch/url_loader/dedicated_or_shared_worker_fetch_context_impl.cc index 66b4eb0..5b3c0da 100644 --- a/third_party/blink/renderer/platform/loader/fetch/url_loader/dedicated_or_shared_worker_fetch_context_impl.cc +++ b/third_party/blink/renderer/platform/loader/fetch/url_loader/dedicated_or_shared_worker_fetch_context_impl.cc
@@ -14,7 +14,6 @@ #include "base/task/thread_pool.h" #include "services/network/public/cpp/resource_request.h" #include "services/network/public/cpp/wrapper_shared_url_loader_factory.h" -#include "third_party/blink/public/common/features.h" #include "third_party/blink/public/common/loader/loader_constants.h" #include "third_party/blink/public/mojom/service_worker/service_worker_fetch_handler_bypass_option.mojom-blink-forward.h" #include "third_party/blink/public/mojom/service_worker/service_worker_object.mojom.h" @@ -189,51 +188,6 @@ std::move(pending_resource_load_info_notifier)) {} scoped_refptr<WebDedicatedOrSharedWorkerFetchContext> -DedicatedOrSharedWorkerFetchContextImpl::CloneForNestedWorkerDeprecated( - scoped_refptr<base::SingleThreadTaskRunner> task_runner) { - DCHECK(!base::FeatureList::IsEnabled(features::kPlzDedicatedWorker)); - - mojo::PendingReceiver<mojom::blink::ServiceWorkerWorkerClient> - service_worker_client_receiver; - mojo::PendingRemote<mojom::blink::ServiceWorkerWorkerClientRegistry> - service_worker_worker_client_registry; - if (service_worker_worker_client_registry_) { - mojo::PendingRemote<mojom::blink::ServiceWorkerWorkerClient> - service_worker_client; - service_worker_client_receiver = - service_worker_client.InitWithNewPipeAndPassReceiver(); - service_worker_worker_client_registry_->RegisterWorkerClient( - std::move(service_worker_client)); - service_worker_worker_client_registry_->CloneWorkerClientRegistry( - service_worker_worker_client_registry.InitWithNewPipeAndPassReceiver()); - } - - CrossVariantMojoRemote<mojom::ServiceWorkerContainerHostInterfaceBase> - cloned_service_worker_container_host; - if (service_worker_container_host_) { - std::tie(service_worker_container_host_, - cloned_service_worker_container_host) = - Platform::Current()->CloneServiceWorkerContainerHost( - std::move(service_worker_container_host_)); - } - - // |pending_subresource_loader_updater| is not used for - // non-PlzDedicatedWorker. - scoped_refptr<DedicatedOrSharedWorkerFetchContextImpl> new_context = - CloneForNestedWorkerInternal( - std::move(service_worker_client_receiver), - std::move(service_worker_worker_client_registry), - std::move(cloned_service_worker_container_host), - loader_factory_->Clone(), fallback_factory_->Clone(), - /*pending_subresource_loader_updater=*/mojo::NullReceiver(), - std::move(task_runner)); - new_context->controller_service_worker_mode_ = - controller_service_worker_mode_; - - return new_context; -} - -scoped_refptr<WebDedicatedOrSharedWorkerFetchContext> DedicatedOrSharedWorkerFetchContextImpl::CloneForNestedWorker( WebServiceWorkerProviderContext* service_worker_provider_context, std::unique_ptr<network::PendingSharedURLLoaderFactory> @@ -243,7 +197,6 @@ CrossVariantMojoReceiver<mojom::SubresourceLoaderUpdaterInterfaceBase> pending_subresource_loader_updater, scoped_refptr<base::SingleThreadTaskRunner> task_runner) { - DCHECK(base::FeatureList::IsEnabled(features::kPlzDedicatedWorker)); DCHECK(pending_loader_factory); DCHECK(pending_fallback_factory); DCHECK(task_runner);
diff --git a/third_party/blink/renderer/platform/loader/fetch/url_loader/dedicated_or_shared_worker_fetch_context_impl.h b/third_party/blink/renderer/platform/loader/fetch/url_loader/dedicated_or_shared_worker_fetch_context_impl.h index 697d22458..d185043 100644 --- a/third_party/blink/renderer/platform/loader/fetch/url_loader/dedicated_or_shared_worker_fetch_context_impl.h +++ b/third_party/blink/renderer/platform/loader/fetch/url_loader/dedicated_or_shared_worker_fetch_context_impl.h
@@ -77,15 +77,9 @@ pending_resource_load_info_notifier); // WebDedicatedOrSharedWorkerFetchContext implementation: - // Clones this fetch context for a nested worker. - // For non-PlzDedicatedWorker. This will be removed once PlzDedicatedWorker is - // enabled by default. - scoped_refptr<WebDedicatedOrSharedWorkerFetchContext> - CloneForNestedWorkerDeprecated( - scoped_refptr<base::SingleThreadTaskRunner> task_runner) override; - // For PlzDedicatedWorker. The cloned fetch context does not inherit some - // fields (e.g., blink::WebServiceWorkerProviderContext) from this fetch - // context, and instead that takes values passed from the browser process. + // The cloned fetch context does not inherit some fields (e.g., + // blink::WebServiceWorkerProviderContext) from this fetch context, and + // instead that takes values passed from the browser process. scoped_refptr<WebDedicatedOrSharedWorkerFetchContext> CloneForNestedWorker( WebServiceWorkerProviderContext* service_worker_provider_context, std::unique_ptr<network::PendingSharedURLLoaderFactory> @@ -140,13 +134,8 @@ void OnControllerChanged(mojom::ControllerServiceWorkerMode) override; // Sets the controller service worker mode. - // - For dedicated workers (non-PlzDedicatedWorker), they depend on the - // controller of the ancestor frame (directly for non-nested workers, or - // indirectly via its parent worker for nested workers), and inherit its - // controller mode. - // - For dedicated workers (PlzDedicatedWorker) and shared workers, the - // controller mode is passed from the browser processw when starting the - // worker. + // - For dedicated workers and shared workers, the controller mode is passed + // from the browser processw when starting the worker. void set_controller_service_worker_mode( mojom::ControllerServiceWorkerMode mode); @@ -243,11 +232,8 @@ // Initialized on the worker thread when InitializeOnWorkerThread() is called. // Used to reconnect to the Network Service after the Network Service crash. - // This is only used for dedicated workers when PlzDedicatedWorker is enabled. - // When PlzDedicatedWorker is disabled, the ancestor render frame updates the - // loaders via Host/TrackedChildURLLoaderFactoryBundle. For shared workers, - // the renderer process detects the crash, and terminates the worker instead - // of recovery. + // This is only used for dedicated workers. For shared workers, the renderer + // process detects the crash, and terminates the worker instead of recovery. mojo::PendingReceiver<mojom::blink::SubresourceLoaderUpdater> pending_subresource_loader_updater_; mojo::Receiver<mojom::blink::SubresourceLoaderUpdater>
diff --git a/third_party/blink/renderer/platform/loader/fetch/url_loader/worker_main_script_loader_unittest.cc b/third_party/blink/renderer/platform/loader/fetch/url_loader/worker_main_script_loader_unittest.cc index 2033bf3..63dbe45 100644 --- a/third_party/blink/renderer/platform/loader/fetch/url_loader/worker_main_script_loader_unittest.cc +++ b/third_party/blink/renderer/platform/loader/fetch/url_loader/worker_main_script_loader_unittest.cc
@@ -5,7 +5,6 @@ #include "third_party/blink/renderer/platform/loader/fetch/url_loader/worker_main_script_loader.h" #include "base/containers/span.h" -#include "base/test/scoped_feature_list.h" #include "base/test/task_environment.h" #include "build/build_config.h" #include "mojo/public/cpp/bindings/remote.h" @@ -13,7 +12,6 @@ #include "net/http/http_util.h" #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" -#include "third_party/blink/public/common/features.h" #include "third_party/blink/public/mojom/fetch/fetch_api_request.mojom-blink.h" #include "third_party/blink/public/mojom/loader/resource_load_info_notifier.mojom.h" #include "third_party/blink/public/mojom/navigation/renderer_eviction_reason.mojom-blink.h" @@ -42,10 +40,7 @@ public: WorkerMainScriptLoaderTest() : fake_loader_(pending_remote_loader_.InitWithNewPipeAndPassReceiver()), - client_(MakeGarbageCollected<TestClient>()) { - scoped_feature_list_.InitWithFeatureState( - blink::features::kPlzDedicatedWorker, true); - } + client_(MakeGarbageCollected<TestClient>()) {} ~WorkerMainScriptLoaderTest() override { // Forced GC in order to finalize objects depending on MockResourceObserver, // see details https://crbug.com/1132634. @@ -218,7 +213,6 @@ FakeURLLoader fake_loader_; Persistent<TestClient> client_; - base::test::ScopedFeatureList scoped_feature_list_; }; TEST_F(WorkerMainScriptLoaderTest, ResponseWithSucessThenOnComplete) {
diff --git a/third_party/blink/renderer/platform/runtime_enabled_features.json5 b/third_party/blink/renderer/platform/runtime_enabled_features.json5 index cbf2451..405d946 100644 --- a/third_party/blink/renderer/platform/runtime_enabled_features.json5 +++ b/third_party/blink/renderer/platform/runtime_enabled_features.json5
@@ -318,7 +318,7 @@ name: "AIPromptAPI", public: true, status: "test", - implied_by: ["AIPromptAPIForWebPlatform", "AIPromptAPIMultimodalInput"], + implied_by: ["AIPromptAPIForWebPlatform", "AIPromptAPIMultimodalInput", "AIPromptAPIStructuredOutput"], }, { name: "AIPromptAPIForExtension", @@ -340,6 +340,11 @@ status: "test", }, { + name: "AIPromptAPIStructuredOutput", + public: true, + status: "test", + }, + { name: "AIRewriterAPI", public: true, status: "test", @@ -650,11 +655,6 @@ ] }, { - // crbug.com/40681980 - name: "ButtonNoAlignItems", - status: "stable", - }, - { // Enables bypassing CSP checks when we consume a preload. // https://crbug.com/348442535 name: "BypassCSPForPreloads", @@ -1273,17 +1273,20 @@ depends_on: ["PseudoElementsFocusable"], }, { - // TODO(crbug.com/40932006): Non-standard 'reading-flow' keyword - // for reading of grid and flexbox items. + // TODO(crbug.com/40932006): CSS 'reading-flow' property for reading order + // of grid, flexbox and block items. // https://drafts.csswg.org/css-display-4/#reading-flow + // This should ship in M137, and it can be removed after M139. name: "CSSReadingFlow", - status: "experimental", + status: "stable", }, { - // TODO(crbug.com/393550130): Non-standard 'reading-order' keyword - // to overwrite reading-flow order. + // TODO(crbug.com/393550130): CSS 'reading-order' property to overwrite + // reading-flow order. + // https://drafts.csswg.org/css-display-4/#reading-order + // This should ship in M137, and it can be removed after M139. name: "CSSReadingOrder", - status: "experimental", + status: "stable", depends_on: ["CSSReadingFlow"], }, { @@ -3981,10 +3984,13 @@ depends_on: ["CustomizableSelect"], }, { - // Implement Selection API across shadow DOM - // See https://w3c.github.io/selection-api/ + // Implement Selection API across shadow DOM: direction and + // getComposedRanges(). + // https://w3c.github.io/selection-api/#dom-selection-getcomposedranges + // https://w3c.github.io/selection-api/#dom-selection-direction + // This should ship in M137, and it can be removed after M139. name: "SelectionAcrossShadowDOM", - status: "experimental", + status: "stable", }, { // See https://crbug.com/395544401
diff --git a/third_party/blink/tools/blinkpy/tool/commands/rebaseline.py b/third_party/blink/tools/blinkpy/tool/commands/rebaseline.py index ff184d0..d8a437a 100644 --- a/third_party/blink/tools/blinkpy/tool/commands/rebaseline.py +++ b/third_party/blink/tools/blinkpy/tool/commands/rebaseline.py
@@ -166,7 +166,7 @@ def _test_can_have_suffix(self, test_name: str, suffix: BaselineSuffix) -> bool: - wpt_type = self._get_wpt_type(test_name) + wpt_type = self._host_port.get_wpt_type(test_name) # Only legacy reftests can dump text output, not WPT reftests. if wpt_type in {'testharness', 'wdspec'} and suffix == 'txt': return True @@ -179,13 +179,6 @@ # No other WPT-suffix combinations are allowed. return not wpt_type - def _get_wpt_type(self, test_name: str) -> Optional[str]: - wpt_dir, url_from_wpt_dir = self._host_port.split_wpt_dir(test_name) - if not wpt_dir: - return None # Not a WPT. - manifest = self._host_port.wpt_manifest(wpt_dir) - return manifest.get_test_type(url_from_wpt_dir) - class ChangeSet(object): """A record of TestExpectation lines to remove.
diff --git a/third_party/blink/tools/blinkpy/w3c/wpt_expectations_updater.py b/third_party/blink/tools/blinkpy/w3c/wpt_expectations_updater.py index 57b30ac..d45e9a2e 100644 --- a/third_party/blink/tools/blinkpy/w3c/wpt_expectations_updater.py +++ b/third_party/blink/tools/blinkpy/w3c/wpt_expectations_updater.py
@@ -306,16 +306,11 @@ def filter_results_for_update( self, test_results: WebTestResults, - min_attempts_for_update: int = 3, ) -> Tuple[Set[str], WebTestResults]: """Filters for failing test results that need TestExpectations. Arguments: - min_attempts_for_update: Threshold for the number of attempts at - which a test's expectations are updated. This prevents excessive - expectation creation due to infrastructure issues or flakiness. - Note that this threshold is necessary for updating without - `--include-unexpected-pass`, but sufficient otherwise. + test_results: All web test results on a given build step. Returns: * A set of tests that should be handled by rebaselining, which @@ -328,14 +323,11 @@ if not self.options.include_unexpected_pass: # TODO(crbug.com/1149035): Consider suppressing flaky passes. for result in test_results.didnt_run_as_expected_results(): - if (result.attempts >= min_attempts_for_update - and not result.did_pass()): + if not result.did_pass(): failing_results.append(result) else: for result in test_results.didnt_run_as_expected_results(): - if (result.attempts >= min_attempts_for_update - or result.did_pass()): - failing_results.append(result) + failing_results.append(result) failing_results = [ result for result in failing_results @@ -701,23 +693,21 @@ def can_rebaseline(self, result: WebTestResult) -> bool: """Checks if a test can be rebaselined.""" - if self.is_reference_test(result.test_name()): + if not self.port.get_wpt_type( + result.test_name()) in {'testharness', 'wdspec'}: return False statuses = set(result.actual_results()) if {ResultType.Pass, ResultType.Failure} <= statuses: return False # Has nondeterministic output; cannot be rebaselined. - return not (statuses & {ResultType.Crash, ResultType.Timeout}) + return ResultType.Failure in statuses def is_reference_test(self, test_name: str) -> bool: """Checks whether a given test is a reference test.""" return bool(self.port.reference_files(test_name)) @memoized - def _get_try_bots(self, flag_specific: Optional[str] = None): - builders = self.host.builders.filter_builders( - is_try=True, - exclude_specifiers={'android'}, - flag_specific=flag_specific) + def _get_try_bots(self): + builders = self.host.builders.filter_builders(is_try=True) # Exclude CQ builders like `win-rel`. return sorted( set(builders) & self.host.builders.builders_for_rebaselining())
diff --git a/third_party/blink/tools/blinkpy/w3c/wpt_expectations_updater_unittest.py b/third_party/blink/tools/blinkpy/w3c/wpt_expectations_updater_unittest.py index 22bdd3ce..831d990 100644 --- a/third_party/blink/tools/blinkpy/w3c/wpt_expectations_updater_unittest.py +++ b/third_party/blink/tools/blinkpy/w3c/wpt_expectations_updater_unittest.py
@@ -448,17 +448,19 @@ _, filtered_results = updater.filter_results_for_update(results) self.assertEqual(0, len(filtered_results)) - def test_filter_results_for_update_not_retried_test(self): + def test_not_filter_results_for_update_not_retried_test(self): + # Wptrunner will not retry when some subtests unexpectedly pass. + # Such tests should still be rebaselined. host = self.mock_host() results = WebTestResults.from_rdb_responses( - {'external/wpt/reftest.html': [{ + {'external/wpt/test/zzzz.html': [{ 'status': 'FAIL', }]}, step_name='blink_wpt_tests', build=Build('MOCK Try Mac10.10')) updater = WPTExpectationsUpdater(host) - _, filtered_results = updater.filter_results_for_update(results) - self.assertEqual(0, len(filtered_results)) + tests_to_rebaseline, _ = updater.filter_results_for_update(results) + self.assertEqual(1, len(tests_to_rebaseline)) def test_remove_configurations(self): host = self.mock_host() @@ -1066,17 +1068,17 @@ host = self.mock_host() updater = WPTExpectationsUpdater(host) raw_results = WebTestResults.from_rdb_responses({ - 'external/wpt/x/y.html': [{ + 'external/wpt/test/path.html': [{ 'status': 'PASS', }] * 3, - 'external/wpt/x/z.html': [{ + 'external/wpt/test/zzzz.html': [{ 'status': 'FAIL', }] * 3, }) tests_to_rebaseline, results = updater.filter_results_for_update( raw_results) self.assertEqual(len(results), 0) - self.assertEqual(tests_to_rebaseline, {'external/wpt/x/z.html'}) + self.assertEqual(tests_to_rebaseline, {'external/wpt/test/zzzz.html'}) def test_get_test_to_rebaseline_does_not_return_ref_tests(self): host = self.mock_host() @@ -1104,20 +1106,22 @@ host = self.mock_host() updater = WPTExpectationsUpdater(host) raw_results = WebTestResults.from_rdb_responses({ - 'external/wpt/x/y.html': [{ + 'external/wpt/test/path.html': [{ 'status': 'FAIL', }] * 3, - 'external/wpt/x/z.html': [{ + 'external/wpt/reftest.html': [{ 'status': 'ABORT', }] * 3, }) - tests_to_rebaseline, ( - result, ) = updater.filter_results_for_update(raw_results) - self.assertEqual(tests_to_rebaseline, {'external/wpt/x/y.html'}) + tests_to_rebaseline, results = updater.filter_results_for_update( + raw_results) + self.assertEqual(tests_to_rebaseline, {'external/wpt/test/path.html'}) # The record for the builder with a timeout is kept, but not with a text mismatch, # since that should be covered by downloading a new baseline. - self.assertEqual(result.test_name(), 'external/wpt/x/z.html') + self.assertEqual(len(results), 1) + (result, ) = results + self.assertEqual(result.test_name(), 'external/wpt/reftest.html') # The original container isn't modified. self.assertEqual(len(raw_results), 2)
diff --git a/third_party/blink/tools/blinkpy/web_tests/port/base.py b/third_party/blink/tools/blinkpy/web_tests/port/base.py index c7e1607..4e38d40e 100644 --- a/third_party/blink/tools/blinkpy/web_tests/port/base.py +++ b/third_party/blink/tools/blinkpy/web_tests/port/base.py
@@ -1604,6 +1604,16 @@ return self._filesystem.isfile(self.abspath_for_test(path)) return False + def get_wpt_type(self, test_name: str) -> Optional[str]: + """Returns the test type of a web platform test.""" + + base_test = self.lookup_virtual_test_base(test_name) or test_name + wpt_dir, url_from_wpt_dir = self.split_wpt_dir(base_test) + if not wpt_dir: + return None # Not a WPT. + manifest = self.wpt_manifest(wpt_dir) + return manifest.get_test_type(url_from_wpt_dir) + def is_wpt_crash_test(self, test_name): """Returns whether a WPT test is a crashtest.
diff --git a/third_party/blink/web_tests/CfTTestExpecations b/third_party/blink/web_tests/CfTTestExpecations index 818a625..f3c38d151 100644 --- a/third_party/blink/web_tests/CfTTestExpecations +++ b/third_party/blink/web_tests/CfTTestExpecations
@@ -5,4 +5,8 @@ # This file is used for tests that only need to be suppressed on # the Chromium Blink CfT bots. -crbug.com/380778943 external/wpt/ai/translator/* [ Skip ] \ No newline at end of file +crbug.com/380778943 external/wpt/ai/translator/* [ Skip ] + +# These tests require the component updater and thus do not run on CfT bots. +external/wpt/speech-api/SpeechRecognition-installOnDevice.https.html [ Skip Timeout ] +virtual/speech-with-unified-autoplay/external/wpt/speech-api/SpeechRecognition-installOnDevice.https.html [ Skip Timeout ] \ No newline at end of file
diff --git a/third_party/blink/web_tests/TestExpectations b/third_party/blink/web_tests/TestExpectations index 713eb0c9..a5bed9d 100644 --- a/third_party/blink/web_tests/TestExpectations +++ b/third_party/blink/web_tests/TestExpectations
@@ -7394,9 +7394,6 @@ crbug.com/1464614 [ Win11-arm64 ] virtual/pna-workers-enabled/external/wpt/fetch/private-network-access/redirect.tentative.https.window.html [ Failure Pass Timeout ] # Flaky crbug.com/1464614 [ Win11-arm64 ] http/tests/media/video-play-stall-before-meta-data.html [ Failure Pass Timeout ] # Flaky -# Gardener 2023-07-26 -crbug.com/1364187 http/tests/misc/scroll-cross-origin-iframes.html [ Failure Pass ] - # Gardener 2023-07-31 crbug.com/1468996 fast/events/touch/gesture/gesture-tap-active-state.html [ Failure Pass ] # flaky crbug.com/1468996 fast/events/touch/gesture/gesture-tap-active-state-hidden-iframe.html [ Failure Pass ] # flaky
diff --git a/third_party/blink/web_tests/external/wpt/WebCryptoAPI/import_export/okp_importKey_Ed25519.https.any-expected.txt b/third_party/blink/web_tests/external/wpt/WebCryptoAPI/import_export/okp_importKey_Ed25519.https.any-expected.txt deleted file mode 100644 index be7b16f..0000000 --- a/third_party/blink/web_tests/external/wpt/WebCryptoAPI/import_export/okp_importKey_Ed25519.https.any-expected.txt +++ /dev/null
@@ -1,44 +0,0 @@ -This is a testharness.js-based test. -Found 20 FAIL, 0 TIMEOUT, 0 NOTRUN. -[FAIL] Good parameters with JWK alg Ed25519: Ed25519 (jwk, object(kty, crv, x), {name: Ed25519}, true, [verify]) - assert_unreached: Threw an unexpected error: DataError: The JWK "alg" member was inconsistent with that specified by the Web Crypto call Reached unreachable code -[FAIL] Good parameters with JWK alg EdDSA: Ed25519 (jwk, object(kty, crv, x), {name: Ed25519}, true, [verify]) - assert_equals: Correct number of JWK members expected 6 but got 5 -[FAIL] Good parameters with JWK alg Ed25519: Ed25519 (jwk, object(kty, crv, x), Ed25519, true, [verify]) - assert_unreached: Threw an unexpected error: DataError: The JWK "alg" member was inconsistent with that specified by the Web Crypto call Reached unreachable code -[FAIL] Good parameters with JWK alg EdDSA: Ed25519 (jwk, object(kty, crv, x), Ed25519, true, [verify]) - assert_equals: Correct number of JWK members expected 6 but got 5 -[FAIL] Good parameters with JWK alg Ed25519: Ed25519 (jwk, object(kty, crv, x), {name: Ed25519}, true, []) - assert_unreached: Threw an unexpected error: DataError: The JWK "alg" member was inconsistent with that specified by the Web Crypto call Reached unreachable code -[FAIL] Good parameters with JWK alg EdDSA: Ed25519 (jwk, object(kty, crv, x), {name: Ed25519}, true, []) - assert_equals: Correct number of JWK members expected 6 but got 5 -[FAIL] Good parameters with JWK alg Ed25519: Ed25519 (jwk, object(kty, crv, x), Ed25519, true, []) - assert_unreached: Threw an unexpected error: DataError: The JWK "alg" member was inconsistent with that specified by the Web Crypto call Reached unreachable code -[FAIL] Good parameters with JWK alg EdDSA: Ed25519 (jwk, object(kty, crv, x), Ed25519, true, []) - assert_equals: Correct number of JWK members expected 6 but got 5 -[FAIL] Good parameters with JWK alg Ed25519: Ed25519 (jwk, object(kty, crv, x), {name: Ed25519}, true, [verify, verify]) - assert_unreached: Threw an unexpected error: DataError: The JWK "alg" member was inconsistent with that specified by the Web Crypto call Reached unreachable code -[FAIL] Good parameters with JWK alg EdDSA: Ed25519 (jwk, object(kty, crv, x), {name: Ed25519}, true, [verify, verify]) - assert_equals: Correct number of JWK members expected 6 but got 5 -[FAIL] Good parameters with JWK alg Ed25519: Ed25519 (jwk, object(kty, crv, x), Ed25519, true, [verify, verify]) - assert_unreached: Threw an unexpected error: DataError: The JWK "alg" member was inconsistent with that specified by the Web Crypto call Reached unreachable code -[FAIL] Good parameters with JWK alg EdDSA: Ed25519 (jwk, object(kty, crv, x), Ed25519, true, [verify, verify]) - assert_equals: Correct number of JWK members expected 6 but got 5 -[FAIL] Good parameters with JWK alg Ed25519: Ed25519 (jwk, object(crv, d, x, kty), {name: Ed25519}, true, [sign]) - assert_unreached: Threw an unexpected error: DataError: The JWK "alg" member was inconsistent with that specified by the Web Crypto call Reached unreachable code -[FAIL] Good parameters with JWK alg EdDSA: Ed25519 (jwk, object(crv, d, x, kty), {name: Ed25519}, true, [sign]) - assert_equals: Correct number of JWK members expected 7 but got 6 -[FAIL] Good parameters with JWK alg Ed25519: Ed25519 (jwk, object(crv, d, x, kty), Ed25519, true, [sign]) - assert_unreached: Threw an unexpected error: DataError: The JWK "alg" member was inconsistent with that specified by the Web Crypto call Reached unreachable code -[FAIL] Good parameters with JWK alg EdDSA: Ed25519 (jwk, object(crv, d, x, kty), Ed25519, true, [sign]) - assert_equals: Correct number of JWK members expected 7 but got 6 -[FAIL] Good parameters with JWK alg Ed25519: Ed25519 (jwk, object(crv, d, x, kty), {name: Ed25519}, true, [sign, sign]) - assert_unreached: Threw an unexpected error: DataError: The JWK "alg" member was inconsistent with that specified by the Web Crypto call Reached unreachable code -[FAIL] Good parameters with JWK alg EdDSA: Ed25519 (jwk, object(crv, d, x, kty), {name: Ed25519}, true, [sign, sign]) - assert_equals: Correct number of JWK members expected 7 but got 6 -[FAIL] Good parameters with JWK alg Ed25519: Ed25519 (jwk, object(crv, d, x, kty), Ed25519, true, [sign, sign]) - assert_unreached: Threw an unexpected error: DataError: The JWK "alg" member was inconsistent with that specified by the Web Crypto call Reached unreachable code -[FAIL] Good parameters with JWK alg EdDSA: Ed25519 (jwk, object(crv, d, x, kty), Ed25519, true, [sign, sign]) - assert_equals: Correct number of JWK members expected 7 but got 6 -Harness: the test ran to completion. -
diff --git a/third_party/blink/web_tests/external/wpt/WebCryptoAPI/import_export/okp_importKey_Ed25519.https.any.worker-expected.txt b/third_party/blink/web_tests/external/wpt/WebCryptoAPI/import_export/okp_importKey_Ed25519.https.any.worker-expected.txt deleted file mode 100644 index be7b16f..0000000 --- a/third_party/blink/web_tests/external/wpt/WebCryptoAPI/import_export/okp_importKey_Ed25519.https.any.worker-expected.txt +++ /dev/null
@@ -1,44 +0,0 @@ -This is a testharness.js-based test. -Found 20 FAIL, 0 TIMEOUT, 0 NOTRUN. -[FAIL] Good parameters with JWK alg Ed25519: Ed25519 (jwk, object(kty, crv, x), {name: Ed25519}, true, [verify]) - assert_unreached: Threw an unexpected error: DataError: The JWK "alg" member was inconsistent with that specified by the Web Crypto call Reached unreachable code -[FAIL] Good parameters with JWK alg EdDSA: Ed25519 (jwk, object(kty, crv, x), {name: Ed25519}, true, [verify]) - assert_equals: Correct number of JWK members expected 6 but got 5 -[FAIL] Good parameters with JWK alg Ed25519: Ed25519 (jwk, object(kty, crv, x), Ed25519, true, [verify]) - assert_unreached: Threw an unexpected error: DataError: The JWK "alg" member was inconsistent with that specified by the Web Crypto call Reached unreachable code -[FAIL] Good parameters with JWK alg EdDSA: Ed25519 (jwk, object(kty, crv, x), Ed25519, true, [verify]) - assert_equals: Correct number of JWK members expected 6 but got 5 -[FAIL] Good parameters with JWK alg Ed25519: Ed25519 (jwk, object(kty, crv, x), {name: Ed25519}, true, []) - assert_unreached: Threw an unexpected error: DataError: The JWK "alg" member was inconsistent with that specified by the Web Crypto call Reached unreachable code -[FAIL] Good parameters with JWK alg EdDSA: Ed25519 (jwk, object(kty, crv, x), {name: Ed25519}, true, []) - assert_equals: Correct number of JWK members expected 6 but got 5 -[FAIL] Good parameters with JWK alg Ed25519: Ed25519 (jwk, object(kty, crv, x), Ed25519, true, []) - assert_unreached: Threw an unexpected error: DataError: The JWK "alg" member was inconsistent with that specified by the Web Crypto call Reached unreachable code -[FAIL] Good parameters with JWK alg EdDSA: Ed25519 (jwk, object(kty, crv, x), Ed25519, true, []) - assert_equals: Correct number of JWK members expected 6 but got 5 -[FAIL] Good parameters with JWK alg Ed25519: Ed25519 (jwk, object(kty, crv, x), {name: Ed25519}, true, [verify, verify]) - assert_unreached: Threw an unexpected error: DataError: The JWK "alg" member was inconsistent with that specified by the Web Crypto call Reached unreachable code -[FAIL] Good parameters with JWK alg EdDSA: Ed25519 (jwk, object(kty, crv, x), {name: Ed25519}, true, [verify, verify]) - assert_equals: Correct number of JWK members expected 6 but got 5 -[FAIL] Good parameters with JWK alg Ed25519: Ed25519 (jwk, object(kty, crv, x), Ed25519, true, [verify, verify]) - assert_unreached: Threw an unexpected error: DataError: The JWK "alg" member was inconsistent with that specified by the Web Crypto call Reached unreachable code -[FAIL] Good parameters with JWK alg EdDSA: Ed25519 (jwk, object(kty, crv, x), Ed25519, true, [verify, verify]) - assert_equals: Correct number of JWK members expected 6 but got 5 -[FAIL] Good parameters with JWK alg Ed25519: Ed25519 (jwk, object(crv, d, x, kty), {name: Ed25519}, true, [sign]) - assert_unreached: Threw an unexpected error: DataError: The JWK "alg" member was inconsistent with that specified by the Web Crypto call Reached unreachable code -[FAIL] Good parameters with JWK alg EdDSA: Ed25519 (jwk, object(crv, d, x, kty), {name: Ed25519}, true, [sign]) - assert_equals: Correct number of JWK members expected 7 but got 6 -[FAIL] Good parameters with JWK alg Ed25519: Ed25519 (jwk, object(crv, d, x, kty), Ed25519, true, [sign]) - assert_unreached: Threw an unexpected error: DataError: The JWK "alg" member was inconsistent with that specified by the Web Crypto call Reached unreachable code -[FAIL] Good parameters with JWK alg EdDSA: Ed25519 (jwk, object(crv, d, x, kty), Ed25519, true, [sign]) - assert_equals: Correct number of JWK members expected 7 but got 6 -[FAIL] Good parameters with JWK alg Ed25519: Ed25519 (jwk, object(crv, d, x, kty), {name: Ed25519}, true, [sign, sign]) - assert_unreached: Threw an unexpected error: DataError: The JWK "alg" member was inconsistent with that specified by the Web Crypto call Reached unreachable code -[FAIL] Good parameters with JWK alg EdDSA: Ed25519 (jwk, object(crv, d, x, kty), {name: Ed25519}, true, [sign, sign]) - assert_equals: Correct number of JWK members expected 7 but got 6 -[FAIL] Good parameters with JWK alg Ed25519: Ed25519 (jwk, object(crv, d, x, kty), Ed25519, true, [sign, sign]) - assert_unreached: Threw an unexpected error: DataError: The JWK "alg" member was inconsistent with that specified by the Web Crypto call Reached unreachable code -[FAIL] Good parameters with JWK alg EdDSA: Ed25519 (jwk, object(crv, d, x, kty), Ed25519, true, [sign, sign]) - assert_equals: Correct number of JWK members expected 7 but got 6 -Harness: the test ran to completion. -
diff --git a/third_party/blink/web_tests/external/wpt/ai/language_detection/detector.https.tentative.any.js b/third_party/blink/web_tests/external/wpt/ai/language_detection/detector.https.tentative.any.js index 57c98ab..8e4bedd 100644 --- a/third_party/blink/web_tests/external/wpt/ai/language_detection/detector.https.tentative.any.js +++ b/third_party/blink/web_tests/external/wpt/ai/language_detection/detector.https.tentative.any.js
@@ -23,25 +23,7 @@ }, 'Simple LanguageDetector.detect() call'); promise_test(async t => { - let createResult = undefined; - const progressEvents = []; - let options = {}; - const downloadComplete = new Promise(resolve => { - options.monitor = (m) => { - m.addEventListener("downloadprogress", e => { - assert_equals(createResult, undefined); - assert_equals(e.total, 1); - progressEvents.push(e); - if (e.loaded == 1) { resolve(); } - }); - }; - }); - - createResult = await LanguageDetector.create(options); - await downloadComplete; - assert_greater_than_equal(progressEvents.length, 2); - assert_equals(progressEvents.at(0).loaded, 0); - assert_equals(progressEvents.at(-1).loaded, 1); + testMonitor(LanguageDetector.create); }, 'LanguageDetector.create() notifies its monitor on downloadprogress'); promise_test(async t => { @@ -114,9 +96,8 @@ }, 'Aborting LanguageDetector.measureInputUsage().'); promise_test(async () => { - const expected_languages = ['en', 'es']; - const detector = await languageDetector.create({ - expectedInputLanguages: expected_languages - }); - assert_array_equals(detector.expectedInputLanguages(), expected_languages); + const expectedLanguages = ['en', 'es']; + const detector = await LanguageDetector.create( + {expectedInputLanguages: expectedLanguages}); + assert_array_equals(detector.expectedInputLanguages, expectedLanguages); }, 'Creating LanguageDetector with expectedInputLanguages');
diff --git a/third_party/blink/web_tests/external/wpt/ai/resources/util.js b/third_party/blink/web_tests/external/wpt/ai/resources/util.js index 49d677e..6433404 100644 --- a/third_party/blink/web_tests/external/wpt/ai/resources/util.js +++ b/third_party/blink/web_tests/external/wpt/ai/resources/util.js
@@ -25,3 +25,33 @@ await promise_rejects_exactly(t, err, anotherPromise); } }; + +async function testMonitor(createFunc, options = {}) { + let created = false; + const progressEvents = []; + function monitor(m) { + m.addEventListener('downloadprogress', e => { + // No progress events should be fired after `createFunc` resolves. + assert_false(created); + + progressEvents.push(e); + }); + } + + await createFunc({...options, monitor}); + created = true; + + assert_greater_than_equal(progressEvents.length, 2); + assert_equals(progressEvents.at(0).loaded, 0); + assert_equals(progressEvents.at(-1).loaded, 1); + + let lastProgressEventLoaded = -1; + for (const progressEvent of progressEvents) { + assert_equals(progressEvent.total, 1); + assert_less_than_equal(progressEvent.loaded, progressEvent.total); + + // Progress events should have monotonically increasing `loaded` values. + assert_greater_than(progressEvent.loaded, lastProgressEventLoaded); + lastProgressEventLoaded = progressEvent.loaded; + } +}
diff --git a/third_party/blink/web_tests/external/wpt/ai/translator/translator_translate.tentative.https.any.js b/third_party/blink/web_tests/external/wpt/ai/translator/translator_translate.tentative.https.any.js index 1c58675..a8aad5e 100644 --- a/third_party/blink/web_tests/external/wpt/ai/translator/translator_translate.tentative.https.any.js +++ b/third_party/blink/web_tests/external/wpt/ai/translator/translator_translate.tentative.https.any.js
@@ -108,25 +108,8 @@ }, 'Aborting Translator.translate().'); promise_test(async t => { - let createResult = undefined; - const progressEvents = []; - let options = {sourceLanguage: 'en', targetLanguage: 'ja'}; - const downloadComplete = new Promise(resolve => { - options.monitor = (m) => { - m.addEventListener("downloadprogress", e => { - assert_equals(createResult, undefined); - assert_equals(e.total, 1); - progressEvents.push(e); - if (e.loaded == 1) { resolve(); } - }); - }; - }); - - createResult = await createTranslator(options); - await downloadComplete; - assert_greater_than_equal(progressEvents.length, 2); - assert_equals(progressEvents.at(0).loaded, 0); - assert_equals(progressEvents.at(-1).loaded, 1); + await testMonitor( + createTranslator, {sourceLanguage: 'en', targetLanguage: 'ja'}); }, 'Translator.create() notifies its monitor on downloadprogress'); promise_test(async t => {
diff --git a/third_party/blink/web_tests/external/wpt/css/css-gaps/tentative/grid/grid-gap-decorations-016-ref.html b/third_party/blink/web_tests/external/wpt/css/css-gaps/tentative/grid/grid-gap-decorations-016-ref.html new file mode 100644 index 0000000..1d368ab --- /dev/null +++ b/third_party/blink/web_tests/external/wpt/css/css-gaps/tentative/grid/grid-gap-decorations-016-ref.html
@@ -0,0 +1,111 @@ +<!DOCTYPE html> +<link rel="help" href="https://drafts.csswg.org/css-gaps-1/#lists-repeat"> +<link rel="author" title="Sam Davis Omekara Jr." href="mailto:samomekarajr@microsoft.com"> +<style> + body { + margin: 0px; + } + + .grid-container { + display: grid; + grid-gap: 10px; + grid-template-columns: repeat(5, 100px); + height: 540px; + width: 540px; + } + + .row-gap { + height: 0px; + width: 540px; + position: absolute; + } + + .row-gap1 { + border-bottom: 5px dashed red; + top: 102.5px; + } + + .row-gap2 { + border-bottom: 5px groove red; + top: 212.5px; + } + + .row-gap3 { + border-bottom: 5px ridge red; + top: 322.5px; + } + + .row-gap4 { + border-bottom: 5px dashed red; + top: 432.5px; + } + + .col-gap { + width: 0px; + height: 540px; + position: absolute; + top: 0px; + } + + .col-gap1 { + border-left: 5px solid blue; + left: 102.5px; + } + + .col-gap2 { + border-left: 5px dotted blue; + left: 212.5px; + } + + .col-gap3 { + border-left: 5px double blue; + left: 322.5px; + } + + .col-gap4 { + border-left: 5px solid blue; + left: 432.5px; + } + + .item { + background: gray; + opacity: 0.5; + } +</style> +<div class="grid-container"> + <div class="item"></div> + <div class="item"></div> + <div class="item"></div> + <div class="item"></div> + <div class="item"></div> + <div class="item"></div> + <div class="item"></div> + <div class="item"></div> + <div class="item"></div> + <div class="item"></div> + <div class="item"></div> + <div class="item"></div> + <div class="item"></div> + <div class="item"></div> + <div class="item"></div> + <div class="item"></div> + <div class="item"></div> + <div class="item"></div> + <div class="item"></div> + <div class="item"></div> + <div class="item"></div> + <div class="item"></div> + <div class="item"></div> + <div class="item"></div> + <div class="item"></div> +</div> + +<div class="row-gap row-gap1"> </div> +<div class="row-gap row-gap2"> </div> +<div class="row-gap row-gap3"> </div> +<div class="row-gap row-gap4"> </div> + +<div class="col-gap col-gap1"> </div> +<div class="col-gap col-gap2"> </div> +<div class="col-gap col-gap3"> </div> +<div class="col-gap col-gap4"> </div>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-gaps/tentative/grid/grid-gap-decorations-016.html b/third_party/blink/web_tests/external/wpt/css/css-gaps/tentative/grid/grid-gap-decorations-016.html new file mode 100644 index 0000000..a0618e44 --- /dev/null +++ b/third_party/blink/web_tests/external/wpt/css/css-gaps/tentative/grid/grid-gap-decorations-016.html
@@ -0,0 +1,60 @@ +<!DOCTYPE html> +<title> + CSS Gap Decorations: Grid gaps are painted with multiple line-style values for *-rule-style. +</title> +<link rel="help" href="https://drafts.csswg.org/css-gaps-1/#lists-repeat"> +<link rel="match" href="grid-gap-decorations-016-ref.html"> +<link rel="author" title="Sam Davis Omekara Jr." href="mailto:samomekarajr@microsoft.com"> +<style> + body { + margin: 0px; + } + + .grid-container { + display: grid; + grid-gap: 10px; + grid-template-columns: repeat(5, 100px); + height: 540px; + width: 540px; + + column-rule-color: blue; + column-rule-style: solid dotted double; + column-rule-width: 5px; + + row-rule-color: red; + row-rule-style: dashed groove ridge; + row-rule-width: 5px; + } + + .item { + background: gray; + opacity: 0.5; + } +</style> +<div class="grid-container"> + <div class="item"></div> + <div class="item"></div> + <div class="item"></div> + <div class="item"></div> + <div class="item"></div> + <div class="item"></div> + <div class="item"></div> + <div class="item"></div> + <div class="item"></div> + <div class="item"></div> + <div class="item"></div> + <div class="item"></div> + <div class="item"></div> + <div class="item"></div> + <div class="item"></div> + <div class="item"></div> + <div class="item"></div> + <div class="item"></div> + <div class="item"></div> + <div class="item"></div> + <div class="item"></div> + <div class="item"></div> + <div class="item"></div> + <div class="item"></div> + <div class="item"></div> +</div>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-gaps/tentative/grid/grid-gap-decorations-017-ref.html b/third_party/blink/web_tests/external/wpt/css/css-gaps/tentative/grid/grid-gap-decorations-017-ref.html new file mode 100644 index 0000000..2aa226eb --- /dev/null +++ b/third_party/blink/web_tests/external/wpt/css/css-gaps/tentative/grid/grid-gap-decorations-017-ref.html
@@ -0,0 +1,135 @@ +<!DOCTYPE html> +<link rel="help" href="https://drafts.csswg.org/css-gaps-1/#lists-repeat"> +<link rel="author" title="Sam Davis Omekara Jr." href="mailto:samomekarajr@microsoft.com"> +<style> + body { + margin: 0px; + } + + .grid-container { + display: grid; + grid-gap: 10px; + grid-template-columns: repeat(6, 100px); + height: 650px; + width: 650px; + } + + .row-gap { + height: 0px; + width: 650px; + position: absolute; + + } + + .row-gap1 { + border-bottom: 5px double red; + top: 102.5px; + } + + .row-gap2 { + border-bottom: 5px solid red; + top: 212.5px; + } + + .row-gap3 { + border-bottom: 5px dotted red; + top: 322.5px; + } + + .row-gap4 { + border-bottom: 5px ridge red; + top: 432.5px; + } + + .row-gap5 { + border-bottom: 5px ridge red; + top: 542.5px; + } + + .col-gap { + width: 0px; + height: 650px; + position: absolute; + top: 0px; + } + + .col-gap1 { + border-left: 5px solid blue; + left: 102.5px; + } + + .col-gap2 { + border-left: 5px groove blue; + left: 212.5px; + } + + .col-gap3 { + border-left: 5px double blue; + left: 322.5px; + } + + .col-gap4 { + border-left: 5px dotted blue; + left: 432.5px; + } + + .col-gap5 { + border-left: 5px dotted blue; + left: 542.5px; + } + + .item { + background: gray; + opacity: 0.5; + } +</style> +<div class="grid-container"> + <div class="item"></div> + <div class="item"></div> + <div class="item"></div> + <div class="item"></div> + <div class="item"></div> + <div class="item"></div> + <div class="item"></div> + <div class="item"></div> + <div class="item"></div> + <div class="item"></div> + <div class="item"></div> + <div class="item"></div> + <div class="item"></div> + <div class="item"></div> + <div class="item"></div> + <div class="item"></div> + <div class="item"></div> + <div class="item"></div> + <div class="item"></div> + <div class="item"></div> + <div class="item"></div> + <div class="item"></div> + <div class="item"></div> + <div class="item"></div> + <div class="item"></div> + <div class="item"></div> + <div class="item"></div> + <div class="item"></div> + <div class="item"></div> + <div class="item"></div> + <div class="item"></div> + <div class="item"></div> + <div class="item"></div> + <div class="item"></div> + <div class="item"></div> + <div class="item"></div> +</div> + +<div class="row-gap row-gap1"> </div> +<div class="row-gap row-gap2"> </div> +<div class="row-gap row-gap3"> </div> +<div class="row-gap row-gap4"> </div> +<div class="row-gap row-gap5"> </div> + +<div class="col-gap col-gap1"> </div> +<div class="col-gap col-gap2"> </div> +<div class="col-gap col-gap3"> </div> +<div class="col-gap col-gap4"> </div> +<div class="col-gap col-gap5"> </div>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-gaps/tentative/grid/grid-gap-decorations-017.html b/third_party/blink/web_tests/external/wpt/css/css-gaps/tentative/grid/grid-gap-decorations-017.html new file mode 100644 index 0000000..be0f7fc --- /dev/null +++ b/third_party/blink/web_tests/external/wpt/css/css-gaps/tentative/grid/grid-gap-decorations-017.html
@@ -0,0 +1,69 @@ +<!DOCTYPE html> +<title> + CSS Gap Decorations: Grid gaps are painted with multiple line-style values for *-rule-style. +</title> +<link rel="help" href="https://drafts.csswg.org/css-gaps-1/#lists-repeat"> +<link rel="match" href="grid-gap-decorations-017-ref.html"> +<link rel="author" title="Sam Davis Omekara Jr." href="mailto:samomekarajr@microsoft.com"> +<style> + body { + margin: 0px; + } + .grid-container { + display: grid; + grid-gap: 10px; + grid-template-columns: repeat(6, 100px); + height: 650px; + width: 650px; + + column-rule-color: blue; + column-rule-style: solid repeat(auto, groove double) repeat(2, dotted); + column-rule-width: 5px; + + row-rule-color: red; + row-rule-style: repeat(auto, double solid) dotted repeat(2, ridge); + row-rule-width: 5px; + } + .item { + background: gray; + opacity: 0.5; + } +</style> +<div class="grid-container"> + <div class="item"></div> + <div class="item"></div> + <div class="item"></div> + <div class="item"></div> + <div class="item"></div> + <div class="item"></div> + <div class="item"></div> + <div class="item"></div> + <div class="item"></div> + <div class="item"></div> + <div class="item"></div> + <div class="item"></div> + <div class="item"></div> + <div class="item"></div> + <div class="item"></div> + <div class="item"></div> + <div class="item"></div> + <div class="item"></div> + <div class="item"></div> + <div class="item"></div> + <div class="item"></div> + <div class="item"></div> + <div class="item"></div> + <div class="item"></div> + <div class="item"></div> + <div class="item"></div> + <div class="item"></div> + <div class="item"></div> + <div class="item"></div> + <div class="item"></div> + <div class="item"></div> + <div class="item"></div> + <div class="item"></div> + <div class="item"></div> + <div class="item"></div> + <div class="item"></div> +</div>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-gaps/tentative/grid/grid-gap-decorations-018-ref.html b/third_party/blink/web_tests/external/wpt/css/css-gaps/tentative/grid/grid-gap-decorations-018-ref.html new file mode 100644 index 0000000..6fcc175 --- /dev/null +++ b/third_party/blink/web_tests/external/wpt/css/css-gaps/tentative/grid/grid-gap-decorations-018-ref.html
@@ -0,0 +1,134 @@ +<!DOCTYPE html> +<link rel="help" href="https://drafts.csswg.org/css-gaps-1/#lists-repeat"> +<link rel="author" title="Sam Davis Omekara Jr." href="mailto:samomekarajr@microsoft.com"> +<style> + body { + margin: 0px; + } + + .grid-container { + display: grid; + grid-gap: 10px; + grid-template-columns: repeat(6, 100px); + height: 650px; + width: 650px; + } + + .row-gap { + height: 0px; + width: 650px; + position: absolute; + } + + .row-gap1 { + border-bottom: 8px solid red; + top: 101px; + } + + .row-gap2 { + border-bottom: 5px solid red; + top: 212.5px; + } + + .row-gap3 { + border-bottom: 2px solid red; + top: 324px; + } + + .row-gap4 { + border-bottom: 8px solid red; + top: 431px; + } + + .row-gap5 { + border-bottom: 5px solid red; + top: 542.5px; + } + + .col-gap { + width: 0px; + height: 650px; + position: absolute; + top: 0px; + } + + .col-gap1 { + border-left: 2px solid blue; + left: 104px; + } + + .col-gap2 { + border-left: 5px solid blue; + left: 212.5px; + } + + .col-gap3 { + border-left: 8px solid blue; + left: 321px; + } + + .col-gap4 { + border-left: 2px solid blue; + left: 434px; + } + + .col-gap5 { + border-left: 5px solid blue; + left: 542.5px; + } + + .item { + background: gray; + opacity: 0.5; + } +</style> +<div class="grid-container"> + <div class="item"></div> + <div class="item"></div> + <div class="item"></div> + <div class="item"></div> + <div class="item"></div> + <div class="item"></div> + <div class="item"></div> + <div class="item"></div> + <div class="item"></div> + <div class="item"></div> + <div class="item"></div> + <div class="item"></div> + <div class="item"></div> + <div class="item"></div> + <div class="item"></div> + <div class="item"></div> + <div class="item"></div> + <div class="item"></div> + <div class="item"></div> + <div class="item"></div> + <div class="item"></div> + <div class="item"></div> + <div class="item"></div> + <div class="item"></div> + <div class="item"></div> + <div class="item"></div> + <div class="item"></div> + <div class="item"></div> + <div class="item"></div> + <div class="item"></div> + <div class="item"></div> + <div class="item"></div> + <div class="item"></div> + <div class="item"></div> + <div class="item"></div> + <div class="item"></div> +</div> + +<div class="row-gap row-gap1"> </div> +<div class="row-gap row-gap2"> </div> +<div class="row-gap row-gap3"> </div> +<div class="row-gap row-gap4"> </div> +<div class="row-gap row-gap5"> </div> + +<div class="col-gap col-gap1"> </div> +<div class="col-gap col-gap2"> </div> +<div class="col-gap col-gap3"> </div> +<div class="col-gap col-gap4"> </div> +<div class="col-gap col-gap5"> </div>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-gaps/tentative/grid/grid-gap-decorations-018.html b/third_party/blink/web_tests/external/wpt/css/css-gaps/tentative/grid/grid-gap-decorations-018.html new file mode 100644 index 0000000..dc5e2e3 --- /dev/null +++ b/third_party/blink/web_tests/external/wpt/css/css-gaps/tentative/grid/grid-gap-decorations-018.html
@@ -0,0 +1,71 @@ +<!DOCTYPE html> +<title> + CSS Gap Decorations: Grid gaps are painted with multiple line-width values for *-rule-width. +</title> +<link rel="help" href="https://drafts.csswg.org/css-gaps-1/#lists-repeat"> +<link rel="match" href="grid-gap-decorations-018-ref.html"> +<link rel="author" title="Sam Davis Omekara Jr." href="mailto:samomekarajr@microsoft.com"> +<style> + body { + margin: 0px; + } + + .grid-container { + display: grid; + grid-gap: 10px; + grid-template-columns: repeat(6, 100px); + height: 650px; + width: 650px; + + column-rule-color: blue; + column-rule-style: solid; + column-rule-width: 2px 5px 8px; + + row-rule-color: red; + row-rule-style: solid; + row-rule-width: 8px 5px 2px; + } + + .item { + background: gray; + opacity: 0.5; + } +</style> +<div class="grid-container"> + <div class="item"></div> + <div class="item"></div> + <div class="item"></div> + <div class="item"></div> + <div class="item"></div> + <div class="item"></div> + <div class="item"></div> + <div class="item"></div> + <div class="item"></div> + <div class="item"></div> + <div class="item"></div> + <div class="item"></div> + <div class="item"></div> + <div class="item"></div> + <div class="item"></div> + <div class="item"></div> + <div class="item"></div> + <div class="item"></div> + <div class="item"></div> + <div class="item"></div> + <div class="item"></div> + <div class="item"></div> + <div class="item"></div> + <div class="item"></div> + <div class="item"></div> + <div class="item"></div> + <div class="item"></div> + <div class="item"></div> + <div class="item"></div> + <div class="item"></div> + <div class="item"></div> + <div class="item"></div> + <div class="item"></div> + <div class="item"></div> + <div class="item"></div> + <div class="item"></div> +</div>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-gaps/tentative/grid/grid-gap-decorations-019-ref.html b/third_party/blink/web_tests/external/wpt/css/css-gaps/tentative/grid/grid-gap-decorations-019-ref.html new file mode 100644 index 0000000..0705ba07 --- /dev/null +++ b/third_party/blink/web_tests/external/wpt/css/css-gaps/tentative/grid/grid-gap-decorations-019-ref.html
@@ -0,0 +1,137 @@ +<!DOCTYPE html> +<link rel="help" href="https://drafts.csswg.org/css-gaps-1/#lists-repeat"> +<link rel="author" title="Sam Davis Omekara Jr." href="mailto:samomekarajr@microsoft.com"> +<style> + body { + margin: 0px; + } + + .grid-container { + display: grid; + grid-gap: 10px; + grid-template-columns: repeat(6, 100px); + height: 650px; + width: 650px; + } + + .row-gap { + height: 0px; + width: 650px; + position: absolute; + + } + + .row-gap1 { + border-bottom: 10px solid red; + top: 100px; + } + + .row-gap2 { + border-bottom: 8px solid red; + top: 211px; + } + + .row-gap3 { + border-bottom: 2px solid red; + top: 324px; + } + + .row-gap4 { + border-bottom: 2px solid red; + top: 434px; + } + + .row-gap5 { + border-bottom: 5px solid red; + top: 542.5px; + } + + .col-gap { + width: 0px; + height: 650px; + position: absolute; + top: 0px; + } + + .col-gap1 { + border-left: 2px solid blue; + left: 104px; + } + + .col-gap2 { + border-left: 5px solid blue; + left: 212.5px; + } + + .col-gap3 { + border-left: 2px solid blue; + left: 324px; + } + + .col-gap4 { + border-left: 10px solid blue; + left: 430px; + } + + .col-gap5 { + border-left: 10px solid blue; + left: 540px; + } + + .item { + background: gray; + opacity: 0.5; + } +</style> +<div class="grid-container"> + <div class="item"></div> + <div class="item"></div> + <div class="item"></div> + <div class="item"></div> + <div class="item"></div> + <div class="item"></div> + <div class="item"></div> + <div class="item"></div> + <div class="item"></div> + <div class="item"></div> + <div class="item"></div> + <div class="item"></div> + <div class="item"></div> + <div class="item"></div> + <div class="item"></div> + <div class="item"></div> + <div class="item"></div> + <div class="item"></div> + <div class="item"></div> + <div class="item"></div> + <div class="item"></div> + <div class="item"></div> + <div class="item"></div> + <div class="item"></div> + <div class="item"></div> + <div class="item"></div> + <div class="item"></div> + <div class="item"></div> + <div class="item"></div> + <div class="item"></div> + <div class="item"></div> + <div class="item"></div> + <div class="item"></div> + <div class="item"></div> + <div class="item"></div> + <div class="item"></div> +</div> + + +<div class="row-gap row-gap1"> </div> +<div class="row-gap row-gap2"> </div> +<div class="row-gap row-gap3"> </div> +<div class="row-gap row-gap4"> </div> +<div class="row-gap row-gap5"> </div> + + +<div class="col-gap col-gap1"> </div> +<div class="col-gap col-gap2"> </div> +<div class="col-gap col-gap3"> </div> +<div class="col-gap col-gap4"> </div> +<div class="col-gap col-gap5"> </div>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-gaps/tentative/grid/grid-gap-decorations-019.html b/third_party/blink/web_tests/external/wpt/css/css-gaps/tentative/grid/grid-gap-decorations-019.html new file mode 100644 index 0000000..637e0467a --- /dev/null +++ b/third_party/blink/web_tests/external/wpt/css/css-gaps/tentative/grid/grid-gap-decorations-019.html
@@ -0,0 +1,71 @@ +<!DOCTYPE html> +<title> + CSS Gap Decorations: Grid gaps are painted with multiple line-width values for *-rule-width. +</title> +<link rel="help" href="https://drafts.csswg.org/css-gaps-1/"> +<link rel="match" href="grid-gap-decorations-019-ref.html"> +<link rel="author" title="Sam Davis Omekara Jr." href="mailto:samomekarajr@microsoft.com"> +<style> + body { + margin: 0px; + } + + .grid-container { + display: grid; + grid-gap: 10px; + grid-template-columns: repeat(6, 100px); + height: 650px; + width: 650px; + + column-rule-color: blue; + column-rule-style: solid; + column-rule-width: 2px repeat(auto, 5px 2px) repeat(2, 10px); + + row-rule-color: red; + row-rule-style: solid; + row-rule-width: repeat(auto, 10px 8px) repeat(2, 2px) 5px; + } + + .item { + background: gray; + opacity: 0.5; + } +</style> +<div class="grid-container"> + <div class="item"></div> + <div class="item"></div> + <div class="item"></div> + <div class="item"></div> + <div class="item"></div> + <div class="item"></div> + <div class="item"></div> + <div class="item"></div> + <div class="item"></div> + <div class="item"></div> + <div class="item"></div> + <div class="item"></div> + <div class="item"></div> + <div class="item"></div> + <div class="item"></div> + <div class="item"></div> + <div class="item"></div> + <div class="item"></div> + <div class="item"></div> + <div class="item"></div> + <div class="item"></div> + <div class="item"></div> + <div class="item"></div> + <div class="item"></div> + <div class="item"></div> + <div class="item"></div> + <div class="item"></div> + <div class="item"></div> + <div class="item"></div> + <div class="item"></div> + <div class="item"></div> + <div class="item"></div> + <div class="item"></div> + <div class="item"></div> + <div class="item"></div> + <div class="item"></div> +</div>
diff --git a/third_party/blink/web_tests/external/wpt/dom/events/scrolling/scroll-cross-origin-iframes.html b/third_party/blink/web_tests/external/wpt/dom/events/scrolling/scroll-cross-origin-iframes.html new file mode 100644 index 0000000..4c87313 --- /dev/null +++ b/third_party/blink/web_tests/external/wpt/dom/events/scrolling/scroll-cross-origin-iframes.html
@@ -0,0 +1,77 @@ +<!doctype html> +<meta charset=utf-8> +<title>Verify that two sibling cross-origin iframes both correctly scroll on MouseWheel events, as per https://crbug.com/675695.</title> +<script src="/common/get-host-info.sub.js"></script> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="/resources/testdriver.js"></script> +<script src="/resources/testdriver-vendor.js"></script> +<script src="/resources/testdriver-actions.js"></script> +<script src="scroll_support.js"></script> +<body> + <iframe id="target-iframe1" + style="height: 100px; width: 100px; overflow-y: scroll; position: absolute; left: 50px; top: 50px"> + </iframe> + <iframe id="target-iframe2" + style="height: 100px; width: 100px; overflow-y: scroll; position: absolute; left: 50px; top: 200px"> + </iframe> + <script> + "use strict"; + + function waitForFrameLoadAsync(frame) { + return new Promise(async (resolve) => { + frame.addEventListener('load', resolve, { once: true }); + }); + } + + function waitForMessageAsync(expected_frame_id, expected_command) { + return new Promise((resolve) => { + window.addEventListener('message', (event) => { + assert_equals(event.data.command, expected_command); + assert_equals(event.data.frame_id, expected_frame_id); + resolve(event.data.scrollTop); + }, { once: true }); + }); + } + + function waitForCrossOriginFrameSetupAsync(frame) { + return new Promise(async (resolve) => { + const setup_ack_waiter = waitForMessageAsync(frame.id, 'setup'); + const load_ack_waiter = waitForFrameLoadAsync(frame); + + const host = get_host_info(); + frame.src = host.HTTP_NOTSAMESITE_ORIGIN + + "/dom/events/scrolling/scroll-cross-origin-iframes.sub.html"; + await load_ack_waiter; + await frame.contentWindow.postMessage({ + command: 'setup', + frame_id: frame.id + }, "*"); + const scroll_top = await setup_ack_waiter; + resolve(scroll_top); + }); + } + + promise_test(async () => { + var frame_map = new Map(); + for (const frame of document.querySelectorAll('iframe')) { + const scroll_top_before = await waitForCrossOriginFrameSetupAsync(frame); + frame_map.set(frame.id, scroll_top_before); + } + assert_equals(frame_map.size, 2); + + for (const [frame_id, scroll_top_before] of frame_map) { + const frame = document.getElementById(frame_id); + const scrollend_ack_waiter = waitForMessageAsync(frame_id, 'onscrollend'); + await new test_driver.Actions() + .scroll(/*cursor_x=*/0, /*cursor_y=*/0, + /*delta_x=*/0, /*delta_y=*/50, + {origin: frame}) + .send(); + const scroll_top_after = await scrollend_ack_waiter; + assert_greater_than(scroll_top_after, scroll_top_before, + 'Failed to advance scrollTop target'); + } + }, "Verify sibling cross-origin iframes can wheel-scroll."); + </script> +</body>
diff --git a/third_party/blink/web_tests/external/wpt/dom/events/scrolling/scroll-cross-origin-iframes.sub.html b/third_party/blink/web_tests/external/wpt/dom/events/scrolling/scroll-cross-origin-iframes.sub.html new file mode 100644 index 0000000..e6d1279 --- /dev/null +++ b/third_party/blink/web_tests/external/wpt/dom/events/scrolling/scroll-cross-origin-iframes.sub.html
@@ -0,0 +1,51 @@ +<!DOCTYPE html> +<div style="height: 300px"></div> +<script> +var is_setup = false; +var port; +var frame_id; + +function postReplyMessage(command) { + const message_target = (port ? port : parent); + message_target.postMessage({ + command: command, + frame_id: frame_id, + scrollTop: document.documentElement.scrollTop + }, "*"); +} + +function handleMessage(event) { + switch (event.data.command) { + case 'setup': + is_setup = false; + port = event.source; + frame_id = event.data.frame_id; + if (document.scrollingElement.scrollLeft != 0 || + document.scrollingElement.scrollTop != 0) { + document.scrollingElement.scrollTo(0, 0); + } else { + is_setup = true; + postReplyMessage('setup'); + } + break; + default: + break; + } +} + +function handleScrollend() { + var command = 'onscrollend'; + + if (!is_setup && + document.scrollingElement.scrollTop == 0 && + document.scrollingElement.scrollLeft == 0) { + is_setup = true; + command = 'setup'; + } + + postReplyMessage(command); +} + +window.addEventListener('message', handleMessage); +window.addEventListener('scrollend', handleScrollend); +</script>
diff --git a/third_party/blink/web_tests/external/wpt/speech-api/SpeechRecognition-installOnDevice.https.html b/third_party/blink/web_tests/external/wpt/speech-api/SpeechRecognition-installOnDevice.https.html index 08d370d..1d1dd35ed 100644 --- a/third_party/blink/web_tests/external/wpt/speech-api/SpeechRecognition-installOnDevice.https.html +++ b/third_party/blink/web_tests/external/wpt/speech-api/SpeechRecognition-installOnDevice.https.html
@@ -29,6 +29,32 @@ "installOnDevice should resolve with `true` when called with a supported language code." ); + // Verify that the newly installed language pack is available. + const availableOnDeviceResultPromise = SpeechRecognition.availableOnDevice(validLang); + assert_true( + availableOnDeviceResultPromise instanceof Promise, + "availableOnDevice should return a Promise." + ); + + const availableOnDeviceResult = await availableOnDeviceResultPromise; + assert_true( + typeof availableOnDeviceResult === "string", + "The resolved value of the availableOnDevice promise should be a string." + ); + + assert_true(availableOnDeviceResult === "available", + "The resolved value of the availableOnDevice promise should be available." + ); + + // Verify that installing an already installed language pack resolves to true. + const secondResultPromise = SpeechRecognition.installOnDevice(validLang); + const secondResult = await secondResultPromise; + assert_equals( + secondResult, + true, + "installOnDevice should resolve with `true` if the language is already installed." + ); + // Test that it returns a promise. const invalidResultPromise = SpeechRecognition.installOnDevice(invalidLang); const invalidResult = await invalidResultPromise;
diff --git a/third_party/blink/web_tests/http/tests/misc/scroll-cross-origin-iframes.html b/third_party/blink/web_tests/http/tests/misc/scroll-cross-origin-iframes.html deleted file mode 100644 index e7ac09ac..0000000 --- a/third_party/blink/web_tests/http/tests/misc/scroll-cross-origin-iframes.html +++ /dev/null
@@ -1,46 +0,0 @@ -<!DOCTYPE html> -<script src="/resources/testharness.js"></script> -<script src='/resources/testharnessreport.js'></script> -<iframe id="target-iframe1" src="http://localhost:8080/misc/resources/cross-origin-subframe-for-scrolling.html" style="height: 100px; width: 100px; overflow-y: scroll; position: absolute; left: 50px; top: 50px"></iframe> -<iframe id="target-iframe2" src="http://localhost:8080/misc/resources/cross-origin-subframe-for-scrolling.html" style="height: 100px; width: 100px; overflow-y: scroll; position: absolute; left: 50px; top: 200px"></iframe> - -<script> -var scroll_test = async_test("Verify that two sibling cross-origin iframes both correctly scroll on MouseWheel events, as per https://crbug.com/675695."); - -var last_frame_scrolled; -var iframe1 = document.getElementById("target-iframe1"); -var iframe2 = document.getElementById("target-iframe2"); - -function handleMessage(event) { - if (event.data.scrolled == 1 && last_frame_scrolled == 1) { - scroll_test.step(() => { assert_greater_than(event.data.scrollTop, 0, "iframe1 scroll offset greater than 0.")} ); - last_frame_scrolled = 2; - scrollSubframe(iframe2, 2); - } else if (event.data.scrolled == 2) { - scroll_test.step(() => { assert_greater_than(event.data.scrollTop, 0, "iframe2 scroll offset greater than 0.")} ); - scroll_test.done(); - } else if (event.data.scrolled != 1){ - assert_unreached("Received invalid postMessage from subframe."); - } -} - -window.addEventListener("message", handleMessage); - -iframe1.onload = (() => { - last_frame_scrolled = 1; - scrollSubframe(iframe1, 1); -}); - -function scrollSubframe(iframe_element, frame_id) { - iframe_element.contentWindow.postMessage( - {expectScrollBy: 1, frame_id: frame_id}, "*"); - var rect = iframe_element.getBoundingClientRect(); - chrome.gpuBenchmarking.smoothScrollByXY( - 0, 10, - /* resolve */ undefined, - rect.x + 5, rect.y + 5, - chrome.gpuBenchmarking.MOUSE_INPUT, - /* SPEED_INSTANT */ 400000); -} - -</script>
diff --git a/third_party/blink/web_tests/virtual/stable/webexposed/css-properties-as-js-properties-expected.txt b/third_party/blink/web_tests/virtual/stable/webexposed/css-properties-as-js-properties-expected.txt index 0245fdb..cb0fdfc 100644 --- a/third_party/blink/web_tests/virtual/stable/webexposed/css-properties-as-js-properties-expected.txt +++ b/third_party/blink/web_tests/virtual/stable/webexposed/css-properties-as-js-properties-expected.txt
@@ -368,6 +368,8 @@ quotes r range +readingFlow +readingOrder removeProperty resize right
diff --git a/third_party/blink/web_tests/virtual/stable/webexposed/css-property-listing-expected.txt b/third_party/blink/web_tests/virtual/stable/webexposed/css-property-listing-expected.txt index 1906b24c..7344beb 100644 --- a/third_party/blink/web_tests/virtual/stable/webexposed/css-property-listing-expected.txt +++ b/third_party/blink/web_tests/virtual/stable/webexposed/css-property-listing-expected.txt
@@ -318,6 +318,8 @@ print-color-adjust quotes r + reading-flow + reading-order resize right rotate
diff --git a/third_party/blink/web_tests/virtual/stable/webexposed/global-interface-listing-expected.txt b/third_party/blink/web_tests/virtual/stable/webexposed/global-interface-listing-expected.txt index 4661884e..398e74a0 100644 --- a/third_party/blink/web_tests/virtual/stable/webexposed/global-interface-listing-expected.txt +++ b/third_party/blink/web_tests/virtual/stable/webexposed/global-interface-listing-expected.txt
@@ -8742,6 +8742,7 @@ getter anchorOffset getter baseNode getter baseOffset + getter direction getter extentNode getter extentOffset getter focusNode @@ -8758,6 +8759,7 @@ method deleteFromDocument method empty method extend + method getComposedRanges method getRangeAt method modify method removeAllRanges
diff --git a/third_party/blink/web_tests/wpt_internal/ai/language-model-api-response-json-schema.https.any.js b/third_party/blink/web_tests/wpt_internal/ai/language-model-api-response-json-schema.https.any.js new file mode 100644 index 0000000..7a952855 --- /dev/null +++ b/third_party/blink/web_tests/wpt_internal/ai/language-model-api-response-json-schema.https.any.js
@@ -0,0 +1,33 @@ +// META: script=resources/utils.js +// META: script=resources/workaround-for-382640509.js + +promise_test(async t => { + await ensureLanguageModel(); + const session = await LanguageModel.create(); + // Circular reference is not valid. + const invalidRepsonseJsonSchema = {}; + invalidRepsonseJsonSchema.self = invalidRepsonseJsonSchema; + await promise_rejects_dom(t, 'NotSupportedError', + session.prompt(kTestPrompt, { responseJSONSchema: invalidRepsonseJsonSchema }), + 'Response json schema is invalid - it should be an object that can be stringified into a JSON string.'); +}, 'Prompt API should fail if an invalid response json schema is provided'); + +promise_test(async t => { + await ensureLanguageModel(); + const session = await LanguageModel.create(); + const validRepsonseJsonSchema = { + type: "object", + required: ["Rating"], + additionalProperties: false, + properties: { + Rating: { + type: "number", + minimum: 0, + maximum: 5, + }, + }, + }; + const promptPromise = session.prompt(kTestPrompt, { responseJSONSchema : validRepsonseJsonSchema }); + const result = await promptPromise; + assert_true(typeof result === "string"); +}, 'Prompt API should work when a valid response json schema is provided.');
diff --git a/third_party/boringssl/src b/third_party/boringssl/src index c86127e..f0ab911 160000 --- a/third_party/boringssl/src +++ b/third_party/boringssl/src
@@ -1 +1 @@ -Subproject commit c86127e656ae8b95622c79ba3d0fb35415502d53 +Subproject commit f0ab91129d1a234dc127335765b794d62dc9ed4d
diff --git a/third_party/dawn b/third_party/dawn index 8571704..ad16623 160000 --- a/third_party/dawn +++ b/third_party/dawn
@@ -1 +1 @@ -Subproject commit 85717040cc75f7b29ca714b18a31087d2a2b6382 +Subproject commit ad166239934f9105fdfbbfe8ea46f8e05ca43181
diff --git a/third_party/lit/v3_0/cr_lit_element.ts b/third_party/lit/v3_0/cr_lit_element.ts index 09d3f3b..40200ba 100644 --- a/third_party/lit/v3_0/cr_lit_element.ts +++ b/third_party/lit/v3_0/cr_lit_element.ts
@@ -39,8 +39,9 @@ this.$ = new Proxy({}, { get(cache: ElementCache, id: string): HTMLElement|SVGElement { if (!self.hasUpdated && !self.isConnected) { - throw new Error(`CrLitElement ${ - self.tagName} $ dictionary accessed before element is connected at least once.`); + const description = self.tagName + (self.id ? `#${self.id}` : ''); + throw new Error(`CrLitElement ${description} accessed '$.${ + id}' before connected at least once.`); } if (!self.hasUpdated) { @@ -48,8 +49,9 @@ // performUpdate() call below will cause an endless recursion. Local // DOM nodes should not be accessed within willUpdate() anyway. if (self.willUpdatePending_) { - throw new Error(`CrLitElement ${ - self.tagName} tried to access this.$ within willUpdate().`); + const description = self.tagName + (self.id ? `#${self.id}` : ''); + throw new Error(`CrLitElement ${description} accessed '$.${ + id}' within willUpdate().`); } // See Case3 in `ensureInitialRender` docs.
diff --git a/third_party/protobuf/README.chromium b/third_party/protobuf/README.chromium index b3e505e..28e9e4b6 100644 --- a/third_party/protobuf/README.chromium +++ b/third_party/protobuf/README.chromium
@@ -241,3 +241,9 @@ Add the compatability dir to .gitignore as it's unnecessary and contains files that trip CheckForTooLargeFiles presubmit. +- 0054-use-full-types-in-java-context.patch + + Uses full struct definitions in java/context.h via #include, instead + of forward declarations to work around known issue with protobuf + + newer abseil version, + https://github.com/protocolbuffers/protobuf/issues/20331
diff --git a/third_party/protobuf/patches/0054-use-full-types-in-java-context.patch b/third_party/protobuf/patches/0054-use-full-types-in-java-context.patch new file mode 100644 index 0000000..c693b32 --- /dev/null +++ b/third_party/protobuf/patches/0054-use-full-types-in-java-context.patch
@@ -0,0 +1,21 @@ +diff --git a/src/google/protobuf/compiler/java/context.h b/src/google/protobuf/compiler/java/context.h +index 993301bbd796b..8117ce67821bd 100644 +--- a//src/google/protobuf/compiler/java/context.h ++++ b//src/google/protobuf/compiler/java/context.h +@@ -12,1 +12,1 @@ + #include <vector> + + #include "absl/container/flat_hash_map.h" ++#include "google/protobuf/compiler/java/field_common.h" + #include "google/protobuf/compiler/java/helpers.h" + #include "google/protobuf/compiler/java/options.h" + #include "google/protobuf/port.h" +@@ -36,2 +37,2 @@ namespace protobuf { + namespace compiler { + namespace java { + +-struct FieldGeneratorInfo; +-struct OneofGeneratorInfo; + // A context object holds the information that is shared among all code + // generators. + class Context {
diff --git a/third_party/protobuf/src/google/protobuf/compiler/java/context.h b/third_party/protobuf/src/google/protobuf/compiler/java/context.h index 993301bb..8117ce67 100644 --- a/third_party/protobuf/src/google/protobuf/compiler/java/context.h +++ b/third_party/protobuf/src/google/protobuf/compiler/java/context.h
@@ -12,6 +12,7 @@ #include <vector> #include "absl/container/flat_hash_map.h" +#include "google/protobuf/compiler/java/field_common.h" #include "google/protobuf/compiler/java/helpers.h" #include "google/protobuf/compiler/java/options.h" #include "google/protobuf/port.h" @@ -36,8 +37,6 @@ namespace compiler { namespace java { -struct FieldGeneratorInfo; -struct OneofGeneratorInfo; // A context object holds the information that is shared among all code // generators. class Context {
diff --git a/third_party/rust/chromium_crates_io/Cargo.lock b/third_party/rust/chromium_crates_io/Cargo.lock index 8631995..bd47b437 100644 --- a/third_party/rust/chromium_crates_io/Cargo.lock +++ b/third_party/rust/chromium_crates_io/Cargo.lock
@@ -111,6 +111,12 @@ "font-types", "hex", "icu_capi", + "icu_casemap", + "icu_experimental", + "icu_locale_core", + "icu_normalizer", + "icu_properties", + "icu_provider", "lazy_static", "libc", "log",
diff --git a/third_party/rust/chromium_crates_io/Cargo.toml b/third_party/rust/chromium_crates_io/Cargo.toml index 25de3c43..772ce73 100644 --- a/third_party/rust/chromium_crates_io/Cargo.toml +++ b/third_party/rust/chromium_crates_io/Cargo.toml
@@ -50,6 +50,25 @@ default-features = false features = ["calendar", "compiled_data", "experimental", "casemap"] +[dependencies.icu_casemap] +version = "2.0.0-beta2" +default-features = false +[dependencies.icu_experimental] +version = "0.3.0-beta2" +default-features = false +[dependencies.icu_locale_core] +version = "2.0.0-beta2" +default-features = false +[dependencies.icu_normalizer] +version = "2.0.0-beta2" +default-features = false +[dependencies.icu_properties] +version = "2.0.0-beta2" +default-features = false +[dependencies.icu_provider] +version = "2.0.0-beta2" +default-features = false + [dependencies.tinyvec] version = "1.6.0" features = ["rustc_1_55"]
diff --git a/third_party/rust/chromium_crates_io/patches/icu_experimental/0001-temporarily-remove-any-lower.patch b/third_party/rust/chromium_crates_io/patches/icu_experimental/0001-temporarily-remove-any-lower.patch new file mode 100644 index 0000000..bbfab5b --- /dev/null +++ b/third_party/rust/chromium_crates_io/patches/icu_experimental/0001-temporarily-remove-any-lower.patch
@@ -0,0 +1,53 @@ +From e9609b53306e6a142ea0859a1d8e7ebe26df42b5 Mon Sep 17 00:00:00 2001 +From: Frank Tang <ftang@chromium.org> +Date: Tue, 1 Apr 2025 16:36:45 -0700 +Subject: [PATCH] Prototype using ICU4X Transliterator + +Add a flag enable_rust_translit default to false for using ICU4X +gn args dir +and put +enable_rust_translit = true + +to configure the build to build with the ICU4X transliterator + +Change-Id: Id011e6b3b000378609c52c3ad50ac9d03853b4a8 +Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/6332970 +Reviewed-by: Daniel Cheng <dcheng@chromium.org> +Commit-Queue: Frank Tang <ftang@chromium.org> +Cr-Commit-Position: refs/heads/main@{#1441255} +--- + .../icu_experimental-v0_3/src/transliterate/compile/pass2.rs | 3 ++- + .../src/transliterate/transliterator/mod.rs | 3 ++- + 2 files changed, 4 insertions(+), 2 deletions(-) + +diff --git a/third_party/rust/chromium_crates_io/vendor/icu_experimental-v0_3/src/transliterate/compile/pass2.rs b/third_party/rust/chromium_crates_io/vendor/icu_experimental-v0_3/src/transliterate/compile/pass2.rs +index eabb0f9e81417..f7f2eba7b169e 100644 +--- a/third_party/rust/chromium_crates_io/vendor/icu_experimental-v0_3/src/transliterate/compile/pass2.rs ++++ b/third_party/rust/chromium_crates_io/vendor/icu_experimental-v0_3/src/transliterate/compile/pass2.rs +@@ -400,7 +400,8 @@ impl<'a, 'p> Pass2<'a, 'p> { + | "any-nfkd" + | "any-null" + | "any-remove" +- | "any-lower" ++ // Comment out any-lower to allow adding any-lower ++ // | "any-lower" + | "any-upper" + | "any-title" + | "any-hex/unicode" +diff --git a/third_party/rust/chromium_crates_io/vendor/icu_experimental-v0_3/src/transliterate/transliterator/mod.rs b/third_party/rust/chromium_crates_io/vendor/icu_experimental-v0_3/src/transliterate/transliterator/mod.rs +index 3fcc1de7c7cd1..8007361034f78 100644 +--- a/third_party/rust/chromium_crates_io/vendor/icu_experimental-v0_3/src/transliterate/transliterator/mod.rs ++++ b/third_party/rust/chromium_crates_io/vendor/icu_experimental-v0_3/src/transliterate/transliterator/mod.rs +@@ -497,7 +497,8 @@ impl Transliterator { + ), + "any-null" => Some(Ok(InternalTransliterator::Null)), + "any-remove" => Some(Ok(InternalTransliterator::Remove)), +- "any-lower" => Some(Err(DataError::custom("any-lower not implemented"))), ++ // Comment out any-lower to allow adding any-lower ++ // "any-lower" => Some(Err(DataError::custom("any-lower not implemented"))), + "any-upper" => Some(Err(DataError::custom("any-upper not implemented"))), + "any-title" => Some(Err(DataError::custom("any-title not implemented"))), + "any-hex/unicode" => Some(Ok(InternalTransliterator::Hex( +-- +2.49.0.472.ge94155a9ec-goog +
diff --git a/third_party/rust/icu_casemap/v2/BUILD.gn b/third_party/rust/icu_casemap/v2/BUILD.gn index 0719a61..81dadf7c 100644 --- a/third_party/rust/icu_casemap/v2/BUILD.gn +++ b/third_party/rust/icu_casemap/v2/BUILD.gn
@@ -60,13 +60,4 @@ rustflags = [ "--cap-lints=allow", # Suppress all warnings in crates.io crates ] - - # Only for usage from third-party crates. Add the crate to - # //third_party/rust/chromium_crates_io/Cargo.toml to use - # it from first-party code. - visibility = [ - "//base:i18n_transliterator_ffi", - "//base:i18n_transliterator_ffi_cxx_generated", - "//third_party/rust/*", - ] }
diff --git a/third_party/rust/icu_experimental/v0_3/BUILD.gn b/third_party/rust/icu_experimental/v0_3/BUILD.gn index d8fbd6c7..ee7f3f9 100644 --- a/third_party/rust/icu_experimental/v0_3/BUILD.gn +++ b/third_party/rust/icu_experimental/v0_3/BUILD.gn
@@ -157,13 +157,4 @@ rustflags = [ "--cap-lints=allow", # Suppress all warnings in crates.io crates ] - - # Only for usage from third-party crates. Add the crate to - # //third_party/rust/chromium_crates_io/Cargo.toml to use - # it from first-party code. - visibility = [ - "//base:i18n_transliterator_ffi", - "//base:i18n_transliterator_ffi_cxx_generated", - "//third_party/rust/*", - ] }
diff --git a/third_party/rust/icu_locale_core/v2/BUILD.gn b/third_party/rust/icu_locale_core/v2/BUILD.gn index ccd63595..8aa65f8 100644 --- a/third_party/rust/icu_locale_core/v2/BUILD.gn +++ b/third_party/rust/icu_locale_core/v2/BUILD.gn
@@ -108,13 +108,4 @@ rustflags = [ "--cap-lints=allow", # Suppress all warnings in crates.io crates ] - - # Only for usage from third-party crates. Add the crate to - # //third_party/rust/chromium_crates_io/Cargo.toml to use - # it from first-party code. - visibility = [ - "//base:i18n_transliterator_ffi", - "//base:i18n_transliterator_ffi_cxx_generated", - "//third_party/rust/*", - ] }
diff --git a/third_party/rust/icu_normalizer/v2/BUILD.gn b/third_party/rust/icu_normalizer/v2/BUILD.gn index 8be5bb1..f72951db 100644 --- a/third_party/rust/icu_normalizer/v2/BUILD.gn +++ b/third_party/rust/icu_normalizer/v2/BUILD.gn
@@ -47,13 +47,4 @@ rustflags = [ "--cap-lints=allow", # Suppress all warnings in crates.io crates ] - - # Only for usage from third-party crates. Add the crate to - # //third_party/rust/chromium_crates_io/Cargo.toml to use - # it from first-party code. - visibility = [ - "//base:i18n_transliterator_ffi", - "//base:i18n_transliterator_ffi_cxx_generated", - "//third_party/rust/*", - ] }
diff --git a/third_party/rust/icu_properties/v2/BUILD.gn b/third_party/rust/icu_properties/v2/BUILD.gn index 22d91c653..92521cf 100644 --- a/third_party/rust/icu_properties/v2/BUILD.gn +++ b/third_party/rust/icu_properties/v2/BUILD.gn
@@ -59,13 +59,4 @@ rustflags = [ "--cap-lints=allow", # Suppress all warnings in crates.io crates ] - - # Only for usage from third-party crates. Add the crate to - # //third_party/rust/chromium_crates_io/Cargo.toml to use - # it from first-party code. - visibility = [ - "//base:i18n_transliterator_ffi", - "//base:i18n_transliterator_ffi_cxx_generated", - "//third_party/rust/*", - ] }
diff --git a/third_party/rust/icu_provider/v2/BUILD.gn b/third_party/rust/icu_provider/v2/BUILD.gn index c6cdb53..20cb03e 100644 --- a/third_party/rust/icu_provider/v2/BUILD.gn +++ b/third_party/rust/icu_provider/v2/BUILD.gn
@@ -62,13 +62,4 @@ rustflags = [ "--cap-lints=allow", # Suppress all warnings in crates.io crates ] - - # Only for usage from third-party crates. Add the crate to - # //third_party/rust/chromium_crates_io/Cargo.toml to use - # it from first-party code. - visibility = [ - "//base:i18n_transliterator_ffi", - "//base:i18n_transliterator_ffi_cxx_generated", - "//third_party/rust/*", - ] }
diff --git a/third_party/skia b/third_party/skia index 5342f22..c106d78 160000 --- a/third_party/skia +++ b/third_party/skia
@@ -1 +1 @@ -Subproject commit 5342f227267ef899adb5da0473fe3b513f3c6790 +Subproject commit c106d7831592fbc7245b6e032164506db3038f2a
diff --git a/third_party/vulkan-deps b/third_party/vulkan-deps index a7952ef..07e305f 160000 --- a/third_party/vulkan-deps +++ b/third_party/vulkan-deps
@@ -1 +1 @@ -Subproject commit a7952ef72c943513045046f4f1ab0f91998a9307 +Subproject commit 07e305fc66d3132b515ed22bc78dee79bf7eb9fc
diff --git a/third_party/vulkan-validation-layers/src b/third_party/vulkan-validation-layers/src index 7c7fd77..fcc588f 160000 --- a/third_party/vulkan-validation-layers/src +++ b/third_party/vulkan-validation-layers/src
@@ -1 +1 @@ -Subproject commit 7c7fd77a6ba3ff7699d042ee1d396aa5803850df +Subproject commit fcc588f5171b007d433bf7c02184708dbc7d9d89
diff --git a/third_party/webgpu-cts/src b/third_party/webgpu-cts/src index eb1d526..5aba8ba 160000 --- a/third_party/webgpu-cts/src +++ b/third_party/webgpu-cts/src
@@ -1 +1 @@ -Subproject commit eb1d526134f6789e9ce6d5e3b654fbfc10043267 +Subproject commit 5aba8ba55eb2496852d622854a99fac74f074c65
diff --git a/tools/accessibility/rebase_dump_accessibility_tree_tests.py b/tools/accessibility/rebase_dump_accessibility_tree_tests.py index 46abca9d..075a2385 100755 --- a/tools/accessibility/rebase_dump_accessibility_tree_tests.py +++ b/tools/accessibility/rebase_dump_accessibility_tree_tests.py
@@ -55,11 +55,11 @@ line = result.group(1) # For Android tests: if line[:2] == 'I ' or line[:2] == 'E ': - if result := re.search('[I|E].*run_tests_on_device\([^\)]+\)\s+(.*)', line): + if result := re.search(r'[IE].*run_tests_on_device\([^\)]+\)\s+(.*)', line): line = result.group(1) # For Android content_shell_test_apk tests: elif line[:2] == 'C ': - if result := re.search('C\s+\d+\.\d+s Main\s+([T|E|A|a|W|\+](.*))', line): + if result := re.search(r'C\s+\d+\.\d+s Main\s+([TEAaW+](.*))', line): line = result.group(1) return line
diff --git a/tools/android/avd/proto/android_32_google_apis_x64_foldable.textpb b/tools/android/avd/proto/android_32_google_apis_x64_foldable.textpb index f4a66ed7a..b72bfd5a 100644 --- a/tools/android/avd/proto/android_32_google_apis_x64_foldable.textpb +++ b/tools/android/avd/proto/android_32_google_apis_x64_foldable.textpb
@@ -17,8 +17,8 @@ avd_package { package_name: "chromium/third_party/android_sdk/public/avds/android-32/google_apis/x86_64" - # Created in https://ci.chromium.org/ui/b/8735783234873256961 - # Patched gmscore version 24.26.32 in https://crrev.com/c/5735581 - version: "Uz9_H4xQPMkWRFtmUJKSvZGLuEVimRmDuJezRXxIk70C" + # Created in https://ci.chromium.org/ui/b/8719089370664121393 + # Patched gmscore version 25.07.33 in https://crrev.com/c/6377466 + version: "toKIcJSMVTa2LXqcfNjRNtLywQ1kuhmdkg6w-jTy1-kC" } avd_name: "android_32_google_apis_x64_foldable"
diff --git a/tools/android/avd/proto/android_32_google_apis_x64_foldable_landscape.textpb b/tools/android/avd/proto/android_32_google_apis_x64_foldable_landscape.textpb index 692185a..763fc57f 100644 --- a/tools/android/avd/proto/android_32_google_apis_x64_foldable_landscape.textpb +++ b/tools/android/avd/proto/android_32_google_apis_x64_foldable_landscape.textpb
@@ -18,8 +18,8 @@ avd_package { package_name: "chromium/third_party/android_sdk/public/avds/android-32/google_apis/x86_64" - # Created in https://ci.chromium.org/ui/b/8735783234873256961 - # Patched gmscore version 24.26.32 in https://crrev.com/c/5735581 - version: "Ggu6SCWhbO7GLyFjZgU3xfOkziQGFE6e0hfWY9a8A9kC" + # Created in https://ci.chromium.org/ui/b/8719089370664121393 + # Patched gmscore version 25.07.33 in https://crrev.com/c/6377466 + version: "8suNvYCLZCZxPMUCgs0JOXpKVm5XIW8pfef4yETgfTkC" } -avd_name: "android_32_google_apis_x64_foldable_landscape" +avd_name: "android_32_google_apis_x64_foldable"
diff --git a/tools/android/avd/proto/android_32_google_apis_x64_foldable_landscape_local.textpb b/tools/android/avd/proto/android_32_google_apis_x64_foldable_landscape_local.textpb index cebbbba..fc9fb27 100644 --- a/tools/android/avd/proto/android_32_google_apis_x64_foldable_landscape_local.textpb +++ b/tools/android/avd/proto/android_32_google_apis_x64_foldable_landscape_local.textpb
@@ -18,8 +18,8 @@ avd_package { package_name: "chromium/third_party/android_sdk/public/avds/android-32/google_apis/x86_64" - # Created in https://ci.chromium.org/ui/b/8734060994898362593 - # Patched gmscore version 24.26.32 in https://crrev.com/c/5735581 - version: "Vi7cavzXATzHa6M9We7TJautKBe11Pur-IoL49lF8FgC" + # Created in https://ci.chromium.org/ui/b/8719089370664121393 + # Patched gmscore version 25.07.33 in https://crrev.com/c/6377466 + version: "GD86_70Cb_Nx1rYa7qc5v6iA5CVZPXdA7StO26sD8w0C" } avd_name: "android_32_google_apis_x64_foldable"
diff --git a/tools/android/avd/proto/android_32_google_apis_x64_foldable_local.textpb b/tools/android/avd/proto/android_32_google_apis_x64_foldable_local.textpb index 9a89379..26032d3 100644 --- a/tools/android/avd/proto/android_32_google_apis_x64_foldable_local.textpb +++ b/tools/android/avd/proto/android_32_google_apis_x64_foldable_local.textpb
@@ -17,8 +17,8 @@ avd_package { package_name: "chromium/third_party/android_sdk/public/avds/android-32/google_apis/x86_64" - # Created in https://ci.chromium.org/ui/b/8734060994898362593 - # Patched gmscore version 24.26.32 in https://crrev.com/c/5735581 - version: "tptXMzBoJL68sKlejiMi-dUMg2YdKcTLTozV6ohjF_UC" + # Created in https://ci.chromium.org/ui/b/8719089370664121393 + # Patched gmscore version 25.07.33 in https://crrev.com/c/6377466 + version: "V5d_dJhj7yKkb5LjQ_K8Gm7d9EiQvOnEbOsKhNUNMOsC" } avd_name: "android_32_google_apis_x64_foldable"
diff --git a/tools/android/avd/proto/android_33_google_apis_x64.textpb b/tools/android/avd/proto/android_33_google_apis_x64.textpb index 5982db47..365357be 100644 --- a/tools/android/avd/proto/android_33_google_apis_x64.textpb +++ b/tools/android/avd/proto/android_33_google_apis_x64.textpb
@@ -17,9 +17,9 @@ avd_package { package_name: "chromium/third_party/android_sdk/public/avds/android-33/google_apis/x86_64" - # Created in https://ci.chromium.org/ui/b/8735783234873256961 - # Patched gmscore version 24.26.32 in https://crrev.com/c/5735581 - version: "Ee3kRDaGcJ0kQ4GP7TUNOGswwt_Xj6uGe9gUzP7axPwC" + # Created in https://ci.chromium.org/ui/b/8719089370664121393 + # Patched gmscore version 25.07.33 in https://crrev.com/c/6377466 + version: "6h6ubFZF3bm6whQnysgyfb7dq5C8j43pMHV8DXGVfZcC" } avd_name: "android_33_google_apis_x64"
diff --git a/tools/android/avd/proto/android_33_google_apis_x64_local.textpb b/tools/android/avd/proto/android_33_google_apis_x64_local.textpb index 23c70ae..39a309c 100644 --- a/tools/android/avd/proto/android_33_google_apis_x64_local.textpb +++ b/tools/android/avd/proto/android_33_google_apis_x64_local.textpb
@@ -17,9 +17,9 @@ avd_package { package_name: "chromium/third_party/android_sdk/public/avds/android-33/google_apis/x86_64" - # Created in https://ci.chromium.org/ui/b/8734084655359483009 - # Patched gmscore version 24.26.32 in https://crrev.com/c/5735581 - version: "4hXIfKQVfT-w-WwOBofV9AOCoyovO6iKNTSJ_LoHcZ8C" + # Created in https://ci.chromium.org/ui/b/8719089370664121393 + # Patched gmscore version 25.07.33 in https://crrev.com/c/6377466 + version: "W4oP8iGij6Yq67thheoevC4aKehiCrqlVDcEbWozkfkC" } avd_name: "android_33_google_apis_x64"
diff --git a/tools/mb/mb.py b/tools/mb/mb.py index 0879ce1..47fb735 100755 --- a/tools/mb/mb.py +++ b/tools/mb/mb.py
@@ -128,13 +128,13 @@ self.DumpInputFiles() return ret except KeyboardInterrupt: - self.Print('interrupted, exiting') + self.Print('interrupted, exiting', file=sys.stderr) return 130 except Exception as e: self.DumpInputFiles() s = traceback.format_exc() for l in s.splitlines(): - self.Print(l) + self.Print(l, file=sys.stderr) return getattr(e, 'retcode', _DEFAULT_ERROR_RETCODE) def ParseArgs(self, argv): @@ -457,11 +457,12 @@ def DumpContentsOfFilePassedTo(arg_name, path): if path and self.Exists(path): - self.Print("\n# To recreate the file passed to %s:" % arg_name) - self.Print("%% cat > %s <<EOF" % path) + self.Print("\n# To recreate the file passed to %s:" % arg_name, + file=sys.stderr) + self.Print("%% cat > %s <<EOF" % path, file=sys.stderr) contents = self.ReadFile(path) - self.Print(contents) - self.Print("EOF\n%\n") + self.Print(contents, file=sys.stderr) + self.Print("EOF\n%\n", file=sys.stderr) if getattr(self.args, 'input_path', None): DumpContentsOfFilePassedTo( @@ -483,7 +484,8 @@ def CmdTrain(self): expectations_dir = self.args.expectations_dir if not self.Exists(expectations_dir): - self.Print('Expectations dir (%s) does not exist.' % expectations_dir) + self.Print('Expectations dir (%s) does not exist.' % expectations_dir, + file=sys.stderr) return 1 # Removing every expectation file then immediately re-generating them will # clear out deleted groups. @@ -564,10 +566,12 @@ # notice has been live for a few months. if not self.args.force: self.Print( - '`mb run` is deprecated in favor of the UTR. For more info, see ' - 'https://chromium.googlesource.com/chromium/src/+/main/tools/utr/README.md. ' - 'To skip this warning, re-run with "--force". Note that `mb run` ' - 'will be deleted sometime in 2025.') + ('`mb run` is deprecated in favor of the UTR. For more info, see ' + 'https://chromium.googlesource.com/chromium/src/+/main/tools/utr/README.md. ' + 'To skip this warning, re-run with "--force". Note that `mb run` ' + 'will be deleted sometime in 2025.'), + file=sys.stderr, + ) return 1 vals = self.GetConfig() if not vals: @@ -864,7 +868,8 @@ raise MBErr('mb config file not sorted:\n' + '\n'.join(errs)) if print_ok: - self.Print('mb config file %s looks ok.' % self.args.config_file) + self.Print('mb config file %s looks ok.' % self.args.config_file, + file=sys.stderr) return 0 def _ValidateEach(self, errs, validate): @@ -918,9 +923,10 @@ toolchain_path = self.PathJoin(self.ToAbsPath(build_dir), 'toolchain.ninja') if not self.Exists(toolchain_path): - self.Print('Must either specify a path to an existing GN build dir ' - 'or pass in a -m/-b pair or a -c flag to specify the ' - 'configuration') + self.Print(('Must either specify a path to an existing GN build dir ' + 'or pass in a -m/-b pair or a -c flag to specify the ' + 'configuration'), + file=sys.stderr) return {} vals['gn_args'] = self.GNArgsFromDir(build_dir) @@ -1169,7 +1175,7 @@ self.WriteJSON({'output': output}, self.args.json_output) # If `gn gen` failed, we should exit early rather than trying to # generate isolates. Run() will have already logged any error output. - self.Print('GN gen failed: %d' % ret) + self.Print('GN gen failed: %d' % ret, file=sys.stderr) return ret if isolate_targets is not None: @@ -1192,7 +1198,7 @@ if ret != 0: # If `gn ls` failed, we should exit early rather than trying to # generate isolates. - self.Print('GN ls failed: %d' % ret) + self.Print('GN ls failed: %d' % ret, file=sys.stderr) return ret # Create a reverse map from isolate label to isolate dict. @@ -1372,7 +1378,8 @@ filtered_command.append('--test-launcher-filter-file=%s' % filter_file_path) self.Print('added test selection filter file to command: %s' % - filter_file_path) + filter_file_path, + file=sys.stderr) return filtered_command return None @@ -1462,7 +1469,7 @@ ret, out, _ = self.Call(cmd) if ret != 0: if out: - self.Print(out) + self.Print(out, file=sys.stderr) return ret runtime_deps = self._DedupDependencies(out.splitlines()) @@ -1580,8 +1587,9 @@ err += '\n' + build_dir + '/' + f if err: - self.Print('error: gn `data` items may not list generated directories; ' - 'list files in directory instead for:' + err) + self.Print(('error: gn `data` items may not list generated directories; ' + 'list files in directory instead for:' + err), + file=sys.stderr) return 1 isolate = { @@ -1598,7 +1606,7 @@ if target not in self.rts_banned_suites: rts_command = self.AddFilterFileArg(target, command) if rts_command: - self.Print('Adding RTS filter file to command.') + self.Print('Adding RTS filter file to command.', file=sys.stderr) isolate['variables']['rts_command'] = rts_command self.WriteFile(isolate_path, json.dumps(isolate, sort_keys=True) + '\n') @@ -1913,17 +1921,18 @@ inp = self.ReadInputJSON(['files', 'test_targets', 'additional_compile_targets']) if self.args.verbose: - self.Print() - self.Print('analyze input:') - self.PrintJSON(inp) - self.Print() + self.Print(file=sys.stderr) + self.Print('analyze input:', file=sys.stderr) + self.PrintJSON(inp, file=sys.stderr) + self.Print(file=sys.stderr) # This shouldn't normally happen, but could due to unusual race conditions, # like a try job that gets scheduled before a patch lands but runs after # the patch has landed. if not inp['files']: - self.Print('Warning: No files modified in patch, bailing out early.') + self.Print('Warning: No files modified in patch, bailing out early.', + file=sys.stderr) self.WriteJSON({ 'status': 'No dependency', 'compile_targets': [], @@ -1962,8 +1971,9 @@ try: gn_outp = json.loads(gn_outp_str) except Exception as e: - self.Print("Failed to parse the JSON string GN returned: %s\n%s" - % (repr(gn_outp_str), str(e))) + self.Print(("Failed to parse the JSON string GN returned: %s\n%s" % + (repr(gn_outp_str), str(e))), + file=sys.stderr) raise outp = {} @@ -1994,8 +2004,9 @@ # and build everything. Probably the right thing to do here is # to have GN return the compile targets directly. if any("(" in target for target in outp['compile_targets']): - self.Print('WARNING: targets with non-default toolchains were ' - 'found, building everything instead.') + self.Print(('WARNING: targets with non-default toolchains were ' + 'found, building everything instead.'), + file=sys.stderr) outp['compile_targets'] = all_input_compile_targets else: outp['compile_targets'] = [ @@ -2012,9 +2023,11 @@ max_cmd_length_kb = 64 if platform.system() == 'Linux' else 7 if len(' '.join(outp['compile_targets'])) > max_cmd_length_kb * 1024: - self.Print('WARNING: Too many compile targets were affected.') - self.Print('WARNING: Building everything instead to avoid ' - 'command-line length issues.') + self.Print('WARNING: Too many compile targets were affected.', + file=sys.stderr) + self.Print(('WARNING: Building everything instead to avoid ' + 'command-line length issues.'), + file=sys.stderr) outp['compile_targets'] = all_input_compile_targets @@ -2023,10 +2036,10 @@ labels_to_targets[label] for label in gn_outp['test_targets']] if self.args.verbose: - self.Print() - self.Print('analyze output:') - self.PrintJSON(outp) - self.Print() + self.Print(file=sys.stderr) + self.Print('analyze output:', file=sys.stderr) + self.PrintJSON(outp, file=sys.stderr) + self.Print(file=sys.stderr) self.WriteJSON(outp, output_path) @@ -2070,7 +2083,7 @@ except Exception as e: raise MBErr('Error %s writing to the output path "%s"' % (e, path)) from e - def PrintCmd(self, cmd): + def PrintCmd(self, cmd, **kwargs): if self.platform == 'win32': shell_quoter = QuoteForCmd else: @@ -2078,10 +2091,10 @@ if cmd[0] == self.executable: cmd = ['python'] + cmd[1:] - self.Print(*[shell_quoter(arg) for arg in cmd]) + self.Print(*[shell_quoter(arg) for arg in cmd], **kwargs) - def PrintJSON(self, obj): - self.Print(json.dumps(obj, indent=2, sort_keys=True)) + def PrintJSON(self, obj, **kwargs): + self.Print(json.dumps(obj, indent=2, sort_keys=True), **kwargs) def Build(self, target): build_dir = self.ToSrcRelPath(self.args.path) @@ -2099,14 +2112,14 @@ def Run(self, cmd, env=None, force_verbose=True, capture_output=True): # This function largely exists so it can be overridden for testing. if self.args.dryrun or self.args.verbose or force_verbose: - self.PrintCmd(cmd) + self.PrintCmd(cmd, file=sys.stderr) if self.args.dryrun: return 0, '', '' ret, out, err = self.Call(cmd, env=env, capture_output=capture_output) if self.args.verbose or force_verbose: if ret != 0: - self.Print(' -> returned %d' % ret) + self.Print(' -> returned %d' % ret, file=sys.stderr) if out: # This is the error seen on the logs self.Print(out, end='') @@ -2201,7 +2214,8 @@ def WriteFile(self, path, contents, force_verbose=False): # This function largely exists so it can be overriden for testing. if self.args.dryrun or self.args.verbose or force_verbose: - self.Print('\nWriting """\\\n%s""" to %s.\n' % (contents, path)) + self.Print('\nWriting """\\\n%s""" to %s.\n' % (contents, path), + file=sys.stderr) with open(path, 'w', encoding='utf-8', newline='') as fp: return fp.write(contents)
diff --git a/tools/mb/mb_unittest.py b/tools/mb/mb_unittest.py index 9e8f00b..9cadd96b 100755 --- a/tools/mb/mb_unittest.py +++ b/tools/mb/mb_unittest.py
@@ -528,7 +528,7 @@ # Make sure we log both what is written to args.gn and the command line. self.assertIn('Writing """', mbw.out) self.assertIn('/fake_src/buildtools/linux64/gn gen //out/Default --check', - mbw.out) + mbw.err) mbw = self.fake_mbw(win32=True) self.check(['gen', '-c', 'debug_remoteexec', '//out/Debug'], mbw=mbw, ret=0) @@ -537,7 +537,7 @@ 'use_remoteexec = true\n')) self.assertIn( 'c:\\fake_src\\buildtools\\win\\gn.exe gen //out/Debug ' - '--check', mbw.out) + '--check', mbw.err) mbw = self.fake_mbw() self.check(['gen', '-m', 'fake_builder_group', '-b', 'fake_args_bot', @@ -813,7 +813,7 @@ expected_err = ('error: gn `data` items may not list generated directories;' ' list files in directory instead for:\n' '//out/Default/test_data/\n') - self.assertIn(expected_err, mbw.out) + self.assertIn(expected_err, mbw.err) def test_isolate_dir(self): files = { @@ -835,8 +835,7 @@ 'isolate', '-c', 'debug_remoteexec', '//out/Default', 'base_unittests' ], mbw=mbw, - ret=0, - err='') + ret=0) def test_isolate_generated_dir(self): files = { @@ -862,7 +861,7 @@ ], mbw=mbw, ret=1) - self.assertEqual(mbw.out[-len(expected_err):], expected_err) + self.assertEqual(mbw.err[-len(expected_err):], expected_err) def test_run(self): files = { @@ -896,7 +895,7 @@ # command line in the call to `isolate`. self.assertIn( 'relative-cwd out/Default -- vpython3 ' - '../../testing/test_env.py', mbw.out) + '../../testing/test_env.py', mbw.err) def test_run_swarmed(self): files = { @@ -1102,7 +1101,7 @@ ret=1) self.assertIn( 'MBErr: Must not specify a build --phase ' - 'for linux-official on chromium', mbw.out) + 'for linux-official on chromium', mbw.err) def test_lookup_starlark_phased_gn_args(self): mbw = self.gen_starlark_gn_args_mbw(TEST_PHASED_GN_ARGS_JSON) @@ -1129,7 +1128,7 @@ ret=1) self.assertIn( 'MBErr: Must specify a build --phase for linux-official on chromium', - mbw.out) + mbw.err) def test_lookup_starlark_phased_gn_args_wrong_phase(self): mbw = self.gen_starlark_gn_args_mbw(TEST_PHASED_GN_ARGS_JSON) @@ -1140,7 +1139,7 @@ ret=1) self.assertIn( 'MBErr: Phase phase_3 doesn\'t exist for linux-official on chromium', - mbw.out) + mbw.err) def test_lookup_gn_args_with_non_existent_gn_args_location_file(self): files = { @@ -1187,17 +1186,17 @@ # Check that not passing a --phase to a multi-phase builder fails. mbw = self.check(['lookup', '-m', 'fake_builder_group', '-b', 'fake_multi_phase'], ret=1) - self.assertIn('Must specify a build --phase', mbw.out) + self.assertIn('Must specify a build --phase', mbw.err) # Check that passing a --phase to a single-phase builder fails. mbw = self.check(['lookup', '-m', 'fake_builder_group', '-b', 'fake_builder', '--phase', 'phase_1'], ret=1) - self.assertIn('Must not specify a build --phase', mbw.out) + self.assertIn('Must not specify a build --phase', mbw.err) # Check that passing a wrong phase key to a multi-phase builder fails. mbw = self.check(['lookup', '-m', 'fake_builder_group', '-b', 'fake_multi_phase', '--phase', 'wrong_phase'], ret=1) - self.assertIn('Phase wrong_phase doesn\'t exist', mbw.out) + self.assertIn('Phase wrong_phase doesn\'t exist', mbw.err) # Check that passing a correct phase key to a multi-phase builder passes. mbw = self.check(['lookup', '-m', 'fake_builder_group', '-b', @@ -1247,7 +1246,7 @@ self.assertIn( 'Duplicate configs detected. When evaluated fully, the ' 'following configs are all equivalent: \'some_config\', ' - '\'some_other_config\'.', mbw.out) + '\'some_other_config\'.', mbw.err) def test_good_expectations_validate(self): mbw = self.fake_mbw() @@ -1266,7 +1265,7 @@ mbw.files.pop(os.path.join(temp_dir, 'fake_builder_group.json')) # Now validating should fail. self.check(['validate', '--expectations-dir', temp_dir], mbw=mbw, ret=1) - self.assertIn('Expectations out of date', mbw.out) + self.assertIn('Expectations out of date', mbw.err) def test_build_command_unix(self): files = { @@ -1323,7 +1322,7 @@ ) self.assertIn( 'MBErr: Builder group name "non-existent-builder-group" not found', - mbw.out) + mbw.err) def test_lookup_non_existent_builder(self): """Ensure correct behavior when non-existent builder is specified. @@ -1338,7 +1337,7 @@ ret=2) self.assertIn( 'MBErr: Builder name "non-existent-builder" not found under groups', - mbw.out) + mbw.err) if __name__ == '__main__':
diff --git a/tools/metrics/histograms/enums.xml b/tools/metrics/histograms/enums.xml index c740c7ed..a118ecf 100644 --- a/tools/metrics/histograms/enums.xml +++ b/tools/metrics/histograms/enums.xml
@@ -11974,6 +11974,7 @@ <int value="-884783216" label="ForceEnableFastCheckoutCapabilities:enabled"/> <int value="-884480360" label="ServiceWorkerAutoPreload:disabled"/> <int value="-884142134" label="TaskManagerDesktopRefresh:enabled"/> + <int value="-883752382" label="FeedHeaderRemoval:disabled"/> <int value="-883694393" label="SyncPseudoUSSSupervisedUsers:disabled"/> <int value="-883608641" label="enable-cros-action-recorder"/> <int value="-882434910" label="EnableAggregatedMlSearchRanking:enabled"/> @@ -15087,6 +15088,7 @@ <int value="325907076" label="SanitizerAPI:enabled"/> <int value="326414607" label="WallpaperFastRefresh:enabled"/> <int value="327045548" label="SafeSearchUrlReporting:enabled"/> + <int value="328355983" label="DisableSystemBlur:enabled"/> <int value="328722396" label="NTPCondensedLayout:disabled"/> <int value="329171361" label="DesktopPWAsDetailedInstallDialog:disabled"/> <int value="329891894" label="BookmarkPaneAndroid:disabled"/> @@ -15252,6 +15254,7 @@ <int value="384054552" label="EnableDrDcVulkan:disabled"/> <int value="384677240" label="AssistMultiWordLacrosSupport:enabled"/> <int value="384927690" label="AutofillEnableLocalIban:disabled"/> + <int value="385046753" label="DisableSystemBlur:disabled"/> <int value="385206456" label="ShowReadyToPayDebugInfo:enabled"/> <int value="385339273" label="TextBasedAudioDescription:disabled"/> <int value="385969127" label="disable-win32k-lockdown"/> @@ -19779,6 +19782,7 @@ <int value="2118374092" label="MessagesForAndroidReaderMode:disabled"/> <int value="2119575519" label="ComposeUiRefinement:enabled"/> <int value="2119964154" label="enable-download-resumption"/> + <int value="2120179658" label="FeedHeaderRemoval:enabled"/> <int value="2120363312" label="QuickDim:enabled"/> <int value="2120659210" label="BackForwardCache:enabled"/> <int value="2121056855" label="IncreaseInputAudioBufferSize:disabled"/>
diff --git a/tools/metrics/histograms/metadata/accessibility/histograms.xml b/tools/metrics/histograms/metadata/accessibility/histograms.xml index 7186727..fee06e7 100644 --- a/tools/metrics/histograms/metadata/accessibility/histograms.xml +++ b/tools/metrics/histograms/metadata/accessibility/histograms.xml
@@ -3796,6 +3796,17 @@ </summary> </histogram> +<histogram name="DomDistiller.Time.RunDistillationJavaScript" units="ms" + expires_after="2025-08-24"> + <owner>wylieb@google.com</owner> + <owner>chrome-reader-mode-team@google.com</owner> + <summary> + The amount of time the distiller script spent running on the page. This is + includes loading the script onto the web page. Recorded after the user has + chosen to view a simplified version of the current page. + </summary> +</histogram> + <histogram name="DomDistiller.Time.TimeToProvideResultToAccumulator" units="ms" expires_after="2025-08-24"> <owner>wylieb@google.com</owner> @@ -3818,6 +3829,17 @@ </summary> </histogram> +<histogram name="DomDistiller.WordCount" units="count" + expires_after="2025-08-24"> + <owner>wylieb@google.com</owner> + <owner>chrome-reader-mode-team@google.com</owner> + <summary> + Records the word count for the distilled page. Recorded when the page + distillation result is received. Recorded per-page even if the article has + multiple pages (e.g. a continue reading button). + </summary> +</histogram> + <histogram name="PumpkinInstaller.InstallationSuccess" enum="BooleanSuccess" expires_after="2025-05-04"> <owner>akihiroota@chromium.org</owner>
diff --git a/tools/metrics/histograms/metadata/android/enums.xml b/tools/metrics/histograms/metadata/android/enums.xml index 4e7e809..e0372ff 100644 --- a/tools/metrics/histograms/metadata/android/enums.xml +++ b/tools/metrics/histograms/metadata/android/enums.xml
@@ -455,9 +455,10 @@ <enum name="AndroidWebViewSupervisedUserUrlCheckResult"> <int value="0" label="ALLOWED_SITE_LOADED"/> - <int value="1" label="ALLOWED_SITE_DETECTED"/> + <int value="1" label="(Obsolete) ALLOWED_SITE_DETECTED. Removed in 2025/02"/> <int value="2" label="DISALLOWED_SITE_BLOCKED"/> - <int value="3" label="DISALLOWED_SITE_DETECTED"/> + <int value="3" + label="(Obsolete) DISALLOWED_SITE_DETECTED. Removed in 2025/02"/> <int value="4" label="TIMEOUT"/> <int value="5" label="UNKNOWN_ERROR"/> </enum>
diff --git a/tools/metrics/histograms/metadata/android/histograms.xml b/tools/metrics/histograms/metadata/android/histograms.xml index a01750b..e7ca93f80 100644 --- a/tools/metrics/histograms/metadata/android/histograms.xml +++ b/tools/metrics/histograms/metadata/android/histograms.xml
@@ -7729,10 +7729,9 @@ <owner>src/android_webview/OWNERS</owner> <summary> Records whether a given URL is allowed to be loaded by the current device - user. This is based on a combination of a call to GMS Core, which checks if - the url is allowed, and the setting - AwFeatures.WEBVIEW_SUPERVISED_USER_SITE_BLOCK, which determines if we - actually block urls or only log the results. + user. This is based on a call to GMS Core, which checks if the url is + allowed. This is recorded during each URL load, however only for the + resource types which undergo this type of URL check. </summary> </histogram> @@ -7742,7 +7741,9 @@ <owner>src/android_webview/OWNERS</owner> <summary> Records the time taken to return from the call to GMS Core that checks if a - given URL is allowed to be loaded by the current device user. + given URL is allowed to be loaded by the current device user. This is + recorded during each URL load, however only for the resource types which + undergo this type of URL check. </summary> </histogram> @@ -7753,7 +7754,9 @@ <summary> Records the time taken to return from the call to GMS Core that checks if a given URL is allowed to be loaded by the current device user. This metric is - only recorded on the first call to GMS Core. + only recorded on the first call to GMS Core. This is recorded only for the + first URL load, and only for the resource types which undergo this type of + URL check. </summary> </histogram> @@ -7764,7 +7767,9 @@ <summary> Records the time taken to return from the call to GMS Core that checks if a given URL is allowed to be loaded by the current device user. This metric is - not recorded on the first call, only on subsequent ones. + not recorded on the first call, only on subsequent ones. This is recorded + during each URL load after the initial load, however only for the resource + types which undergo this type of URL check. </summary> </histogram>
diff --git a/tools/metrics/histograms/metadata/autofill/enums.xml b/tools/metrics/histograms/metadata/autofill/enums.xml index 86411584..013604a 100644 --- a/tools/metrics/histograms/metadata/autofill/enums.xml +++ b/tools/metrics/histograms/metadata/autofill/enums.xml
@@ -2983,6 +2983,16 @@ <!-- LINT.ThenChange(/components/autofill/core/browser/webdata/autofill_webdata_backend_impl.cc:Result) --> +<!-- LINT.IfChange(BnplPopupWindowResult) --> + +<enum name="BnplPopupWindowResult"> + <int value="0" label="Success"/> + <int value="1" label="Failure"/> + <int value="2" label="User closed before finishing"/> +</enum> + +<!-- LINT.ThenChange(/components/autofill/core/browser/payments/payments_window_manager.h:BnplFlowResult) --> + <enum name="BnplSuggestionNotShownReason"> <int value="0" label="Amount extraction failure"/> <int value="1" label="Checkout amount not supported"/>
diff --git a/tools/metrics/histograms/metadata/autofill/histograms.xml b/tools/metrics/histograms/metadata/autofill/histograms.xml index 94cbfcb0e3..45552d7 100644 --- a/tools/metrics/histograms/metadata/autofill/histograms.xml +++ b/tools/metrics/histograms/metadata/autofill/histograms.xml
@@ -55,6 +55,12 @@ summary="3DS (Three Domain Secure) authentication only"/> </variants> +<variants name="Autofill.BnplIssuer"> + <variant name="Affirm" summary="Buy-now-pay-later issued by Affirm"/> + <variant name="Afterpay" summary="Buy-now-pay-later issued by Afterpay"/> + <variant name="Zip" summary="Buy-now-pay-later issued by Zip"/> +</variants> + <variants name="Autofill.CardMetadataVisible"> <variant name="ArtImageShown" summary="Only art image was visible"/> <variant name="MetadataNotShown" summary="No metadata was visible"/> @@ -1599,6 +1605,30 @@ </summary> </histogram> +<histogram name="Autofill.Bnpl.PopupWindowResult.{Issuer}" + enum="BnplPopupWindowResult" expires_after="2025-07-01"> + <owner>wilsonlow@google.com</owner> + <owner>vinnypersky@google.com</owner> + <owner>payments-autofill-team@google.com</owner> + <summary> + Tracks the result of the BNPL pop-up window for {Issuer}, i.e. Success, + Failure, or NotFinished. Logged once when the pop-up window is closed. + </summary> + <token key="Issuer" variants="Autofill.BnplIssuer"/> +</histogram> + +<histogram name="Autofill.Bnpl.PopupWindowShown.{Issuer}" enum="BooleanShown" + expires_after="2025-07-01"> + <owner>wilsonlow@google.com</owner> + <owner>vinnypersky@google.com</owner> + <owner>payments-autofill-team@google.com</owner> + <summary> + The count for the number of times the BNPL popup window for {Issuer} is + shown. Records true every time the popup window is shown. + </summary> + <token key="Issuer" variants="Autofill.BnplIssuer"/> +</histogram> + <histogram name="Autofill.Bnpl.SuggestionNotShownReason" enum="BnplSuggestionNotShownReason" expires_after="2025-07-01"> <owner>wilsonlow@google.com</owner> @@ -1612,19 +1642,15 @@ </summary> </histogram> -<histogram name="Autofill.Bnpl.TosDialogShown.{IssuerName}" enum="BooleanShown" +<histogram name="Autofill.Bnpl.TosDialogShown.{Issuer}" enum="BooleanShown" expires_after="2025-07-01"> <owner>longsheng@google.com</owner> <owner>payments-autofill-team@google.com</owner> <summary> - The count for the number of times the BNPL ToS Dialog is shown. Records true - every time the dialog is shown. + The count for the number of times the BNPL ToS Dialog is shown for {Issuer}. + Records true every time the dialog is shown. </summary> - <token key="IssuerName"> - <variant name="Affirm"/> - <variant name="Afterpay"/> - <variant name="Zip"/> - </token> + <token key="Issuer" variants="Autofill.BnplIssuer"/> </histogram> <histogram name="Autofill.CardholderNameFixFlowPrompt.Events"
diff --git a/tools/metrics/histograms/metadata/collaboration_service/enums.xml b/tools/metrics/histograms/metadata/collaboration_service/enums.xml index f5729a2..1beefc9 100644 --- a/tools/metrics/histograms/metadata/collaboration_service/enums.xml +++ b/tools/metrics/histograms/metadata/collaboration_service/enums.xml
@@ -30,6 +30,11 @@ <enum name="CollaborationServiceJoinEntryPoint"> <int value="0" label="Unknown"/> + <int value="1" label="Link Click"/> + <int value="2" label="User Typed"/> + <int value="3" label="External Application"/> + <int value="4" label="Forward or Back Button"/> + <int value="5" label="Redirect"/> </enum> <!-- LINT.ThenChange(//components/collaboration/public/collaboration_flow_entry_point.h:CollaborationServiceJoinEntryPoint) -->
diff --git a/tools/metrics/histograms/metadata/extensions/histograms.xml b/tools/metrics/histograms/metadata/extensions/histograms.xml index 84e2252a..5f7cca0 100644 --- a/tools/metrics/histograms/metadata/extensions/histograms.xml +++ b/tools/metrics/histograms/metadata/extensions/histograms.xml
@@ -3819,20 +3819,6 @@ </summary> </histogram> -<histogram name="Extensions.LoadUserScript" units="units" - expires_after="2023-07-07"> - <owner>rdevlin.cronin@chromium.org</owner> - <owner>extensions-core@chromium.org</owner> - <summary> - The number of converted user scripts loaded at profile open. - - Histogram is in the process of being removed in favor of its incremented - version that emits only on profile open for "user" profiles - (profiles where people can install extensions, specifically profiles that - can have non-component extensions installed). - </summary> -</histogram> - <histogram name="Extensions.LoadUserScript2" units="units" expires_after="2025-04-20"> <owner>rdevlin.cronin@chromium.org</owner>
diff --git a/tools/metrics/histograms/metadata/ios/enums.xml b/tools/metrics/histograms/metadata/ios/enums.xml index 7462ba4f..164e763 100644 --- a/tools/metrics/histograms/metadata/ios/enums.xml +++ b/tools/metrics/histograms/metadata/ios/enums.xml
@@ -343,6 +343,38 @@ <int value="3" label="Tertiary Action Button"/> </enum> +<!-- LINT.IfChange(IOSDefaultStatusAPIOutcomeType) --> + +<enum name="IOSDefaultStatusAPIOutcomeType"> + <int value="1" label="Success"/> + <int value="2" label="Cooldown error"/> + <int value="3" label="Other error"/> +</enum> + +<!-- LINT.ThenChange(/ios/chrome/browser/default_browser/model/default_status/default_status_helper_types.h:DefaultStatusAPIOutcomeType) --> + +<!-- LINT.IfChange(IOSDefaultStatusHeuristicAssessment) --> + +<enum name="IOSDefaultStatusHeuristicAssessment"> + <int value="0" label="True negative"/> + <int value="1" label="False negative"/> + <int value="2" label="True positive"/> + <int value="3" label="False positive"/> +</enum> + +<!-- LINT.ThenChange(/ios/chrome/browser/default_browser/model/default_status/default_status_helper_types.h:DefaultStatusHeuristicAssessment) --> + +<!-- LINT.IfChange(IOSDefaultStatusRetention) --> + +<enum name="IOSDefaultStatusRetention"> + <int value="0" label="Became default"/> + <int value="1" label="Became non-default"/> + <int value="2" label="Remained default"/> + <int value="3" label="Remained non-default"/> +</enum> + +<!-- LINT.ThenChange(/ios/chrome/browser/default_browser/model/default_status/default_status_helper_types.h:DefaultStatusRetention) --> + <enum name="IOSDeleteAllSavedCredentialsActions"> <int value="0" label="User confirmed delete all saved credentials"/> <int value="1" label="User cancelled delete all saved credentials"/>
diff --git a/tools/metrics/histograms/metadata/ios/histograms.xml b/tools/metrics/histograms/metadata/ios/histograms.xml index f50a2baa..3abd085 100644 --- a/tools/metrics/histograms/metadata/ios/histograms.xml +++ b/tools/metrics/histograms/metadata/ios/histograms.xml
@@ -1285,6 +1285,189 @@ </summary> </histogram> +<histogram name="IOS.DefaultStatusAPI.CooldownError.DaysLeft" units="Days" + expires_after="2026-02-25"> + <owner>gujen@google.com</owner> + <owner>sebsg@chromium.org</owner> + <owner>rkgibson@google.com</owner> + <owner>rohitrao@chromium.org</owner> + <owner>chrome-metrics-team@google.com</owner> + <summary> + The number of days remaining before the default status API can be called + again. The number of days is calculated from the time span between now and + the date of next availability provided in the error status. + + Recorded once per failed API call. An API call is made three times per year + at browser startup. Only recorded if the error is recognized as a cooldown + error (i.e. the API was called too soon). Only recorded on iOS 18.4+. + </summary> +</histogram> + +<histogram name="IOS.DefaultStatusAPI.DaysSinceLastSuccessfulCall" units="Days" + expires_after="2026-02-25"> + <owner>gujen@google.com</owner> + <owner>sebsg@chromium.org</owner> + <owner>rkgibson@google.com</owner> + <owner>rohitrao@chromium.org</owner> + <owner>chrome-metrics-team@google.com</owner> + <summary> + The number of days since the last successful call to the default status + system API. + + Recorded once per successful API call. An API call is made three times per + year at browser startup. If the call is not successful (i.e. there was an + error), this histogram is not recorded. Only recorded on iOS 18.4+. Only + recorded starting from the second successful API call for this client. + </summary> +</histogram> + +<histogram name="IOS.DefaultStatusAPI.DefaultStatusRetention" + enum="IOSDefaultStatusRetention" expires_after="2026-02-25"> + <owner>gujen@google.com</owner> + <owner>sebsg@chromium.org</owner> + <owner>rkgibson@google.com</owner> + <owner>rohitrao@chromium.org</owner> + <owner>chrome-metrics-team@google.com</owner> + <summary> + Records the change in default status since the last successful call to the + default status system API for this client. + + Recorded once per successful API call. An API call is made three times per + year at browser startup. If the call is not successful (i.e. there was an + error), this histogram is not recorded. Only recorded on iOS 18.4+. Only + recorded starting from the second successful API call for this client. + </summary> +</histogram> + +<histogram name="IOS.DefaultStatusAPI.HeuristicAssessment{Days}" + enum="IOSDefaultStatusHeuristicAssessment" expires_after="2026-02-25"> + <owner>gujen@google.com</owner> + <owner>sebsg@chromium.org</owner> + <owner>rkgibson@google.com</owner> + <owner>rohitrao@chromium.org</owner> + <owner>chrome-metrics-team@google.com</owner> + <summary> + Records the difference between the system reported default status, and the + custom, heuristic-based default status (see IOS.IsDefaultBrowser, + IOS.IsDefaultBrowser21, and IOS.IsDefaultBrowser[NumberOfDays]). + + A true positive means both the system API and the heuristic had a default + status of true. A false positive means the heuristic had a default status of + true, but the system API reported false. A true negative means both reported + false, and a false negative means the heuristic reported false while the + system reported true. + + Recorded once per successful API call. An API call is made three times per + year at browser startup. If the call is not successful (i.e. there was an + error), this histogram is not recorded. Only recorded on iOS 18.4+. + + This histogram variant is for the {Days}-day heuristic. + </summary> + <token key="Days"> + <variant name="1"/> + <variant name="3"/> + <variant name="7"/> + <variant name="14"/> + <variant name="21"/> + <variant name="28"/> + <variant name="35"/> + <variant name="42"/> + </token> +</histogram> + +<!-- TODO(crbug.com/401551080) Mark this "expires-never" after validating it works as intended. --> + +<histogram name="IOS.DefaultStatusAPI.IsDefaultBrowser" enum="Boolean" + expires_after="2026-02-25"> + <owner>gujen@google.com</owner> + <owner>sebsg@chromium.org</owner> + <owner>rkgibson@google.com</owner> + <owner>rohitrao@chromium.org</owner> + <owner>chrome-metrics-team@google.com</owner> + <summary> + Whether Chrome is the default browser or not according to the default status + system API. + + Recorded once per successful API call. An API call is made three times per + year at browser startup. If the call is not successful (i.e. there was an + error), this histogram is not recorded. Only recorded on iOS 18.4+. + </summary> +</histogram> + +<!-- LINT.IfChange(CohortNumber) --> + +<histogram name="IOS.DefaultStatusAPI.IsDefaultBrowser.Cohort{Number}" + enum="Boolean" expires_after="2026-02-25"> + <owner>gujen@google.com</owner> + <owner>sebsg@chromium.org</owner> + <owner>rkgibson@google.com</owner> + <owner>rohitrao@chromium.org</owner> + <owner>chrome-metrics-team@google.com</owner> + <summary> + Whether Chrome is the default browser or not according to the default status + system API. Works the same as IOS.DefaultStatusAPI.IsDefaultBrowser, except + this histogram has a variant for each cohort, and clients record only to the + histogram variant corresponding to the cohort they are assigned to. The + timeline of this histogram can be used to monitor cohort drift. + + Recorded once per successful API call. An API call is made three times per + year at browser startup. If the call is not successful (i.e. there was an + error), this histogram is not recorded. Only recorded on iOS 18.4+. + + This histogram variant is for cohort {Number}. + </summary> + <token key="Number"> + <variant name="1" summary="1 (January, May and September)"/> + <variant name="2" summary="2 (February, June, October)"/> + <variant name="3" summary="3 (March, July, November)"/> + <variant name="4" summary="4 (April, August, December)"/> + </token> +</histogram> + +<!-- LINT.ThenChange(/ios/chrome/browser/default_browser/model/default_status/default_status_helper_constants.mm:kCohortCount) --> + +<!-- TODO(crbug.com/401551080) Mark this "expires-never" after validating it works as intended. --> + +<histogram name="IOS.DefaultStatusAPI.IsDefaultBrowser.StrictCohorts" + enum="Boolean" expires_after="2026-02-25"> + <owner>gujen@google.com</owner> + <owner>sebsg@chromium.org</owner> + <owner>rkgibson@google.com</owner> + <owner>rohitrao@chromium.org</owner> + <owner>chrome-metrics-team@google.com</owner> + <summary> + Whether Chrome is the default browser or not according to the default status + system API, but only for clients who made the API call during their assigned + cohort's reporting window. Clients are split into 4 cohorts, each with 3 + assigned months in the year. Depending on usage frequency, it's possible + some clients miss their window. When this happens, the API call is still + made even if the client is outside their reporting window so that the API + call opportunity is not wasted. However, those results will only be recorded + to IOS.DefaultStatusAPI.IsDefaultBrowser, not to this histogram. + + Recorded once per successful API call. An API call is made three times per + year at browser startup. If the call is not successful (i.e. there was an + error), this histogram is not recorded. Only recorded on iOS 18.4+. + </summary> +</histogram> + +<histogram name="IOS.DefaultStatusAPI.OutcomeType" + enum="IOSDefaultStatusAPIOutcomeType" expires_after="2026-02-25"> + <owner>gujen@google.com</owner> + <owner>sebsg@chromium.org</owner> + <owner>rkgibson@google.com</owner> + <owner>rohitrao@chromium.org</owner> + <owner>chrome-metrics-team@google.com</owner> + <summary> + Whether a call to the default status system API successfully returned a + result or yielded an error. Errors are split into 2 buckets, + cooldown-related errors and all other errors. + + Recorded once per API call. An API call is made three times per year at + browser startup. Only recorded on iOS 18.4+. + </summary> +</histogram> + <histogram name="IOS.Desktop.{PromoType}.Shown" enum="DesktopIOSPromoImpression" expires_after="2025-08-05"> <owner>nicolasmacbeth@google.com</owner>
diff --git a/tools/metrics/histograms/metadata/lens/enums.xml b/tools/metrics/histograms/metadata/lens/enums.xml index bfe5b336..c89d3ae35 100644 --- a/tools/metrics/histograms/metadata/lens/enums.xml +++ b/tools/metrics/histograms/metadata/lens/enums.xml
@@ -219,6 +219,7 @@ <int value="2" label="Medium"/> <int value="3" label="Peaking"/> <int value="4" label="Consent"/> + <int value="5" label="InfoMessage"/> </enum> <!-- LINT.ThenChange(//ios/chrome/browser/lens_overlay/model/lens_overlay_sheet_detent_state.h:SheetDimensionState) -->
diff --git a/tools/metrics/histograms/metadata/optimization/enums.xml b/tools/metrics/histograms/metadata/optimization/enums.xml index 4310082..7d11e73 100644 --- a/tools/metrics/histograms/metadata/optimization/enums.xml +++ b/tools/metrics/histograms/metadata/optimization/enums.xml
@@ -750,6 +750,7 @@ <int value="88" label="BMO_CREDIT_CARD_WHOLESALE_CLUB_BENEFITS"/> <int value="90" label="GLIC_CONTEXTUAL_CUEING"/> <int value="91" label="GLIC_ZERO_STATE_SUGGESTIONS"/> + <int value="92" label="GLIC_ACTION_PAGE_BLOCK"/> </enum> <enum name="PageContentAnnotationsStorageStatus">
diff --git a/tools/metrics/histograms/metadata/optimization/histograms.xml b/tools/metrics/histograms/metadata/optimization/histograms.xml index e73dba3..6427045 100644 --- a/tools/metrics/histograms/metadata/optimization/histograms.xml +++ b/tools/metrics/histograms/metadata/optimization/histograms.xml
@@ -291,6 +291,8 @@ <variant name="FormsAnnotations" summary="Provides information about whether a URL is eligible for generating user annotations derived from forms."/> + <variant name="GlicActionPageBlock" + summary="Provides information about whether GLIC may act on a page."/> <variant name="GlicContextualCueing" summary="Provides information about whether a page is eligible to cue for GLIC."/>
diff --git a/tools/perf/core/bot_platforms.py b/tools/perf/core/bot_platforms.py index 80aaa6b..a782e7d 100644 --- a/tools/perf/core/bot_platforms.py +++ b/tools/perf/core/bot_platforms.py
@@ -524,6 +524,16 @@ ]), ]) +_CROSSBENCH_PIXEL9 = frozenset([ + _crossbench_jetstream2(), + _crossbench_speedometer3_1(arguments=['--fileserver']), + _crossbench_motionmark1_3(), + _crossbench_loadline_phone(arguments=[ + '--cool-down-threshold=moderate', + '--no-splash', + ]), +]) + _CROSSBENCH_TANGOR = frozenset([ _crossbench_loadline_tablet(arguments=[ '--cool-down-threshold=moderate', @@ -703,11 +713,11 @@ _GetBenchmarkConfig('speedometer2'), _GetBenchmarkConfig('speedometer3'), ]) -_ANDROID_GO_WEBVIEW_BENCHMARK_CONFIGS = _ANDROID_GO_BENCHMARK_CONFIGS -_ANDROID_PIXEL4_BENCHMARK_CONFIGS = PerfSuite(OFFICIAL_BENCHMARK_CONFIGS) -_ANDROID_PIXEL4_EXECUTABLE_CONFIGS = frozenset([ +_ANDROID_DEFAULT_EXECUTABLE_CONFIGS = frozenset([ _components_perftests(60), ]) +_ANDROID_GO_WEBVIEW_BENCHMARK_CONFIGS = _ANDROID_GO_BENCHMARK_CONFIGS +_ANDROID_PIXEL4_BENCHMARK_CONFIGS = PerfSuite(OFFICIAL_BENCHMARK_CONFIGS) _ANDROID_PIXEL4_WEBVIEW_BENCHMARK_CONFIGS = PerfSuite( OFFICIAL_BENCHMARK_CONFIGS).Remove([ 'jetstream2', @@ -732,15 +742,6 @@ _GetBenchmarkConfig('speedometer2-minorms'), _GetBenchmarkConfig('speedometer3-minorms'), ]) -_ANDROID_PIXEL6_EXECUTABLE_CONFIGS = frozenset([ - _components_perftests(60), -]) -_ANDROID_PIXEL6_PGO_EXECUTABLE_CONFIGS = frozenset([ - _components_perftests(60), -]) -_ANDROID_PIXEL6_PRO_EXECUTABLE_CONFIGS = frozenset([ - _components_perftests(60), -]) # Pixel fold _ANDROID_PIXEL_FOLD_BENCHMARK_CONFIGS = PerfSuite( OFFICIAL_BENCHMARK_CONFIGS).Add([ @@ -748,9 +749,6 @@ _GetBenchmarkConfig('speedometer2-minorms'), _GetBenchmarkConfig('speedometer3-minorms'), ]) -_ANDROID_PIXEL_FOLD_EXECUTABLE_CONFIGS = frozenset([ - _components_perftests(60), -]) # Pixel Tangor _ANDROID_PIXEL_TANGOR_BENCHMARK_CONFIGS = PerfSuite( OFFICIAL_BENCHMARK_CONFIGS).Add([ @@ -758,8 +756,6 @@ _GetBenchmarkConfig('speedometer2-minorms'), _GetBenchmarkConfig('speedometer3-minorms') ]) -_ANDROID_PIXEL_TANGOR_EXECUTABLE_CONFIGS = frozenset( - [_components_perftests(60)]) _CHROMEOS_KEVIN_FYI_BENCHMARK_CONFIGS = PerfSuite( [_GetBenchmarkConfig('rendering.desktop')]) _FUCHSIA_PERF_SMARTDISPLAY_BENCHMARK_CONFIGS = PerfSuite([ @@ -940,14 +936,14 @@ _ANDROID_PIXEL4_BENCHMARK_CONFIGS, 44, 'android', - executables=_ANDROID_PIXEL4_EXECUTABLE_CONFIGS) + executables=_ANDROID_DEFAULT_EXECUTABLE_CONFIGS) ANDROID_PIXEL4_PGO = PerfPlatform( 'android-pixel4-perf-pgo', 'Android R', _ANDROID_PIXEL4_BENCHMARK_CONFIGS, 28, 'android', - executables=_ANDROID_PIXEL4_EXECUTABLE_CONFIGS, + executables=_ANDROID_DEFAULT_EXECUTABLE_CONFIGS, pinpoint_only=True) ANDROID_PIXEL4_WEBVIEW = PerfPlatform( 'android-pixel4_webview-perf', 'Android R', @@ -960,7 +956,7 @@ _ANDROID_PIXEL6_BENCHMARK_CONFIGS, 14, 'android', - executables=_ANDROID_PIXEL6_EXECUTABLE_CONFIGS, + executables=_ANDROID_DEFAULT_EXECUTABLE_CONFIGS, crossbench=_CROSSBENCH_ANDROID) ANDROID_PIXEL6_PGO = PerfPlatform( 'android-pixel6-perf-pgo', @@ -968,7 +964,7 @@ _ANDROID_PIXEL6_PGO_BENCHMARK_CONFIGS, 8, 'android', - executables=_ANDROID_PIXEL6_PGO_EXECUTABLE_CONFIGS, + executables=_ANDROID_DEFAULT_EXECUTABLE_CONFIGS, crossbench=_CROSSBENCH_ANDROID) ANDROID_PIXEL6_PRO = PerfPlatform( 'android-pixel6-pro-perf', @@ -976,14 +972,14 @@ _ANDROID_PIXEL6_PRO_BENCHMARK_CONFIGS, 10, 'android', - executables=_ANDROID_PIXEL6_PRO_EXECUTABLE_CONFIGS) + executables=_ANDROID_DEFAULT_EXECUTABLE_CONFIGS) ANDROID_PIXEL6_PRO_PGO = PerfPlatform( 'android-pixel6-pro-perf-pgo', 'Android T', _ANDROID_PIXEL6_PRO_BENCHMARK_CONFIGS, 16, 'android', - executables=_ANDROID_PIXEL6_PRO_EXECUTABLE_CONFIGS, + executables=_ANDROID_DEFAULT_EXECUTABLE_CONFIGS, pinpoint_only=True) ANDROID_PIXEL_FOLD = PerfPlatform( 'android-pixel-fold-perf', @@ -991,21 +987,43 @@ _ANDROID_PIXEL_FOLD_BENCHMARK_CONFIGS, 15, 'android', - executables=_ANDROID_PIXEL_FOLD_EXECUTABLE_CONFIGS) + executables=_ANDROID_DEFAULT_EXECUTABLE_CONFIGS) ANDROID_PIXEL_TANGOR = PerfPlatform( 'android-pixel-tangor-perf', 'Android U', _ANDROID_PIXEL_TANGOR_BENCHMARK_CONFIGS, 8, 'android', - executables=_ANDROID_PIXEL_TANGOR_EXECUTABLE_CONFIGS, + executables=_ANDROID_DEFAULT_EXECUTABLE_CONFIGS, crossbench=_CROSSBENCH_TANGOR) ANDROID_GO_WEMBLEY = PerfPlatform('android-go-wembley-perf', 'Android U', _ANDROID_GO_BENCHMARK_CONFIGS, 15, 'android') ANDROID_GO_WEMBLEY_WEBVIEW = PerfPlatform( 'android-go-wembley_webview-perf', 'Android U', _ANDROID_GO_WEBVIEW_BENCHMARK_CONFIGS, 20, 'android') - +ANDROID_PIXEL9 = PerfPlatform('android-pixel9-perf', + 'Android B', + PerfSuite([]), + 4, + 'android', + executables=_ANDROID_DEFAULT_EXECUTABLE_CONFIGS, + crossbench=_CROSSBENCH_PIXEL9) +ANDROID_PIXEL9_PRO = PerfPlatform( + 'android-pixel9-pro-perf', + 'Android B', + PerfSuite([]), + 4, + 'android', + executables=_ANDROID_DEFAULT_EXECUTABLE_CONFIGS, + crossbench=_CROSSBENCH_PIXEL9) +ANDROID_PIXEL9_PRO_XL = PerfPlatform( + 'android-pixel9-pro-xl-perf', + 'Android B', + PerfSuite([]), + 4, + 'android', + executables=_ANDROID_DEFAULT_EXECUTABLE_CONFIGS, + crossbench=_CROSSBENCH_PIXEL9) # Cros FUCHSIA_PERF_NELSON = PerfPlatform('fuchsia-perf-nsn', '',
diff --git a/tools/perf/core/perf_data_generator.py b/tools/perf/core/perf_data_generator.py index 891f6ad..944cbb0 100755 --- a/tools/perf/core/perf_data_generator.py +++ b/tools/perf/core/perf_data_generator.py
@@ -738,6 +738,51 @@ 'device_os_flavor': 'google', }, }, + 'android-pixel9-perf': { + 'tests': [{ + 'isolate': + 'performance_test_suite_android_trichrome_chrome_google_64_32_bundle', + }], + 'platform': + 'android-trichrome-chrome-google-64-32-bundle', + 'dimension': { + 'pool': 'chrome.tests.perf', + 'os': 'Android', + 'device_type': 'tokay', + 'device_os': 'B', + 'device_os_flavor': 'google', + }, + }, + 'android-pixel9-pro-perf': { + 'tests': [{ + 'isolate': + 'performance_test_suite_android_trichrome_chrome_google_64_32_bundle', + }], + 'platform': + 'android-trichrome-chrome-google-64-32-bundle', + 'dimension': { + 'pool': 'chrome.tests.perf', + 'os': 'Android', + 'device_type': 'caiman', + 'device_os': 'B', + 'device_os_flavor': 'google', + }, + }, + 'android-pixel9-pro-xl-perf': { + 'tests': [{ + 'isolate': + 'performance_test_suite_android_trichrome_chrome_google_64_32_bundle', + }], + 'platform': + 'android-trichrome-chrome-google-64-32-bundle', + 'dimension': { + 'pool': 'chrome.tests.perf', + 'os': 'Android', + 'device_type': 'komodo', + 'device_os': 'B', + 'device_os_flavor': 'google', + }, + }, 'android-go-processor-perf': { 'platform': 'linux', 'perf_processor': True,
diff --git a/tools/perf/core/shard_maps/android-pixel9-perf_map.json b/tools/perf/core/shard_maps/android-pixel9-perf_map.json new file mode 100644 index 0000000..9658aba --- /dev/null +++ b/tools/perf/core/shard_maps/android-pixel9-perf_map.json
@@ -0,0 +1,62 @@ +{ + "0": { + "executables": { + "components_perftests": { + "arguments": [ + "--xvfb" + ], + "path": "components_perftests" + } + }, + "crossbench": { + "jetstream_2.2": { + "display_name": "jetstream2.crossbench", + "arguments": [] + } + }, + "benchmarks": {} + }, + "1": { + "crossbench": { + "loadline-phone-fast": { + "display_name": "loadline_phone.crossbench", + "arguments": [ + "--cool-down-threshold=moderate", + "--no-splash" + ] + } + }, + "benchmarks": {} + }, + "2": { + "crossbench": { + "motionmark_1.3": { + "display_name": "motionmark1.3.crossbench", + "arguments": [] + } + }, + "benchmarks": {} + }, + "3": { + "crossbench": { + "speedometer_3.1": { + "display_name": "speedometer3.1.crossbench", + "arguments": [ + "--fileserver" + ] + } + }, + "benchmarks": {} + }, + "extra_infos": { + "num_stories": 5, + "predicted_min_shard_time": 60, + "predicted_min_shard_index": 3, + "predicted_max_shard_time": 7000, + "predicted_max_shard_index": 1, + "shard #0": 240.0, + "shard #1": 7000, + "shard #2": 360, + "shard #3": 60 + } +}
diff --git a/tools/perf/core/shard_maps/android-pixel9-pro-perf_map.json b/tools/perf/core/shard_maps/android-pixel9-pro-perf_map.json new file mode 100644 index 0000000..9658aba --- /dev/null +++ b/tools/perf/core/shard_maps/android-pixel9-pro-perf_map.json
@@ -0,0 +1,62 @@ +{ + "0": { + "executables": { + "components_perftests": { + "arguments": [ + "--xvfb" + ], + "path": "components_perftests" + } + }, + "crossbench": { + "jetstream_2.2": { + "display_name": "jetstream2.crossbench", + "arguments": [] + } + }, + "benchmarks": {} + }, + "1": { + "crossbench": { + "loadline-phone-fast": { + "display_name": "loadline_phone.crossbench", + "arguments": [ + "--cool-down-threshold=moderate", + "--no-splash" + ] + } + }, + "benchmarks": {} + }, + "2": { + "crossbench": { + "motionmark_1.3": { + "display_name": "motionmark1.3.crossbench", + "arguments": [] + } + }, + "benchmarks": {} + }, + "3": { + "crossbench": { + "speedometer_3.1": { + "display_name": "speedometer3.1.crossbench", + "arguments": [ + "--fileserver" + ] + } + }, + "benchmarks": {} + }, + "extra_infos": { + "num_stories": 5, + "predicted_min_shard_time": 60, + "predicted_min_shard_index": 3, + "predicted_max_shard_time": 7000, + "predicted_max_shard_index": 1, + "shard #0": 240.0, + "shard #1": 7000, + "shard #2": 360, + "shard #3": 60 + } +}
diff --git a/tools/perf/core/shard_maps/android-pixel9-pro-xl-perf_map.json b/tools/perf/core/shard_maps/android-pixel9-pro-xl-perf_map.json new file mode 100644 index 0000000..9658aba --- /dev/null +++ b/tools/perf/core/shard_maps/android-pixel9-pro-xl-perf_map.json
@@ -0,0 +1,62 @@ +{ + "0": { + "executables": { + "components_perftests": { + "arguments": [ + "--xvfb" + ], + "path": "components_perftests" + } + }, + "crossbench": { + "jetstream_2.2": { + "display_name": "jetstream2.crossbench", + "arguments": [] + } + }, + "benchmarks": {} + }, + "1": { + "crossbench": { + "loadline-phone-fast": { + "display_name": "loadline_phone.crossbench", + "arguments": [ + "--cool-down-threshold=moderate", + "--no-splash" + ] + } + }, + "benchmarks": {} + }, + "2": { + "crossbench": { + "motionmark_1.3": { + "display_name": "motionmark1.3.crossbench", + "arguments": [] + } + }, + "benchmarks": {} + }, + "3": { + "crossbench": { + "speedometer_3.1": { + "display_name": "speedometer3.1.crossbench", + "arguments": [ + "--fileserver" + ] + } + }, + "benchmarks": {} + }, + "extra_infos": { + "num_stories": 5, + "predicted_min_shard_time": 60, + "predicted_min_shard_index": 3, + "predicted_max_shard_time": 7000, + "predicted_max_shard_index": 1, + "shard #0": 240.0, + "shard #1": 7000, + "shard #2": 360, + "shard #3": 60 + } +}
diff --git a/tools/perf/core/shard_maps/timing_data/android-pixel9-perf_timing.json b/tools/perf/core/shard_maps/timing_data/android-pixel9-perf_timing.json new file mode 100644 index 0000000..7a81fb3 --- /dev/null +++ b/tools/perf/core/shard_maps/timing_data/android-pixel9-perf_timing.json
@@ -0,0 +1,6 @@ +[ + { + "duration": "60.0", + "name": "components_perftests/_gtest_" + } +]
diff --git a/tools/perf/core/shard_maps/timing_data/android-pixel9-pro-perf_timing.json b/tools/perf/core/shard_maps/timing_data/android-pixel9-pro-perf_timing.json new file mode 100644 index 0000000..7a81fb3 --- /dev/null +++ b/tools/perf/core/shard_maps/timing_data/android-pixel9-pro-perf_timing.json
@@ -0,0 +1,6 @@ +[ + { + "duration": "60.0", + "name": "components_perftests/_gtest_" + } +]
diff --git a/tools/perf/core/shard_maps/timing_data/android-pixel9-pro-xl-perf_timing.json b/tools/perf/core/shard_maps/timing_data/android-pixel9-pro-xl-perf_timing.json new file mode 100644 index 0000000..7a81fb3 --- /dev/null +++ b/tools/perf/core/shard_maps/timing_data/android-pixel9-pro-xl-perf_timing.json
@@ -0,0 +1,6 @@ +[ + { + "duration": "60.0", + "name": "components_perftests/_gtest_" + } +]
diff --git a/ui/android/java/res/layout/dropdown_item.xml b/ui/android/java/res/layout/dropdown_item.xml index 71d015f..15be133 100644 --- a/ui/android/java/res/layout/dropdown_item.xml +++ b/ui/android/java/res/layout/dropdown_item.xml
@@ -14,13 +14,6 @@ android:orientation="horizontal" > <!-- These layout params are overwritten in DropdownAdapter.java --> - <ImageView - android:id="@+id/start_dropdown_icon" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_margin="@dimen/dropdown_icon_margin" - tools:ignore="ContentDescription" /> - <LinearLayout android:id="@+id/dropdown_label_wrapper" android:layout_width="0dp"
diff --git a/ui/android/java/src/org/chromium/ui/DropdownAdapter.java b/ui/android/java/src/org/chromium/ui/DropdownAdapter.java index 684173857..8d6e8ff 100644 --- a/ui/android/java/src/org/chromium/ui/DropdownAdapter.java +++ b/ui/android/java/src/org/chromium/ui/DropdownAdapter.java
@@ -136,15 +136,7 @@ sublabelView.setVisibility(View.VISIBLE); } - ImageView iconViewStart = (ImageView) layout.findViewById(R.id.start_dropdown_icon); - ImageView iconViewEnd = (ImageView) layout.findViewById(R.id.end_dropdown_icon); - if (item.isIconAtStart()) { - iconViewEnd.setVisibility(View.GONE); - } else { - iconViewStart.setVisibility(View.GONE); - } - - ImageView iconView = item.isIconAtStart() ? iconViewStart : iconViewEnd; + ImageView iconView = (ImageView) layout.findViewById(R.id.end_dropdown_icon); if (item.getIconId() == DropdownItem.NO_ICON) { iconView.setVisibility(View.GONE); } else {
diff --git a/ui/android/java/src/org/chromium/ui/DropdownItem.java b/ui/android/java/src/org/chromium/ui/DropdownItem.java index 4eddfca..918605ab 100644 --- a/ui/android/java/src/org/chromium/ui/DropdownItem.java +++ b/ui/android/java/src/org/chromium/ui/DropdownItem.java
@@ -69,12 +69,6 @@ /** Returns resource ID of sublabel's font size. */ int getSublabelFontSizeResId(); - /** - * Returns whether the icon should be displayed at the start, before label - * and sublabel. - */ - boolean isIconAtStart(); - /** Returns the resource ID of the icon's size, or 0 to use WRAP_CONTENT. */ int getIconSizeResId();
diff --git a/ui/android/java/src/org/chromium/ui/DropdownItemBase.java b/ui/android/java/src/org/chromium/ui/DropdownItemBase.java index 393dcccd..141a691f 100644 --- a/ui/android/java/src/org/chromium/ui/DropdownItemBase.java +++ b/ui/android/java/src/org/chromium/ui/DropdownItemBase.java
@@ -72,11 +72,6 @@ } @Override - public boolean isIconAtStart() { - return false; - } - - @Override public int getIconSizeResId() { return 0; }
diff --git a/ui/gtk/gtk_ui.cc b/ui/gtk/gtk_ui.cc index aba90916..76ec70f2 100644 --- a/ui/gtk/gtk_ui.cc +++ b/ui/gtk/gtk_ui.cc
@@ -673,9 +673,7 @@ gint size = 0; g_object_get(gtk_settings_get_default(), "gtk-cursor-theme-size", &size, nullptr); - if (GtkCheckVersion(4)) { - // GTK4 supports per-monitor scaling, so the gtk-cursor-theme-size is not - // premultiplied by the scale factor. + if (platform_->IncludeScaleInCursorSize()) { size *= display_config().primary_scale; } return size;
diff --git a/ui/gtk/gtk_ui_platform.h b/ui/gtk/gtk_ui_platform.h index c14de10..e87ab685 100644 --- a/ui/gtk/gtk_ui_platform.h +++ b/ui/gtk/gtk_ui_platform.h
@@ -60,6 +60,9 @@ // If true, the device scale factor should be multiplied by the font scale. If // false, the font size should be multiplied by the font scale. virtual bool IncludeFontScaleInDeviceScale() const = 0; + + // Returns true if the cursor size should be multiplied by the scale factor. + virtual bool IncludeScaleInCursorSize() const = 0; }; } // namespace gtk
diff --git a/ui/gtk/gtk_ui_platform_stub.cc b/ui/gtk/gtk_ui_platform_stub.cc index fcc7e7fe..aa50fc2 100644 --- a/ui/gtk/gtk_ui_platform_stub.cc +++ b/ui/gtk/gtk_ui_platform_stub.cc
@@ -50,4 +50,8 @@ return false; } +bool GtkUiPlatformStub::IncludeScaleInCursorSize() const { + return false; +} + } // namespace gtk
diff --git a/ui/gtk/gtk_ui_platform_stub.h b/ui/gtk/gtk_ui_platform_stub.h index 0feb75c9..ebdeadc 100644 --- a/ui/gtk/gtk_ui_platform_stub.h +++ b/ui/gtk/gtk_ui_platform_stub.h
@@ -28,6 +28,7 @@ std::unique_ptr<ui::LinuxInputMethodContext> CreateInputMethodContext( ui::LinuxInputMethodContextDelegate* delegate) const override; bool IncludeFontScaleInDeviceScale() const override; + bool IncludeScaleInCursorSize() const override; }; } // namespace gtk
diff --git a/ui/gtk/wayland/gtk_ui_platform_wayland.cc b/ui/gtk/wayland/gtk_ui_platform_wayland.cc index af7181e..b9d1b2087 100644 --- a/ui/gtk/wayland/gtk_ui_platform_wayland.cc +++ b/ui/gtk/wayland/gtk_ui_platform_wayland.cc
@@ -167,4 +167,8 @@ return base::FeatureList::IsEnabled(features::kWaylandUiScale); } +bool GtkUiPlatformWayland::IncludeScaleInCursorSize() const { + return false; +} + } // namespace gtk
diff --git a/ui/gtk/wayland/gtk_ui_platform_wayland.h b/ui/gtk/wayland/gtk_ui_platform_wayland.h index a294174..e805838 100644 --- a/ui/gtk/wayland/gtk_ui_platform_wayland.h +++ b/ui/gtk/wayland/gtk_ui_platform_wayland.h
@@ -33,6 +33,7 @@ std::unique_ptr<ui::LinuxInputMethodContext> CreateInputMethodContext( ui::LinuxInputMethodContextDelegate* delegate) const override; bool IncludeFontScaleInDeviceScale() const override; + bool IncludeScaleInCursorSize() const override; private: GdkDisplay* GetDefaultGdkDisplay();
diff --git a/ui/gtk/x/gtk_ui_platform_x11.cc b/ui/gtk/x/gtk_ui_platform_x11.cc index bd5ac21c..d131bb7 100644 --- a/ui/gtk/x/gtk_ui_platform_x11.cc +++ b/ui/gtk/x/gtk_ui_platform_x11.cc
@@ -120,4 +120,10 @@ return true; } +bool GtkUiPlatformX11::IncludeScaleInCursorSize() const { + // GTK4 supports per-monitor scaling, so the gtk-cursor-theme-size is not + // premultiplied by the scale factor. + return GtkCheckVersion(4); +} + } // namespace gtk
diff --git a/ui/gtk/x/gtk_ui_platform_x11.h b/ui/gtk/x/gtk_ui_platform_x11.h index 7425966..5634e06 100644 --- a/ui/gtk/x/gtk_ui_platform_x11.h +++ b/ui/gtk/x/gtk_ui_platform_x11.h
@@ -36,6 +36,7 @@ std::unique_ptr<ui::LinuxInputMethodContext> CreateInputMethodContext( ui::LinuxInputMethodContextDelegate* delegate) const override; bool IncludeFontScaleInDeviceScale() const override; + bool IncludeScaleInCursorSize() const override; private: GdkDisplay* GetGdkDisplay();
diff --git a/v8 b/v8 index 8688374..ebd5dc0 160000 --- a/v8 +++ b/v8
@@ -1 +1 @@ -Subproject commit 86883747d3c5aba2f21b1963c6c24931f3d00067 +Subproject commit ebd5dc00c889238ea48d84b19c1bc0a8233d41b1