diff --git a/DEPS b/DEPS index 1cc30465..12613f8 100644 --- a/DEPS +++ b/DEPS
@@ -316,19 +316,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': 'dc7e5ad5e5fdd614ce0e6883a9d04ec6e68026a2', + 'src_internal_revision': '4744296d88ca4b44359a69b4c5b77b5a82ab38b1', # 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': '3c73323d12686f4d9e571f3a9fa02fb76a19b967', + 'skia_revision': '926c09741ce26563e615e8cc670e0edbd66965c2', # 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': '05fdb31f32a0b6e9771eb9ecd088bd1cde6799c5', + 'v8_revision': 'e05f632b216d7407e7570cfc88e5391a1850e685', # 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': '272c2ae3264a7ae019546c163c230a7cd29a2252', + 'angle_revision': 'ab41985470ba0a74e80ecfd7ec21e4d9ada428da', # Three lines of non-changing comments so that # the commit queue can handle CLs rolling SwiftShader # and whatever else without interference from each other. @@ -340,7 +340,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': '0e71ca41ee051e666a507033ecf30460fda01583', + 'boringssl_revision': 'd03b45948ec9af4ec01f3a9b5adc381c01966659', # 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. @@ -360,7 +360,7 @@ # Three lines of non-changing comments so that # the commit queue can handle CLs rolling freetype # and whatever else without interference from each other. - 'freetype_revision': 'b6bcd2177f72bb4842c7701d7b7f633bb3fc951a', + 'freetype_revision': '7e3750982be8c6c71a572ef6829c89f27f9c989b', # Three lines of non-changing comments so that # the commit queue can handle CLs rolling HarfBuzz # and whatever else without interference from each other. @@ -400,7 +400,7 @@ # Three lines of non-changing comments so that # the commit queue can handle CLs rolling devtools-frontend # and whatever else without interference from each other. - 'devtools_frontend_revision': '47e1cb2b0f26f7a01873359c9f864c5c377f60ae', + 'devtools_frontend_revision': '14644b8ee2cb32c76c825c69e77d8a276bc2631b', # Three lines of non-changing comments so that # the commit queue can handle CLs rolling libprotobuf-mutator # and whatever else without interference from each other. @@ -532,18 +532,18 @@ # Three lines of non-changing comments so that # the commit queue can handle CLs rolling clusterfuzz-data # and whatever else without interference from each other. - 'clusterfuzz_data_revision':'922b2f13ce9a0192bb7354417de8a2967abbacf0', + 'clusterfuzz_data_revision':'7080c2e947de8422346d5593ea52f87e6b28b13d', # Three lines of non-changing comments so that # the commit queue can handle CLs rolling agents-internal # and whatever else without interference from each other. - 'agents_internal_revision': 'eda60a5a7e6373fb28f76d984b070f92bc1d8e80', + 'agents_internal_revision': '0930fb1c6044c36bd542f0a8c444e5633b8531f1', # If you change this, also update the libc++ revision in # //buildtools/deps_revisions.gni. 'libcxx_revision': 'be1c391acca009d8d80535ce924e3d285451cdfa', # GN CIPD package version. - 'gn_version': 'git_revision:576a0ebd4cc674fb52681bd1becfd0facec8449e', + 'gn_version': 'git_revision:88604adbcec2101f25b2e3ebd7f39b38163a6a33', # ninja CIPD package. 'ninja_package': 'infra/3pp/tools/ninja/', @@ -1126,10 +1126,10 @@ 'condition': 'non_git_source', 'objects': [ { - 'object_name': 'meet-gpu-tests/911680305.tar.gz', - 'sha256sum': 'f69d460e61e84c71144e4c3e6e0efbb72a6b077bf4e67b911f42c5c0bcfb5ad7', - 'size_bytes': 277409131, - 'generation': 1778143848009972, + 'object_name': 'meet-gpu-tests/901020169.tar.gz', + 'sha256sum': 'c3213b03e0ca5062dc9dccce8043ff1244d4741b06eae41e0e5e9d62d87de86c', + 'size_bytes': 277398073, + 'generation': 1776415594533310, }, ], }, @@ -1413,7 +1413,7 @@ 'packages': [ { 'package': 'chromium/third_party/updater/chromium_linux64', - 'version': 'version:2@1621004', + 'version': 'version:2@1622008', }, ], }, @@ -1424,7 +1424,7 @@ 'packages': [ { 'package': 'chromium/third_party/updater/chromium_mac_amd64', - 'version': 'version:2@1621015', + 'version': 'version:2@1622023', }, ], }, @@ -1435,7 +1435,7 @@ 'packages': [ { 'package': 'chromium/third_party/updater/chromium_mac_arm64', - 'version': 'version:2@1621008', + 'version': 'version:2@1622035', }, ], }, @@ -1446,7 +1446,7 @@ 'packages': [ { 'package': 'chromium/third_party/updater/chromium_win_arm64', - 'version': 'version:2@1621011', + 'version': 'version:2@1622064', }, ], }, @@ -1457,7 +1457,7 @@ 'packages': [ { 'package': 'chromium/third_party/updater/chromium_win_x86', - 'version': 'version:2@1621037', + 'version': 'version:2@1622058', }, ], }, @@ -1468,7 +1468,7 @@ 'packages': [ { 'package': 'chromium/third_party/updater/chromium_win_x86_64', - 'version': 'version:2@1621004', + 'version': 'version:2@1622040', }, ], }, @@ -1568,7 +1568,7 @@ 'packages': [ { 'package': 'chromium/third_party/enterprise_companion/chromium_linux64', - 'version': 'Jag6VA9n4d_-GQIe9RIlm6PrGQDYU5yLkjR7kdYOpUUC', + 'version': '0-YikUZkqXVL5ak4GgRh_3EZyVJW7ZRZ3AY6WJVek5cC', }, ], }, @@ -1579,7 +1579,7 @@ 'packages': [ { 'package': 'chromium/third_party/enterprise_companion/chromium_mac_amd64', - 'version': '0TXWU2PMuVgV9euuiEDrQC4DWkzCn1PTgqIxYo-1GCQC', + 'version': 'JoDP0VBlwsoHVJPQWu_qm0JEWrnyCcpt_x2IiEOG0I0C', }, ], }, @@ -1590,7 +1590,7 @@ 'packages': [ { 'package': 'chromium/third_party/enterprise_companion/chromium_mac_arm64', - 'version': 'GujH93HrCL-_0Oy_Mpyz_0jEu0bh3F-vXa22Dm_zkxcC', + 'version': 'MD5tnhZFgZjYw6f1eaYrrSNvARd0DlVY_wXWO9soljAC', }, ], }, @@ -1601,7 +1601,7 @@ 'packages': [ { 'package': 'chromium/third_party/enterprise_companion/chromium_win_x86', - 'version': 'A_tWZ7xOJ2r4L95_6PHw-mPMQpodWuh3ThR_IksipKoC', + 'version': 'ALofacA2lAPoEJ1uWAOiiZJ9ST5x42IBLvIR5xjdznYC', }, ], }, @@ -1612,7 +1612,7 @@ 'packages': [ { 'package': 'chromium/third_party/enterprise_companion/chromium_win_x86_64', - 'version': 'Bap9oa7H6bMZQ_fzy29oq7kRu0VrN8txSyzgtQ5chkYC', + 'version': 'ga9HSxtOV4tptLKH4CVKmPzP_XG_ODri7zxVp_1DLgcC', }, ], }, @@ -1648,7 +1648,7 @@ 'packages': [ { 'package': 'chromium/chrome/test/data/variations/cipd', - 'version': 'sTvh0HfBFuyltf3j6WX3tyeIjP6kHU_KB-3AGkWvZEsC', + 'version': 'Ea2rv4UlZ9IlnjhPujAD29mwCrJrdjAdE5vAo9jI_esC', }, ], 'condition': 'non_git_source', @@ -1660,12 +1660,12 @@ 'src/clank': { 'url': Var('chrome_git') + '/clank/internal/apps.git' + '@' + - '26315709f46b62ef07ed9a32c27d6051823c3137', + '60a8da0b6e82b9642ebfd2164e28b063229e9967', 'condition': 'checkout_android and checkout_src_internal', }, 'src/docs/website': { - 'url': Var('chromium_git') + '/website.git' + '@' + '81a7ea93897de0f405c0105c06e94b2c7d8f9b34', + 'url': Var('chromium_git') + '/website.git' + '@' + '5a23c51d744c6e0483d985769143f1b6ecd8b2b8', }, 'src/ios/third_party/earl_grey2/src': { @@ -1853,7 +1853,7 @@ 'packages': [ { 'package': 'chromium/third_party/android_build_tools/lint', - 'version': 'rukWDmO9QqkYXQfoaI6ucwbQS8CT_khNcoQz6p7jL30C', + 'version': '4Tet8ogI2zPDUkn10tYTOBlRplGWMIiihMcvp3Pg-OUC', }, ], 'condition': 'checkout_android and non_git_source', @@ -1864,7 +1864,7 @@ 'packages': [ { 'package': 'chromium/third_party/android_build_tools/manifest_merger', - 'version': 'JGv89b8oLAeQpYCqjtNB1othFMX_kCZNcWrcV5A8UT0C', + 'version': '3ThUZ9vtQeJj4CIwESivL5uAsRoZ82zN0ZcTZ6Fd3CoC', }, ], 'condition': 'checkout_android and non_git_source', @@ -2096,7 +2096,7 @@ Var('chromium_git') + '/chromium/web-tests.git' + '@' + Var('crossbench_web_tests_revision'), 'src/third_party/depot_tools': - Var('chromium_git') + '/chromium/tools/depot_tools.git' + '@' + '8a58b411c2ee5869edbd00672c9c78b943039ff1', + Var('chromium_git') + '/chromium/tools/depot_tools.git' + '@' + '86ddfc92bce7b2eebf0107abfbc2cc33a4aae265', 'src/third_party/devtools-frontend/src': Var('chromium_git') + '/devtools/devtools-frontend' + '@' + Var('devtools_frontend_revision'), @@ -2108,7 +2108,7 @@ Var('chromium_git') + '/external/github.com/jk-jeon/dragonbox.git' + '@' + 'beeeef91cf6fef89a4d4ba5e95d47ca64ccb3a44', 'src/third_party/eigen3/src': - Var('chromium_git') + '/external/gitlab.com/libeigen/eigen.git' + '@' + 'ff72fba837a29c5f7cd032e13bcf9b5270ab99ec', + Var('chromium_git') + '/external/gitlab.com/libeigen/eigen.git' + '@' + '83184e67da29564662761cd25d477d8885be13a2', 'src/third_party/emoji-metadata/src': { 'url': Var('chromium_git') + '/external/github.com/googlefonts/emoji-metadata' + '@' + '045f146fca682a836e01cd265171312bfb300e06', @@ -2647,7 +2647,7 @@ Var('pdfium_git') + '/pdfium.git' + '@' + Var('pdfium_revision'), 'src/third_party/perfetto': - Var('chromium_git') + '/external/github.com/google/perfetto.git' + '@' + 'b6b8fef3ca7840c15cfb92cd03aea4e10ca39367', + Var('chromium_git') + '/external/github.com/google/perfetto.git' + '@' + '1272c9dd9ca9b05bc1cb87fad0caebbf37a0ebc1', 'src/base/tracing/test/data': { 'bucket': 'perfetto', @@ -2988,16 +2988,16 @@ 'dep_type': 'cipd', }, - 'src/third_party/vulkan-deps': '{chromium_git}/vulkan-deps@d234b7b29748c07ef389279dd24f533ebd04cadc', - 'src/third_party/glslang/src': '{chromium_git}/external/github.com/KhronosGroup/glslang@458ff50a67cb69371850068a62b78f1990a1ff9a', + 'src/third_party/vulkan-deps': '{chromium_git}/vulkan-deps@11781c266f0c58d36b0c7a35ebf0b87f2bcdf049', + 'src/third_party/glslang/src': '{chromium_git}/external/github.com/KhronosGroup/glslang@13625556d3ae5fbe198bcebf11878e5b7bf720c1', '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@126038020c2bd47efaa942ccc364ca5353ffccde', - 'src/third_party/spirv-tools/src': '{chromium_git}/external/github.com/KhronosGroup/SPIRV-Tools@2ec8457ab33d539b6f1fecc998360c0b8b05ed4f', - 'src/third_party/vulkan-headers/src': '{chromium_git}/external/github.com/KhronosGroup/Vulkan-Headers@f6a6f7ab165cedbfa2a7d0c93fe27a2d01ce09c8', - 'src/third_party/vulkan-loader/src': '{chromium_git}/external/github.com/KhronosGroup/Vulkan-Loader@15a84652b94e465e9a7b25eb507193929863bc2f', - 'src/third_party/vulkan-tools/src': '{chromium_git}/external/github.com/KhronosGroup/Vulkan-Tools@7c46da2b39036a80ce088576d5794bf39e667f56', - 'src/third_party/vulkan-utility-libraries/src': '{chromium_git}/external/github.com/KhronosGroup/Vulkan-Utility-Libraries@2c909c1ab6f9c6caba39a84a4887186b3fafdead', - 'src/third_party/vulkan-validation-layers/src': '{chromium_git}/external/github.com/KhronosGroup/Vulkan-ValidationLayers@b105d8ea361af258abed65efb5a1565c031dcf1c', + 'src/third_party/spirv-headers/src': '{chromium_git}/external/github.com/KhronosGroup/SPIRV-Headers@58006c901d1d5c37dece6b6610e9af87fa951375', + 'src/third_party/spirv-tools/src': '{chromium_git}/external/github.com/KhronosGroup/SPIRV-Tools@6337eb62cadd7d124ac6789bf39c0f71148f0a73', + 'src/third_party/vulkan-headers/src': '{chromium_git}/external/github.com/KhronosGroup/Vulkan-Headers@0e9de566b7d4051c5cc1b762e242c46565956bdf', + 'src/third_party/vulkan-loader/src': '{chromium_git}/external/github.com/KhronosGroup/Vulkan-Loader@fc1daa956375aacd8c7fbdbaa0579f061c932416', + 'src/third_party/vulkan-tools/src': '{chromium_git}/external/github.com/KhronosGroup/Vulkan-Tools@d2994039d549970c5d8f60603209a947c5241cb2', + 'src/third_party/vulkan-utility-libraries/src': '{chromium_git}/external/github.com/KhronosGroup/Vulkan-Utility-Libraries@d88097b51e70f357a96237c4571ded3433ccde99', + 'src/third_party/vulkan-validation-layers/src': '{chromium_git}/external/github.com/KhronosGroup/Vulkan-ValidationLayers@14d771d9b83fa33f4d18d80fb0069f16d945d17e', 'src/third_party/vulkan_memory_allocator': Var('chromium_git') + '/external/github.com/GPUOpen-LibrariesAndSDKs/VulkanMemoryAllocator.git' + '@' + 'cb0597213b0fcb999caa9ed08c2f88dc45eb7d50', @@ -3040,7 +3040,7 @@ Var('chromium_git') + '/webpagereplay.git' + '@' + Var('webpagereplay_revision'), 'src/third_party/webrtc': - Var('webrtc_git') + '/src.git' + '@' + '0ea80479e5364ba2ff702d919347ec22f97ab951', + Var('webrtc_git') + '/src.git' + '@' + '85a228f95fc2b273f732b8f68c8c966d861a3702', # Wuffs' canonical repository is at github.com/google/wuffs, but we use # Skia's mirror of Wuffs, the same as in upstream Skia's DEPS file. @@ -3612,7 +3612,7 @@ 'src/chrome/browser/resources/webui_toolbar/internal': { 'url': Var('chrome_git') + '/chrome/browser/resources/webui_toolbar/internal.git' + '@' + - 'e46972fcb0e671a5e6e22a635e4594a99a2e3c29', + 'e3ecf9532c7a3623ce09fe4884e0bbc290eef2e4', 'condition': 'checkout_src_internal', }, @@ -3720,7 +3720,7 @@ 'src/components/accessibility_annotator/core/resources/internal': { 'url': Var('chrome_git') + '/chrome/components/accessibility_annotator/core/resources/internal.git' + '@' + - '907d2755f984daebc630d424b32e4fda1b70b2f3', + '93e2b65b210aecb1fa9421c52289b5194dc43e6e', 'condition': 'checkout_src_internal', }, @@ -3767,7 +3767,7 @@ 'src/components/optimization_guide/internal': { 'url': Var('chrome_git') + '/chrome/components/optimization_guide.git' + '@' + - '4cdea65aa8a2b6e12cb76f98b88652194dddd6c1', + '16736d9dc9992b20fc79ed10118edab62c722d48', 'condition': 'checkout_src_internal', }, @@ -3845,7 +3845,7 @@ 'src/ios_internal': { 'url': Var('chrome_git') + '/chrome/ios_internal.git' + '@' + - 'e67a7a64f5d58d6fd22e4bc7ae4a607110a4de16', + 'fdd657893e5c5e824838933a1707f3ff5ce72c3b', 'condition': 'checkout_ios and checkout_src_internal', }, @@ -4919,8 +4919,4 @@ 'src/clank', 'src/components/optimization_guide/internal', 'src/ios_internal', - # TODO(crbug.com/430571901): Fix telemetry_unittests to use - # third_party/webpagereplay instead of the copy inside catapult and remove - # this. - 'src/third_party/catapult', ]
diff --git a/agents/internal b/agents/internal index eda60a5..0930fb1 160000 --- a/agents/internal +++ b/agents/internal
@@ -1 +1 @@ -Subproject commit eda60a5a7e6373fb28f76d984b070f92bc1d8e80 +Subproject commit 0930fb1c6044c36bd542f0a8c444e5633b8531f1
diff --git a/agents/prompts/eval/build_target/eval.promptfoo.yaml b/agents/prompts/eval/build_target/eval.promptfoo.yaml index 60a0744..077a5d3 100644 --- a/agents/prompts/eval/build_target/eval.promptfoo.yaml +++ b/agents/prompts/eval/build_target/eval.promptfoo.yaml
@@ -13,5 +13,5 @@ - type: icontains value: viz_unittests metadata: - pass_k_threshold: 1 - runs_per_test: 3 \ No newline at end of file + tags: + - stable
diff --git a/agents/skills/utr/SKILL.md b/agents/skills/utr/SKILL.md index f08f9ca..def6631 100644 --- a/agents/skills/utr/SKILL.md +++ b/agents/skills/utr/SKILL.md
@@ -40,3 +40,20 @@ Information about cross-compiling Windows targets on Linux can be found at [docs/win_cross.md](https://chromium.googlesource.com/chromium/src/+/main/docs/win_cross.md). + +## Troubleshooting in Non-Interactive Environments + +When running UTR inside non-interactive remote sessions, you may encounter +BeyondCorp / Context Aware Access (CAA) authentication blockers or missing +remote `.cipd_bin/` packages: + +1. **Explicit Re-authentication:** + If fetching binaries or updating datasets stalls or raises an authentication failure, explicitly generate a fresh Context Aware Access token in the terminal: + ```sh + luci-auth login -scopes https://www.googleapis.com/auth/userinfo.email + ``` +2. **Forcing Narrow Execution Scope:** + Avoid broad isolation failures by always supplying specific test targets and the force flag: + ```sh + vpython3 tools/utr/run.py --force -t <test_suite> -p chromium -B try -b <builder> compile + ```
diff --git a/android_webview/browser/aw_field_trials.cc b/android_webview/browser/aw_field_trials.cc index 27f45a19..06fef4d 100644 --- a/android_webview/browser/aw_field_trials.cc +++ b/android_webview/browser/aw_field_trials.cc
@@ -81,10 +81,6 @@ aw_feature_overrides.DisableFeature( blink::features::kEnforceNoopenerOnBlobURLNavigation); - // Disable the passthrough on WebView. - aw_feature_overrides.DisableFeature( - ::features::kDefaultPassthroughCommandDecoder); - // HDR does not support webview yet. See crbug.com/1493153 for an explanation. aw_feature_overrides.DisableFeature(ui::kAndroidHDR);
diff --git a/android_webview/browser/content_restriction/aw_content_restriction_manager_client.cc b/android_webview/browser/content_restriction/aw_content_restriction_manager_client.cc index 8782189..5e415d2 100644 --- a/android_webview/browser/content_restriction/aw_content_restriction_manager_client.cc +++ b/android_webview/browser/content_restriction/aw_content_restriction_manager_client.cc
@@ -16,6 +16,18 @@ namespace android_webview { +AwContentRestrictionManagerClient::AwContentRestrictionManagerClient() { + DCHECK_CURRENTLY_ON(content::BrowserThread::UI); + JNIEnv* env = base::android::AttachCurrentThread(); + java_bridge_.Reset(Java_AwContentRestrictionManagerBridge_Constructor(env)); +} + +AwContentRestrictionManagerClient::~AwContentRestrictionManagerClient() { + DCHECK_CURRENTLY_ON(content::BrowserThread::UI); + JNIEnv* env = base::android::AttachCurrentThread(); + Java_AwContentRestrictionManagerBridge_destroy(env, java_bridge_); +} + bool AwContentRestrictionManagerClient::IsContentRestrictionEnabled() { DCHECK_CURRENTLY_ON(content::BrowserThread::UI); JNIEnv* env = base::android::AttachCurrentThread(); @@ -23,10 +35,11 @@ // TODO(crbug.com/481115059): Cache the result of this call to avoid repeated // IPCs. return Java_AwContentRestrictionManagerBridge_isContentRestrictionEnabled( - env); + env, java_bridge_); } void AwContentRestrictionManagerClient::RequestContentClassification( + int64_t navigation_id, const network::ResourceRequest& request, ContentClassificationCallback callback) { DCHECK_CURRENTLY_ON(content::BrowserThread::UI); @@ -39,7 +52,7 @@ } Java_AwContentRestrictionManagerBridge_requestContentClassification( - env, request.url.spec(), mime_type, + env, java_bridge_, navigation_id, request.url.spec(), mime_type, base::android::ToJniCallback(env, std::move(callback))); } @@ -48,7 +61,15 @@ DCHECK_CURRENTLY_ON(content::BrowserThread::UI); JNIEnv* env = base::android::AttachCurrentThread(); return Java_AwContentRestrictionManagerBridge_sendShowRestrictedContentIntent( - env, url.spec()); + env, java_bridge_, url.spec()); +} + +int AwContentRestrictionManagerClient::CreateRequestBodyPipeAndGetWriteFd( + int64_t navigation_id) { + DCHECK_CURRENTLY_ON(content::BrowserThread::UI); + JNIEnv* env = base::android::AttachCurrentThread(); + return Java_AwContentRestrictionManagerBridge_createRequestBodyPipeAndGetWriteFd( + env, java_bridge_, navigation_id); } } // namespace android_webview
diff --git a/android_webview/browser/content_restriction/aw_content_restriction_manager_client.h b/android_webview/browser/content_restriction/aw_content_restriction_manager_client.h index abced8a3..bf903da 100644 --- a/android_webview/browser/content_restriction/aw_content_restriction_manager_client.h +++ b/android_webview/browser/content_restriction/aw_content_restriction_manager_client.h
@@ -5,24 +5,25 @@ #ifndef ANDROID_WEBVIEW_BROWSER_CONTENT_RESTRICTION_AW_CONTENT_RESTRICTION_MANAGER_CLIENT_H_ #define ANDROID_WEBVIEW_BROWSER_CONTENT_RESTRICTION_AW_CONTENT_RESTRICTION_MANAGER_CLIENT_H_ +#include "base/android/scoped_java_ref.h" #include "base/functional/callback_forward.h" #include "services/network/public/cpp/resource_request.h" namespace android_webview { -// Client wrapper implementation for managing interactions with the +// Client implementation for managing interactions with the // `ContentRestrictionManager` system service via the // `AwContentRestrictionManagerBridge`. class AwContentRestrictionManagerClient { public: using ContentClassificationCallback = base::OnceCallback<void(bool)>; - AwContentRestrictionManagerClient() = default; + AwContentRestrictionManagerClient(); AwContentRestrictionManagerClient(const AwContentRestrictionManagerClient&) = delete; AwContentRestrictionManagerClient& operator=( const AwContentRestrictionManagerClient&) = delete; - virtual ~AwContentRestrictionManagerClient() = default; + virtual ~AwContentRestrictionManagerClient(); // Returns true if the content restriction feature is enabled for WebViews. // False otherwise. @@ -31,6 +32,7 @@ // Requests content restriction classification for the given request and // invokes the callback with the classification result. virtual void RequestContentClassification( + int64_t navigation_id, const network::ResourceRequest& request, ContentClassificationCallback callback); @@ -38,6 +40,13 @@ // restricted content. Returns true if the intent was sent successfully, false // otherwise. virtual bool SendShowRestrictedContentIntent(const GURL& url); + + // Creates a pipe for streaming the request body and returns the write file + // descriptor handle. Returns -1 on failure. + virtual int CreateRequestBodyPipeAndGetWriteFd(int64_t navigation_id); + + private: + base::android::ScopedJavaGlobalRef<jobject> java_bridge_; }; } // namespace android_webview
diff --git a/android_webview/browser/content_restriction/aw_content_restriction_url_loader_throttle.cc b/android_webview/browser/content_restriction/aw_content_restriction_url_loader_throttle.cc index 19087b9..2d97aac 100644 --- a/android_webview/browser/content_restriction/aw_content_restriction_url_loader_throttle.cc +++ b/android_webview/browser/content_restriction/aw_content_restriction_url_loader_throttle.cc
@@ -26,10 +26,13 @@ network::ResourceRequest* request, bool* defer) { DCHECK(content_restriction_manager_client_); - if (content_restriction_manager_client_->IsContentRestrictionEnabled()) { + if (navigation_id_.has_value() && + content_restriction_manager_client_->IsContentRestrictionEnabled()) { *defer = true; + + // TODO(crbug.com/481113476): Also process the request body. content_restriction_manager_client_->RequestContentClassification( - *request, + navigation_id_.value(), *request, base::BindOnce( &AwContentRestrictionURLLoaderThrottle::OnClassificationResult, weak_ptr_factory_.GetWeakPtr()));
diff --git a/android_webview/browser/content_restriction/aw_content_restriction_url_loader_throttle_unittest.cc b/android_webview/browser/content_restriction/aw_content_restriction_url_loader_throttle_unittest.cc index 30e70d44..56e669dc 100644 --- a/android_webview/browser/content_restriction/aw_content_restriction_url_loader_throttle_unittest.cc +++ b/android_webview/browser/content_restriction/aw_content_restriction_url_loader_throttle_unittest.cc
@@ -34,8 +34,11 @@ MOCK_METHOD(bool, IsContentRestrictionEnabled, (), (override)); MOCK_METHOD(void, RequestContentClassification, - (const network::ResourceRequest&, ContentClassificationCallback), + (int64_t, + const network::ResourceRequest&, + ContentClassificationCallback), (override)); + MOCK_METHOD(int, CreateRequestBodyPipeAndGetWriteFd, (int64_t), (override)); }; class TestThrottleDelegate : public blink::URLLoaderThrottle::Delegate { @@ -89,11 +92,29 @@ EXPECT_FALSE(tracker_.IsNavigationBlocked(kTestNavigationId)); } +TEST_F(AwContentRestrictionURLLoaderThrottleTest, + AllowRequestsWhenNoNavigationIdSet) { + // Set up a separate throttle instance with the navigation id not set. + TestThrottleDelegate delegate; + AwContentRestrictionURLLoaderThrottle throttle{&mock_client_, &tracker_, + std::nullopt}; + throttle.set_delegate(&delegate); + + network::ResourceRequest request; + request.url = GURL(kTestUrl); + bool defer = false; + throttle.WillStartRequest(&request, &defer); + + EXPECT_FALSE(defer); + EXPECT_FALSE(delegate.resume_called()); + EXPECT_FALSE(delegate.cancel_called()); +} + TEST_F(AwContentRestrictionURLLoaderThrottleTest, AllowRequest) { EXPECT_CALL(mock_client_, IsContentRestrictionEnabled()) .WillOnce(Return(true)); - EXPECT_CALL(mock_client_, RequestContentClassification(_, _)) - .WillOnce(WithArgs<1>( + EXPECT_CALL(mock_client_, RequestContentClassification(_, _, _)) + .WillOnce(WithArgs<2>( [](AwContentRestrictionManagerClient::ContentClassificationCallback callback) { std::move(callback).Run(true); })); @@ -110,8 +131,8 @@ TEST_F(AwContentRestrictionURLLoaderThrottleTest, BlockRequest) { EXPECT_CALL(mock_client_, IsContentRestrictionEnabled()) .WillOnce(Return(true)); - EXPECT_CALL(mock_client_, RequestContentClassification(_, _)) - .WillOnce(WithArgs<1>( + EXPECT_CALL(mock_client_, RequestContentClassification(_, _, _)) + .WillOnce(WithArgs<2>( [](AwContentRestrictionManagerClient::ContentClassificationCallback callback) { std::move(callback).Run(false); }));
diff --git a/android_webview/java/src/org/chromium/android_webview/AwContentRestrictionManagerBridge.java b/android_webview/java/src/org/chromium/android_webview/AwContentRestrictionManagerBridge.java index 18a2f069..db5dfc23 100644 --- a/android_webview/java/src/org/chromium/android_webview/AwContentRestrictionManagerBridge.java +++ b/android_webview/java/src/org/chromium/android_webview/AwContentRestrictionManagerBridge.java
@@ -6,12 +6,15 @@ import android.net.Uri; import android.os.ParcelFileDescriptor; +import androidx.annotation.VisibleForTesting; + import org.jni_zero.CalledByNative; import org.jni_zero.JNINamespace; import org.jni_zero.JniType; import org.chromium.android_webview.common.AwFeatureMap; import org.chromium.android_webview.common.AwFeatures; +import org.chromium.android_webview.common.Lifetime; import org.chromium.base.AconfigFlaggedApiDelegate; import org.chromium.base.ContextUtils; import org.chromium.base.JniOnceCallback; @@ -20,15 +23,39 @@ import org.chromium.build.annotations.NullMarked; import org.chromium.build.annotations.Nullable; +import java.io.IOException; +import java.util.HashMap; +import java.util.Map; + /** * JNI bridge for tapping into the ContentRestrictionManager system service in order to enforce - * content restriction in WebView. + * content restriction in WebView. This class is owned and instantiated by the + * AwContentRestrictionManagerClient (C++). */ +@Lifetime.Profile @JNINamespace("android_webview") @NullMarked public class AwContentRestrictionManagerBridge { private static final String TAG = "AwCRMBridge"; + @VisibleForTesting + public interface ParcelFileDescriptorPipeFactory { + ParcelFileDescriptor[] createPipe() throws IOException; + } + + private static ParcelFileDescriptorPipeFactory sPipeFactory = + new ParcelFileDescriptorPipeFactory() { + @Override + public ParcelFileDescriptor[] createPipe() throws IOException { + return ParcelFileDescriptor.createPipe(); + } + }; + + private final Map<Long, ParcelFileDescriptor> mReadFileDescriptorMap = new HashMap<>(); + + @CalledByNative + public AwContentRestrictionManagerBridge() {} + private static @Nullable Uri parseUrl(@Nullable String url) { if (url == null) { return null; @@ -37,7 +64,31 @@ } @CalledByNative - public static boolean isContentRestrictionEnabled() { + public void destroy() { + for (ParcelFileDescriptor pfd : mReadFileDescriptorMap.values()) { + try { + pfd.close(); + } catch (IOException e) { + Log.e(TAG, e); + } + } + mReadFileDescriptorMap.clear(); + } + + @CalledByNative + public int createRequestBodyPipeAndGetWriteFd(long navigationId) { + try { + ParcelFileDescriptor[] fdPipes = sPipeFactory.createPipe(); + mReadFileDescriptorMap.put(navigationId, fdPipes[0]); + return fdPipes[1].detachFd(); + } catch (IOException e) { + Log.e(TAG, "Failed to create pipe", e); + return -1; + } + } + + @CalledByNative + public boolean isContentRestrictionEnabled() { if (!AwFeatureMap.isEnabled(AwFeatures.WEBVIEW_CONTENT_RESTRICTION_SUPPORT)) { return false; } @@ -53,7 +104,8 @@ } @CalledByNative - public static void requestContentClassification( + public void requestContentClassification( + long navigationId, @JniType("std::string") String url, @JniType("std::string") String mimeType, JniOnceCallback<Boolean> callback) { @@ -69,8 +121,7 @@ return; } - // TODO(crbug.com/481113476): Also process the request body. - ParcelFileDescriptor requestBody = null; + ParcelFileDescriptor requestBody = mReadFileDescriptorMap.remove(navigationId); Promise<Boolean> promise = delegate.requestContentRestrictionClassification( uri, @@ -90,7 +141,7 @@ } @CalledByNative - public static boolean sendShowRestrictedContentIntent(@JniType("std::string") String url) { + public boolean sendShowRestrictedContentIntent(@JniType("std::string") String url) { @Nullable Uri uri = parseUrl(url); if (uri == null) { return false; @@ -102,4 +153,9 @@ } return delegate.sendShowRestrictedContentIntent(uri); } + + public static void setParcelFileDescriptorPipeFactoryForTesting( + ParcelFileDescriptorPipeFactory factory) { + sPipeFactory = factory; + } }
diff --git a/android_webview/junit/src/org/chromium/android_webview/robolectric/AwContentRestrictionManagerBridgeTest.java b/android_webview/junit/src/org/chromium/android_webview/robolectric/AwContentRestrictionManagerBridgeTest.java index 5d10039..ed0f4e6 100644 --- a/android_webview/junit/src/org/chromium/android_webview/robolectric/AwContentRestrictionManagerBridgeTest.java +++ b/android_webview/junit/src/org/chromium/android_webview/robolectric/AwContentRestrictionManagerBridgeTest.java
@@ -12,6 +12,7 @@ import android.content.ComponentName; import android.net.Uri; import android.os.Bundle; +import android.os.ParcelFileDescriptor; import androidx.test.filters.SmallTest; @@ -54,10 +55,13 @@ "android.webkit.MetaDataHolderService"; private static final String TEST_URL = "https://example.com"; private static final String TEST_MIME_TYPE = "text/html"; + private static final long TEST_NAVIGATION_ID = 123; private ManifestMetadataMockApplicationContext mContext; private ComponentName mMetadataServiceName; private Boolean mCallbackResult; + private AwContentRestrictionManagerBridge mBridge; + private final JniOnceCallback<Boolean> mMockCallback = new JniOnceCallback<Boolean>() { @Override @@ -70,13 +74,22 @@ }; @Before - public void setUp() { + public void setUp() throws Exception { mCallbackResult = null; mContext = new ManifestMetadataMockApplicationContext(RuntimeEnvironment.application); mMetadataServiceName = new ComponentName(mContext, METADATA_HOLDER_SERVICE_NAME); ContextUtils.initApplicationContextForTests(mContext); AconfigFlaggedApiDelegate.setInstanceForTesting(mFlaggedApiDelegate); setEnableContentRestrictionMetadata(true); + + // Stub out ParcelFileDescriptor.createPipe() to prevent crashes on detachFd(). + ParcelFileDescriptor mockReadFd = Mockito.mock(ParcelFileDescriptor.class); + ParcelFileDescriptor mockWriteFd = Mockito.mock(ParcelFileDescriptor.class); + when(mockWriteFd.detachFd()).thenReturn(42); + AwContentRestrictionManagerBridge.setParcelFileDescriptorPipeFactoryForTesting( + () -> new ParcelFileDescriptor[] {mockReadFd, mockWriteFd}); + + mBridge = new AwContentRestrictionManagerBridge(); } private void setEnableContentRestrictionMetadata(boolean enabled) { @@ -91,7 +104,7 @@ @Feature({"AndroidWebView"}) @DisableFeatures({AwFeatures.WEBVIEW_CONTENT_RESTRICTION_SUPPORT}) public void testIsContentRestrictionEnabled_featureDisabled() { - Assert.assertFalse(AwContentRestrictionManagerBridge.isContentRestrictionEnabled()); + Assert.assertFalse(mBridge.isContentRestrictionEnabled()); verifyNoInteractions(mFlaggedApiDelegate); } @@ -101,11 +114,11 @@ @EnableFeatures({AwFeatures.WEBVIEW_CONTENT_RESTRICTION_SUPPORT}) public void testIsContentRestrictionEnabled_featureEnabled() { when(mFlaggedApiDelegate.isContentRestrictionEnabled()).thenReturn(true); - Assert.assertTrue(AwContentRestrictionManagerBridge.isContentRestrictionEnabled()); + Assert.assertTrue(mBridge.isContentRestrictionEnabled()); verify(mFlaggedApiDelegate).isContentRestrictionEnabled(); when(mFlaggedApiDelegate.isContentRestrictionEnabled()).thenReturn(false); - Assert.assertFalse(AwContentRestrictionManagerBridge.isContentRestrictionEnabled()); + Assert.assertFalse(mBridge.isContentRestrictionEnabled()); verify(mFlaggedApiDelegate, times(2)).isContentRestrictionEnabled(); } @@ -115,7 +128,7 @@ @EnableFeatures({AwFeatures.WEBVIEW_CONTENT_RESTRICTION_SUPPORT}) public void testIsContentRestrictionEnabled_appOptOut() { setEnableContentRestrictionMetadata(false); - Assert.assertFalse(AwContentRestrictionManagerBridge.isContentRestrictionEnabled()); + Assert.assertFalse(mBridge.isContentRestrictionEnabled()); verifyNoInteractions(mFlaggedApiDelegate); } @@ -124,9 +137,9 @@ @Feature({"AndroidWebView"}) @EnableFeatures({AwFeatures.WEBVIEW_CONTENT_RESTRICTION_SUPPORT}) public void testRequestContentClassification_invalidUrl() { - AwContentRestrictionManagerBridge.requestContentClassification( - /* url= */ null, TEST_MIME_TYPE, mMockCallback); - Assert.assertEquals(false, mCallbackResult); + mBridge.requestContentClassification( + TEST_NAVIGATION_ID, /* url= */ null, TEST_MIME_TYPE, mMockCallback); + Assert.assertFalse("Should block requests with invalid URL", mCallbackResult); verifyNoInteractions(mFlaggedApiDelegate); } @@ -136,10 +149,10 @@ @EnableFeatures({AwFeatures.WEBVIEW_CONTENT_RESTRICTION_SUPPORT}) public void testRequestContentClassification_delegateMissing() { AconfigFlaggedApiDelegate.setInstanceForTesting(null); - AwContentRestrictionManagerBridge.requestContentClassification( - TEST_URL, TEST_MIME_TYPE, mMockCallback); + mBridge.requestContentClassification( + TEST_NAVIGATION_ID, TEST_URL, TEST_MIME_TYPE, mMockCallback); ShadowLooper.runUiThreadTasksIncludingDelayedTasks(); - Assert.assertEquals(false, mCallbackResult); + Assert.assertFalse("Should block requests when delegate is missing", mCallbackResult); verifyNoInteractions(mFlaggedApiDelegate); } @@ -150,13 +163,16 @@ public void testRequestContentClassification_allowed() { Promise<Boolean> promise = new Promise<>(); when(mFlaggedApiDelegate.requestContentRestrictionClassification( - Mockito.any(), Mockito.eq(null), Mockito.eq(TEST_MIME_TYPE), Mockito.any())) + /* uri= */ Mockito.any(), + /* requestBody= */ Mockito.eq(null), + /* mimeType= */ Mockito.eq(TEST_MIME_TYPE), + /* executor= */ Mockito.any())) .thenReturn(promise); - AwContentRestrictionManagerBridge.requestContentClassification( - TEST_URL, TEST_MIME_TYPE, mMockCallback); + mBridge.requestContentClassification( + TEST_NAVIGATION_ID, TEST_URL, TEST_MIME_TYPE, mMockCallback); promise.fulfill(true); ShadowLooper.runUiThreadTasksIncludingDelayedTasks(); - Assert.assertEquals(true, mCallbackResult); + Assert.assertTrue("Should allow request when delegate allows", mCallbackResult); } @Test @@ -166,13 +182,60 @@ public void testRequestContentClassification_blocked() { Promise<Boolean> promise = new Promise<>(); when(mFlaggedApiDelegate.requestContentRestrictionClassification( - Mockito.any(), Mockito.eq(null), Mockito.eq(TEST_MIME_TYPE), Mockito.any())) + /* uri= */ Mockito.any(), + /* requestBody= */ Mockito.eq(null), + /* mimeType= */ Mockito.eq(TEST_MIME_TYPE), + /* executor= */ Mockito.any())) .thenReturn(promise); - AwContentRestrictionManagerBridge.requestContentClassification( - TEST_URL, TEST_MIME_TYPE, mMockCallback); + mBridge.requestContentClassification( + TEST_NAVIGATION_ID, TEST_URL, TEST_MIME_TYPE, mMockCallback); promise.fulfill(false); ShadowLooper.runUiThreadTasksIncludingDelayedTasks(); - Assert.assertEquals(false, mCallbackResult); + Assert.assertFalse("Should block request when delegate denies", mCallbackResult); + } + + @Test + @SmallTest + @Feature({"AndroidWebView"}) + @EnableFeatures({AwFeatures.WEBVIEW_CONTENT_RESTRICTION_SUPPORT}) + public void testRequestContentClassification_withRequestBody() { + mBridge.createRequestBodyPipeAndGetWriteFd(TEST_NAVIGATION_ID); + Promise<Boolean> promise = new Promise<>(); + when(mFlaggedApiDelegate.requestContentRestrictionClassification( + /* uri= */ Mockito.any(), + /* requestBody= */ Mockito.isNotNull(), + /* mimeType= */ Mockito.eq(TEST_MIME_TYPE), + /* executor= */ Mockito.any())) + .thenReturn(promise); + mBridge.requestContentClassification( + TEST_NAVIGATION_ID, TEST_URL, TEST_MIME_TYPE, mMockCallback); + promise.fulfill(true); + ShadowLooper.runUiThreadTasksIncludingDelayedTasks(); + Assert.assertTrue("Should allow request when delegate allows", mCallbackResult); + } + + @Test + @SmallTest + @Feature({"AndroidWebView"}) + @EnableFeatures({AwFeatures.WEBVIEW_CONTENT_RESTRICTION_SUPPORT}) + public void testDestroyCleansUpReadFileDescriptorMap() { + mBridge.createRequestBodyPipeAndGetWriteFd(TEST_NAVIGATION_ID); + mBridge.destroy(); + + // Although non-conventional, we verify that there is no request body being tracked by + // triggering a request to classify content. + Promise<Boolean> promise = new Promise<>(); + when(mFlaggedApiDelegate.requestContentRestrictionClassification( + /* uri= */ Mockito.any(), + /* requestBody= */ Mockito.eq(null), + /* mimeType= */ Mockito.eq(TEST_MIME_TYPE), + /* executor= */ Mockito.any())) + .thenReturn(promise); + mBridge.requestContentClassification( + TEST_NAVIGATION_ID, TEST_URL, TEST_MIME_TYPE, mMockCallback); + promise.fulfill(true); + ShadowLooper.runUiThreadTasksIncludingDelayedTasks(); + Assert.assertTrue("Should allow request when delegate allows", mCallbackResult); } @Test @@ -180,7 +243,7 @@ @Feature({"AndroidWebView"}) @EnableFeatures({AwFeatures.WEBVIEW_CONTENT_RESTRICTION_SUPPORT}) public void testSendShowRestrictedContentIntent_invalidUrl() { - Assert.assertFalse(AwContentRestrictionManagerBridge.sendShowRestrictedContentIntent(null)); + Assert.assertFalse(mBridge.sendShowRestrictedContentIntent(null)); verifyNoInteractions(mFlaggedApiDelegate); } @@ -190,8 +253,7 @@ @EnableFeatures({AwFeatures.WEBVIEW_CONTENT_RESTRICTION_SUPPORT}) public void testSendShowRestrictedContentIntent_delegateMissing() { AconfigFlaggedApiDelegate.setInstanceForTesting(null); - Assert.assertFalse( - AwContentRestrictionManagerBridge.sendShowRestrictedContentIntent(TEST_URL)); + Assert.assertFalse(mBridge.sendShowRestrictedContentIntent(TEST_URL)); verifyNoInteractions(mFlaggedApiDelegate); } @@ -203,8 +265,7 @@ Uri testUri = Uri.parse(TEST_URL); when(mFlaggedApiDelegate.sendShowRestrictedContentIntent(Mockito.eq(testUri))) .thenReturn(true); - Assert.assertTrue( - AwContentRestrictionManagerBridge.sendShowRestrictedContentIntent(TEST_URL)); + Assert.assertTrue(mBridge.sendShowRestrictedContentIntent(TEST_URL)); verify(mFlaggedApiDelegate).sendShowRestrictedContentIntent(Mockito.eq(testUri)); } @@ -216,8 +277,7 @@ Uri testUri = Uri.parse(TEST_URL); when(mFlaggedApiDelegate.sendShowRestrictedContentIntent(Mockito.eq(testUri))) .thenReturn(false); - Assert.assertFalse( - AwContentRestrictionManagerBridge.sendShowRestrictedContentIntent(TEST_URL)); + Assert.assertFalse(mBridge.sendShowRestrictedContentIntent(TEST_URL)); verify(mFlaggedApiDelegate).sendShowRestrictedContentIntent(Mockito.eq(testUri)); } }
diff --git a/android_webview/lib/aw_main_delegate.cc b/android_webview/lib/aw_main_delegate.cc index 005e6df..a1910dbc 100644 --- a/android_webview/lib/aw_main_delegate.cc +++ b/android_webview/lib/aw_main_delegate.cc
@@ -37,6 +37,7 @@ #include "base/strings/string_number_conversions.h" #include "base/strings/string_split.h" #include "base/strings/string_util.h" +#include "base/synchronization/lock.h" #include "base/threading/thread_restrictions.h" #include "base/time/default_clock.h" #include "base/trace_event/trace_log.h" @@ -327,6 +328,7 @@ } InitializeMemorySystem(is_browser_process); + base::Lock::InitializeFeatures(); return std::nullopt; }
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 4a578f92..24d1dca 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
@@ -3437,6 +3437,7 @@ getter draggable getter editContext getter enterKeyHint + getter focusgroup getter hidden getter inert getter innerText @@ -3580,6 +3581,7 @@ setter draggable setter editContext setter enterKeyHint + setter focusgroup setter hidden setter inert setter innerText @@ -5358,6 +5360,7 @@ getter attributeStyleMap getter autofocus getter dataset + getter focusgroup getter nonce getter onabort getter onanimationcancel @@ -5471,6 +5474,7 @@ method constructor method focus setter autofocus + setter focusgroup setter nonce setter onabort setter onanimationcancel @@ -7458,6 +7462,7 @@ getter autofocus getter className getter dataset + getter focusgroup getter nonce getter onabort getter onanimationcancel @@ -7573,6 +7578,7 @@ method constructor method focus setter autofocus + setter focusgroup setter nonce setter onabort setter onanimationcancel
diff --git a/android_webview/test/data/web_tests/webexposed/global-interface-listing-expected.txt b/android_webview/test/data/web_tests/webexposed/global-interface-listing-expected.txt index 499b1d17..ac47931aa 100644 --- a/android_webview/test/data/web_tests/webexposed/global-interface-listing-expected.txt +++ b/android_webview/test/data/web_tests/webexposed/global-interface-listing-expected.txt
@@ -6408,6 +6408,7 @@ attribute @@toStringTag getter ontoolchange method constructor + method executeTool method getTools method registerTool setter ontoolchange
diff --git a/android_webview/tools/android-webview-arm.orderfile.txt b/android_webview/tools/android-webview-arm.orderfile.txt index 27979cd..8f62bce 100644 --- a/android_webview/tools/android-webview-arm.orderfile.txt +++ b/android_webview/tools/android-webview-arm.orderfile.txt
@@ -1 +1 @@ -iOhtACeCm1lLtAhj9UHSzg4EDOTNrbYXRpJzzMg8XvcC +7SIwM-gVEtRlD-ipcF3sNSO8LORpTQcDLDd4BfJ-7zwC
diff --git a/android_webview/tools/android-webview-arm64.orderfile.txt b/android_webview/tools/android-webview-arm64.orderfile.txt index 0b8530d0..1fd3325e 100644 --- a/android_webview/tools/android-webview-arm64.orderfile.txt +++ b/android_webview/tools/android-webview-arm64.orderfile.txt
@@ -1 +1 @@ -JNvvEfLiPSa_iIYFnUvqzT_DWtlcFAmU9kLiLc-FdMYC +-BrAy2mZf5h6ZD-fNLK1Kyt4oKXVzVJE_UUn8eWEGaQC
diff --git a/android_webview/tools/system_webview_shell/apk/AndroidManifest.xml b/android_webview/tools/system_webview_shell/apk/AndroidManifest.xml index 527e87e..1be00d2 100644 --- a/android_webview/tools/system_webview_shell/apk/AndroidManifest.xml +++ b/android_webview/tools/system_webview_shell/apk/AndroidManifest.xml
@@ -19,6 +19,7 @@ <uses-permission android:name="android.permission.USE_CREDENTIALS"/> <uses-permission android:name="android.permission.ACCESS_ADSERVICES_ATTRIBUTION"/> <uses-permission android:name="android.permission.MANAGE_ACCOUNTS"/> + <uses-permission android:name="android.permission.CREDENTIAL_MANAGER_SET_ORIGIN"/> <!-- "Dangerous" permissions which require user prompt -->
diff --git a/android_webview/tools/system_webview_shell/apk/res/menu/main_menu.xml b/android_webview/tools/system_webview_shell/apk/res/menu/main_menu.xml index 4ac5f003..8e9e1186 100644 --- a/android_webview/tools/system_webview_shell/apk/res/menu/main_menu.xml +++ b/android_webview/tools/system_webview_shell/apk/res/menu/main_menu.xml
@@ -25,6 +25,14 @@ <item android:id="@+id/menu_force_dark_on" android:title="@string/menu_force_dark_on"/> </group> + <group android:checkableBehavior="single"> + <item android:id="@+id/menu_webauthn_browser" + android:title="@string/menu_webauthn_browser"/> + <item android:id="@+id/menu_webauthn_app" + android:title="@string/menu_webauthn_app"/> + <item android:id="@+id/menu_webauthn_off" + android:title="@string/menu_webauthn_off"/> + </group> <item android:id="@+id/menu_night_mode_on" android:checkable="true" android:title="@string/menu_night_mode_on"/>
diff --git a/android_webview/tools/system_webview_shell/apk/res/values/strings.xml b/android_webview/tools/system_webview_shell/apk/res/values/strings.xml index c47d8f4d..e9860ce 100644 --- a/android_webview/tools/system_webview_shell/apk/res/values/strings.xml +++ b/android_webview/tools/system_webview_shell/apk/res/values/strings.xml
@@ -24,6 +24,9 @@ <string name="menu_force_dark_off">Force Dark Off</string> <string name="menu_force_dark_auto">Force Dark Auto</string> <string name="menu_force_dark_on">Force Dark On</string> + <string name="menu_webauthn_browser">WebAuthn Browser Mode</string> + <string name="menu_webauthn_app">WebAuthn App Mode</string> + <string name="menu_webauthn_off">WebAuthn Off</string> <string name="menu_algorithmic_darkening_on">Algorithmic Darkening ON</string> <string name="menu_enable_third_party_cookies">Enable third-party cookies</string> <string name="menu_night_mode_on">Night Mode ON</string>
diff --git a/android_webview/tools/system_webview_shell/apk/src/org/chromium/webview_shell/WebViewBrowserActivity.java b/android_webview/tools/system_webview_shell/apk/src/org/chromium/webview_shell/WebViewBrowserActivity.java index eb202abc..23aa9c5 100644 --- a/android_webview/tools/system_webview_shell/apk/src/org/chromium/webview_shell/WebViewBrowserActivity.java +++ b/android_webview/tools/system_webview_shell/apk/src/org/chromium/webview_shell/WebViewBrowserActivity.java
@@ -107,6 +107,11 @@ menu.findItem(R.id.menu_force_dark_auto).setEnabled(false); menu.findItem(R.id.menu_force_dark_on).setEnabled(false); } + if (!WebViewFeature.isFeatureSupported(WebViewFeature.WEB_AUTHENTICATION)) { + menu.findItem(R.id.menu_webauthn_browser).setEnabled(false); + menu.findItem(R.id.menu_webauthn_app).setEnabled(false); + menu.findItem(R.id.menu_webauthn_off).setEnabled(false); + } if (!WebViewFeature.isFeatureSupported(WebViewFeature.MULTI_PROFILE)) { menu.findItem(R.id.menu_multi_profile).setEnabled(false); } @@ -142,6 +147,21 @@ break; } } + if (WebViewFeature.isFeatureSupported(WebViewFeature.WEB_AUTHENTICATION)) { + int webAuthnSupport = + WebSettingsCompat.getWebAuthenticationSupport(mWebView.getSettings()); + switch (webAuthnSupport) { + case WebSettingsCompat.WEB_AUTHENTICATION_SUPPORT_FOR_BROWSER: + menu.findItem(R.id.menu_webauthn_browser).setChecked(true); + break; + case WebSettingsCompat.WEB_AUTHENTICATION_SUPPORT_FOR_APP: + menu.findItem(R.id.menu_webauthn_app).setChecked(true); + break; + case WebSettingsCompat.WEB_AUTHENTICATION_SUPPORT_NONE: + menu.findItem(R.id.menu_webauthn_off).setChecked(true); + break; + } + } if (WebViewFeature.isFeatureSupported(WebViewFeature.ALGORITHMIC_DARKENING)) { menu.findItem(R.id.menu_algorithmic_darkening_on) @@ -228,6 +248,27 @@ WebSettingsCompat.setForceDark(mWebView.getSettings(), WebSettingsCompat.FORCE_DARK_ON); item.setChecked(true); return true; + } else if (itemId == R.id.menu_webauthn_browser) { + WebSettingsCompat.setWebAuthenticationSupport( + mWebView.getSettings(), + WebSettingsCompat.WEB_AUTHENTICATION_SUPPORT_FOR_BROWSER); + item.setChecked(true); + Toast.makeText( + this, + "Some password managers don't honor passkeys in this shell.", + Toast.LENGTH_LONG) + .show(); + return true; + } else if (itemId == R.id.menu_webauthn_app) { + WebSettingsCompat.setWebAuthenticationSupport( + mWebView.getSettings(), WebSettingsCompat.WEB_AUTHENTICATION_SUPPORT_FOR_APP); + item.setChecked(true); + return true; + } else if (itemId == R.id.menu_webauthn_off) { + WebSettingsCompat.setWebAuthenticationSupport( + mWebView.getSettings(), WebSettingsCompat.WEB_AUTHENTICATION_SUPPORT_NONE); + item.setChecked(true); + return true; } else if (itemId == R.id.menu_night_mode_on) { AppCompatDelegate.setDefaultNightMode( item.isChecked()
diff --git a/ash/constants/ash_features.cc b/ash/constants/ash_features.cc index 06df9b5..9a5cc2e 100644 --- a/ash/constants/ash_features.cc +++ b/ash/constants/ash_features.cc
@@ -1327,6 +1327,10 @@ // Enables notifications to be shown within context menus. BASE_FEATURE(kNotificationsInContextMenu, base::FEATURE_DISABLED_BY_DEFAULT); +// Kill switch for https://crbug.com/502771678. Forces OEM apps to receive +// updates from the Chrome Web Store, which is the safe default. +BASE_FEATURE(kOemAppsMustUpdateFromWebstore, base::FEATURE_ENABLED_BY_DEFAULT); + // Controls whether to enable on-device grammar check service. BASE_FEATURE(kOnDeviceGrammarCheck, base::FEATURE_ENABLED_BY_DEFAULT); @@ -2965,6 +2969,10 @@ return base::FeatureList::IsEnabled(kOngoingProcesses); } +bool IsOemAppsMustUpdateFromWebstoreEnabled() { + return base::FeatureList::IsEnabled(kOemAppsMustUpdateFromWebstore); +} + bool IsOobeJellyEnabled() { return base::FeatureList::IsEnabled(kOobeJelly); }
diff --git a/ash/constants/ash_features.h b/ash/constants/ash_features.h index 8e1e872..ced9e12 100644 --- a/ash/constants/ash_features.h +++ b/ash/constants/ash_features.h
@@ -603,6 +603,8 @@ COMPONENT_EXPORT(ASH_CONSTANTS) BASE_DECLARE_FEATURE(kNotificationScrollBar); COMPONENT_EXPORT(ASH_CONSTANTS) BASE_DECLARE_FEATURE(kNotificationsInContextMenu); +COMPONENT_EXPORT(ASH_CONSTANTS) +BASE_DECLARE_FEATURE(kOemAppsMustUpdateFromWebstore); COMPONENT_EXPORT(ASH_CONSTANTS) BASE_DECLARE_FEATURE(kOnDeviceGrammarCheck); COMPONENT_EXPORT(ASH_CONSTANTS) BASE_DECLARE_FEATURE(kOnDeviceSpeechRecognition); @@ -1211,6 +1213,7 @@ COMPONENT_EXPORT(ASH_CONSTANTS) bool IsNssDbClientCertsRollbackEnabled(); COMPONENT_EXPORT(ASH_CONSTANTS) bool IsOAuthIppEnabled(); COMPONENT_EXPORT(ASH_CONSTANTS) bool AreOngoingProcessesEnabled(); +COMPONENT_EXPORT(ASH_CONSTANTS) bool IsOemAppsMustUpdateFromWebstoreEnabled(); COMPONENT_EXPORT(ASH_CONSTANTS) bool IsOobeAddUserDuringEnrollmentEnabled(); COMPONENT_EXPORT(ASH_CONSTANTS) bool IsOobeChoobeEnabled(); COMPONENT_EXPORT(ASH_CONSTANTS) bool IsOobeCrosEventsEnabled();
diff --git a/ash/constants/ash_pref_names.h b/ash/constants/ash_pref_names.h index ef45c0b..725fcb0 100644 --- a/ash/constants/ash_pref_names.h +++ b/ash/constants/ash_pref_names.h
@@ -3152,6 +3152,25 @@ "tpm_auto_update.planned_notification_shown_time"; //----------------------------------------------------------------------------- +// CryptAuth related Prefs +//----------------------------------------------------------------------------- + +// Device identifier used by CryptAuth stored in local state. This ID is +// combined with a user ID before being registered with the CryptAuth server, +// so it can't correlate users on the same device. +// Note: This constant was previously specific to EasyUnlock, so the string +// constant contains "easy_unlock". +inline constexpr char kCryptAuthDeviceId[] = "easy_unlock.device_id"; + +// The most recently retrieved Instance ID and Instance ID token for the app ID, +// "com.google.chrome.cryptauth", used by the CryptAuth client. These prefs are +// used to track how often (if ever) the Instance ID and Instance ID token +// rotate because CryptAuth assumes the Instance ID is static. +inline constexpr char kCryptAuthInstanceId[] = "cryptauth.instance_id"; +inline constexpr char kCryptAuthInstanceIdToken[] = + "cryptauth.instance_id_token"; + +//----------------------------------------------------------------------------- // File manager/file system related Prefs //----------------------------------------------------------------------------- @@ -3806,6 +3825,14 @@ inline constexpr char kKnownUserParentAccessCodeConfig[] = "child_user.parent_access_code.config"; +// Last time that the kChildScreenTimeMilliseconds pref was reset. +inline constexpr char kLastChildScreenTimeReset[] = + "last_child_screen_time_reset"; + +// Last time the kChildScreenTimeMilliseconds was saved. +inline constexpr char kLastChildScreenTimeSaved[] = + "last_child_screen_time_saved"; + // Dictionary pref containing configuration used to verify Parent Access Code. // Controlled by ParentAccessCodeConfig policy. inline constexpr char kParentAccessCodeConfig[] =
diff --git a/ash/webui/boca_receiver_app_ui/BUILD.gn b/ash/webui/boca_receiver_app_ui/BUILD.gn index baab4b7..d27329d 100644 --- a/ash/webui/boca_receiver_app_ui/BUILD.gn +++ b/ash/webui/boca_receiver_app_ui/BUILD.gn
@@ -29,6 +29,7 @@ "//ash/webui/resources:boca_receiver_app_bundle_resources", "//base", "//chromeos/ash/components/boca", + "//chromeos/ash/components/boca:boca_request", "//chromeos/ash/components/boca:invalidation_delegate", "//chromeos/ash/components/boca:invalidations", "//chromeos/ash/components/boca:spotlight_lib", @@ -61,6 +62,7 @@ "//base", "//base/test:test_support", "//chromeos/ash/components/boca", + "//chromeos/ash/components/boca:boca_request", "//chromeos/ash/components/boca:invalidation_delegate", "//chromeos/ash/components/boca:invalidations", "//chromeos/ash/components/boca:spotlight_lib",
diff --git a/ash/webui/boca_ui/boca_app_page_handler.cc b/ash/webui/boca_ui/boca_app_page_handler.cc index 0a7ed64..974cf2d 100644 --- a/ash/webui/boca_ui/boca_app_page_handler.cc +++ b/ash/webui/boca_ui/boca_app_page_handler.cc
@@ -762,6 +762,17 @@ StartSpotlightCallback callback) { if (!ash::features::IsBocaSpotlightRobotRequesterEnabled()) { std::move(callback).Run(); + return; + } + if (!is_producer_) { + receiver_.ReportBadMessage( + "StartSpotlight without active producer session"); + return; + } + auto* session = GetSessionManager()->GetCurrentSession(); + if (!session || !IsActiveSession(session->session_id())) { + std::move(callback).Run(); + return; } GetSessionManager()->StartCrdClient( crd_connection_code,
diff --git a/ash/webui/boca_ui/boca_app_page_handler_unittest.cc b/ash/webui/boca_ui/boca_app_page_handler_unittest.cc index abd8a534..035c15d 100644 --- a/ash/webui/boca_ui/boca_app_page_handler_unittest.cc +++ b/ash/webui/boca_ui/boca_app_page_handler_unittest.cc
@@ -27,6 +27,7 @@ #include "base/test/bind.h" #include "base/test/gmock_callback_support.h" #include "base/test/metrics/histogram_tester.h" +#include "base/test/run_until.h" #include "base/test/scoped_feature_list.h" #include "base/test/task_environment.h" #include "base/test/test_future.h" @@ -76,6 +77,7 @@ #include "mojo/public/cpp/bindings/pending_receiver.h" #include "mojo/public/cpp/bindings/receiver.h" #include "mojo/public/cpp/bindings/remote.h" +#include "mojo/public/cpp/system/functions.h" #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" #include "third_party/skia/include/core/SkBitmap.h" @@ -513,6 +515,7 @@ class BocaAppPageHandlerTest : public testing::Test { public: BocaAppPageHandlerTest() = default; + mojo::Remote<mojom::PageHandler>& remote() { return remote_; } void SetUp() override { scoped_feature_list_.InitWithFeatures( {ash::features::kBoca, ash::features::kBocaScreenSharingStudent, @@ -1225,6 +1228,46 @@ EXPECT_EQ("google", activities[0]->activity->active_tab); } +TEST_F(BocaAppPageHandlerConsumerTest, StartSpotlightFailsForConsumer) { + base::test::ScopedFeatureList scoped_feature_list; + scoped_feature_list.InitAndEnableFeature( + ash::features::kBocaSpotlightRobotRequester); + + std::string bad_message; + mojo::SetDefaultProcessErrorHandler(base::BindLambdaForTesting( + [&bad_message](const std::string& error) { bad_message = error; })); + + remote().get()->StartSpotlight("123456789012", base::DoNothing()); + + ASSERT_TRUE( + base::test::RunUntil([&bad_message]() { return !bad_message.empty(); })); + + EXPECT_EQ("StartSpotlight without active producer session", bad_message); + + mojo::SetDefaultProcessErrorHandler(base::NullCallback()); +} + +TEST_F(BocaAppPageHandlerProducerTest, StartSpotlightIgnoresRaceCondition) { + base::test::ScopedFeatureList scoped_feature_list; + scoped_feature_list.InitAndEnableFeature( + ash::features::kBocaSpotlightRobotRequester); + + EXPECT_CALL(*session_manager(), GetCurrentSession()) + .WillRepeatedly(Return(nullptr)); + + std::string bad_message; + mojo::SetDefaultProcessErrorHandler(base::BindLambdaForTesting( + [&bad_message](const std::string& error) { bad_message = error; })); + + base::test::TestFuture<void> future; + remote().get()->StartSpotlight("123456789012", future.GetCallback()); + + EXPECT_TRUE(future.Wait()); + EXPECT_TRUE(bad_message.empty()); + + mojo::SetDefaultProcessErrorHandler(base::NullCallback()); +} + TEST_F(BocaAppPageHandlerProducerTest, GetSessionWithPartialInputTest) { // Page handler callback. base::test::TestFuture<base::expected<std::unique_ptr<::boca::Session>,
diff --git a/ash/webui/shimless_rma/OWNERS b/ash/webui/shimless_rma/OWNERS index 90ca269..50c4f96b 100644 --- a/ash/webui/shimless_rma/OWNERS +++ b/ash/webui/shimless_rma/OWNERS
@@ -4,3 +4,4 @@ # Backup OWNERS jimmyxgong@chromium.org chungsheng@google.com +jeffulin@google.com
diff --git a/ash/wm/tablet_mode/tablet_mode_multitask_menu.cc b/ash/wm/tablet_mode/tablet_mode_multitask_menu.cc index 0dfe0fa..5400165 100644 --- a/ash/wm/tablet_mode/tablet_mode_multitask_menu.cc +++ b/ash/wm/tablet_mode/tablet_mode_multitask_menu.cc
@@ -63,8 +63,11 @@ TabletModeMultitaskMenuView(aura::Window* window, base::RepeatingClosure close_callback, base::RepeatingClosure dismiss_callback) { - SetBackground(views::CreateRoundedRectBackground( - kColorAshShieldAndBaseOpaque, kCornerRadius)); + SetPaintToLayer(); + layer()->SetFillsBoundsOpaquely(false); + + SetBackground(views::CreateRoundedRectBackground(ui::kColorSysSurface3, + kCornerRadius)); SetBorder(std::make_unique<views::HighlightBorder>( kCornerRadius, views::HighlightBorder::Type::kHighlightBorderOnShadow)); @@ -121,9 +124,6 @@ layout->set_cross_axis_alignment( views::BoxLayout::CrossAxisAlignment::kCenter); - SetPaintToLayer(); - layer()->SetFillsBoundsOpaquely(false); - shadow_ = SystemShadow::CreateShadowOnNinePatchLayer( SystemShadow::Type::kElevation12, SystemShadow::LayerRecreatedCallback()); @@ -166,6 +166,7 @@ params.parent = window->GetRootWindow()->GetChildById( kShellWindowId_AlwaysOnTopContainer); params.name = "TabletModeMultitaskMenuWidget"; + params.layer_type = ui::LAYER_NOT_DRAWN; widget_->Init(std::move(params)); widget_->SetVisibilityChangedAnimationsEnabled(false);
diff --git a/base/features.cc b/base/features.cc index c38e13a..dad9644b 100644 --- a/base/features.cc +++ b/base/features.cc
@@ -8,6 +8,7 @@ #include "base/debug/stack_trace.h" #include "base/files/file_path.h" +#include "base/synchronization/lock.h" #include "base/task/sequence_manager/sequence_manager_impl.h" #include "base/task/thread_pool/job_task_source.h" #include "base/threading/platform_thread.h" @@ -216,20 +217,7 @@ BASE_FEATURE_PARAM(int, kSpinCountX86, &kBaseLockTrySpin, "spin_count_x86", 0); #elif defined(ARCH_CPU_ARM_FAMILY) BASE_FEATURE_PARAM(int, kSpinCountArm, &kBaseLockTrySpin, "spin_count_arm", 0); -#endif - -namespace { -int GetBaseLockSpinCount() { -#if defined(ARCH_CPU_X86_FAMILY) - return kSpinCountX86.Get(); -#elif defined(ARCH_CPU_ARM_FAMILY) - return kSpinCountArm.Get(); -#else - return 0; #endif // defined(ARCH_CPU_X86_FAMILY) -} -} // namespace - #endif // BUILDFLAG(IS_POSIX) bool IsReducePPMsEnabled() { @@ -240,9 +228,7 @@ g_is_reduce_ppms_enabled.store(FeatureList::IsEnabled(kReducePPMs), std::memory_order_relaxed); #if BUILDFLAG(IS_POSIX) - if (FeatureList::IsEnabled(kBaseLockTrySpin)) { - base::internal::LockImpl::SetTrySpinCount(GetBaseLockSpinCount()); - } + base::Lock::InitializeFeatures(); #endif // BUILDFLAG(IS_POSIX) sequence_manager::internal::SequenceManagerImpl::InitializeFeatures();
diff --git a/base/features.h b/base/features.h index 196f4fb..3d072928 100644 --- a/base/features.h +++ b/base/features.h
@@ -8,6 +8,7 @@ #include "base/base_export.h" #include "base/feature_list.h" #include "base/metrics/field_trial_params.h" +#include "build/build_config.h" namespace base::features { @@ -79,7 +80,11 @@ #if BUILDFLAG(IS_POSIX) BASE_EXPORT BASE_DECLARE_FEATURE(kBaseLockTrySpin); -BASE_EXPORT BASE_DECLARE_FEATURE_PARAM(int, kSpinCount); +#if defined(ARCH_CPU_X86_FAMILY) +BASE_EXPORT BASE_DECLARE_FEATURE_PARAM(int, kSpinCountX86); +#elif defined(ARCH_CPU_ARM_FAMILY) +BASE_EXPORT BASE_DECLARE_FEATURE_PARAM(int, kSpinCountArm); +#endif // defined(ARCH_CPU_X86_FAMILY) #endif // BUILDFLAG(IS_POSIX) // Whether the ReducePPMs feature is enabled. Unlike
diff --git a/base/memory_coordinator/async_memory_consumer_registration.cc b/base/memory_coordinator/async_memory_consumer_registration.cc index c0f7ed5..b845762 100644 --- a/base/memory_coordinator/async_memory_consumer_registration.cc +++ b/base/memory_coordinator/async_memory_consumer_registration.cc
@@ -37,6 +37,15 @@ check_registry_exists); registration_->SetAsyncHandleDestroyedFlag( &async_handle_destroyed_, PassKey<AsyncMemoryConsumerRegistration>()); + + // The memory limit could already have been set by a policy. The + // consumer implementation on the other sequence must be notified. + if (memory_limit() != base::MemoryConsumer::kDefaultMemoryLimit) { + consumer_task_runner_->PostTask( + FROM_HERE, + BindOnce(&AsyncMemoryConsumerRegistration::NotifyUpdateMemoryLimit, + parent_, memory_limit())); + } } void NotifyAsyncHandleDestroyed() {
diff --git a/base/memory_coordinator/memory_consumer.cc b/base/memory_coordinator/memory_consumer.cc index f28fd1b8..d0aa4f6 100644 --- a/base/memory_coordinator/memory_consumer.cc +++ b/base/memory_coordinator/memory_consumer.cc
@@ -22,10 +22,15 @@ void MemoryConsumer::UpdateMemoryLimit(int percentage) { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + UpdateMemoryLimitNoNotification(percentage); + OnUpdateMemoryLimit(); +} + +void MemoryConsumer::UpdateMemoryLimitNoNotification(int percentage) { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); // The percentage can never be negative (but it can be higher than 100). CHECK_GE(percentage, 0); memory_limit_ = percentage; - OnUpdateMemoryLimit(); } // MemoryConsumerRegistration ---------------------------------------
diff --git a/base/memory_coordinator/memory_consumer.h b/base/memory_coordinator/memory_consumer.h index 9ae0343..731b7ba 100644 --- a/base/memory_coordinator/memory_consumer.h +++ b/base/memory_coordinator/memory_consumer.h
@@ -50,6 +50,13 @@ // a consumer should wait for a subsequent call to `OnReleaseMemory()` to free // any memory that exceeds that limit. // +// IMPORTANT: For synchronous registrations (via `MemoryConsumerRegistration`), +// `OnUpdateMemoryLimit()` is NOT invoked during registration to avoid +// re-entrancy during construction. If your implementation maintains state +// derived from the limit, you must query `memory_limit()` in your constructor +// body to initialize it correctly. (Asynchronous registrations do not have +// this limitation as they notify asynchronously after construction). +// // Here is an example implementation for a consumer that manages a cache with a // LRU eviction policy. // @@ -107,6 +114,10 @@ // comment above for a detailed description of how this limit works. void UpdateMemoryLimit(int percentage); + // Similar to UpdateMemoryLimit, but does not invoke OnUpdateMemoryLimit + // callback. + void UpdateMemoryLimitNoNotification(int percentage); + // Instructs this consumer to release memory that is above the current // `memory_limit()`. void ReleaseMemory();
diff --git a/base/memory_coordinator/memory_consumer_registry.cc b/base/memory_coordinator/memory_consumer_registry.cc index 7ead77436..520a4fcb 100644 --- a/base/memory_coordinator/memory_consumer_registry.cc +++ b/base/memory_coordinator/memory_consumer_registry.cc
@@ -20,6 +20,10 @@ memory_consumer_->UpdateMemoryLimit(percentage); } +void RegisteredMemoryConsumer::UpdateMemoryLimitNoNotification(int percentage) { + memory_consumer_->UpdateMemoryLimitNoNotification(percentage); +} + void RegisteredMemoryConsumer::ReleaseMemory() { memory_consumer_->ReleaseMemory(); }
diff --git a/base/memory_coordinator/memory_consumer_registry.h b/base/memory_coordinator/memory_consumer_registry.h index c3290d6..8bc25fa 100644 --- a/base/memory_coordinator/memory_consumer_registry.h +++ b/base/memory_coordinator/memory_consumer_registry.h
@@ -25,6 +25,7 @@ class BASE_EXPORT RegisteredMemoryConsumer { public: void UpdateMemoryLimit(int percentage); + void UpdateMemoryLimitNoNotification(int percentage); void ReleaseMemory(); friend bool operator==(const RegisteredMemoryConsumer& lhs,
diff --git a/base/rand_util.h b/base/rand_util.h index f05e61c..6746580 100644 --- a/base/rand_util.h +++ b/base/rand_util.h
@@ -52,8 +52,6 @@ BASE_EXPORT uint64_t RandUint64(); // Returns a random number between min and max (inclusive). Thread-safe. -// -// Returns a random number between min and max (inclusive). Thread-safe. BASE_EXPORT int RandIntInclusive(int min, int max); // Returns a random number in range [0, range). Thread-safe.
diff --git a/base/synchronization/lock.cc b/base/synchronization/lock.cc index 45fcc2a..926bac7 100644 --- a/base/synchronization/lock.cc +++ b/base/synchronization/lock.cc
@@ -11,6 +11,8 @@ #include <cstdint> #include "base/compiler_specific.h" +#include "base/feature_list.h" +#include "base/features.h" #if DCHECK_IS_ON() #include <array> @@ -18,11 +20,13 @@ #include "base/check_op.h" #include "base/synchronization/lock_subtle.h" #include "base/threading/platform_thread.h" +#endif // DCHECK_IS_ON() namespace base { namespace { +#if DCHECK_IS_ON() // List of locks held by a thread. // // As of May 2024, no more than 5 locks were held simultaneously by a thread in @@ -38,9 +42,23 @@ // Number of non-nullptr elements in `g_tracked_locks_held_by_thread`. thread_local size_t g_num_tracked_locks_held_by_thread = 0; +#endif // DCHECK_IS_ON() + +#if BUILDFLAG(IS_POSIX) +int GetBaseLockSpinCount() { +#if defined(ARCH_CPU_X86_FAMILY) + return base::features::kSpinCountX86.Get(); +#elif defined(ARCH_CPU_ARM_FAMILY) + return base::features::kSpinCountArm.Get(); +#else + return 0; +#endif // defined(ARCH_CPU_X86_FAMILY) +} +#endif // BUILDFLAG(IS_POSIX) } // namespace +#if DCHECK_IS_ON() Lock::~Lock() { DCHECK(owning_thread_ref_.is_null()); } @@ -135,7 +153,15 @@ } } // namespace subtle +#endif // DCHECK_IS_ON() + +#if BUILDFLAG(IS_POSIX) +// static +void Lock::InitializeFeatures() { + if (FeatureList::IsEnabled(base::features::kBaseLockTrySpin)) { + base::internal::LockImpl::SetTrySpinCount(GetBaseLockSpinCount()); + } +} +#endif // BUILDFLAG(IS_POSIX) } // namespace base - -#endif // DCHECK_IS_ON()
diff --git a/base/synchronization/lock.h b/base/synchronization/lock.h index af7cc4e..fc537e9 100644 --- a/base/synchronization/lock.h +++ b/base/synchronization/lock.h
@@ -91,6 +91,13 @@ #endif } +#if BUILDFLAG(IS_POSIX) + // Applies features configured by field trials to base::Lock. Must be called + // on the main thread only after the feature list has been initialized and + // only once. + static void InitializeFeatures(); +#endif // BUILDFLAG(IS_POSIX) + // Both Windows and POSIX implementations of ConditionVariable need to be // able to see our lock and tweak our debugging counters, as they release and // acquire locks inside of their condition variable APIs.
diff --git a/base/synchronization/lock_unittest.cc b/base/synchronization/lock_unittest.cc index 63ed0f0..29a37a9 100644 --- a/base/synchronization/lock_unittest.cc +++ b/base/synchronization/lock_unittest.cc
@@ -26,6 +26,7 @@ #include "base/memory/raw_ref.h" #include "base/profiler/thread_delegate.h" #include "base/rand_util.h" +#include "base/strings/string_number_conversions.h" #include "base/synchronization/lock_impl.h" #include "base/synchronization/lock_metrics_recorder.h" #include "base/synchronization/lock_subtle.h" @@ -37,6 +38,7 @@ #include "base/threading/platform_thread.h" #include "base/time/time.h" #include "base/timer/elapsed_timer.h" +#include "build/build_config.h" #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" @@ -654,9 +656,27 @@ #endif // BUILDFLAG(IS_APPLE) TEST_F(LockTrySpinTest, MAYBE_TrySpinAvoidsSyscall) { +#if !defined(ARCH_CPU_X86_FAMILY) && !defined(ARCH_CPU_ARM_FAMILY) + GTEST_SKIP() << "Skipping test on platforms that don't support spinning in " + "base::Lock."; +#endif + // Set the try-spin count to an absurdly large value to ensure that the // thread never waits for the lock in the kernel. - internal::LockImpl::SetTrySpinCount(std::numeric_limits<int>::max()); + base::test::ScopedFeatureList feature_list; + feature_list.InitAndEnableFeatureWithParameters( + base::features::kBaseLockTrySpin, + { +#if defined(ARCH_CPU_X86_FAMILY) + {base::features::kSpinCountX86.name, + base::NumberToString(std::numeric_limits<int>::max())} +#elif defined(ARCH_CPU_ARM_FAMILY) + {base::features::kSpinCountArm.name, + base::NumberToString(std::numeric_limits<int>::max())} +#endif + }); + base::Lock::InitializeFeatures(); + ClearLockMetricsSamples(); { TestThread thread;
diff --git a/build/config/gcc/BUILD.gn b/build/config/gcc/BUILD.gn index a659210..444e870 100644 --- a/build/config/gcc/BUILD.gn +++ b/build/config/gcc/BUILD.gn
@@ -32,7 +32,13 @@ # See http://gcc.gnu.org/wiki/Visibility config("symbol_visibility_hidden") { cflags = [ "-fvisibility=hidden" ] - rustflags = [ "-Zdefault-visibility=hidden" ] + + # In component builds, `--crate-type=dylib` is used, which can't interoperate + # with `hidden` symbol visibility. For some more details see + # https://github.com/rust-lang/rfcs/blob/master/text/3834-export-visibility.md#open-question-export_visibility--hidden-vs-dylibs + if (!is_component_build) { + rustflags = [ "-Zdefault-visibility=hidden" ] + } # Visibility attribute is not supported on AIX. if (current_os != "aix") {
diff --git a/build/config/siso/proto_linux.star b/build/config/siso/proto_linux.star index 06f47db0..8836b082 100644 --- a/build/config/siso/proto_linux.star +++ b/build/config/siso/proto_linux.star
@@ -16,6 +16,15 @@ { "name": "proto/protoc_wrapper", "command_prefix": "python3 ../../tools/protoc_wrapper/protoc_wrapper.py", + "indirect_inputs": { + "includes": [ + # Working around https://crbug.com/gn/509549092 as + # suggested in https://crbug.com/498216362#comment30 + "*.so", + "*.dll", + "*.dylib", + ], + }, "exclude_input_patterns": [ "*.o", "*.a",
diff --git a/build/config/siso/v8.star b/build/config/siso/v8.star index 1a70f9028..8cb09c51 100644 --- a/build/config/siso/v8.star +++ b/build/config/siso/v8.star
@@ -27,6 +27,15 @@ { "name": "v8/mksnapshot", "command_prefix": platform.python_bin + " ../../v8/tools/run.py ./mksnapshot", + "indirect_inputs": { + "includes": [ + # Working around https://crbug.com/gn/509549092 as + # suggested in https://crbug.com/498216362#comment30 + "*.so", + "*.dll", + "*.dylib", + ], + }, "remote": remote_run, "timeout": "2m", # This action may consume a lot of memory on sanitizer builders.
diff --git a/build/rust/std/BUILD.gn b/build/rust/std/BUILD.gn index 105a1d5..4780835 100644 --- a/build/rust/std/BUILD.gn +++ b/build/rust/std/BUILD.gn
@@ -19,6 +19,7 @@ import("//build/config/coverage/coverage.gni") import("//build/config/rust.gni") import("//build/config/sanitizers/sanitizers.gni") +import("//build/rust/std/local_rustc_sysroot.gni") if (toolchain_has_rust) { # List of Rust stdlib rlibs which are present in the official Rust toolchain @@ -124,7 +125,12 @@ ] } - # From rust/library/std/src/sys/unix/mod.rs. + # `#[link...]` directives from `rust/library/std/src/sys/pal/...` [1] are + # applied when linking is driven by `rustc` (e.g. when `std` is built as + # `--crate-type=dylib` if `is_component_build`). When `rustc` is not + # participating in the final linking (such as when `std` is linked + # statically into the shipping browser), then these `#[link...]` directives + # need to be replicated as GN's `libs = ...` directives. # # libs = [ "System" ] (meaning /usr/lib/libSystem.B.dylib) is intentionally # omitted here on Apple platforms, because the linker driver is responsible @@ -135,29 +141,33 @@ # # TODO(danakj): We should generate this list somehow when building or # rolling the Rust toolchain? - if (is_android) { - libs = [ "dl" ] - } else if (target_os == "freebsd") { - libs = [ - "execinfo", - "pthread", - ] - } else if (target_os == "netbsd") { - libs = [ - "rt", - "pthread", - ] - } else if (is_ios) { - libs = [ "objc" ] - frameworks = [ - "Security.framework", - "Foundation.framework", - ] - } else if (is_fuchsia) { - libs = [ - "zircon", - "fdio", - ] + # + # [1] https://github.com/rust-lang/rust/blob/f5eca4fcfa908d1e038afd19c6e746f075859130/library/std/src/sys/pal/unix/mod.rs#L308-L315 + if (!is_component_build) { + if (is_android) { + libs = [ "dl" ] + } else if (target_os == "freebsd") { + libs = [ + "execinfo", + "pthread", + ] + } else if (target_os == "netbsd") { + libs = [ + "rt", + "pthread", + ] + } else if (is_ios) { + libs = [ "objc" ] + frameworks = [ + "Security.framework", + "Foundation.framework", + ] + } else if (is_fuchsia) { + libs = [ + "zircon", + "fdio", + ] + } } } @@ -169,8 +179,6 @@ sysroot_lib_subdir = "lib/rustlib/$rust_abi_target/lib" if (!rust_prebuilt_stdlib) { - local_rustc_sysroot = "$root_out_dir/local_rustc_sysroot" - # All std targets starting with core build with our sysroot. It starts empty # and is incrementally built. The directory must exist at the start. generated_file("empty_sysroot_for_std_build") {
diff --git a/build/rust/std/BUILD.gn.hbs b/build/rust/std/BUILD.gn.hbs index 8f1780c..de385fd 100644 --- a/build/rust/std/BUILD.gn.hbs +++ b/build/rust/std/BUILD.gn.hbs
@@ -4,8 +4,8 @@ # @generated from build/rust/std/BUILD.gn.hbs by tools/crates/gnrt. Do not edit! -import("//build/config/rust.gni") import("//build/rust/cargo_crate.gni") +import("//build/rust/std/local_rustc_sysroot.gni") config("max_codegen_units") { # Force one CGU per module (it won't actually be 10,000). This is useful for @@ -191,7 +191,7 @@ "-Zlink-directives=false", {{/if}} ] - output_dir = "$root_out_dir/local_rustc_sysroot/lib/rustlib/$rust_abi_target/lib/" + output_dir = "$local_rustc_sysroot/lib/rustlib/$rust_abi_target/lib/" } {{/with}} {{/each}}
diff --git a/build/rust/std/gnrt_config.toml b/build/rust/std/gnrt_config.toml index 9b354d167..a11ad19 100644 --- a/build/rust/std/gnrt_config.toml +++ b/build/rust/std/gnrt_config.toml
@@ -112,13 +112,13 @@ '../../portable-simd/crates/std_float/src', '../../stdarch/crates/core_arch/src/core_arch_docs.md', ] - # Remove this from std. It will be depended on directly when needed. exclude_deps_in_gn = ['profiler_builtins'] [crate.std.extra_kv] -immediate_abort = true cpp_api_from_rust = { deps = [ "alloc", "core" ] } +immediate_abort = true +override_crate_type_as_dylib_in_component_builds = true [crate.test] # test only depends on proc_macro as an internal detail of the Rust build, so
diff --git a/build/rust/std/local_rustc_sysroot.gni b/build/rust/std/local_rustc_sysroot.gni new file mode 100644 index 0000000..4a8e9371 --- /dev/null +++ b/build/rust/std/local_rustc_sysroot.gni
@@ -0,0 +1,27 @@ +# Copyright 2026 The Chromium Authors +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +# Implementation detail for `build/rust/std` - the path of the sysroot +# directory is shared by: +# +# 1. `build/rust/std/BUILD.gn` +# 2. `build/rust/std/BUILD.gn.hbs` +# (which is expanded into `build/rust/std/rules/BUILD.gn`) +if (is_component_build) { + # In various scenarios a crate can initially get built as `--crate-type=rlib` + # and later built as `--crate-type=dylib`. This can happen after opting in + # a crate into `override_crate_type_as_dylib_in_component_builds`, or after + # switching `is_component_build` in `args.gn`. Without a separate directory, + # the presence of stale `.rlib`, `.rmeta`, and/or `.so` files can confuse + # `rustc`: + # * `error[E0464]: multiple candidates for `dylib` dependency `std` found` + # * `error[E0460]: found possibly newer version of crate `core` which `std` + # depends on` + # + # This is covered in more detail in https://crbug.com/498216362 (among other + # problems). + local_rustc_sysroot = "$root_out_dir/local_rustc_sysroot_for_component_build" +} else { + local_rustc_sysroot = "$root_out_dir/local_rustc_sysroot" +}
diff --git a/build/rust/std/rules/BUILD.gn b/build/rust/std/rules/BUILD.gn index 27103bac..01c13ec 100644 --- a/build/rust/std/rules/BUILD.gn +++ b/build/rust/std/rules/BUILD.gn
@@ -6,6 +6,7 @@ import("//build/config/rust.gni") import("//build/rust/cargo_crate.gni") +import("//build/rust/std/local_rustc_sysroot.gni") config("max_codegen_units") { # Force one CGU per module (it won't actually be 10,000). This is useful for @@ -79,8 +80,7 @@ "-Zforce-unstable-if-unmarked", "--cap-lints=allow", # Suppress all warnings in stdlib crates ] - output_dir = - "$root_out_dir/local_rustc_sysroot/lib/rustlib/$rust_abi_target/lib/" + output_dir = "$local_rustc_sysroot/lib/rustlib/$rust_abi_target/lib/" } cargo_crate("adler2") { crate_type = "rlib" @@ -135,8 +135,7 @@ "-Zforce-unstable-if-unmarked", "--cap-lints=allow", # Suppress all warnings in stdlib crates ] - output_dir = - "$root_out_dir/local_rustc_sysroot/lib/rustlib/$rust_abi_target/lib/" + output_dir = "$local_rustc_sysroot/lib/rustlib/$rust_abi_target/lib/" } cargo_crate("alloc") { crate_type = "rlib" @@ -260,8 +259,7 @@ "-Zforce-unstable-if-unmarked", "--cap-lints=allow", # Suppress all warnings in stdlib crates ] - output_dir = - "$root_out_dir/local_rustc_sysroot/lib/rustlib/$rust_abi_target/lib/" + output_dir = "$local_rustc_sysroot/lib/rustlib/$rust_abi_target/lib/" } cargo_crate("cfg_if") { crate_type = "rlib" @@ -309,8 +307,7 @@ "-Zforce-unstable-if-unmarked", "--cap-lints=allow", # Suppress all warnings in stdlib crates ] - output_dir = - "$root_out_dir/local_rustc_sysroot/lib/rustlib/$rust_abi_target/lib/" + output_dir = "$local_rustc_sysroot/lib/rustlib/$rust_abi_target/lib/" } cargo_crate("compiler_builtins") { crate_type = "rlib" @@ -563,8 +560,7 @@ "-Zforce-unstable-if-unmarked", "--cap-lints=allow", # Suppress all warnings in stdlib crates ] - output_dir = - "$root_out_dir/local_rustc_sysroot/lib/rustlib/$rust_abi_target/lib/" + output_dir = "$local_rustc_sysroot/lib/rustlib/$rust_abi_target/lib/" } cargo_crate("core") { crate_type = "rlib" @@ -1085,8 +1081,7 @@ "-Zforce-unstable-if-unmarked", "--cap-lints=allow", # Suppress all warnings in stdlib crates ] - output_dir = - "$root_out_dir/local_rustc_sysroot/lib/rustlib/$rust_abi_target/lib/" + output_dir = "$local_rustc_sysroot/lib/rustlib/$rust_abi_target/lib/" } cargo_crate("getopts") { crate_type = "rlib" @@ -1140,8 +1135,7 @@ "-Zforce-unstable-if-unmarked", "--cap-lints=allow", # Suppress all warnings in stdlib crates ] - output_dir = - "$root_out_dir/local_rustc_sysroot/lib/rustlib/$rust_abi_target/lib/" + output_dir = "$local_rustc_sysroot/lib/rustlib/$rust_abi_target/lib/" } cargo_crate("gimli") { crate_type = "rlib" @@ -1243,8 +1237,7 @@ "-Zforce-unstable-if-unmarked", "--cap-lints=allow", # Suppress all warnings in stdlib crates ] - output_dir = - "$root_out_dir/local_rustc_sysroot/lib/rustlib/$rust_abi_target/lib/" + output_dir = "$local_rustc_sysroot/lib/rustlib/$rust_abi_target/lib/" } cargo_crate("hashbrown") { crate_type = "rlib" @@ -1326,8 +1319,7 @@ "-Zforce-unstable-if-unmarked", "--cap-lints=allow", # Suppress all warnings in stdlib crates ] - output_dir = - "$root_out_dir/local_rustc_sysroot/lib/rustlib/$rust_abi_target/lib/" + output_dir = "$local_rustc_sysroot/lib/rustlib/$rust_abi_target/lib/" } cargo_crate("libc") { crate_type = "rlib" @@ -1718,8 +1710,7 @@ "-Zforce-unstable-if-unmarked", "--cap-lints=allow", # Suppress all warnings in stdlib crates ] - output_dir = - "$root_out_dir/local_rustc_sysroot/lib/rustlib/$rust_abi_target/lib/" + output_dir = "$local_rustc_sysroot/lib/rustlib/$rust_abi_target/lib/" } cargo_crate("memchr") { crate_type = "rlib" @@ -1816,8 +1807,7 @@ "-Zforce-unstable-if-unmarked", "--cap-lints=allow", # Suppress all warnings in stdlib crates ] - output_dir = - "$root_out_dir/local_rustc_sysroot/lib/rustlib/$rust_abi_target/lib/" + output_dir = "$local_rustc_sysroot/lib/rustlib/$rust_abi_target/lib/" } cargo_crate("miniz_oxide") { crate_type = "rlib" @@ -1890,8 +1880,7 @@ "-Zforce-unstable-if-unmarked", "--cap-lints=allow", # Suppress all warnings in stdlib crates ] - output_dir = - "$root_out_dir/local_rustc_sysroot/lib/rustlib/$rust_abi_target/lib/" + output_dir = "$local_rustc_sysroot/lib/rustlib/$rust_abi_target/lib/" } cargo_crate("object") { crate_type = "rlib" @@ -2039,8 +2028,7 @@ "-Zforce-unstable-if-unmarked", "--cap-lints=allow", # Suppress all warnings in stdlib crates ] - output_dir = - "$root_out_dir/local_rustc_sysroot/lib/rustlib/$rust_abi_target/lib/" + output_dir = "$local_rustc_sysroot/lib/rustlib/$rust_abi_target/lib/" } cargo_crate("panic_abort") { crate_type = "rlib" @@ -2093,8 +2081,7 @@ "-Zforce-unstable-if-unmarked", "--cap-lints=allow", # Suppress all warnings in stdlib crates ] - output_dir = - "$root_out_dir/local_rustc_sysroot/lib/rustlib/$rust_abi_target/lib/" + output_dir = "$local_rustc_sysroot/lib/rustlib/$rust_abi_target/lib/" } cargo_crate("proc_macro") { crate_type = "rlib" @@ -2155,8 +2142,7 @@ "-Zforce-unstable-if-unmarked", "--cap-lints=allow", # Suppress all warnings in stdlib crates ] - output_dir = - "$root_out_dir/local_rustc_sysroot/lib/rustlib/$rust_abi_target/lib/" + output_dir = "$local_rustc_sysroot/lib/rustlib/$rust_abi_target/lib/" } cargo_crate("profiler_builtins") { crate_type = "rlib" @@ -2191,8 +2177,7 @@ "-Zforce-unstable-if-unmarked", "--cap-lints=allow", # Suppress all warnings in stdlib crates ] - output_dir = - "$root_out_dir/local_rustc_sysroot/lib/rustlib/$rust_abi_target/lib/" + output_dir = "$local_rustc_sysroot/lib/rustlib/$rust_abi_target/lib/" } cargo_crate("rustc_demangle") { crate_type = "rlib" @@ -2244,8 +2229,7 @@ "-Zforce-unstable-if-unmarked", "--cap-lints=allow", # Suppress all warnings in stdlib crates ] - output_dir = - "$root_out_dir/local_rustc_sysroot/lib/rustlib/$rust_abi_target/lib/" + output_dir = "$local_rustc_sysroot/lib/rustlib/$rust_abi_target/lib/" } cargo_crate("rustc_literal_escaper") { crate_type = "rlib" @@ -2289,8 +2273,7 @@ "-Zforce-unstable-if-unmarked", "--cap-lints=allow", # Suppress all warnings in stdlib crates ] - output_dir = - "$root_out_dir/local_rustc_sysroot/lib/rustlib/$rust_abi_target/lib/" + output_dir = "$local_rustc_sysroot/lib/rustlib/$rust_abi_target/lib/" } cargo_crate("rustc_std_workspace_alloc") { crate_type = "rlib" @@ -2329,8 +2312,7 @@ "-Zforce-unstable-if-unmarked", "--cap-lints=allow", # Suppress all warnings in stdlib crates ] - output_dir = - "$root_out_dir/local_rustc_sysroot/lib/rustlib/$rust_abi_target/lib/" + output_dir = "$local_rustc_sysroot/lib/rustlib/$rust_abi_target/lib/" } cargo_crate("rustc_std_workspace_core") { crate_type = "rlib" @@ -2370,8 +2352,7 @@ "-Zforce-unstable-if-unmarked", "--cap-lints=allow", # Suppress all warnings in stdlib crates ] - output_dir = - "$root_out_dir/local_rustc_sysroot/lib/rustlib/$rust_abi_target/lib/" + output_dir = "$local_rustc_sysroot/lib/rustlib/$rust_abi_target/lib/" } cargo_crate("rustc_std_workspace_std") { crate_type = "rlib" @@ -2410,11 +2391,11 @@ "-Zforce-unstable-if-unmarked", "--cap-lints=allow", # Suppress all warnings in stdlib crates ] - output_dir = - "$root_out_dir/local_rustc_sysroot/lib/rustlib/$rust_abi_target/lib/" + output_dir = "$local_rustc_sysroot/lib/rustlib/$rust_abi_target/lib/" } cargo_crate("std") { crate_type = "rlib" + override_crate_type_as_dylib_in_component_builds = true crate_root = "//third_party/rust-toolchain/lib/rustlib/src/rust/library/std/src/lib.rs" sources = [ @@ -3229,8 +3210,7 @@ "-Zforce-unstable-if-unmarked", "--cap-lints=allow", # Suppress all warnings in stdlib crates ] - output_dir = - "$root_out_dir/local_rustc_sysroot/lib/rustlib/$rust_abi_target/lib/" + output_dir = "$local_rustc_sysroot/lib/rustlib/$rust_abi_target/lib/" } cargo_crate("std_detect") { crate_type = "rlib" @@ -3323,8 +3303,7 @@ "-Zforce-unstable-if-unmarked", "--cap-lints=allow", # Suppress all warnings in stdlib crates ] - output_dir = - "$root_out_dir/local_rustc_sysroot/lib/rustlib/$rust_abi_target/lib/" + output_dir = "$local_rustc_sysroot/lib/rustlib/$rust_abi_target/lib/" } cargo_crate("sysroot") { crate_type = "rlib" @@ -3369,8 +3348,7 @@ "-Zforce-unstable-if-unmarked", "--cap-lints=allow", # Suppress all warnings in stdlib crates ] - output_dir = - "$root_out_dir/local_rustc_sysroot/lib/rustlib/$rust_abi_target/lib/" + output_dir = "$local_rustc_sysroot/lib/rustlib/$rust_abi_target/lib/" } cargo_crate("test") { crate_type = "rlib" @@ -3449,8 +3427,7 @@ "-Zforce-unstable-if-unmarked", "--cap-lints=allow", # Suppress all warnings in stdlib crates ] - output_dir = - "$root_out_dir/local_rustc_sysroot/lib/rustlib/$rust_abi_target/lib/" + output_dir = "$local_rustc_sysroot/lib/rustlib/$rust_abi_target/lib/" } cargo_crate("unwind") { crate_type = "rlib" @@ -3501,8 +3478,7 @@ "--cap-lints=allow", # Suppress all warnings in stdlib crates "-Zlink-directives=false", ] - output_dir = - "$root_out_dir/local_rustc_sysroot/lib/rustlib/$rust_abi_target/lib/" + output_dir = "$local_rustc_sysroot/lib/rustlib/$rust_abi_target/lib/" } cargo_crate("windows_link") { crate_type = "rlib" @@ -3541,6 +3517,5 @@ "-Zforce-unstable-if-unmarked", "--cap-lints=allow", # Suppress all warnings in stdlib crates ] - output_dir = - "$root_out_dir/local_rustc_sysroot/lib/rustlib/$rust_abi_target/lib/" + output_dir = "$local_rustc_sysroot/lib/rustlib/$rust_abi_target/lib/" }
diff --git a/cc/BUILD.gn b/cc/BUILD.gn index 9dd0282..7fb8b58 100644 --- a/cc/BUILD.gn +++ b/cc/BUILD.gn
@@ -431,7 +431,7 @@ "trees/layer_tree_host_impl.cc", "trees/layer_tree_host_impl.h", "trees/layer_tree_host_impl_delegate.h", - "trees/layer_tree_host_single_thread_client.h", + "trees/layer_tree_host_single_thread_delegate.h", "trees/layer_tree_impl.cc", "trees/layer_tree_impl.h", "trees/layer_tree_mutator.cc", @@ -680,8 +680,8 @@ "test/stub_input_handler_client.h", "test/stub_layer_tree_host_delegate.cc", "test/stub_layer_tree_host_delegate.h", - "test/stub_layer_tree_host_single_thread_client.cc", - "test/stub_layer_tree_host_single_thread_client.h", + "test/stub_layer_tree_host_single_thread_delegate.cc", + "test/stub_layer_tree_host_single_thread_delegate.h", "test/task_graph_runner_test_template.cc", "test/task_graph_runner_test_template.h", "test/test_client_shared_image_interface.cc",
diff --git a/cc/animation/animation_host_perftest.cc b/cc/animation/animation_host_perftest.cc index 95e4858..7be59ec 100644 --- a/cc/animation/animation_host_perftest.cc +++ b/cc/animation/animation_host_perftest.cc
@@ -14,7 +14,7 @@ #include "cc/test/fake_layer_tree_host.h" #include "cc/test/fake_layer_tree_host_delegate.h" #include "cc/test/fake_layer_tree_host_impl.h" -#include "cc/test/stub_layer_tree_host_single_thread_client.h" +#include "cc/test/stub_layer_tree_host_single_thread_delegate.h" #include "cc/test/test_task_graph_runner.h" #include "testing/gtest/include/gtest/gtest.h" #include "testing/perf/perf_result_reporter.h" @@ -31,7 +31,7 @@ layer_tree_host_ = FakeLayerTreeHost::Create( &fake_client_, &task_graph_runner_, animation_host_.get(), settings); layer_tree_host_->InitializeSingleThreaded( - &single_thread_client_, + &single_thread_delegate_, base::SingleThreadTaskRunner::GetCurrentDefault()); root_layer_ = Layer::Create(); @@ -132,7 +132,7 @@ } private: - StubLayerTreeHostSingleThreadClient single_thread_client_; + StubLayerTreeHostSingleThreadDelegate single_thread_delegate_; FakeLayerTreeHostDelegate fake_client_; std::unique_ptr<AnimationHost> animation_host_; std::unique_ptr<FakeLayerTreeHost> layer_tree_host_;
diff --git a/cc/layers/layer_perftest.cc b/cc/layers/layer_perftest.cc index a787e023..68e0727a 100644 --- a/cc/layers/layer_perftest.cc +++ b/cc/layers/layer_perftest.cc
@@ -11,7 +11,7 @@ #include "cc/test/fake_layer_tree_host.h" #include "cc/test/fake_layer_tree_host_delegate.h" #include "cc/test/fake_layer_tree_host_impl.h" -#include "cc/test/stub_layer_tree_host_single_thread_client.h" +#include "cc/test/stub_layer_tree_host_single_thread_delegate.h" #include "cc/test/test_task_graph_runner.h" #include "testing/gtest/include/gtest/gtest.h" #include "testing/perf/perf_result_reporter.h" @@ -37,7 +37,7 @@ layer_tree_host_ = FakeLayerTreeHost::Create( &fake_client_, &task_graph_runner_, animation_host_.get()); layer_tree_host_->InitializeSingleThreaded( - &single_thread_client_, + &single_thread_delegate_, base::SingleThreadTaskRunner::GetCurrentDefault()); } @@ -58,7 +58,7 @@ TestTaskGraphRunner task_graph_runner_; FakeLayerTreeHostImpl host_impl_; - StubLayerTreeHostSingleThreadClient single_thread_client_; + StubLayerTreeHostSingleThreadDelegate single_thread_delegate_; FakeLayerTreeHostDelegate fake_client_; std::unique_ptr<AnimationHost> animation_host_; std::unique_ptr<FakeLayerTreeHost> layer_tree_host_;
diff --git a/cc/layers/layer_unittest.cc b/cc/layers/layer_unittest.cc index 143326c..8ec8ff57 100644 --- a/cc/layers/layer_unittest.cc +++ b/cc/layers/layer_unittest.cc
@@ -25,7 +25,7 @@ #include "cc/test/fake_layer_tree_host_delegate.h" #include "cc/test/fake_layer_tree_host_impl.h" #include "cc/test/layer_test_common.h" -#include "cc/test/stub_layer_tree_host_single_thread_client.h" +#include "cc/test/stub_layer_tree_host_single_thread_delegate.h" #include "cc/test/test_task_graph_runner.h" #include "cc/trees/clip_node.h" #include "cc/trees/layer_tree_host.h" @@ -143,12 +143,12 @@ class FakeLayerTreeHost : public LayerTreeHost { public: - FakeLayerTreeHost(LayerTreeHostSingleThreadClient* single_thread_client, + FakeLayerTreeHost(LayerTreeHostSingleThreadDelegate* single_thread_delegate, LayerTreeHost::InitParams params) : LayerTreeHost(std::move(params), CompositorMode::SINGLE_THREADED), mock_delegate_( std::make_unique<StrictMock<MockLayerTreeHostDelegate>>()) { - InitializeSingleThreaded(single_thread_client, + InitializeSingleThreaded(single_thread_delegate, base::SingleThreadTaskRunner::GetCurrentDefault()); } @@ -216,7 +216,7 @@ params.mutator_host = animation_host_.get(); layer_tree_host_ = std::make_unique<FakeLayerTreeHost>( - &single_thread_client_, std::move(params)); + &single_thread_delegate_, std::move(params)); } void TearDown() override { @@ -306,7 +306,7 @@ TestTaskGraphRunner task_graph_runner_; FakeLayerTreeHostImpl host_impl_; - StubLayerTreeHostSingleThreadClient single_thread_client_; + StubLayerTreeHostSingleThreadDelegate single_thread_delegate_; FakeLayerTreeHostDelegate fake_client_; std::unique_ptr<FakeLayerTreeHost> layer_tree_host_; std::unique_ptr<AnimationHost> animation_host_; @@ -1211,13 +1211,13 @@ params.main_task_runner = base::SingleThreadTaskRunner::GetCurrentDefault(); params.mutator_host = mutator_host; - return LayerTreeHost::CreateSingleThreaded(&single_thread_client_, + return LayerTreeHost::CreateSingleThreaded(&single_thread_delegate_, std::move(params)); } private: FakeLayerTreeHostDelegate client_; - StubLayerTreeHostSingleThreadClient single_thread_client_; + StubLayerTreeHostSingleThreadDelegate single_thread_delegate_; TestTaskGraphRunner task_graph_runner_; };
diff --git a/cc/layers/mirror_layer_impl.cc b/cc/layers/mirror_layer_impl.cc index d07a7dc..fed0aa71 100644 --- a/cc/layers/mirror_layer_impl.cc +++ b/cc/layers/mirror_layer_impl.cc
@@ -84,7 +84,7 @@ render_pass->CreateAndAppendDrawQuad<viz::CompositorRenderPassDrawQuad>(); quad->SetNew(shared_quad_state, content_rect, unoccluded_content_rect, mirrored_layer_render_pass_id(), mask_resource_id, mask_uv_rect, - mask_texture_size, gfx::RectF(gfx::Rect(content_rect.size())), + mask_texture_size, !layer_tree_impl()->settings().enable_edge_anti_aliasing); quad->SetFilters(mirrored_effect_node->surface_contents_scale, gfx::PointF(), 0.f);
diff --git a/cc/layers/picture_layer_unittest.cc b/cc/layers/picture_layer_unittest.cc index 90c83e6..8695a22 100644 --- a/cc/layers/picture_layer_unittest.cc +++ b/cc/layers/picture_layer_unittest.cc
@@ -25,7 +25,7 @@ #include "cc/test/layer_test_common.h" #include "cc/test/property_tree_test_utils.h" #include "cc/test/skia_common.h" -#include "cc/test/stub_layer_tree_host_single_thread_client.h" +#include "cc/test/stub_layer_tree_host_single_thread_delegate.h" #include "cc/test/test_task_graph_runner.h" #include "cc/trees/single_thread_proxy.h" #include "testing/gmock/include/gmock/gmock.h" @@ -263,7 +263,7 @@ settings.single_thread_proxy_scheduler = false; settings.use_zero_copy = true; - StubLayerTreeHostSingleThreadClient single_thread_client; + StubLayerTreeHostSingleThreadDelegate single_thread_delegate; FakeLayerTreeHostDelegate host_client1; FakeLayerTreeHostDelegate host_client2; TestTaskGraphRunner task_graph_runner; @@ -281,7 +281,7 @@ params.main_task_runner = base::SingleThreadTaskRunner::GetCurrentDefault(); params.mutator_host = animation_host.get(); std::unique_ptr<LayerTreeHost> host1 = LayerTreeHost::CreateSingleThreaded( - &single_thread_client, std::move(params)); + &single_thread_delegate, std::move(params)); host1->SetVisible(true); host_client1.SetLayerTreeHost(host1.get()); @@ -295,7 +295,7 @@ params2.client = &host_client2; params2.mutator_host = animation_host2.get(); std::unique_ptr<LayerTreeHost> host2 = LayerTreeHost::CreateSingleThreaded( - &single_thread_client, std::move(params2)); + &single_thread_delegate, std::move(params2)); host2->SetVisible(true); host_client2.SetLayerTreeHost(host2.get()); @@ -338,7 +338,7 @@ LayerTreeSettings settings = LayerTreeSettings(); settings.single_thread_proxy_scheduler = false; - StubLayerTreeHostSingleThreadClient single_thread_client; + StubLayerTreeHostSingleThreadDelegate single_thread_delegate; FakeLayerTreeHostDelegate host_client1; FakeLayerTreeHostDelegate host_client2; TestTaskGraphRunner task_graph_runner; @@ -356,7 +356,7 @@ params.main_task_runner = base::SingleThreadTaskRunner::GetCurrentDefault(); params.mutator_host = animation_host.get(); std::unique_ptr<LayerTreeHost> host1 = LayerTreeHost::CreateSingleThreaded( - &single_thread_client, std::move(params)); + &single_thread_delegate, std::move(params)); host1->SetVisible(true); host_client1.SetLayerTreeHost(host1.get()); @@ -370,7 +370,7 @@ params2.client = &host_client2; params2.mutator_host = animation_host2.get(); std::unique_ptr<LayerTreeHost> host2 = LayerTreeHost::CreateSingleThreaded( - &single_thread_client, std::move(params2)); + &single_thread_delegate, std::move(params2)); host2->SetVisible(true); host_client2.SetLayerTreeHost(host2.get());
diff --git a/cc/layers/render_surface_impl.cc b/cc/layers/render_surface_impl.cc index becba7e..f83d58a7 100644 --- a/cc/layers/render_surface_impl.cc +++ b/cc/layers/render_surface_impl.cc
@@ -628,7 +628,6 @@ mask_uv_size.height() / unclipped_mask_target_size.height()); } - gfx::RectF tex_coord_rect(gfx::Rect(output_rect.size())); auto* quad = render_pass->CreateAndAppendDrawQuad<viz::CompositorRenderPassDrawQuad>(); const auto& append_render_pass_id = @@ -640,7 +639,7 @@ shared_quad_state, output_rect, unoccluded_output_rect, /*needs_blending=*/true, append_render_pass_id, mask_resource_id, mask_uv_rect, mask_texture_size, surface_contents_scale, gfx::PointF(), - tex_coord_rect, !layer_tree_impl_->settings().enable_edge_anti_aliasing, + !layer_tree_impl_->settings().enable_edge_anti_aliasing, OwningEffectNode()->backdrop_filter_quality, intersects_damage_under_); }
diff --git a/cc/layers/scrollbar_layer_unittest.cc b/cc/layers/scrollbar_layer_unittest.cc index cd603af..f3adcd97 100644 --- a/cc/layers/scrollbar_layer_unittest.cc +++ b/cc/layers/scrollbar_layer_unittest.cc
@@ -31,7 +31,7 @@ #include "cc/test/fake_scrollbar_layer.h" #include "cc/test/layer_tree_impl_test_base.h" #include "cc/test/layer_tree_test.h" -#include "cc/test/stub_layer_tree_host_single_thread_client.h" +#include "cc/test/stub_layer_tree_host_single_thread_delegate.h" #include "cc/test/test_task_graph_runner.h" #include "cc/trees/effect_node.h" #include "cc/trees/layer_tree_host.h" @@ -98,10 +98,10 @@ std::unordered_map<UIResourceId, UIResourceBitmap>; UIResourceBitmapMap ui_resource_bitmap_map_; - StubLayerTreeHostSingleThreadClient single_thread_client_; - int next_id_; - int total_ui_resource_created_; - int total_ui_resource_deleted_; + StubLayerTreeHostSingleThreadDelegate single_thread_delegate_; + int next_id_ = 1; + int total_ui_resource_created_ = 0; + int total_ui_resource_deleted_ = 0; }; class BaseScrollbarLayerTest : public testing::Test { @@ -136,7 +136,7 @@ layer_tree_host_->SetUIResourceManagerForTesting( std::move(fake_ui_resource_manager)); layer_tree_host_->InitializeSingleThreaded( - &single_thread_client_, + &single_thread_delegate_, base::SingleThreadTaskRunner::GetCurrentDefault()); layer_tree_host_->SetVisible(true); fake_client_.SetLayerTreeHost(layer_tree_host_.get()); @@ -144,7 +144,7 @@ protected: FakeLayerTreeHostDelegate fake_client_; - StubLayerTreeHostSingleThreadClient single_thread_client_; + StubLayerTreeHostSingleThreadDelegate single_thread_delegate_; TestTaskGraphRunner task_graph_runner_; LayerTreeSettings layer_tree_settings_; std::unique_ptr<AnimationHost> animation_host_;
diff --git a/cc/layers/texture_layer_unittest.cc b/cc/layers/texture_layer_unittest.cc index 6f4cf251..82622cb1 100644 --- a/cc/layers/texture_layer_unittest.cc +++ b/cc/layers/texture_layer_unittest.cc
@@ -36,7 +36,7 @@ #include "cc/test/fake_layer_tree_host_impl.h" #include "cc/test/layer_test_common.h" #include "cc/test/layer_tree_test.h" -#include "cc/test/stub_layer_tree_host_single_thread_client.h" +#include "cc/test/stub_layer_tree_host_single_thread_delegate.h" #include "cc/test/test_layer_tree_frame_sink.h" #include "cc/test/test_task_graph_runner.h" #include "cc/trees/layer_tree_host.h" @@ -132,11 +132,11 @@ private: explicit MockLayerTreeHost(LayerTreeHost::InitParams params) : LayerTreeHost(std::move(params), CompositorMode::SINGLE_THREADED) { - InitializeSingleThreaded(&single_thread_client_, + InitializeSingleThreaded(&single_thread_delegate_, base::SingleThreadTaskRunner::GetCurrentDefault()); } - StubLayerTreeHostSingleThreadClient single_thread_client_; + StubLayerTreeHostSingleThreadDelegate single_thread_delegate_; }; class MockReleaseCallback { @@ -270,7 +270,7 @@ bool gpu = i == 0; SCOPED_TRACE(gpu); // Make our own LayerTreeHost for this test so we can control the lifetime. - StubLayerTreeHostSingleThreadClient single_thread_client; + StubLayerTreeHostSingleThreadDelegate single_thread_delegate; RunOnCommitLayerTreeHostDelegate client; LayerTreeHost::InitParams params; params.client = &client; @@ -279,7 +279,7 @@ LayerTreeSettings settings; params.settings = &settings; params.main_task_runner = base::SingleThreadTaskRunner::GetCurrentDefault(); - auto host = LayerTreeHost::CreateSingleThreaded(&single_thread_client, + auto host = LayerTreeHost::CreateSingleThreaded(&single_thread_delegate, std::move(params)); client.SetLayerTreeHost(host.get());
diff --git a/cc/layers/ui_resource_layer_unittest.cc b/cc/layers/ui_resource_layer_unittest.cc index bf50fd0..f5e8058 100644 --- a/cc/layers/ui_resource_layer_unittest.cc +++ b/cc/layers/ui_resource_layer_unittest.cc
@@ -9,7 +9,7 @@ #include "cc/resources/ui_resource_manager.h" #include "cc/test/fake_layer_tree_host.h" #include "cc/test/layer_tree_impl_test_base.h" -#include "cc/test/stub_layer_tree_host_single_thread_client.h" +#include "cc/test/stub_layer_tree_host_single_thread_delegate.h" #include "cc/trees/single_thread_proxy.h" #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h"
diff --git a/cc/slim/layer_tree_impl.cc b/cc/slim/layer_tree_impl.cc index f044406..7600da8 100644 --- a/cc/slim/layer_tree_impl.cc +++ b/cc/slim/layer_tree_impl.cc
@@ -911,14 +911,13 @@ auto* quad = parent_pass.CreateAndAppendDrawQuad<viz::CompositorRenderPassDrawQuad>(); - gfx::RectF tex_coord_rect(gfx::Rect(content_rect.size())); quad->SetAll(shared_quad_state, content_rect, content_rect, /*needs_blending=*/true, new_pass->id, /*mask_resource_id=*/viz::kInvalidResourceId, /*mask_uv_rect=*/gfx::RectF(), /*mask_texture_size=*/gfx::Size(), /*filters_scale=*/scale_to_new_pass, - /*filters_origin=*/gfx::PointF(), tex_coord_rect, + /*filters_origin=*/gfx::PointF(), /*force_anti_aliasing_off=*/false, /*backdrop_filter_quality=*/1.f, /*intersects_damage_under=*/true);
diff --git a/cc/test/layer_tree_test.cc b/cc/test/layer_tree_test.cc index ca85ec9..5b46df9 100644 --- a/cc/test/layer_tree_test.cc +++ b/cc/test/layer_tree_test.cc
@@ -37,7 +37,7 @@ #include "cc/trees/client_layer_tree_host_impl.h" #include "cc/trees/layer_tree_host_delegate.h" #include "cc/trees/layer_tree_host_impl.h" -#include "cc/trees/layer_tree_host_single_thread_client.h" +#include "cc/trees/layer_tree_host_single_thread_delegate.h" #include "cc/trees/layer_tree_impl.h" #include "cc/trees/paint_holding_reason.h" #include "cc/trees/proxy_impl.h" @@ -426,9 +426,10 @@ }; // Implementation of LayerTreeHost callback interface. -class LayerTreeHostDelegateForTesting : public LayerTreeHostDelegate, - public LayerTreeHostSchedulingDelegate, - public LayerTreeHostSingleThreadClient { +class LayerTreeHostDelegateForTesting + : public LayerTreeHostDelegate, + public LayerTreeHostSchedulingDelegate, + public LayerTreeHostSingleThreadDelegate { public: static std::unique_ptr<LayerTreeHostDelegateForTesting> Create( TestHooks* test_hooks) { @@ -537,7 +538,7 @@ CompositorMode mode, LayerTreeHostDelegate* client, LayerTreeHostSchedulingDelegate* scheduling_delegate, - LayerTreeHostSingleThreadClient* single_thread_client, + LayerTreeHostSingleThreadDelegate* single_thread_delegate, TaskGraphRunner* task_graph_runner, const LayerTreeSettings& settings, scoped_refptr<base::SingleThreadTaskRunner> main_task_runner, @@ -562,7 +563,7 @@ switch (mode) { case CompositorMode::SINGLE_THREADED: proxy = SingleThreadProxy::Create(layer_tree_host.get(), - single_thread_client, + single_thread_delegate, task_runner_provider.get()); break; case CompositorMode::THREADED:
diff --git a/cc/test/render_pass_test_utils.cc b/cc/test/render_pass_test_utils.cc index cadb140..b1d30e0 100644 --- a/cc/test/render_pass_test_utils.cc +++ b/cc/test/render_pass_test_utils.cc
@@ -144,8 +144,7 @@ /*layer_id=*/0u, /*fast_rounded_corner=*/false); auto* quad = to_pass->template CreateAndAppendDrawQuad<QuadType>(); quad->SetNew(shared_state, output_rect, output_rect, contributing_pass->id, - viz::kInvalidResourceId, gfx::RectF(), gfx::Size(), gfx::RectF(), - false); + viz::kInvalidResourceId, gfx::RectF(), gfx::Size(), false); return quad; } @@ -179,7 +178,7 @@ gfx::Size arbitrary_nonzero_size(1, 1); quad->SetNew(shared_state, output_rect, output_rect, contributing_pass->id, mask_resource_id, gfx::RectF(output_rect), - arbitrary_nonzero_size, gfx::RectF(), false); + arbitrary_nonzero_size, false); } std::vector<viz::ResourceId> AddOneOfEveryQuadType( @@ -227,7 +226,7 @@ to_pass->CreateAndAppendDrawQuad<viz::CompositorRenderPassDrawQuad>(); render_pass_quad->SetNew(shared_state, rect, visible_rect, child_pass_id, resource5, gfx::RectF(rect), gfx::Size(73, 26), - gfx::RectF(), false); + false); } auto* solid_color_quad =
diff --git a/cc/test/stub_layer_tree_host_single_thread_client.cc b/cc/test/stub_layer_tree_host_single_thread_client.cc deleted file mode 100644 index badb033..0000000 --- a/cc/test/stub_layer_tree_host_single_thread_client.cc +++ /dev/null
@@ -1,12 +0,0 @@ -// Copyright 2016 The Chromium Authors -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "cc/test/stub_layer_tree_host_single_thread_client.h" - -namespace cc { - -StubLayerTreeHostSingleThreadClient::~StubLayerTreeHostSingleThreadClient() = - default; - -} // namespace cc
diff --git a/cc/test/stub_layer_tree_host_single_thread_client.h b/cc/test/stub_layer_tree_host_single_thread_client.h deleted file mode 100644 index 20086cb..0000000 --- a/cc/test/stub_layer_tree_host_single_thread_client.h +++ /dev/null
@@ -1,24 +0,0 @@ -// Copyright 2016 The Chromium Authors -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef CC_TEST_STUB_LAYER_TREE_HOST_SINGLE_THREAD_CLIENT_H_ -#define CC_TEST_STUB_LAYER_TREE_HOST_SINGLE_THREAD_CLIENT_H_ - -#include "cc/trees/layer_tree_host_single_thread_client.h" - -namespace cc { - -class StubLayerTreeHostSingleThreadClient - : public LayerTreeHostSingleThreadClient { - public: - ~StubLayerTreeHostSingleThreadClient() override; - - // LayerTreeHostSingleThreadClient implementation. - void DidSubmitCompositorFrame() override {} - void DidLoseLayerTreeFrameSink() override {} -}; - -} // namespace cc - -#endif // CC_TEST_STUB_LAYER_TREE_HOST_SINGLE_THREAD_CLIENT_H_
diff --git a/cc/test/stub_layer_tree_host_single_thread_delegate.cc b/cc/test/stub_layer_tree_host_single_thread_delegate.cc new file mode 100644 index 0000000..1c5a9e0 --- /dev/null +++ b/cc/test/stub_layer_tree_host_single_thread_delegate.cc
@@ -0,0 +1,12 @@ +// Copyright 2016 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "cc/test/stub_layer_tree_host_single_thread_delegate.h" + +namespace cc { + +StubLayerTreeHostSingleThreadDelegate:: + ~StubLayerTreeHostSingleThreadDelegate() = default; + +} // namespace cc
diff --git a/cc/test/stub_layer_tree_host_single_thread_delegate.h b/cc/test/stub_layer_tree_host_single_thread_delegate.h new file mode 100644 index 0000000..032aa330 --- /dev/null +++ b/cc/test/stub_layer_tree_host_single_thread_delegate.h
@@ -0,0 +1,24 @@ +// Copyright 2016 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CC_TEST_STUB_LAYER_TREE_HOST_SINGLE_THREAD_DELEGATE_H_ +#define CC_TEST_STUB_LAYER_TREE_HOST_SINGLE_THREAD_DELEGATE_H_ + +#include "cc/trees/layer_tree_host_single_thread_delegate.h" + +namespace cc { + +class StubLayerTreeHostSingleThreadDelegate + : public LayerTreeHostSingleThreadDelegate { + public: + ~StubLayerTreeHostSingleThreadDelegate() override; + + // LayerTreeHostSingleThreadDelegate implementation. + void DidSubmitCompositorFrame() override {} + void DidLoseLayerTreeFrameSink() override {} +}; + +} // namespace cc + +#endif // CC_TEST_STUB_LAYER_TREE_HOST_SINGLE_THREAD_DELEGATE_H_
diff --git a/cc/trees/client_layer_tree_host_impl_unittest.cc b/cc/trees/client_layer_tree_host_impl_unittest.cc index efd2c90..9fcd9bef 100644 --- a/cc/trees/client_layer_tree_host_impl_unittest.cc +++ b/cc/trees/client_layer_tree_host_impl_unittest.cc
@@ -12,6 +12,7 @@ #include <memory> #include <optional> #include <utility> +#include <vector> #include "base/functional/bind.h" #include "base/functional/callback_helpers.h" @@ -144,6 +145,10 @@ return std::get<0>(arg).get() == std::get<1>(arg); } +ClientLayerTreeHostImpl* client_host_impl(LayerTreeHostImpl* host) { + return static_cast<ClientLayerTreeHostImpl*>(host); +} + class TestInputHandlerClient : public InputHandlerClient { public: TestInputHandlerClient() = default; @@ -308,6 +313,28 @@ scroll_layer_size); } + void ExpectViewportGeometries(float expected_browser_controls_shown_ratio) { + auto* tree = host_impl_->active_tree(); + auto* property_trees = tree->property_trees(); + EXPECT_EQ(expected_browser_controls_shown_ratio, + tree->CurrentTopControlsShownRatio()); + EXPECT_EQ( + tree->top_controls_height() * expected_browser_controls_shown_ratio, + host_impl_->browser_controls_manager()->ContentTopOffset()); + int delta = (tree->top_controls_height() + tree->bottom_controls_height()) * + (1 - expected_browser_controls_shown_ratio); + int scaled_delta = delta / tree->min_page_scale_factor(); + gfx::Size inner_scroll_bounds = tree->InnerViewportScrollNode()->bounds; + inner_scroll_bounds.Enlarge(0, scaled_delta); + EXPECT_EQ(inner_scroll_bounds, InnerViewportScrollLayer()->bounds()); + EXPECT_EQ(gfx::RectF(gfx::SizeF(inner_scroll_bounds)), + tree->OuterViewportClipNode()->clip); + EXPECT_EQ(gfx::Vector2dF(0, delta), + property_trees->inner_viewport_container_bounds_delta()); + EXPECT_EQ(gfx::Vector2dF(0, scaled_delta), + property_trees->outer_viewport_container_bounds_delta()); + } + gfx::Size layer_size_; gfx::Size clip_size_; gfx::Size viewport_size_; @@ -359,29 +386,6 @@ std::optional<RenderFrameMetadata> last_metadata_; }; -#define EXPECT_VIEWPORT_GEOMETRIES(expected_browser_controls_shown_ratio) \ - do { \ - auto* tree = host_impl_->active_tree(); \ - auto* property_trees = tree->property_trees(); \ - EXPECT_EQ(expected_browser_controls_shown_ratio, \ - tree->CurrentTopControlsShownRatio()); \ - EXPECT_EQ( \ - tree->top_controls_height() * expected_browser_controls_shown_ratio, \ - host_impl_->browser_controls_manager()->ContentTopOffset()); \ - int delta = \ - (tree->top_controls_height() + tree->bottom_controls_height()) * \ - (1 - expected_browser_controls_shown_ratio); \ - int scaled_delta = delta / tree->min_page_scale_factor(); \ - gfx::Size inner_scroll_bounds = tree->InnerViewportScrollNode()->bounds; \ - inner_scroll_bounds.Enlarge(0, scaled_delta); \ - EXPECT_EQ(inner_scroll_bounds, InnerViewportScrollLayer()->bounds()); \ - EXPECT_EQ(gfx::RectF(gfx::SizeF(inner_scroll_bounds)), \ - tree->OuterViewportClipNode()->clip); \ - EXPECT_EQ(gfx::Vector2dF(0, delta), \ - property_trees->inner_viewport_container_bounds_delta()); \ - EXPECT_EQ(gfx::Vector2dF(0, scaled_delta), \ - property_trees->outer_viewport_container_bounds_delta()); \ - } while (false) // Checks that we use the memory limits provided. // TODO(crbug.com/458776836): Review memory limit related cc_unittests for @@ -689,10 +693,7 @@ host_impl_->WillBeginImplFrame(args); // Expect no crash because the operation is within an impl frame. // TODO(496580137): Move this to ClientLayerTreeHostImpl specific tests. - if (!host_impl_->settings().trees_in_viz_in_viz_process) { - static_cast<ClientLayerTreeHostImpl*>(host_impl_.get()) - ->InvalidateContentOnImplSide(); - } + client_host_impl(host_impl_.get())->InvalidateContentOnImplSide(); // Once the impl frame is finished the impl thread phase is set to IDLE. host_impl_->DidFinishImplFrame(args); @@ -702,10 +703,7 @@ // Expect no crash when using synchronous renderer compositor regardless the // impl thread phase. // TODO(496580137): Move this to ClientLayerTreeHostImpl specific tests. - if (!host_impl_->settings().trees_in_viz_in_viz_process) { - static_cast<ClientLayerTreeHostImpl*>(host_impl_.get()) - ->InvalidateContentOnImplSide(); - } + client_host_impl(host_impl_.get())->InvalidateContentOnImplSide(); // Test passes when there is no crash. } @@ -772,10 +770,7 @@ // This call sets the invalidate_raster_scroll bit. // TODO(496580137): Move this to ClientLayerTreeHostImpl specific tests. - if (!host_impl_->settings().trees_in_viz_in_viz_process) { - static_cast<ClientLayerTreeHostImpl*>(host_impl_.get()) - ->InvalidateContentOnImplSide(); - } + client_host_impl(host_impl_.get())->InvalidateContentOnImplSide(); if (!CommitsToActiveTree()) { // Activate the pending tree before drawing layers. host_impl_->ActivateSyncTree(); @@ -1134,7 +1129,7 @@ // The browser controls should start off showing so the viewport should be // shrunk. - EXPECT_VIEWPORT_GEOMETRIES(1); + ExpectViewportGeometries(1); EXPECT_EQ(gfx::SizeF(50, 50), active_tree->ScrollableSize()); EXPECT_EQ(ScrollThread::kScrollOnImplThread, @@ -1150,17 +1145,17 @@ // Hide the browser controls by a bit, the scrollable size should increase but // the actual content bounds shouldn't. host_impl_->browser_controls_manager()->ScrollBy(gfx::Vector2dF(0, 25)); - EXPECT_VIEWPORT_GEOMETRIES(0.5f); + ExpectViewportGeometries(0.5f); EXPECT_EQ(gfx::SizeF(50, 75), active_tree->ScrollableSize()); // Fully hide the browser controls. host_impl_->browser_controls_manager()->ScrollBy(gfx::Vector2dF(0, 25)); - EXPECT_VIEWPORT_GEOMETRIES(0); + ExpectViewportGeometries(0); EXPECT_EQ(gfx::SizeF(50, 100), active_tree->ScrollableSize()); // Scrolling additionally shouldn't have any effect. host_impl_->browser_controls_manager()->ScrollBy(gfx::Vector2dF(0, 25)); - EXPECT_VIEWPORT_GEOMETRIES(0); + ExpectViewportGeometries(0); EXPECT_EQ(gfx::SizeF(50, 100), active_tree->ScrollableSize()); host_impl_->browser_controls_manager()->ScrollEnd(); @@ -1281,7 +1276,7 @@ host_impl_->OuterViewportScrollNode()->snap_container_data.emplace(container); DrawFrame(); - EXPECT_VIEWPORT_GEOMETRIES(1.0f); + ExpectViewportGeometries(1.0f); LayerTreeImpl* active_tree = host_impl_->active_tree(); PropertyTrees* property_trees = active_tree->property_trees(); @@ -1352,7 +1347,7 @@ // The browser controls should start off showing so the viewport should be // shrunk. - EXPECT_VIEWPORT_GEOMETRIES(1.0f); + ExpectViewportGeometries(1.0f); EXPECT_EQ(gfx::SizeF(200, 1000), active_tree->ScrollableSize()); ASSERT_EQ(ScrollThread::kScrollOnImplThread, @@ -1370,7 +1365,7 @@ GetInputHandler().ScrollUpdate( UpdateState(gfx::Point(0, 0), gfx::Vector2dF(0, 25), ui::ScrollInputType::kTouchscreen)); - EXPECT_VIEWPORT_GEOMETRIES(0.5f); + ExpectViewportGeometries(0.5f); GetInputHandler().ScrollEnd(/*should_snap=*/false, std::nullopt); } @@ -1401,7 +1396,7 @@ // The browser controls should start off showing so the viewport should be // shrunk. - EXPECT_VIEWPORT_GEOMETRIES(1.0f); + ExpectViewportGeometries(1.0f); EXPECT_EQ(gfx::SizeF(50, 50), active_tree->ScrollableSize()); EXPECT_EQ(gfx::Size(50, 15), scrollbar_layer->bounds()); EXPECT_EQ(gfx::Rect(20, 0, 10, 3), scrollbar_layer->ComputeThumbQuadRect()); @@ -1420,7 +1415,7 @@ // the actual content bounds shouldn't. { host_impl_->browser_controls_manager()->ScrollBy(gfx::Vector2dF(0, 25)); - EXPECT_VIEWPORT_GEOMETRIES(0.5f); + ExpectViewportGeometries(0.5f); EXPECT_EQ(gfx::SizeF(50, 75), active_tree->ScrollableSize()); EXPECT_EQ(gfx::Size(50, 15), scrollbar_layer->bounds()); EXPECT_EQ(gfx::Rect(20, 25, 10, 3), @@ -1430,7 +1425,7 @@ // Fully hide the browser controls. { host_impl_->browser_controls_manager()->ScrollBy(gfx::Vector2dF(0, 25)); - EXPECT_VIEWPORT_GEOMETRIES(0); + ExpectViewportGeometries(0); EXPECT_EQ(gfx::SizeF(50, 100), active_tree->ScrollableSize()); EXPECT_EQ(gfx::Size(50, 15), scrollbar_layer->bounds()); EXPECT_EQ(gfx::Rect(20, 50, 10, 3), @@ -1440,7 +1435,7 @@ // Additional scrolling shouldn't have any effect. { host_impl_->browser_controls_manager()->ScrollBy(gfx::Vector2dF(0, 25)); - EXPECT_VIEWPORT_GEOMETRIES(0); + ExpectViewportGeometries(0); EXPECT_EQ(gfx::SizeF(50, 100), active_tree->ScrollableSize()); EXPECT_EQ(gfx::Size(50, 15), scrollbar_layer->bounds()); EXPECT_EQ(gfx::Rect(20, 50, 10, 3), @@ -2433,7 +2428,77 @@ // Test fixture that enables only Pending tree commit modes, // CommitToPendingTree and CommitToPendingTreeTreesInVizClient. -class PendingTreeLayerTreeHostImplTest : public LayerTreeHostImplTest {}; +class PendingTreeLayerTreeHostImplTest : public LayerTreeHostImplTest { + protected: + void SetupScrollDelayTest() { + CreateHostImpl(DefaultSettings(), CreateLayerTreeFrameSink()); + SetupRootLayer<SolidColorLayerImpl>(host_impl_->active_tree(), + gfx::Size(10, 10)); + UpdateDrawProperties(host_impl_->active_tree()); + EXPECT_EQ(first_scroll_observed, 0); + } + + void QueueLatencyInfoSwapPromise(ui::LatencyComponentType type, + int trace_id) { + ui::LatencyInfo latency_info; + latency_info.set_trace_id(trace_id); + latency_info.AddLatencyNumber(type); + std::unique_ptr<SwapPromise> swap_promise( + new LatencyInfoSwapPromise(latency_info)); + host_impl_->active_tree()->QueuePinnedSwapPromise(std::move(swap_promise)); + } + + void DrawAndPresentFrame(uint32_t frame_token, bool damage = true) { + if (damage) { + host_impl_->SetFullViewportDamage(); + host_impl_->SetNeedsRedraw(/*animation_only=*/false, + /*skip_if_inside_draw=*/false); + } + DrawFrame(); + viz::FrameTimingDetails mock_details; + mock_details.presentation_feedback = ExampleFeedback(); + host_impl_->DidPresentCompositorFrame(frame_token, mock_details); + } + + void RunPaintWorkletCommitTest() { + auto painter_owned = std::make_unique<TestPaintWorkletLayerPainter>(); + TestPaintWorkletLayerPainter* painter = painter_owned.get(); + host_impl_->SetPaintWorkletLayerPainter(std::move(painter_owned)); + + client_host_impl(host_impl_.get())->CreatePendingTree(); + auto* root = SetupRootLayer<PictureLayerImpl>(host_impl_->pending_tree(), + gfx::Size(100, 100)); + root->SetNeedsPushProperties(); + + scoped_refptr<RasterSource> raster_source_with_pws = + FakeRasterSource::CreateFilledWithPaintWorklet(root->bounds()); + root->SetRasterSourceForTesting(raster_source_with_pws); + UpdateDrawProperties(host_impl_->pending_tree()); + + did_prepare_tiles_ = false; + client_host_impl(host_impl_.get())->CommitComplete(); + EXPECT_FALSE(did_prepare_tiles_); + + ASSERT_EQ(root->GetPaintWorkletRecordMap().size(), 1u); + scoped_refptr<const PaintWorkletInput> input = + root->GetPaintWorkletRecordMap().begin()->first; + int worklet_id = input->WorkletId(); + + PaintWorkletJob painted_job(worklet_id, input, {}); + PaintRecord record; + painted_job.SetOutput(record); + + auto painted_job_vector = base::MakeRefCounted<PaintWorkletJobVector>(); + painted_job_vector->data.push_back(std::move(painted_job)); + PaintWorkletJobMap painted_job_map; + painted_job_map[worklet_id] = std::move(painted_job_vector); + + std::move(painter->TakeDoneCallback()).Run(std::move(painted_job_map)); + EXPECT_TRUE(root->GetPaintWorkletRecordMap() + .find(input) + ->second.second->EqualsForTesting(record)); + } +}; INSTANTIATE_COMMIT_TO_PENDING_TREE_TEST_P(PendingTreeLayerTreeHostImplTest); @@ -2749,8 +2814,7 @@ scroll->element_id(), false})); EXPECT_FALSE(scrollbar_controller->visibility_changed()); EXPECT_TRUE(did_request_redraw_); - EXPECT_EQ(!host_impl_->settings().trees_in_viz_in_viz_process, - did_request_commit_); + EXPECT_TRUE(did_request_commit_); } } @@ -2916,108 +2980,37 @@ } TEST_P(PendingTreeLayerTreeHostImplTest, OneScrollForFirstScrollDelay) { - CreateHostImpl(DefaultSettings(), CreateLayerTreeFrameSink()); - SetupRootLayer<SolidColorLayerImpl>(host_impl_->active_tree(), - gfx::Size(10, 10)); - UpdateDrawProperties(host_impl_->active_tree()); - EXPECT_EQ(first_scroll_observed, 0); + SetupScrollDelayTest(); - // LatencyInfo for the first scroll. - ui::LatencyInfo latency_info; - latency_info.set_trace_id(5); - latency_info.AddLatencyNumber( - ui::INPUT_EVENT_LATENCY_SCROLL_UPDATE_ORIGINAL_COMPONENT); - std::unique_ptr<SwapPromise> swap_promise( - new LatencyInfoSwapPromise(latency_info)); - host_impl_->active_tree()->QueuePinnedSwapPromise(std::move(swap_promise)); + QueueLatencyInfoSwapPromise( + ui::INPUT_EVENT_LATENCY_SCROLL_UPDATE_ORIGINAL_COMPONENT, 5); - host_impl_->SetFullViewportDamage(); - host_impl_->SetNeedsRedraw(/*animation_only=*/false, - /*skip_if_inside_draw=*/false); - DrawFrame(); - - constexpr uint32_t frame_token_1 = 1; - viz::FrameTimingDetails mock_details; - mock_details.presentation_feedback = ExampleFeedback(); - // When the LayerTreeHostImpl receives presentation feedback, the callback - // will be fired. - host_impl_->DidPresentCompositorFrame(frame_token_1, mock_details); + DrawAndPresentFrame(1); EXPECT_EQ(first_scroll_observed, 1); } TEST_P(PendingTreeLayerTreeHostImplTest, OtherInputsForFirstScrollDelay) { - CreateHostImpl(DefaultSettings(), CreateLayerTreeFrameSink()); - SetupRootLayer<SolidColorLayerImpl>(host_impl_->active_tree(), - gfx::Size(10, 10)); - UpdateDrawProperties(host_impl_->active_tree()); - EXPECT_EQ(first_scroll_observed, 0); + SetupScrollDelayTest(); - // LatencyInfo for the first input, which is not scroll. - ui::LatencyInfo latency_info; - latency_info.set_trace_id(5); - latency_info.AddLatencyNumber(ui::INPUT_EVENT_LATENCY_BEGIN_RWH_COMPONENT); - std::unique_ptr<SwapPromise> swap_promise( - new LatencyInfoSwapPromise(latency_info)); - host_impl_->active_tree()->QueuePinnedSwapPromise(std::move(swap_promise)); + QueueLatencyInfoSwapPromise(ui::INPUT_EVENT_LATENCY_BEGIN_RWH_COMPONENT, 5); - host_impl_->SetFullViewportDamage(); - host_impl_->SetNeedsRedraw(/*animation_only=*/false, - /*skip_if_inside_draw=*/false); - DrawFrame(); - - constexpr uint32_t frame_token_1 = 1; - viz::FrameTimingDetails mock_details; - mock_details.presentation_feedback = ExampleFeedback(); - // When the LayerTreeHostImpl receives presentation feedback, the callback - // will be fired. - host_impl_->DidPresentCompositorFrame(frame_token_1, mock_details); + DrawAndPresentFrame(1); EXPECT_EQ(first_scroll_observed, 0); } TEST_P(PendingTreeLayerTreeHostImplTest, MultipleScrollsForFirstScrollDelay) { - CreateHostImpl(DefaultSettings(), CreateLayerTreeFrameSink()); - SetupRootLayer<SolidColorLayerImpl>(host_impl_->active_tree(), - gfx::Size(10, 10)); - UpdateDrawProperties(host_impl_->active_tree()); - EXPECT_EQ(first_scroll_observed, 0); + SetupScrollDelayTest(); - // LatencyInfo for the first scroll. - ui::LatencyInfo latency_info; - latency_info.set_trace_id(5); - latency_info.AddLatencyNumber( - ui::INPUT_EVENT_LATENCY_SCROLL_UPDATE_ORIGINAL_COMPONENT); - std::unique_ptr<SwapPromise> swap_promise( - new LatencyInfoSwapPromise(latency_info)); - host_impl_->active_tree()->QueuePinnedSwapPromise(std::move(swap_promise)); - DrawFrame(); - constexpr uint32_t frame_token_1 = 1; - viz::FrameTimingDetails mock_details; - mock_details.presentation_feedback = ExampleFeedback(); - // When the LayerTreeHostImpl receives presentation feedback, the callback - // will be fired. - host_impl_->DidPresentCompositorFrame(frame_token_1, mock_details); + QueueLatencyInfoSwapPromise( + ui::INPUT_EVENT_LATENCY_SCROLL_UPDATE_ORIGINAL_COMPONENT, 5); + DrawAndPresentFrame(1, /*damage=*/false); EXPECT_EQ(first_scroll_observed, 1); - // LatencyInfo for the second scroll. - ui::LatencyInfo latency_info2; - latency_info2.set_trace_id(6); - latency_info2.AddLatencyNumber( - ui::INPUT_EVENT_LATENCY_SCROLL_UPDATE_ORIGINAL_COMPONENT); - std::unique_ptr<SwapPromise> swap_promise2( - new LatencyInfoSwapPromise(latency_info2)); - host_impl_->active_tree()->QueuePinnedSwapPromise(std::move(swap_promise2)); - host_impl_->SetFullViewportDamage(); - host_impl_->SetNeedsRedraw(/*animation_only=*/false, - /*skip_if_inside_draw=*/false); - DrawFrame(); - constexpr uint32_t frame_token_2 = 2; - viz::FrameTimingDetails mock_details2; - mock_details2.presentation_feedback = ExampleFeedback(); - // When the LayerTreeHostImpl receives presentation feedback, the callback - // will be fired. - host_impl_->DidPresentCompositorFrame(frame_token_2, mock_details2); + QueueLatencyInfoSwapPromise( + ui::INPUT_EVENT_LATENCY_SCROLL_UPDATE_ORIGINAL_COMPONENT, 6); + DrawAndPresentFrame(2, /*damage=*/true); EXPECT_EQ(first_scroll_observed, 1); } @@ -3175,10 +3168,7 @@ // Create the pending tree. // TODO(496580137): Move this to ClientLayerTreeHostImpl specific tests. - if (!host_impl_->settings().trees_in_viz_in_viz_process) { - static_cast<ClientLayerTreeHostImpl*>(host_impl_.get()) - ->BeginCommit(0, BeginMainFrameTraceId{1}); - } + client_host_impl(host_impl_.get())->BeginCommit(0, BeginMainFrameTraceId{1}); LayerTreeImpl* pending_tree = host_impl_->pending_tree(); auto* root = SetupRootLayer<FakePictureLayerImpl>(pending_tree, layer_size, raster_source); @@ -3188,9 +3178,7 @@ // CompleteCommit which should perform a PrepareTiles, adding tilings for the // root layer, each one having a raster task. // TODO(496580137): Move this to ClientLayerTreeHostImpl specific tests. - if (!host_impl_->settings().trees_in_viz_in_viz_process) { - static_cast<ClientLayerTreeHostImpl*>(host_impl_.get())->CommitComplete(); - } + client_host_impl(host_impl_.get())->CommitComplete(); EXPECT_EQ(root->num_tilings(), 1U); const PictureLayerTiling* tiling = root->tilings()->tiling_at(0); EXPECT_EQ(tiling->AllTilesForTesting().size(), 9U); @@ -3213,10 +3201,7 @@ // Invalidate content on impl-side and ensure that the correct tiles are // invalidated on the pending tree. // TODO(496580137): Move this to ClientLayerTreeHostImpl specific tests. - if (!host_impl_->settings().trees_in_viz_in_viz_process) { - static_cast<ClientLayerTreeHostImpl*>(host_impl_.get()) - ->InvalidateContentOnImplSide(); - } + client_host_impl(host_impl_.get())->InvalidateContentOnImplSide(); pending_tree = host_impl_->pending_tree(); root = static_cast<FakePictureLayerImpl*>(pending_tree->root_layer()); for (auto* tile : root->tilings()->tiling_at(0)->AllTilesForTesting()) { @@ -3378,18 +3363,13 @@ TEST_P(PendingTreeLayerTreeHostImplTest, CommitWithNoPaintWorkletLayerPainter) { ASSERT_FALSE(host_impl_->GetPaintWorkletLayerPainterForTesting()); // TODO(496580137): Move this to ClientLayerTreeHostImpl specific tests. - if (!host_impl_->settings().trees_in_viz_in_viz_process) { - static_cast<ClientLayerTreeHostImpl*>(host_impl_.get()) - ->CreatePendingTree(); - } + client_host_impl(host_impl_.get())->CreatePendingTree(); // When there is no PaintWorkletLayerPainter registered, commits should finish // immediately and move onto preparing tiles. ASSERT_FALSE(did_prepare_tiles_); // TODO(496580137): Move this to ClientLayerTreeHostImpl specific tests. - if (!host_impl_->settings().trees_in_viz_in_viz_process) { - static_cast<ClientLayerTreeHostImpl*>(host_impl_.get())->CommitComplete(); - } + client_host_impl(host_impl_.get())->CommitComplete(); EXPECT_TRUE(did_prepare_tiles_); } @@ -3397,74 +3377,18 @@ host_impl_->SetPaintWorkletLayerPainter( std::make_unique<TestPaintWorkletLayerPainter>()); // TODO(496580137): Move this to ClientLayerTreeHostImpl specific tests. - if (!host_impl_->settings().trees_in_viz_in_viz_process) { - static_cast<ClientLayerTreeHostImpl*>(host_impl_.get()) - ->CreatePendingTree(); - } + client_host_impl(host_impl_.get())->CreatePendingTree(); // When there are no PaintWorklets in the committed display lists, commits // should finish immediately and move onto preparing tiles. ASSERT_FALSE(did_prepare_tiles_); // TODO(496580137): Move this to ClientLayerTreeHostImpl specific tests. - if (!host_impl_->settings().trees_in_viz_in_viz_process) { - static_cast<ClientLayerTreeHostImpl*>(host_impl_.get())->CommitComplete(); - } + client_host_impl(host_impl_.get())->CommitComplete(); EXPECT_TRUE(did_prepare_tiles_); } TEST_P(PendingTreeLayerTreeHostImplTest, CommitWithDirtyPaintWorklets) { - auto painter_owned = std::make_unique<TestPaintWorkletLayerPainter>(); - TestPaintWorkletLayerPainter* painter = painter_owned.get(); - host_impl_->SetPaintWorkletLayerPainter(std::move(painter_owned)); - - // Setup the pending tree with a PictureLayerImpl that will contain - // PaintWorklets. - // TODO(496580137): Move this to ClientLayerTreeHostImpl specific tests. - if (!host_impl_->settings().trees_in_viz_in_viz_process) { - static_cast<ClientLayerTreeHostImpl*>(host_impl_.get()) - ->CreatePendingTree(); - } - auto* root = SetupRootLayer<PictureLayerImpl>(host_impl_->pending_tree(), - gfx::Size(100, 100)); - root->SetNeedsPushProperties(); - - // Add a PaintWorkletInput to the PictureLayerImpl. - scoped_refptr<RasterSource> raster_source_with_pws = - FakeRasterSource::CreateFilledWithPaintWorklet(root->bounds()); - root->SetRasterSourceForTesting(raster_source_with_pws); - UpdateDrawProperties(host_impl_->pending_tree()); - - // Since we have dirty PaintWorklets, committing will not cause tile - // preparation to happen. Instead, it will be delayed until the callback - // passed to the PaintWorkletLayerPainter is called. - did_prepare_tiles_ = false; - // TODO(496580137): Move this to ClientLayerTreeHostImpl specific tests. - if (!host_impl_->settings().trees_in_viz_in_viz_process) { - static_cast<ClientLayerTreeHostImpl*>(host_impl_.get())->CommitComplete(); - } - EXPECT_FALSE(did_prepare_tiles_); - - // Set up a result to have been 'painted'. - ASSERT_EQ(root->GetPaintWorkletRecordMap().size(), 1u); - scoped_refptr<const PaintWorkletInput> input = - root->GetPaintWorkletRecordMap().begin()->first; - int worklet_id = input->WorkletId(); - - PaintWorkletJob painted_job(worklet_id, input, {}); - PaintRecord record; - painted_job.SetOutput(record); - - auto painted_job_vector = base::MakeRefCounted<PaintWorkletJobVector>(); - painted_job_vector->data.push_back(std::move(painted_job)); - PaintWorkletJobMap painted_job_map; - painted_job_map[worklet_id] = std::move(painted_job_vector); - - // Finally, 'paint' the content. This should unlock tile preparation and - // update the PictureLayerImpl's map. - std::move(painter->TakeDoneCallback()).Run(std::move(painted_job_map)); - EXPECT_TRUE(root->GetPaintWorkletRecordMap() - .find(input) - ->second.second->EqualsForTesting(record)); + RunPaintWorkletCommitTest(); EXPECT_TRUE(did_prepare_tiles_); } @@ -3473,10 +3397,7 @@ std::make_unique<TestPaintWorkletLayerPainter>()); // TODO(496580137): Move this to ClientLayerTreeHostImpl specific tests. - if (!host_impl_->settings().trees_in_viz_in_viz_process) { - static_cast<ClientLayerTreeHostImpl*>(host_impl_.get()) - ->CreatePendingTree(); - } + client_host_impl(host_impl_.get())->CreatePendingTree(); auto* root = SetupRootLayer<PictureLayerImpl>(host_impl_->pending_tree(), gfx::Size(100, 100)); root->SetNeedsPushProperties(); @@ -3497,9 +3418,7 @@ // prepare tiles. ASSERT_FALSE(did_prepare_tiles_); // TODO(496580137): Move this to ClientLayerTreeHostImpl specific tests. - if (!host_impl_->settings().trees_in_viz_in_viz_process) { - static_cast<ClientLayerTreeHostImpl*>(host_impl_.get())->CommitComplete(); - } + client_host_impl(host_impl_.get())->CommitComplete(); EXPECT_TRUE(did_prepare_tiles_); } @@ -3582,10 +3501,7 @@ } // This call creates a new pending tree. // TODO(496580137): Move this to ClientLayerTreeHostImpl specific tests. - if (!host_impl_->settings().trees_in_viz_in_viz_process) { - static_cast<ClientLayerTreeHostImpl*>(host_impl_.get()) - ->InvalidateContentOnImplSide(); - } + client_host_impl(host_impl_.get())->InvalidateContentOnImplSide(); if (!CommitsToActiveTree()) { // If a pending tree exists, we expect to see that there are metrics // associated with the raster frame associated with it. @@ -3800,61 +3716,7 @@ TEST_P(ForceActivateAfterPaintWorkletPaintLayerTreeHostImplTest, ForceActivationAfterPaintWorkletsFinishPainting) { - auto painter_owned = std::make_unique<TestPaintWorkletLayerPainter>(); - TestPaintWorkletLayerPainter* painter = painter_owned.get(); - host_impl_->SetPaintWorkletLayerPainter(std::move(painter_owned)); - - // Setup the pending tree with a PictureLayerImpl that will contain - // PaintWorklets. - // TODO(496580137): Move this to ClientLayerTreeHostImpl specific tests. - if (!host_impl_->settings().trees_in_viz_in_viz_process) { - static_cast<ClientLayerTreeHostImpl*>(host_impl_.get()) - ->CreatePendingTree(); - } - auto* root = SetupRootLayer<PictureLayerImpl>(host_impl_->pending_tree(), - gfx::Size(100, 100)); - root->SetNeedsPushProperties(); - - // Add a PaintWorkletInput to the PictureLayerImpl. - scoped_refptr<RasterSource> raster_source_with_pws = - FakeRasterSource::CreateFilledWithPaintWorklet(root->bounds()); - root->SetRasterSourceForTesting(raster_source_with_pws); - - UpdateDrawProperties(host_impl_->pending_tree()); - - // Since we have dirty PaintWorklets, committing will not cause tile - // preparation to happen. Instead, it will be delayed until the callback - // passed to the PaintWorkletLayerPainter is called. - did_prepare_tiles_ = false; - // TODO(496580137): Move this to ClientLayerTreeHostImpl specific tests. - if (!host_impl_->settings().trees_in_viz_in_viz_process) { - static_cast<ClientLayerTreeHostImpl*>(host_impl_.get())->CommitComplete(); - } - EXPECT_FALSE(did_prepare_tiles_); - - // Set up a result to have been 'painted'. - ASSERT_EQ(root->GetPaintWorkletRecordMap().size(), 1u); - scoped_refptr<const PaintWorkletInput> input = - root->GetPaintWorkletRecordMap().begin()->first; - int worklet_id = input->WorkletId(); - - PaintWorkletJob painted_job(worklet_id, input, {}); - PaintRecord record; - painted_job.SetOutput(record); - - auto painted_job_vector = base::MakeRefCounted<PaintWorkletJobVector>(); - painted_job_vector->data.push_back(std::move(painted_job)); - PaintWorkletJobMap painted_job_map; - painted_job_map[worklet_id] = std::move(painted_job_vector); - - // Finally, 'paint' the content. The test class causes a forced activation - // during NotifyPaintWorkletStateChange. The PictureLayerImpl should still be - // updated, but since the tree was force activated there should be no tile - // preparation. - std::move(painter->TakeDoneCallback()).Run(std::move(painted_job_map)); - EXPECT_TRUE(root->GetPaintWorkletRecordMap() - .find(input) - ->second.second->EqualsForTesting(record)); + RunPaintWorkletCommitTest(); EXPECT_FALSE(did_prepare_tiles_); }
diff --git a/cc/trees/layer_tree_host.cc b/cc/trees/layer_tree_host.cc index c78a99aa..419dc37b 100644 --- a/cc/trees/layer_tree_host.cc +++ b/cc/trees/layer_tree_host.cc
@@ -128,14 +128,14 @@ } std::unique_ptr<LayerTreeHost> LayerTreeHost::CreateSingleThreaded( - LayerTreeHostSingleThreadClient* single_thread_client, + LayerTreeHostSingleThreadDelegate* single_thread_delegate, InitParams params) { DCHECK(params.settings); scoped_refptr<base::SingleThreadTaskRunner> main_task_runner = params.main_task_runner; auto layer_tree_host = base::WrapUnique( new LayerTreeHost(std::move(params), CompositorMode::SINGLE_THREADED)); - layer_tree_host->InitializeSingleThreaded(single_thread_client, + layer_tree_host->InitializeSingleThreaded(single_thread_delegate, std::move(main_task_runner)); return layer_tree_host; } @@ -223,10 +223,10 @@ } void LayerTreeHost::InitializeSingleThreaded( - LayerTreeHostSingleThreadClient* single_thread_client, + LayerTreeHostSingleThreadDelegate* single_thread_delegate, scoped_refptr<base::SingleThreadTaskRunner> main_task_runner) { task_runner_provider_ = TaskRunnerProvider::Create(main_task_runner, nullptr); - InitializeProxy(SingleThreadProxy::Create(this, single_thread_client, + InitializeProxy(SingleThreadProxy::Create(this, single_thread_delegate, task_runner_provider_.get())); } @@ -1050,8 +1050,7 @@ if (auto callback = request->TakeFinishedCallback()) { view_transition_callbacks_[request->sequence_id()] = std::move(callback); } - if (request->maybe_cross_frame_sink() && - features::ShouldAckCOREarlyForViewTransition()) { + if (request->maybe_cross_frame_sink()) { auto request_token = request->token(); auto request_type = request->type(); if (request_type == viz::CompositorFrameTransitionDirective::Type::kSave &&
diff --git a/cc/trees/layer_tree_host.h b/cc/trees/layer_tree_host.h index f908e899..a2db563 100644 --- a/cc/trees/layer_tree_host.h +++ b/cc/trees/layer_tree_host.h
@@ -77,7 +77,7 @@ class LayerTreeHostImpl; class ClientLayerTreeHostImpl; class LayerTreeHostImplDelegate; -class LayerTreeHostSingleThreadClient; +class LayerTreeHostSingleThreadDelegate; class LayerTreeMutator; class MutatorEvents; class MutatorHost; @@ -182,7 +182,7 @@ // blocked, so moving work to another thread and the overhead it adds are not // required. static std::unique_ptr<LayerTreeHost> CreateSingleThreaded( - LayerTreeHostSingleThreadClient* single_thread_client, + LayerTreeHostSingleThreadDelegate* single_thread_delegate, InitParams params); LayerTreeHost(const LayerTreeHost&) = delete; @@ -993,7 +993,7 @@ scoped_refptr<base::SingleThreadTaskRunner> main_task_runner, scoped_refptr<base::SingleThreadTaskRunner> impl_task_runner); void InitializeSingleThreaded( - LayerTreeHostSingleThreadClient* single_thread_client, + LayerTreeHostSingleThreadDelegate* single_thread_delegate, scoped_refptr<base::SingleThreadTaskRunner> main_task_runner); void InitializeForTesting( std::unique_ptr<TaskRunnerProvider> task_runner_provider,
diff --git a/cc/trees/layer_tree_host_impl.cc b/cc/trees/layer_tree_host_impl.cc index fd5814e0..e8ba91f 100644 --- a/cc/trees/layer_tree_host_impl.cc +++ b/cc/trees/layer_tree_host_impl.cc
@@ -2302,8 +2302,7 @@ // Uses InnerViewportScrollNode's container bounds in since it represents // visual scroll layers. metadata.visible_viewport_size = - gfx::ScaleToFlooredSize(InnerViewportScrollNode()->container_bounds, - 1 / metadata.device_scale_factor); + InnerViewportScrollNode()->container_bounds; } metadata.root_background_color = active_tree_->background_color(); @@ -2959,8 +2958,7 @@ metadata.transition_directives.push_back(request->ConstructDirective( view_transition_element_map, display_color_spaces, request->delay_layer_tree_view_deletion())); - if (features::ShouldAckCOREarlyForViewTransition() && - request->delay_layer_tree_view_deletion()) { + if (request->delay_layer_tree_view_deletion()) { OnCompositorFrameTransitionDirectiveProcessed(request->sequence_id()); if (request->type() == ViewTransitionRequest::Type::kAnimateRenderer) { @@ -2979,16 +2977,13 @@ if (active_tree_->HasViewTransitionRequests()) { active_tree_->set_needs_update_draw_properties(); } - if (features::ShouldAckCOREarlyForViewTransition()) { - for (auto& request : active_tree_->view_transition_requests()) { - if (request->delay_layer_tree_view_deletion()) { - OnCompositorFrameTransitionDirectiveProcessed(request->sequence_id()); - if (request->type() == - ViewTransitionRequest::Type::kAnimateRenderer) { - has_view_transition_with_animate = true; - } - delay_layer_tree_view_deletion = true; + for (auto& request : active_tree_->view_transition_requests()) { + if (request->delay_layer_tree_view_deletion()) { + OnCompositorFrameTransitionDirectiveProcessed(request->sequence_id()); + if (request->type() == ViewTransitionRequest::Type::kAnimateRenderer) { + has_view_transition_with_animate = true; } + delay_layer_tree_view_deletion = true; } } } @@ -3003,8 +2998,7 @@ // // Use the cached values because `TakeViewTransitionRequests()` clears the // requests from the tree. - if (features::ShouldAckCOREarlyForViewTransition() && - delay_layer_tree_view_deletion && has_view_transition_with_animate) { + if (delay_layer_tree_view_deletion && has_view_transition_with_animate) { frame_deadline = 240; } metadata.deadline =
diff --git a/cc/trees/layer_tree_host_single_thread_client.h b/cc/trees/layer_tree_host_single_thread_delegate.h similarity index 84% rename from cc/trees/layer_tree_host_single_thread_client.h rename to cc/trees/layer_tree_host_single_thread_delegate.h index 666f1e7..0d491723 100644 --- a/cc/trees/layer_tree_host_single_thread_client.h +++ b/cc/trees/layer_tree_host_single_thread_delegate.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 CC_TREES_LAYER_TREE_HOST_SINGLE_THREAD_CLIENT_H_ -#define CC_TREES_LAYER_TREE_HOST_SINGLE_THREAD_CLIENT_H_ +#ifndef CC_TREES_LAYER_TREE_HOST_SINGLE_THREAD_DELEGATE_H_ +#define CC_TREES_LAYER_TREE_HOST_SINGLE_THREAD_DELEGATE_H_ #include "base/containers/flat_set.h" #include "base/time/time.h" @@ -11,7 +11,7 @@ namespace cc { -class LayerTreeHostSingleThreadClient { +class LayerTreeHostSingleThreadDelegate { public: // Tells single-threaded web tests that a new commit needs to be scheduled. virtual void ScheduleAnimationForWebTests() {} @@ -39,9 +39,9 @@ const base::flat_set<viz::FrameSinkId>& ids) {} protected: - virtual ~LayerTreeHostSingleThreadClient() {} + virtual ~LayerTreeHostSingleThreadDelegate() = default; }; } // namespace cc -#endif // CC_TREES_LAYER_TREE_HOST_SINGLE_THREAD_CLIENT_H_ +#endif // CC_TREES_LAYER_TREE_HOST_SINGLE_THREAD_DELEGATE_H_
diff --git a/cc/trees/layer_tree_settings.h b/cc/trees/layer_tree_settings.h index 26eb8bd..e9a58a1 100644 --- a/cc/trees/layer_tree_settings.h +++ b/cc/trees/layer_tree_settings.h
@@ -203,7 +203,7 @@ // Enables ThrottleDecider which produces a list of FrameSinkIds that are // candidates for throttling. - // LayerTreeHostSingleThreadClient::FrameSinksToThrottleUpdated() will be + // LayerTreeHostSingleThreadDelegate::FrameSinksToThrottleUpdated() will be // called with candidates. bool enable_compositing_based_throttling = false;
diff --git a/cc/trees/single_thread_proxy.cc b/cc/trees/single_thread_proxy.cc index 4a31212..de77681 100644 --- a/cc/trees/single_thread_proxy.cc +++ b/cc/trees/single_thread_proxy.cc
@@ -32,7 +32,7 @@ #include "cc/trees/latency_info_swap_promise.h" #include "cc/trees/layer_tree_frame_sink.h" #include "cc/trees/layer_tree_host.h" -#include "cc/trees/layer_tree_host_single_thread_client.h" +#include "cc/trees/layer_tree_host_single_thread_delegate.h" #include "cc/trees/layer_tree_impl.h" #include "cc/trees/mutator_host.h" #include "cc/trees/paint_holding_reason.h" @@ -47,17 +47,18 @@ std::unique_ptr<Proxy> SingleThreadProxy::Create( LayerTreeHost* layer_tree_host, - LayerTreeHostSingleThreadClient* client, + LayerTreeHostSingleThreadDelegate* delegate, TaskRunnerProvider* task_runner_provider) { return base::WrapUnique( - new SingleThreadProxy(layer_tree_host, client, task_runner_provider)); + new SingleThreadProxy(layer_tree_host, delegate, task_runner_provider)); } -SingleThreadProxy::SingleThreadProxy(LayerTreeHost* layer_tree_host, - LayerTreeHostSingleThreadClient* client, - TaskRunnerProvider* task_runner_provider) +SingleThreadProxy::SingleThreadProxy( + LayerTreeHost* layer_tree_host, + LayerTreeHostSingleThreadDelegate* delegate, + TaskRunnerProvider* task_runner_provider) : layer_tree_host_(layer_tree_host), - single_thread_client_(client), + single_thread_delegate_(delegate), task_runner_provider_(task_runner_provider), next_frame_is_newly_committed_frame_(false), #if DCHECK_IS_ON() @@ -530,7 +531,7 @@ task_runner_provider_->IsImplThread()); TRACE_EVENT0("cc", "SingleThreadProxy::SetNeedsOneBeginImplFrameOnImplThread"); - single_thread_client_->ScheduleAnimationForWebTests(); + single_thread_delegate_->ScheduleAnimationForWebTests(); if (scheduler_on_impl_thread_) scheduler_on_impl_thread_->SetNeedsOneBeginImplFrame(); needs_impl_frame_ = true; @@ -547,7 +548,7 @@ void SingleThreadProxy::SetNeedsCommitOnImplThread(bool urgent) { DCHECK(!task_runner_provider_->HasImplThread() || task_runner_provider_->IsImplThread()); - single_thread_client_->ScheduleAnimationForWebTests(); + single_thread_delegate_->ScheduleAnimationForWebTests(); if (scheduler_on_impl_thread_) scheduler_on_impl_thread_->SetNeedsBeginMainFrame(urgent); commit_requested_ = true; @@ -613,7 +614,7 @@ // This must happen before we notify the scheduler as it may try to recreate // the output surface if already in BEGIN_IMPL_FRAME_STATE_IDLE. layer_tree_host_->DidLoseLayerTreeFrameSink(); - single_thread_client_->DidLoseLayerTreeFrameSink(); + single_thread_delegate_->DidLoseLayerTreeFrameSink(); } if (scheduler_on_impl_thread_) scheduler_on_impl_thread_->DidLoseLayerTreeFrameSink(); @@ -749,7 +750,7 @@ const base::flat_set<viz::FrameSinkId>& ids) { DCHECK(!task_runner_provider_->HasImplThread() || task_runner_provider_->IsImplThread()); - single_thread_client_->FrameSinksToThrottleUpdated(ids); + single_thread_delegate_->FrameSinksToThrottleUpdated(ids); } void SingleThreadProxy::RequestBeginMainFrameNotExpected(bool new_state) { @@ -908,7 +909,7 @@ scheduler_on_impl_thread_->DidSubmitCompositorFrame( submit_info.value()); } - single_thread_client_->DidSubmitCompositorFrame(); + single_thread_delegate_->DidSubmitCompositorFrame(); } } host_impl_->DidDrawAllLayers(*frame); @@ -1042,7 +1043,7 @@ void SingleThreadProxy::FrameIntervalUpdated(base::TimeDelta interval) { DebugScopedSetImplThread impl(task_runner_provider_); - single_thread_client_->FrameIntervalUpdated(interval); + single_thread_delegate_->FrameIntervalUpdated(interval); } void SingleThreadProxy::OnBeginImplFrameDeadline() {
diff --git a/cc/trees/single_thread_proxy.h b/cc/trees/single_thread_proxy.h index 8072aae..afc5646 100644 --- a/cc/trees/single_thread_proxy.h +++ b/cc/trees/single_thread_proxy.h
@@ -34,7 +34,7 @@ class ClientLayerTreeHostImpl; class LayerTreeHost; -class LayerTreeHostSingleThreadClient; +class LayerTreeHostSingleThreadDelegate; class RenderFrameMetadataObserver; class CC_EXPORT SingleThreadProxy : public Proxy, @@ -43,7 +43,7 @@ public: static std::unique_ptr<Proxy> Create( LayerTreeHost* layer_tree_host, - LayerTreeHostSingleThreadClient* client, + LayerTreeHostSingleThreadDelegate* delegate, TaskRunnerProvider* task_runner_provider); SingleThreadProxy(const SingleThreadProxy&) = delete; ~SingleThreadProxy() override; @@ -190,7 +190,7 @@ protected: SingleThreadProxy(LayerTreeHost* layer_tree_host, - LayerTreeHostSingleThreadClient* client, + LayerTreeHostSingleThreadDelegate* delegate, TaskRunnerProvider* task_runner_provider); private: @@ -213,7 +213,7 @@ // Accessed on main thread only. raw_ptr<LayerTreeHost> layer_tree_host_; - raw_ptr<LayerTreeHostSingleThreadClient> single_thread_client_; + raw_ptr<LayerTreeHostSingleThreadDelegate> single_thread_delegate_; raw_ptr<TaskRunnerProvider> task_runner_provider_;
diff --git a/cc/trees/tree_synchronizer_unittest.cc b/cc/trees/tree_synchronizer_unittest.cc index a5c845f..dd7e6ac 100644 --- a/cc/trees/tree_synchronizer_unittest.cc +++ b/cc/trees/tree_synchronizer_unittest.cc
@@ -26,7 +26,7 @@ #include "cc/test/fake_layer_tree_host.h" #include "cc/test/fake_rendering_stats_instrumentation.h" #include "cc/test/layer_test_common.h" -#include "cc/test/stub_layer_tree_host_single_thread_client.h" +#include "cc/test/stub_layer_tree_host_single_thread_delegate.h" #include "cc/test/test_task_graph_runner.h" #include "cc/trees/compositor_commit_data.h" #include "cc/trees/effect_node.h" @@ -126,7 +126,7 @@ host_ = FakeLayerTreeHost::Create(&delegate_, &task_graph_runner_, animation_host_.get(), settings); host_->InitializeSingleThreaded( - &single_thread_client_, + &single_thread_delegate_, base::SingleThreadTaskRunner::GetCurrentDefault()); host_->host_impl()->CreatePendingTree(); } @@ -169,7 +169,7 @@ const FakeLayerTreeHost* const_host() const { return host_.get(); } FakeLayerTreeHostDelegate delegate_; - StubLayerTreeHostSingleThreadClient single_thread_client_; + StubLayerTreeHostSingleThreadDelegate single_thread_delegate_; TestTaskGraphRunner task_graph_runner_; std::unique_ptr<AnimationHost> animation_host_; std::unique_ptr<FakeLayerTreeHost> host_;
diff --git a/chrome/VERSION b/chrome/VERSION index caec2162..919e8a2 100644 --- a/chrome/VERSION +++ b/chrome/VERSION
@@ -1,4 +1,4 @@ MAJOR=150 MINOR=0 -BUILD=7832 +BUILD=7833 PATCH=0
diff --git a/chrome/android/BUILD.gn b/chrome/android/BUILD.gn index 0412814..8025498 100644 --- a/chrome/android/BUILD.gn +++ b/chrome/android/BUILD.gn
@@ -573,7 +573,10 @@ "//chrome/browser/ui/android/overlay_panel:java", "//chrome/browser/ui/android/page_info:java", "//chrome/browser/ui/android/pdf:java", + "//chrome/browser/ui/android/pdf:pdf_info_java", + "//chrome/browser/ui/android/pdf:pdf_page_java", "//chrome/browser/ui/android/pdf:pdf_provider_java", + "//chrome/browser/ui/android/pdf:pdf_utils_java", "//chrome/browser/ui/android/preloading:java", "//chrome/browser/ui/android/quickactionsearchwidget:java", "//chrome/browser/ui/android/searchactivityutils:java", @@ -1236,7 +1239,7 @@ "//chrome/browser/ui/android/multiwindow:junit", "//chrome/browser/ui/android/night_mode:junit", "//chrome/browser/ui/android/omnibox:junit", - "//chrome/browser/ui/android/pdf:junit", + "//chrome/browser/ui/android/pdf/internal:junit", "//chrome/browser/ui/android/searchactivityutils:junit", "//chrome/browser/ui/android/settings_promo_card:junit", "//chrome/browser/ui/android/signin:junit", @@ -1529,7 +1532,7 @@ "//chrome/browser/translate/android:javatests", "//chrome/browser/ui/android/device_lock:javatests", "//chrome/browser/ui/android/multiwindow:javatests", - "//chrome/browser/ui/android/pdf:javatests", + "//chrome/browser/ui/android/pdf/internal:javatests", "//chrome/browser/ui/android/quickactionsearchwidget:javatests", "//chrome/browser/ui/android/signin:javatests", "//components/browser_ui/share/android:javatests", @@ -3240,6 +3243,10 @@ "//components/ukm/android:jni_headers", "//components/webauthn/android:jni_headers", ] + + if (enable_extensions_core) { + public_deps += [ "//chrome/browser/ui/android/extensions:jni_headers" ] + } } chrome_common_shared_library("libchromefortest") {
diff --git a/chrome/android/chrome_java_sources.gni b/chrome/android/chrome_java_sources.gni index 9198b39..fb31826 100644 --- a/chrome/android/chrome_java_sources.gni +++ b/chrome/android/chrome_java_sources.gni
@@ -340,6 +340,7 @@ "java/src/org/chromium/chrome/browser/compositor/layouts/eventfilter/OverlayPanelEventFilter.java", "java/src/org/chromium/chrome/browser/compositor/layouts/phone/AnimationFreezeChecker.java", "java/src/org/chromium/chrome/browser/compositor/layouts/phone/AnimationInterruptor.java", + "java/src/org/chromium/chrome/browser/compositor/layouts/phone/NewBackgroundTabAnimationData.java", "java/src/org/chromium/chrome/browser/compositor/layouts/phone/NewBackgroundTabAnimationHostView.java", "java/src/org/chromium/chrome/browser/compositor/layouts/phone/NewBackgroundTabFakeTabSwitcherButton.java", "java/src/org/chromium/chrome/browser/compositor/layouts/phone/NewForegroundTabAnimationHostView.java", @@ -828,6 +829,7 @@ "java/src/org/chromium/chrome/browser/multiwindow/MultiWindowModeStateDispatcherImpl.java", "java/src/org/chromium/chrome/browser/multiwindow/MultiWindowUtils.java", "java/src/org/chromium/chrome/browser/multiwindow/TabReparentingDelegate.java", + "java/src/org/chromium/chrome/browser/multiwindow/TabbedCrashRecoveryDelegate.java", "java/src/org/chromium/chrome/browser/multiwindow/WindowOcclusionMetrics.java", "java/src/org/chromium/chrome/browser/multiwindow/WindowOcclusionTracker.java", "java/src/org/chromium/chrome/browser/multiwindow/WindowZOrderTracker.java",
diff --git a/chrome/android/expectations/trichrome_chrome_64_32_bundle__chrome.AndroidManifest.expected b/chrome/android/expectations/trichrome_chrome_64_32_bundle__chrome.AndroidManifest.expected index 2b82e6b2..2200d6b8 100644 --- a/chrome/android/expectations/trichrome_chrome_64_32_bundle__chrome.AndroidManifest.expected +++ b/chrome/android/expectations/trichrome_chrome_64_32_bundle__chrome.AndroidManifest.expected
@@ -11,15 +11,10 @@ <dist:module dist:onDemand="false"> # DIFF-ANCHOR: 2805007f <dist:fusing dist:include="true"/> </dist:module> # DIFF-ANCHOR: 2805007f - <queries> # DIFF-ANCHOR: 46eb35d4 - <intent> # DIFF-ANCHOR: dd112d1c - <action android:name="android.intent.action.VIEW"/> - <category android:name="android.intent.category.BROWSABLE"/> - <data android:scheme="https"/> - </intent> # DIFF-ANCHOR: dd112d1c + <queries> # DIFF-ANCHOR: a4b06bd5 <package android:name="com.google.android.aicore"/> <package android:name="com.google.android.gms.policy_cast_dynamite"/> - </queries> # DIFF-ANCHOR: 46eb35d4 + </queries> # DIFF-ANCHOR: a4b06bd5 <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/> <uses-permission android:name="android.permission.FOREGROUND_SERVICE"/> <uses-permission android:name="android.permission.INTERNET"/> @@ -883,11 +878,6 @@ android:exported="false"> </receiver> # DIFF-ANCHOR: 2553238b <service android:name="androidx.browser.customtabs.PostMessageService"/> - <service # DIFF-ANCHOR: 5d82e3ff - android:name="androidx.pdf.service.PdfDocumentServiceImpl" - android:exported="false" - android:isolatedProcess="true"> - </service> # DIFF-ANCHOR: 5d82e3ff <service # DIFF-ANCHOR: 293d124f android:name="org.chromium.chrome.browser.actor.ActorForegroundService" android:exported="false"
diff --git a/chrome/android/expectations/trichrome_chrome_64_32_bundle__on_demand.AndroidManifest.expected b/chrome/android/expectations/trichrome_chrome_64_32_bundle__on_demand.AndroidManifest.expected index 4c67df8..5f95396 100644 --- a/chrome/android/expectations/trichrome_chrome_64_32_bundle__on_demand.AndroidManifest.expected +++ b/chrome/android/expectations/trichrome_chrome_64_32_bundle__on_demand.AndroidManifest.expected
@@ -11,7 +11,20 @@ <dist:module dist:onDemand="false"> # DIFF-ANCHOR: 2805007f <dist:fusing dist:include="true"/> </dist:module> # DIFF-ANCHOR: 2805007f + <queries> # DIFF-ANCHOR: ac70d2a8 + <intent> # DIFF-ANCHOR: dd112d1c + <action android:name="android.intent.action.VIEW"/> + <category android:name="android.intent.category.BROWSABLE"/> + <data android:scheme="https"/> + </intent> # DIFF-ANCHOR: dd112d1c + </queries> # DIFF-ANCHOR: ac70d2a8 <uses-sdk android:minSdkVersion="29" android:targetSdkVersion="36"/> <uses-split android:name="chrome"/> - <application/> + <application> + <service # DIFF-ANCHOR: 5d82e3ff + android:name="androidx.pdf.service.PdfDocumentServiceImpl" + android:exported="false" + android:isolatedProcess="true"> + </service> # DIFF-ANCHOR: 5d82e3ff + </application> </manifest>
diff --git a/chrome/android/features/keyboard_accessory/javatests/src/org/chromium/chrome/browser/keyboard_accessory/bar_component/KeyboardAccessoryViewTest.java b/chrome/android/features/keyboard_accessory/javatests/src/org/chromium/chrome/browser/keyboard_accessory/bar_component/KeyboardAccessoryViewTest.java index 2d0c4ea2..f880f86 100644 --- a/chrome/android/features/keyboard_accessory/javatests/src/org/chromium/chrome/browser/keyboard_accessory/bar_component/KeyboardAccessoryViewTest.java +++ b/chrome/android/features/keyboard_accessory/javatests/src/org/chromium/chrome/browser/keyboard_accessory/bar_component/KeyboardAccessoryViewTest.java
@@ -112,6 +112,7 @@ import org.chromium.ui.AsyncViewProvider; import org.chromium.ui.AsyncViewStub; import org.chromium.ui.ViewProvider; +import org.chromium.ui.base.DeviceFormFactor; import org.chromium.ui.modelutil.LazyConstructionPropertyMcp; import org.chromium.ui.modelutil.PropertyModel; import org.chromium.ui.test.util.ViewUtils; @@ -345,6 +346,7 @@ @Test @MediumTest + @DisableIf.Device(DeviceFormFactor.DESKTOP_FREEFORM) // crbug.com/444482498 public void testAddsClickableAutofillSuggestions() { AtomicReference<Boolean> clickRecorded = new AtomicReference<>(); ThreadUtils.runOnUiThreadBlocking( @@ -363,6 +365,7 @@ @Test @MediumTest + @DisableIf.Device(DeviceFormFactor.DESKTOP_FREEFORM) // crbug.com/444482498 public void testGroupedSuggestionsAreClickable() { AtomicReference<Boolean> clickRecorded1 = new AtomicReference<>(); AtomicReference<Boolean> clickRecorded2 = new AtomicReference<>(); @@ -389,6 +392,7 @@ @Test @MediumTest + @DisableIf.Device(DeviceFormFactor.DESKTOP_FREEFORM) // crbug.com/444482498 public void testAddsLongClickableAutofillSuggestions() { AtomicReference<Boolean> clickRecorded = new AtomicReference<>(); ThreadUtils.runOnUiThreadBlocking( @@ -513,6 +517,7 @@ @Test @MediumTest + @DisableIf.Device(DeviceFormFactor.DESKTOP_FREEFORM) // crbug.com/444482498 public void testDismissesCardInfoRetrievalBubbleOnFilling() throws InterruptedException { String descriptionText = "You can autofill this card because your PayPay account is linked to Google"; @@ -556,6 +561,7 @@ @Test @MediumTest + @DisableIf.Device(DeviceFormFactor.DESKTOP_FREEFORM) // crbug.com/444482498 public void testDismissesHomeAndWorkdEducationBubbleOnFilling() throws InterruptedException { AutofillBarItem itemWithIph = new AutofillBarItem( @@ -595,6 +601,7 @@ @Test @MediumTest + @DisableIf.Device(DeviceFormFactor.DESKTOP_FREEFORM) // crbug.com/444482498 public void testDismissesPasswordEducationBubbleOnFilling() throws InterruptedException { AutofillBarItem itemWithIph = new AutofillBarItem( @@ -634,6 +641,7 @@ @Test @MediumTest + @DisableIf.Device(DeviceFormFactor.DESKTOP_FREEFORM) // crbug.com/444482498 public void testDismissesAddressEducationBubbleOnFilling() throws InterruptedException { AutofillBarItem itemWithIph = new AutofillBarItem( @@ -671,6 +679,7 @@ @Test @MediumTest + @DisableIf.Device(DeviceFormFactor.DESKTOP_FREEFORM) // crbug.com/444482498 public void testDismissesPaymentEducationBubbleOnFilling() throws InterruptedException { AutofillBarItem itemWithIph = new AutofillBarItem(
diff --git a/chrome/android/features/keyboard_accessory/javatests/src/org/chromium/chrome/browser/keyboard_accessory/sheet_tabs/CreditCardAccessorySheetViewTest.java b/chrome/android/features/keyboard_accessory/javatests/src/org/chromium/chrome/browser/keyboard_accessory/sheet_tabs/CreditCardAccessorySheetViewTest.java index 3e2b36f..8dab7cb 100644 --- a/chrome/android/features/keyboard_accessory/javatests/src/org/chromium/chrome/browser/keyboard_accessory/sheet_tabs/CreditCardAccessorySheetViewTest.java +++ b/chrome/android/features/keyboard_accessory/javatests/src/org/chromium/chrome/browser/keyboard_accessory/sheet_tabs/CreditCardAccessorySheetViewTest.java
@@ -199,7 +199,7 @@ assertThat(getChipText(R.id.exp_year), is("2034")); assertThat(getChipText(R.id.cardholder), is("Kirby Puckett")); // Verify that the icon is correctly set. - ImageView iconImageView = (ImageView) mView.get().getChildAt(0).findViewById(R.id.icon); + ImageView iconImageView = mView.get().getChildAt(0).findViewById(R.id.icon); Drawable expectedIcon = mActivityTestRule.getActivity().getDrawable(R.drawable.visa_metadata_card); assertTrue(getBitmap(expectedIcon).sameAs(getBitmap(iconImageView.getDrawable()))); @@ -250,7 +250,7 @@ assertThat(getChipText(R.id.cardholder), is("Kirby Puckett")); // Verify that the icon is set to the cached image returned by // AutofillImageFetcher.getImageIfAvailable. - ImageView iconImageView = (ImageView) mView.get().getChildAt(0).findViewById(R.id.icon); + ImageView iconImageView = mView.get().getChildAt(0).findViewById(R.id.icon); assertTrue( ((BitmapDrawable) iconImageView.getDrawable()) .getBitmap() @@ -296,7 +296,7 @@ assertThat(getChipText(R.id.exp_year), is("2034")); assertThat(getChipText(R.id.cardholder), is("Kirby Puckett")); // Verify that the icon is set to the drawable corresponding to `visaCC`. - ImageView iconImageView = (ImageView) mView.get().getChildAt(0).findViewById(R.id.icon); + ImageView iconImageView = mView.get().getChildAt(0).findViewById(R.id.icon); Drawable expectedIcon = mActivityTestRule.getActivity().getDrawable(R.drawable.visa_metadata_card); assertTrue(getBitmap(expectedIcon).sameAs(getBitmap(iconImageView.getDrawable()))); @@ -468,7 +468,7 @@ assertThat(detailsText.getText(), is(kDetailsText)); // Verify that the icon is correctly set. - ImageView iconImageView = (ImageView) mView.get().getChildAt(0).findViewById(R.id.icon); + ImageView iconImageView = mView.get().getChildAt(0).findViewById(R.id.icon); Drawable expectedIcon = mActivityTestRule .getActivity()
diff --git a/chrome/android/features/tab_ui/java/res/layout/tab_grid_dialog_layout.xml b/chrome/android/features/tab_ui/java/res/layout/tab_grid_dialog_layout.xml index bd70b9c..7f0621c579 100644 --- a/chrome/android/features/tab_ui/java/res/layout/tab_grid_dialog_layout.xml +++ b/chrome/android/features/tab_ui/java/res/layout/tab_grid_dialog_layout.xml
@@ -17,8 +17,6 @@ android:layout_width="match_parent" android:layout_height="match_parent" android:id="@+id/dialog_container_view" - android:focusable="true" - android:focusableInTouchMode="true" android:clipToOutline="true" android:background="@drawable/tab_grid_dialog_background" android:contentDescription="@string/accessibility_tab_grid_dialog">
diff --git a/chrome/android/features/tab_ui/java/res/layout/tab_grid_dialog_toolbar.xml b/chrome/android/features/tab_ui/java/res/layout/tab_grid_dialog_toolbar.xml index 2b8c2dbf..f9b4adc20 100644 --- a/chrome/android/features/tab_ui/java/res/layout/tab_grid_dialog_toolbar.xml +++ b/chrome/android/features/tab_ui/java/res/layout/tab_grid_dialog_toolbar.xml
@@ -20,9 +20,7 @@ android:paddingLeft="@dimen/tab_grid_dialog_app_bar_padding" android:paddingRight="@dimen/tab_grid_dialog_app_bar_padding" android:orientation="horizontal" - android:gravity="center_vertical" - android:focusable="true" - android:focusableInTouchMode="true"> + android:gravity="center_vertical"> <include layout="@layout/toolbar_back_button"/> <FrameLayout android:id="@+id/tab_group_color_icon_container"
diff --git a/chrome/android/features/tab_ui/java/res/layout/tab_grid_dialog_toolbar_two_row.xml b/chrome/android/features/tab_ui/java/res/layout/tab_grid_dialog_toolbar_two_row.xml index 5e7c501..4b57e7a 100644 --- a/chrome/android/features/tab_ui/java/res/layout/tab_grid_dialog_toolbar_two_row.xml +++ b/chrome/android/features/tab_ui/java/res/layout/tab_grid_dialog_toolbar_two_row.xml
@@ -21,17 +21,13 @@ android:paddingLeft="@dimen/tab_grid_dialog_app_bar_padding" android:paddingRight="@dimen/tab_grid_dialog_app_bar_padding" android:orientation="vertical" - android:gravity="center" - android:focusable="true" - android:focusableInTouchMode="true"> + android:gravity="center"> <LinearLayout android:id="@+id/first_row" android:layout_width="match_parent" android:layout_height="@dimen/min_touch_target_size" android:layout_gravity="center_vertical" - android:orientation="horizontal" - android:focusable="true" - android:focusableInTouchMode="true"> + android:orientation="horizontal"> <include layout="@layout/toolbar_back_button"/> <Space android:layout_height="match_parent" @@ -75,9 +71,7 @@ android:layout_width="match_parent" android:layout_height="@dimen/min_touch_target_size" android:layout_gravity="center_vertical" - android:orientation="horizontal" - android:focusable="true" - android:focusableInTouchMode="true"> + android:orientation="horizontal"> <FrameLayout android:id="@+id/tab_group_color_icon_container" android:layout_height="@dimen/min_touch_target_size"
diff --git a/chrome/android/features/tab_ui/java/res/layout/toolbar_back_button.xml b/chrome/android/features/tab_ui/java/res/layout/toolbar_back_button.xml index 25c3f86..daad6c9 100644 --- a/chrome/android/features/tab_ui/java/res/layout/toolbar_back_button.xml +++ b/chrome/android/features/tab_ui/java/res/layout/toolbar_back_button.xml
@@ -9,6 +9,8 @@ xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/toolbar_back_button" + android:focusable="true" + android:focusableInTouchMode="true" style="@style/BottomToolbarButton" android:src="@drawable/ic_arrow_back_24dp" app:tint="@color/default_icon_color_tint_list"
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridDialogView.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridDialogView.java index 114e6440..c096858 100644 --- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridDialogView.java +++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridDialogView.java
@@ -95,11 +95,13 @@ private final Context mContext; private final float mTabGridCardPadding; private final Map<View, Integer> mAccessibilityImportanceMap = new HashMap<>(); + private final Map<ViewGroup, Integer> mDescendantFocusabilityMap = new HashMap<>(); private FrameLayout mAnimationClip; private FrameLayout mToolbarContainer; private FrameLayout mRecyclerViewContainer; private RoundedCornerImageView mBackgroundFrame; private View mAnimationCardView; + private @Nullable View mBackButton; private @Nullable View mItemView; private View mUngroupBar; private TextView mUngroupBarTextView; @@ -296,7 +298,9 @@ @Override public void onAnimationEnd(Animator animation) { mCurrentDialogAnimator = null; - mDialogContainerView.requestFocus(); + if (mBackButton != null) { + mBackButton.requestFocus(); + } mDialogContainerView.sendAccessibilityEvent( AccessibilityEvent.TYPE_VIEW_FOCUSED); // TODO(crbug.com/40138401): Move clear/restore accessibility importance @@ -317,7 +321,9 @@ public void onAnimationEnd(Animator animation) { setVisibility(View.GONE); mCurrentDialogAnimator = null; - mDialogContainerView.clearFocus(); + if (mBackButton != null) { + mBackButton.clearFocus(); + } restoreBackgroundViewAccessibilityImportance(); notifyVisibilityListenerOnHide(); } @@ -373,6 +379,7 @@ private void clearBackgroundViewAccessibilityImportance() { assert mAccessibilityImportanceMap.isEmpty(); + assert mDescendantFocusabilityMap.isEmpty(); ViewGroup parent = (ViewGroup) getParent(); ViewGroup grandparent = parent == null ? null : (ViewGroup) parent.getParent(); // Fix for crbug.com/424865865, this can happen if the animation is forced to finish before @@ -389,6 +396,11 @@ } mAccessibilityImportanceMap.put(view, view.getImportantForAccessibility()); view.setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS); + + if (view instanceof ViewGroup viewGroup) { + mDescendantFocusabilityMap.put(viewGroup, viewGroup.getDescendantFocusability()); + viewGroup.setDescendantFocusability(ViewGroup.FOCUS_BLOCK_DESCENDANTS); + } } } @@ -401,15 +413,25 @@ for (View view : mAccessibilityImportanceMap.keySet()) { view.setImportantForAccessibility(mAccessibilityImportanceMap.get(view)); } + for (ViewGroup viewGroup : mDescendantFocusabilityMap.keySet()) { + viewGroup.setDescendantFocusability(mDescendantFocusabilityMap.get(viewGroup)); + } } else { for (int i = 0; i < grandparent.getChildCount(); i++) { View view = grandparent.getChildAt(i); if (view == parent) break; setImportance(view, mAccessibilityImportanceMap.get(view)); + if (view instanceof ViewGroup viewGroup) { + Integer focusability = mDescendantFocusabilityMap.get(viewGroup); + if (focusability != null) { + viewGroup.setDescendantFocusability(focusability); + } + } } } mAccessibilityImportanceMap.clear(); + mDescendantFocusabilityMap.clear(); } private static void setImportance(View view, @Nullable Integer importance) { @@ -1038,6 +1060,9 @@ mRecyclerViewContainer.removeAllViews(); mRecyclerViewContainer.addView(recyclerView); + mBackButton = toolbarView.findViewById(R.id.toolbar_back_button); + assumeNonNull(mBackButton); + recyclerView.setVisibility(View.VISIBLE); } @@ -1198,6 +1223,10 @@ }); } + @Nullable View getBackButtonForTesting() { + return mBackButton; + } + @Nullable Animator getCurrentDialogAnimatorForTesting() { return mCurrentDialogAnimator; }
diff --git a/chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/TabGridDialogTest.java b/chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/TabGridDialogTest.java index e8e7f94..b33b87a 100644 --- a/chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/TabGridDialogTest.java +++ b/chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/TabGridDialogTest.java
@@ -2472,7 +2472,7 @@ } private RecyclerView getRecyclerView(ChromeTabbedActivity cta) { - ViewGroup group = (ViewGroup) cta.findViewById(getTabSwitcherAncestorId(cta)); + ViewGroup group = cta.findViewById(getTabSwitcherAncestorId(cta)); return group.findViewById(R.id.tab_list_recycler_view); }
diff --git a/chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/TabGridDialogViewTest.java b/chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/TabGridDialogViewTest.java index b39f3d4..a3f7dbb 100644 --- a/chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/TabGridDialogViewTest.java +++ b/chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/TabGridDialogViewTest.java
@@ -119,6 +119,12 @@ sActivity .getResources() .getDimensionPixelSize(R.dimen.tab_grid_dialog_max_margin); + + View toolbarView = + LayoutInflater.from(sActivity) + .inflate(R.layout.tab_grid_dialog_toolbar, null); + View recyclerView = new View(sActivity); + mTabGridDialogView.resetDialog(toolbarView, recyclerView); }); } @@ -195,7 +201,8 @@ @SmallTest @UiThreadTest public void testResetDialog() { - View toolbarView = new View(sActivity); + View toolbarView = + LayoutInflater.from(sActivity).inflate(R.layout.tab_grid_dialog_toolbar, null); View recyclerView = new View(sActivity); recyclerView.setVisibility(View.GONE); @@ -364,7 +371,8 @@ () -> { mTabGridDialogView.setupDialogAnimation(mSourceView); parentViewReference.set((ViewGroup) mTabGridDialogContainer.getParent()); - assertFalse(mTabGridDialogContainer.isFocused()); + assertFalse(mTabGridDialogContainer.isFocusable()); + assertFalse(mTabGridDialogView.getBackButtonForTesting().isFocused()); }); ViewGroup parent = parentViewReference.get(); @@ -394,7 +402,7 @@ ThreadUtils.runOnUiThreadBlocking( () -> { assertEquals(0f, mBackgroundFrameView.getAlpha(), 0.0); - assertTrue(mTabGridDialogContainer.isFocused()); + assertTrue(mTabGridDialogView.getBackButtonForTesting().isFocused()); }); // Hide the dialog with zoom-in animation. @@ -433,7 +441,8 @@ assertEquals(0f, mTabGridDialogContainer.getTranslationY(), 0.0); assertEquals(1f, mTabGridDialogContainer.getScaleX(), 0.0); assertEquals(1f, mTabGridDialogContainer.getScaleY(), 0.0); - assertFalse(mTabGridDialogContainer.isFocused()); + assertFalse(mTabGridDialogContainer.isFocusable()); + assertFalse(mTabGridDialogView.getBackButtonForTesting().isFocused()); }); } @@ -450,7 +459,8 @@ ThreadUtils.runOnUiThreadBlocking( () -> { mTabGridDialogView.setupDialogAnimation(mSourceView); - assertFalse(mTabGridDialogContainer.isFocused()); + assertFalse(mTabGridDialogContainer.isFocusable()); + assertFalse(mTabGridDialogView.getBackButtonForTesting().isFocused()); }); // Show the dialog. ThreadUtils.runOnUiThreadBlocking(() -> mTabGridDialogView.showDialog()); @@ -464,7 +474,7 @@ () -> { assertEquals(0f, mAnimationCardView.getAlpha(), 0.0); assertEquals(0f, mBackgroundFrameView.getAlpha(), 0.0); - assertTrue(mTabGridDialogContainer.isFocused()); + assertTrue(mTabGridDialogView.getBackButtonForTesting().isFocused()); }); // Hide the dialog with basic fade-out animation. @@ -490,7 +500,8 @@ assertEquals(View.GONE, mTabGridDialogView.getVisibility()); assertEquals(0f, mAnimationCardView.getAlpha(), 0.0); assertEquals(0f, mBackgroundFrameView.getAlpha(), 0.0); - assertFalse(mTabGridDialogContainer.isFocused()); + assertFalse(mTabGridDialogContainer.isFocusable()); + assertFalse(mTabGridDialogView.getBackButtonForTesting().isFocused()); }); } @@ -504,7 +515,8 @@ // Initially alpha of animation related views should be 0. assertEquals(0f, mAnimationCardView.getAlpha(), 0.0); assertEquals(0f, mBackgroundFrameView.getAlpha(), 0.0); - assertFalse(mTabGridDialogContainer.isFocused()); + assertFalse(mTabGridDialogContainer.isFocusable()); + assertFalse(mTabGridDialogView.getBackButtonForTesting().isFocused()); }); // Show the dialog with basic fade-in animation. @@ -525,7 +537,7 @@ () -> { assertEquals(0f, mAnimationCardView.getAlpha(), 0.0); assertEquals(0f, mBackgroundFrameView.getAlpha(), 0.0); - assertTrue(mTabGridDialogContainer.isFocused()); + assertTrue(mTabGridDialogView.getBackButtonForTesting().isFocused()); }); // Hide the dialog with basic fade-out animation. @@ -553,7 +565,8 @@ assertEquals(View.GONE, mTabGridDialogView.getVisibility()); assertEquals(0f, mAnimationCardView.getAlpha(), 0.0); assertEquals(0f, mBackgroundFrameView.getAlpha(), 0.0); - assertFalse(mTabGridDialogContainer.isFocused()); + assertFalse(mTabGridDialogContainer.isFocusable()); + assertFalse(mTabGridDialogView.getBackButtonForTesting().isFocused()); }); } @@ -629,7 +642,7 @@ } }; textView.setId(R.id.title); - mTabGridDialogView.addView(textView); + mTabGridDialogView.addView(textView, 0); }); long time = SystemClock.uptimeMillis();
diff --git a/chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/FeedSurfaceCoordinator.java b/chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/FeedSurfaceCoordinator.java index 2c4e5d9..bd75e52 100644 --- a/chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/FeedSurfaceCoordinator.java +++ b/chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/FeedSurfaceCoordinator.java
@@ -1193,7 +1193,7 @@ mHeaderView.setVisibility(View.GONE); } else { mHeaderView.setVisibility(View.VISIBLE); - TextView titleView = (TextView) mHeaderView.findViewById(R.id.header_title); + TextView titleView = mHeaderView.findViewById(R.id.header_title); if (titleView != null) { titleView.setText(headerText); }
diff --git a/chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/FeedSurfaceMediator.java b/chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/FeedSurfaceMediator.java index 338d47c..9f980423 100644 --- a/chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/FeedSurfaceMediator.java +++ b/chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/FeedSurfaceMediator.java
@@ -176,8 +176,6 @@ /** Internal implementation of Stream.StreamsMediator. */ @VisibleForTesting public class StreamsMediatorImpl implements Stream.StreamsMediator { - // TODO(crbug.com/407797637): Removes the logic responsible for switching between the - // 'Following' and 'For You' streams. @Override public void refreshStream() { mCoordinator.nonSwipeRefresh();
diff --git a/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/FeedV2NewTabPageTest.java b/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/FeedV2NewTabPageTest.java index e141251..7852064d 100644 --- a/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/FeedV2NewTabPageTest.java +++ b/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/FeedV2NewTabPageTest.java
@@ -354,7 +354,7 @@ } RecyclerView getRecyclerView() { - return (RecyclerView) getRootView().findViewById(R.id.feed_stream_recycler_view); + return getRootView().findViewById(R.id.feed_stream_recycler_view); } private View getRootView() {
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ChromeTabbedActivity.java b/chrome/android/java/src/org/chromium/chrome/browser/ChromeTabbedActivity.java index edae5a1..40c5e52 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/ChromeTabbedActivity.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/ChromeTabbedActivity.java
@@ -1117,6 +1117,10 @@ this, getModalDialogManagerSupplier().get()); + Callback<Boolean> glicClickHandler = + (preventClose) -> + ((TabbedRootUiCoordinator) mRootUiCoordinator).toggleGlic(preventClose); + mLayoutManager = new LayoutManagerChromeTablet( compositorViewHolder, @@ -1143,9 +1147,7 @@ mXrSceneCoreSessionManagerSupplier.get(), mBackPressManager, getSnackbarManager(), - /* glicClickHandler= */ () -> - ((TabbedRootUiCoordinator) mRootUiCoordinator) - .toggleGlic(false), + glicClickHandler, GlicKeyedServiceFactory.getForProfile(mTabModelProfileSupplier.get())); mLayoutStateProviderSupplier.set(mLayoutManager); }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/DEPS b/chrome/android/java/src/org/chromium/chrome/browser/DEPS index 3040146..14c68d5 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/DEPS +++ b/chrome/android/java/src/org/chromium/chrome/browser/DEPS
@@ -116,9 +116,6 @@ "MultiInstanceOrchestratorImpl\.java": [ "+chrome/android/java/src/org/chromium/chrome/browser/ChromeTabbedActivity.java" ], - "TabReparentingDelegate\.java": [ - "+chrome/android/java/src/org/chromium/chrome/browser/ChromeTabbedActivity.java" - ], "MultiWindowUtils\.java": [ "+chrome/android/java/src/org/chromium/chrome/browser/ChromeTabbedActivity.java" ], @@ -128,6 +125,12 @@ "TabModelImpl\.java": [ "+chrome/android/java/src/org/chromium/chrome/browser/ChromeTabbedActivity.java" ], + "TabReparentingDelegate\.java": [ + "+chrome/android/java/src/org/chromium/chrome/browser/ChromeTabbedActivity.java" + ], + "TabbedCrashRecoveryDelegate\.java": [ + "+chrome/android/java/src/org/chromium/chrome/browser/ChromeTabbedActivity.java" + ], "ChromeAsyncTabLauncher\.java": [ "+chrome/android/java/src/org/chromium/chrome/browser/ChromeTabbedActivity.java" ],
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/IntentHandler.java b/chrome/android/java/src/org/chromium/chrome/browser/IntentHandler.java index ed34938..3950460 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/IntentHandler.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/IntentHandler.java
@@ -324,6 +324,14 @@ public static final String EXTRA_CCT_EARLY_NAV = "org.chromium.chrome.browser.cct_early_nav"; /** + * Intent extra indicating that the tab is being reparented from another activity. When this is + * true, the initial navigation in the new activity should be skipped to preserve the existing + * state and ongoing navigations of the reparented tab. + */ + public static final String EXTRA_SKIP_LOAD_ON_REPARENTING = + "org.chromium.chrome.browser.skip_load_on_reparenting"; + + /** * Used to determine the {@link TipsNotificationFeatureType} that the tip is attempting to show. */ public static final String EXTRA_TIPS_NOTIFICATION_FEATURE_TYPE =
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/accessibility_annotator/first_run/AccessibilityAnnotatorFirstRunBottomSheetCoordinator.java b/chrome/android/java/src/org/chromium/chrome/browser/accessibility_annotator/first_run/AccessibilityAnnotatorFirstRunBottomSheetCoordinator.java index 13c70ef..fe78d5b2 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/accessibility_annotator/first_run/AccessibilityAnnotatorFirstRunBottomSheetCoordinator.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/accessibility_annotator/first_run/AccessibilityAnnotatorFirstRunBottomSheetCoordinator.java
@@ -52,8 +52,7 @@ .with( AccessibilityAnnotatorFirstRunBottomSheetProperties .LEARN_MORE_DESCRIPTION, - context.getString( - R.string.accessibility_annotator_info_learn_more_android)) + context.getString(R.string.accessibility_annotator_info_learn_more)) .with( AccessibilityAnnotatorFirstRunBottomSheetProperties.CARD_1_TEXT, context.getString(
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/app/tab_activity_glue/PopupCreatorImpl.java b/chrome/android/java/src/org/chromium/chrome/browser/app/tab_activity_glue/PopupCreatorImpl.java index 3fd81db3..c4f5c6f7a 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/app/tab_activity_glue/PopupCreatorImpl.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/app/tab_activity_glue/PopupCreatorImpl.java
@@ -29,6 +29,7 @@ import org.chromium.build.annotations.NullMarked; import org.chromium.build.annotations.Nullable; import org.chromium.chrome.R; +import org.chromium.chrome.browser.IntentHandler; import org.chromium.chrome.browser.app.ChromeActivity; import org.chromium.chrome.browser.app.tabwindow.TabWindowManagerSingleton; import org.chromium.chrome.browser.browserservices.intents.BrowserServicesIntentDataProvider.CustomTabsUiType; @@ -104,6 +105,7 @@ final Intent intent = createTrustedPopupIntent( windowFeatures, tab.isIncognitoBranded(), additionalIntentExtras); + intent.putExtra(IntentHandler.EXTRA_SKIP_LOAD_ON_REPARENTING, true); boolean success = getReparentingTask(tab).begin(tab.getContext(), intent, optionsBundle, null);
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/background_task_scheduler/ChromeBackgroundTaskFactory.java b/chrome/android/java/src/org/chromium/chrome/browser/background_task_scheduler/ChromeBackgroundTaskFactory.java index a4ab343..7239424 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/background_task_scheduler/ChromeBackgroundTaskFactory.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/background_task_scheduler/ChromeBackgroundTaskFactory.java
@@ -95,7 +95,6 @@ // End of Java tasks. All native tasks should be listed here. case TaskIds.QUERY_TILE_JOB_ID: case TaskIds.FEEDV2_REFRESH_JOB_ID: - case TaskIds.WEBFEEDS_REFRESH_JOB_ID: case TaskIds.UMA_UPLOAD_JOB_ID: case TaskIds.UKM_UPLOAD_JOB_ID: case TaskIds.DWA_UPLOAD_JOB_ID:
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/browsing_data/ClearBrowsingDataFragment.java b/chrome/android/java/src/org/chromium/chrome/browser/browsing_data/ClearBrowsingDataFragment.java index 85ec3b3..1dac616 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/browsing_data/ClearBrowsingDataFragment.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/browsing_data/ClearBrowsingDataFragment.java
@@ -578,7 +578,7 @@ /** Disable the "Clear" button if none of the options are selected. Otherwise, enable it. */ private void updateButtonState() { - Button clearButton = (Button) assumeNonNull(getView()).findViewById(R.id.clear_button); + Button clearButton = assumeNonNull(getView()).findViewById(R.id.clear_button); boolean isEnabled = !getSelectedOptions().isEmpty(); clearButton.setEnabled(isEnabled); }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/LayoutManagerChromeTablet.java b/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/LayoutManagerChromeTablet.java index 830b857..39d393e 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/LayoutManagerChromeTablet.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/LayoutManagerChromeTablet.java
@@ -8,6 +8,7 @@ import android.view.ViewGroup; import android.view.ViewStub; +import org.chromium.base.Callback; import org.chromium.base.Log; import org.chromium.base.supplier.MonotonicObservableSupplier; import org.chromium.base.supplier.NonNullObservableSupplier; @@ -96,7 +97,7 @@ * space modes on XR. * @param backPressManager The {@link BackPressManager} for handling back press. * @param snackbarManager The {@link SnackbarManager} used to show snackbar UI. - * @param glicClickHandler The click handler for the tab strip Glic button. + * @param glicClickHandler The {@link Callback<Boolean>} for the tab strip Glic button. */ public LayoutManagerChromeTablet( LayoutManagerHost host, @@ -123,7 +124,7 @@ @Nullable XrSceneCoreSessionManager xrSceneCoreSessionManager, BackPressManager backPressManager, SnackbarManager snackbarManager, - Runnable glicClickHandler, + Callback<Boolean> glicClickHandler, @Nullable GlicKeyedService glicKeyedService) { super( host,
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/phone/NewBackgroundTabAnimationData.java b/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/phone/NewBackgroundTabAnimationData.java new file mode 100644 index 0000000..942eacc --- /dev/null +++ b/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/phone/NewBackgroundTabAnimationData.java
@@ -0,0 +1,244 @@ +// Copyright 2026 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +package org.chromium.chrome.browser.compositor.layouts.phone; + +import static org.chromium.build.NullUtil.assumeNonNull; + +import android.content.Context; +import android.content.res.ColorStateList; +import android.content.res.Resources; +import android.graphics.Rect; +import android.view.View; + +import androidx.annotation.ColorInt; + +import org.chromium.base.supplier.NonNullObservableSupplier; +import org.chromium.build.annotations.NullMarked; +import org.chromium.build.annotations.Nullable; +import org.chromium.chrome.R; +import org.chromium.chrome.browser.compositor.layouts.phone.NewBackgroundTabAnimationHostView.AnimationType; +import org.chromium.chrome.browser.ntp_customization.NtpCustomizationUtils; +import org.chromium.chrome.browser.tab.Tab; +import org.chromium.chrome.browser.theme.ThemeUtils; +import org.chromium.chrome.browser.toolbar.ToolbarManager; +import org.chromium.chrome.browser.toolbar.ToolbarPositionController; +import org.chromium.chrome.browser.ui.bottombar.BottomBarConfigUtils; +import org.chromium.chrome.browser.ui.bottombar.BottomBarUtils; +import org.chromium.chrome.browser.ui.theme.BrandedColorScheme; + +/** + * Holds a snapshot of the tab switcher button state and associated layout colors for the duration + * of the new background tab animation. + * + * <p>This class combines the computation and storage of animation data to avoid redundant UI + * queries and ensure data consistency during the animation. + */ +@NullMarked +public class NewBackgroundTabAnimationData { + private final Context mContext; + private final View mAnimationHostView; + private final ToolbarManager mToolbarManager; + private final NonNullObservableSupplier<Float> mNtpSearchBoxTransitionPercentageSupplier; + private final boolean mIsBottomBarEnabled; + private final Rect mTabSwitcherButtonRect = new Rect(); + + // View related cache. + private @Nullable View mBottomBarTabSwitcherButton; + private @Nullable View mToolbarTabSwitcherButton; + + // Snapshotted state. + private boolean mIsBottomBarVisible; + private boolean mIsPositionOnTop; + private @Nullable View mTabSwitcherButton; + private @ColorInt int mPrimaryColor; + private @BrandedColorScheme int mBrandedColorScheme; + private @Nullable ColorStateList mIconTint; + private @AnimationType int mAnimationType; + + // Default dimensions cache. Only height is needed for manual calculation when scrolled off. + // Width and bottom bar dimensions are handled by getGlobalVisibleRect. + private final int mDefaultToolbarButtonHeight; + + /** + * Creates an instance of {@link NewBackgroundTabAnimationData}. + * + * @param animationHostView The host view for animations. + * @param toolbarManager The {@link ToolbarManager} instance. + */ + public NewBackgroundTabAnimationData(View animationHostView, ToolbarManager toolbarManager) { + mAnimationHostView = animationHostView; + mContext = animationHostView.getContext(); + mToolbarManager = toolbarManager; + mNtpSearchBoxTransitionPercentageSupplier = + toolbarManager.getNtpSearchBoxTransitionPercentageSupplier(); + mIsBottomBarEnabled = BottomBarConfigUtils.isBottomBarEnabled(mContext); + + Resources resources = mContext.getResources(); + mDefaultToolbarButtonHeight = + resources.getDimensionPixelSize(R.dimen.toolbar_height_no_shadow); + } + + /** + * Captures a snapshot of the current UI state for the duration of the animation. This includes + * computing the visibility of the bottom bar, finding the tab switcher button, calculating its + * location, and retrieving the colors. + * + * <p>This method should be called at the start of the animation to ensure that all getters + * return consistent, snapshotted data. + * + * @param expectedToolbarTop The expected Y coordinate of the toolbar top when visible. + */ + /* package */ void captureState( + @Nullable Tab tab, boolean isRegularNtp, int expectedToolbarTop) { + // Reset target button. + mTabSwitcherButton = null; + mTabSwitcherButtonRect.setEmpty(); + + if (mIsBottomBarEnabled) updateBottomBarTabSwitcherButtonCalculations(isRegularNtp); + + if (mIsBottomBarVisible) { + // When the bottom bar is enabled, NewTabAnimationLayout will force it to be visible, so + // we do not need to worry about the visibility of the bottom bar button. + mTabSwitcherButton = mBottomBarTabSwitcherButton; + } else { + // Fallback to the toolbar button if the bottom bar is not visible. + updateToolbarTabSwitcherButtonViewIfNull(); + mTabSwitcherButton = mToolbarTabSwitcherButton; + } + + assumeNonNull(mTabSwitcherButton); + mIsPositionOnTop = + !mIsBottomBarVisible && ToolbarPositionController.shouldShowToolbarOnTop(tab); + + boolean tabSwitcherButtonIsVisible = + mTabSwitcherButton.getGlobalVisibleRect(mTabSwitcherButtonRect); + + if (mIsPositionOnTop) { + // Override Y coordinates with the calculated expected position to avoid issues when + // scrolled off. + // X coordinates and width from getGlobalVisibleRect are still valid. + mTabSwitcherButtonRect.top = expectedToolbarTop; + mTabSwitcherButtonRect.bottom = expectedToolbarTop + mDefaultToolbarButtonHeight; + } + + mBrandedColorScheme = computeBrandedColorScheme(tab); + mPrimaryColor = computePrimaryColor(); + mIconTint = computeIconTint(); + + float ntpToolbarTransitionPercentage = mNtpSearchBoxTransitionPercentageSupplier.get(); + + mAnimationType = + NewBackgroundTabAnimationHostView.calculateAnimationType( + tabSwitcherButtonIsVisible, + isRegularNtp, + ntpToolbarTransitionPercentage, + mIsBottomBarVisible); + + if (isRegularNtp + && mAnimationType == AnimationType.DEFAULT + && NtpCustomizationUtils.shouldAdjustIconTintForNtp(/* isTablet= */ false) + && !mIsBottomBarVisible) { + mBrandedColorScheme = BrandedColorScheme.DARK_BRANDED_THEME; + mIconTint = ThemeUtils.getThemedToolbarIconTint(mContext, mBrandedColorScheme); + } + } + + /** Returns the calculated animation type. */ + /* package */ @AnimationType + int getAnimationType() { + return mAnimationType; + } + + /** Returns the primary color of the bar containing the button. */ + @ColorInt + /* package */ int getPrimaryColor() { + return mPrimaryColor; + } + + /** Returns the clean bounds of the tab switcher button. */ + /* package */ Rect getTabSwitcherButtonRect() { + return mTabSwitcherButtonRect; + } + + /** Returns the actual tab switcher button view. */ + /* package */ View getTabSwitcherButton() { + assert mTabSwitcherButton != null : "No tab switcher button cached/found."; + return mTabSwitcherButton; + } + + /** Returns whether the bar is positioned on top of the screen. */ + /* package */ boolean isPositionOnTop() { + return mIsPositionOnTop; + } + + /** Returns the branded color scheme for the bar containing the button. */ + @BrandedColorScheme + /* package */ int getBrandedColorScheme() { + return mBrandedColorScheme; + } + + /** Returns whether the bottom bar is visible. */ + /* package */ boolean isBottomBarVisible() { + return mIsBottomBarVisible; + } + + /** Returns the icon tint color. */ + /* package */ ColorStateList getIconTint() { + assert mIconTint != null; + return mIconTint; + } + + private void updateBottomBarTabSwitcherButtonCalculations(boolean isNtp) { + if (mBottomBarTabSwitcherButton == null) { + View bottomBar = + mAnimationHostView.findViewById( + org.chromium.chrome.browser.ui.bottombar.R.id.bottom_bar_container); + assert bottomBar != null : "Bottom bar view not found"; + mBottomBarTabSwitcherButton = bottomBar.findViewById(R.id.tab_switcher_button); + assert mBottomBarTabSwitcherButton != null + : "Tab switcher button not found in bottom bar"; + } + mIsBottomBarVisible = + mIsBottomBarEnabled && !(isNtp && BottomBarConfigUtils.shouldDisableOnNtp()); + } + + private void updateToolbarTabSwitcherButtonViewIfNull() { + if (mToolbarTabSwitcherButton == null) { + View toolbar = mAnimationHostView.findViewById(R.id.toolbar); + assert toolbar != null : "Toolbar view not found"; + mToolbarTabSwitcherButton = toolbar.findViewById(R.id.tab_switcher_button); + assert mToolbarTabSwitcherButton != null : "Tab switcher button not found in toolbar"; + } + } + + private @BrandedColorScheme int computeBrandedColorScheme(@Nullable Tab tab) { + boolean isIncognito = tab != null && tab.isIncognitoBranded(); + + if (mIsBottomBarVisible) { + // TODO(crbug.com/491513154): Update this to use the actual bottom bar color scheme + // from the provider. + return isIncognito + ? BrandedColorScheme.INCOGNITO + : BrandedColorScheme.LIGHT_BRANDED_THEME; + } + + int color = mToolbarManager.getPrimaryColor(); + return ThemeUtils.getBrandedColorScheme(mContext, color, isIncognito); + } + + private @ColorInt int computePrimaryColor() { + if (mIsBottomBarVisible) { + return BottomBarUtils.getBottomBarBackgroundColor(mContext, mBrandedColorScheme); + } + return mToolbarManager.getPrimaryColor(); + } + + private ColorStateList computeIconTint() { + if (mIsBottomBarVisible) { + return BottomBarUtils.getIconColorStateList(mContext, mBrandedColorScheme); + } + return ThemeUtils.getThemedToolbarIconTint(mContext, mBrandedColorScheme); + } +}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/phone/NewBackgroundTabAnimationDataUnitTest.java b/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/phone/NewBackgroundTabAnimationDataUnitTest.java new file mode 100644 index 0000000..66b8df92 --- /dev/null +++ b/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/phone/NewBackgroundTabAnimationDataUnitTest.java
@@ -0,0 +1,237 @@ +// Copyright 2026 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +package org.chromium.chrome.browser.compositor.layouts.phone; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; +import static org.mockito.Mockito.any; +import static org.mockito.Mockito.when; + +import android.app.Activity; +import android.content.res.Resources; +import android.graphics.Color; +import android.graphics.Rect; +import android.view.View; + +import androidx.test.ext.junit.rules.ActivityScenarioRule; + +import org.junit.After; +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.supplier.NonNullObservableSupplier; +import org.chromium.base.supplier.ObservableSuppliers; +import org.chromium.base.test.BaseRobolectricTestRunner; +import org.chromium.base.test.util.Features.DisableFeatures; +import org.chromium.base.test.util.Features.EnableFeatures; +import org.chromium.chrome.R; +import org.chromium.chrome.browser.flags.ChromeFeatureList; +import org.chromium.chrome.browser.ntp_customization.NtpCustomizationConfigManager; +import org.chromium.chrome.browser.ntp_customization.NtpCustomizationUtils.NtpBackgroundType; +import org.chromium.chrome.browser.ntp_customization.policy.NtpCustomizationPolicyManager; +import org.chromium.chrome.browser.tab.Tab; +import org.chromium.chrome.browser.toolbar.ToolbarManager; +import org.chromium.chrome.browser.toolbar.ToolbarPositionController; +import org.chromium.chrome.browser.toolbar.ToolbarPositionController.ToolbarPositionAndSource; +import org.chromium.chrome.browser.toolbar.settings.AddressBarPreference; +import org.chromium.chrome.browser.ui.theme.BrandedColorScheme; +import org.chromium.ui.base.TestActivity; + +/** Unit tests for {@link NewBackgroundTabAnimationData}. */ +@RunWith(BaseRobolectricTestRunner.class) +public class NewBackgroundTabAnimationDataUnitTest { + @Rule public final MockitoRule mMockitoRule = MockitoJUnit.rule(); + + private final NonNullObservableSupplier<Float> mNtpSearchBoxTransitionPercentageSupplier = + ObservableSuppliers.createNonNull(0f); + + @Rule + public ActivityScenarioRule<TestActivity> mActivityScenarioRule = + new ActivityScenarioRule<>(TestActivity.class); + + @Mock private ToolbarManager mToolbarManager; + @Mock private Tab mTab; + @Mock private View mToolbarTabSwitcherButton; + @Mock private View mBottomBarTabSwitcherButton; + @Mock private View mRootView; + @Mock private View mToolbarContainer; + @Mock private View mBottomBarContainer; + @Mock private NtpCustomizationConfigManager mMockConfigManager; + @Mock private NtpCustomizationPolicyManager mMockPolicyManager; + + private Activity mActivity; + private NewBackgroundTabAnimationData mData; + private int mToolbarButtonWidth; + private int mToolbarHeight; + private int mBottomBarHeight; + + @Before + public void setUp() throws Exception { + mActivityScenarioRule.getScenario().onActivity(this::onActivity); + when(mToolbarManager.getNtpSearchBoxTransitionPercentageSupplier()) + .thenReturn(mNtpSearchBoxTransitionPercentageSupplier); + + NtpCustomizationConfigManager.setInstanceForTesting(mMockConfigManager); + NtpCustomizationPolicyManager.setInstanceForTesting(mMockPolicyManager); + } + + @After + public void tearDown() { + NtpCustomizationConfigManager.setInstanceForTesting(null); + NtpCustomizationPolicyManager.setInstanceForTesting(null); + } + + private void onActivity(Activity activity) { + mActivity = activity; + when(mRootView.getContext()).thenReturn(mActivity); + when(mRootView.findViewById(R.id.toolbar)).thenReturn(mToolbarContainer); + when(mRootView.findViewById( + org.chromium.chrome.browser.ui.bottombar.R.id.bottom_bar_container)) + .thenReturn(mBottomBarContainer); + + when(mToolbarContainer.findViewById(R.id.tab_switcher_button)) + .thenReturn(mToolbarTabSwitcherButton); + when(mBottomBarContainer.findViewById(R.id.tab_switcher_button)) + .thenReturn(mBottomBarTabSwitcherButton); + + Resources res = activity.getResources(); + mToolbarButtonWidth = res.getDimensionPixelSize(R.dimen.toolbar_button_width); + mToolbarHeight = res.getDimensionPixelSize(R.dimen.toolbar_height_no_shadow); + mBottomBarHeight = res.getDimensionPixelSize(R.dimen.bottom_bar_height); + + when(mToolbarTabSwitcherButton.getGlobalVisibleRect(any(Rect.class))) + .thenAnswer( + invocation -> { + Rect r = invocation.getArgument(0); + if (ToolbarPositionController.shouldShowToolbarOnTop(mTab)) { + r.set(0, 0, mToolbarButtonWidth, mToolbarHeight); + } else { + // Simulate toolbar being at the bottom of the screen (height 2000). + r.set(0, 2000 - mToolbarHeight, mToolbarButtonWidth, 2000); + } + return true; + }); + + when(mBottomBarTabSwitcherButton.getGlobalVisibleRect(any(Rect.class))) + .thenAnswer( + invocation -> { + Rect r = invocation.getArgument(0); + r.set(0, 2000 - mBottomBarHeight, mToolbarButtonWidth, 2000); + return true; + }); + } + + @Test + @DisableFeatures(ChromeFeatureList.ANDROID_BOTTOM_BAR) + public void testCaptureState_BottomBarDisabled() { + when(mToolbarManager.getPrimaryColor()).thenReturn(Color.RED); + mData = new NewBackgroundTabAnimationData(mRootView, mToolbarManager); + mData.captureState(mTab, /* isRegularNtp= */ false, /* expectedToolbarTop= */ 100); + + assertEquals(mToolbarTabSwitcherButton, mData.getTabSwitcherButton()); + assertFalse(mData.isBottomBarVisible()); + assertTrue(mData.isPositionOnTop()); + + Rect tabSwitcherButtonRect = mData.getTabSwitcherButtonRect(); + assertEquals(mToolbarButtonWidth, tabSwitcherButtonRect.width()); + assertEquals(mToolbarHeight, tabSwitcherButtonRect.height()); + + assertEquals(100, tabSwitcherButtonRect.top); + assertEquals(100 + mToolbarHeight, tabSwitcherButtonRect.bottom); + + assertEquals(Color.RED, mData.getPrimaryColor()); + assertEquals( + NewBackgroundTabAnimationHostView.AnimationType.DEFAULT, mData.getAnimationType()); + } + + @Test + @EnableFeatures(ChromeFeatureList.ANDROID_BOTTOM_BAR + ":disable_on_ntp/false") + public void testCaptureState_BottomBarEnabled_Visible() { + mData = new NewBackgroundTabAnimationData(mRootView, mToolbarManager); + mData.captureState(mTab, /* isRegularNtp= */ false, /* expectedToolbarTop= */ 0); + + assertEquals(mBottomBarTabSwitcherButton, mData.getTabSwitcherButton()); + assertTrue(mData.isBottomBarVisible()); + assertFalse(mData.isPositionOnTop()); + + Rect tabSwitcherButtonRect = mData.getTabSwitcherButtonRect(); + assertEquals(mToolbarButtonWidth, tabSwitcherButtonRect.width()); + assertEquals(mBottomBarHeight, tabSwitcherButtonRect.height()); + + assertEquals(2000 - mBottomBarHeight, tabSwitcherButtonRect.top); + assertEquals(2000, tabSwitcherButtonRect.bottom); + + assertEquals( + NewBackgroundTabAnimationHostView.AnimationType.DEFAULT, mData.getAnimationType()); + } + + @Test + @EnableFeatures(ChromeFeatureList.ANDROID_BOTTOM_BAR + ":disable_on_ntp/true") + public void testCaptureState_BottomBarEnabled_NtpDisabled() { + mData = new NewBackgroundTabAnimationData(mRootView, mToolbarManager); + mData.captureState(mTab, /* isRegularNtp= */ true, /* expectedToolbarTop= */ 0); + + assertEquals(mToolbarTabSwitcherButton, mData.getTabSwitcherButton()); + assertFalse(mData.isBottomBarVisible()); + + Rect tabSwitcherButtonRect = mData.getTabSwitcherButtonRect(); + assertEquals(mToolbarButtonWidth, tabSwitcherButtonRect.width()); + assertEquals(mToolbarHeight, tabSwitcherButtonRect.height()); + assertEquals(0, tabSwitcherButtonRect.top); + assertEquals(mToolbarHeight, tabSwitcherButtonRect.bottom); + } + + @Test + @DisableFeatures(ChromeFeatureList.ANDROID_BOTTOM_BAR) + @EnableFeatures(ChromeFeatureList.NEW_TAB_PAGE_CUSTOMIZATION_V2) + public void testCaptureState_NtpAdjustment() { + when(mMockPolicyManager.isNtpCustomBackgroundEnabled()).thenReturn(true); + when(mMockConfigManager.getBackgroundType()).thenReturn(NtpBackgroundType.IMAGE_FROM_DISK); + + mData = new NewBackgroundTabAnimationData(mRootView, mToolbarManager); + mData.captureState(mTab, /* isRegularNtp= */ true, /* expectedToolbarTop= */ 0); + + assertEquals(BrandedColorScheme.DARK_BRANDED_THEME, mData.getBrandedColorScheme()); + assertEquals(mToolbarTabSwitcherButton, mData.getTabSwitcherButton()); + + Rect tabSwitcherButtonRect = mData.getTabSwitcherButtonRect(); + assertEquals(mToolbarButtonWidth, tabSwitcherButtonRect.width()); + assertEquals(mToolbarHeight, tabSwitcherButtonRect.height()); + assertEquals(0, tabSwitcherButtonRect.top); + assertEquals(mToolbarHeight, tabSwitcherButtonRect.bottom); + } + + @Test + // Disable extra logs to avoid Native / LocalStatePrefs read in + // AddressBarPreference#setToolbarPositionAndSource. + @DisableFeatures({ + ChromeFeatureList.ANDROID_BOTTOM_BAR, + ChromeFeatureList.CROSS_DEVICE_PREF_TRACKER_EXTRA_LOGS + }) + public void testCaptureState_ToolbarPositionBottom() { + ToolbarPositionController.resetCachedToolbarConfigurationForTesting(); + AddressBarPreference.setToolbarPositionAndSource(ToolbarPositionAndSource.BOTTOM_SETTINGS); + + mData = new NewBackgroundTabAnimationData(mRootView, mToolbarManager); + mData.captureState(mTab, /* isRegularNtp= */ false, /* expectedToolbarTop= */ 100); + + assertEquals(mToolbarTabSwitcherButton, mData.getTabSwitcherButton()); + assertFalse(mData.isBottomBarVisible()); + assertFalse(mData.isPositionOnTop()); + + Rect tabSwitcherButtonRect = mData.getTabSwitcherButtonRect(); + assertEquals(2000 - mToolbarHeight, tabSwitcherButtonRect.top); + assertEquals(2000, tabSwitcherButtonRect.bottom); + + ToolbarPositionController.resetCachedToolbarConfigurationForTesting(); + AddressBarPreference.setToolbarPositionAndSource(ToolbarPositionAndSource.TOP_SETTINGS); + } +}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/phone/NewBackgroundTabAnimationHostView.java b/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/phone/NewBackgroundTabAnimationHostView.java index 8a98c1a..40cdeee 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/phone/NewBackgroundTabAnimationHostView.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/phone/NewBackgroundTabAnimationHostView.java
@@ -23,7 +23,6 @@ import org.chromium.build.annotations.NullMarked; import org.chromium.chrome.R; -import org.chromium.chrome.browser.toolbar.top.ToggleTabStackButton; import org.chromium.chrome.browser.ui.theme.BrandedColorScheme; import org.chromium.components.browser_ui.styles.ChromeColors; import org.chromium.components.browser_ui.styles.SemanticColorUtils; @@ -67,7 +66,7 @@ private NewBackgroundTabFakeTabSwitcherButton mFakeTabSwitcherButton; private ImageView mLinkIcon; private @AnimationType int mAnimationType; - private boolean mIsTopToolbar; + private boolean mIsTargetOnTop; private int mStatusBarHeight; private int mXOffset; @@ -85,12 +84,14 @@ * @param isNtp True if the current tab is the regular Ntp. * @param ntpToolbarTransitionPercentage To know the current transition percentage of the ntp * search box. + * @param bottomBarVisible True if the bottom bar is visible. */ public static @AnimationType int calculateAnimationType( boolean tabSwitcherButtonIsVisible, boolean isNtp, - float ntpToolbarTransitionPercentage) { - if (tabSwitcherButtonIsVisible || !isNtp) { + float ntpToolbarTransitionPercentage, + boolean bottomBarVisible) { + if (tabSwitcherButtonIsVisible || !isNtp || bottomBarVisible) { return AnimationType.DEFAULT; } else { return ntpToolbarTransitionPercentage == 1f @@ -156,7 +157,8 @@ /** * Prepares the animation. * - * @param tabSwitcherButton The real Tab Switcher Button. + * @param shouldShowNotificationIcon Whether to show the notification icon on the tab switcher + * button. * @param tabSwitcherRect The {@link Rect} of the tab switcher button. * @param isIncognito True if the current tab is an incognito tab. * @param isTopToolbar True if current tab has a top toolbar. @@ -164,30 +166,29 @@ * @param animationType The {@link AnimationType}. * @param brandedColorScheme The {@link BrandedColorScheme} for the toolbar. * @param tabCount The tab count to display. - * @param toolbarHeight Current height of the toolbar in the screen (absolute y-coordinate in - * the screen). * @param statusBarHeight The status bar height to calculate the y-offset within the screen. * @param xOffset Offset for cases where the screen can't draw from x = 0. */ /* package */ void setUpAnimation( - ToggleTabStackButton tabSwitcherButton, + boolean shouldShowNotificationIcon, Rect tabSwitcherRect, boolean isIncognito, - boolean isTopToolbar, + boolean isTargetOnTop, @ColorInt int backgroundColor, @AnimationType int animationType, @BrandedColorScheme int brandedColorScheme, int tabCount, - int toolbarHeight, int statusBarHeight, - int xOffset) { + int xOffset, + ColorStateList iconTint) { mAnimationType = animationType; mStatusBarHeight = statusBarHeight; mXOffset = xOffset; - mIsTopToolbar = isTopToolbar; + mIsTargetOnTop = isTargetOnTop; mFakeTabSwitcherButton.setTabCount(tabCount, isIncognito); - mFakeTabSwitcherButton.setBrandedColorScheme(brandedColorScheme); + mFakeTabSwitcherButton.setBrandedColorScheme(brandedColorScheme, iconTint); + mFakeTabSwitcherButton.setSize(tabSwitcherRect.width(), tabSwitcherRect.height()); Context context = getContext(); if (ColorUtils.inNightMode(context)) { @@ -203,12 +204,11 @@ } int horizontalMargin = tabSwitcherRect.left - xOffset; - int verticalMargin = toolbarHeight - statusBarHeight; + int verticalMargin = tabSwitcherRect.top - statusBarHeight; if (mAnimationType == AnimationType.DEFAULT) { mFakeTabSwitcherButton.setButtonColor(backgroundColor); - mFakeTabSwitcherButton.setNotificationIconStatus( - tabSwitcherButton.shouldShowNotificationIcon()); + mFakeTabSwitcherButton.setNotificationIconStatus(shouldShowNotificationIcon); } else { mFakeTabSwitcherButton.setUpNtpAnimation(/* incrementCount= */ true); if (mAnimationType == AnimationType.NTP_FULL_SCROLL) { @@ -231,7 +231,7 @@ */ private ObjectAnimator getPathArcAnimator( float originX, float originY, float finalX, float finalY) { - boolean isClockwise = mIsTopToolbar ? (originX >= finalX) : (originX <= finalX); + boolean isClockwise = mIsTargetOnTop ? (originX >= finalX) : (originX <= finalX); ObjectAnimator animator = ViewCurvedMotionAnimatorFactory.build(
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/phone/NewBackgroundTabAnimationHostViewUnitTest.java b/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/phone/NewBackgroundTabAnimationHostViewUnitTest.java index d860c4b..6778977 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/phone/NewBackgroundTabAnimationHostViewUnitTest.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/phone/NewBackgroundTabAnimationHostViewUnitTest.java
@@ -6,7 +6,6 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; -import static org.mockito.Mockito.when; import android.animation.Animator; import android.animation.AnimatorSet; @@ -25,7 +24,6 @@ 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.robolectric.annotation.Config; @@ -34,7 +32,6 @@ import org.chromium.base.test.BaseRobolectricTestRunner; import org.chromium.chrome.R; import org.chromium.chrome.browser.compositor.layouts.phone.NewBackgroundTabAnimationHostView.AnimationType; -import org.chromium.chrome.browser.toolbar.top.ToggleTabStackButton; import org.chromium.chrome.browser.ui.theme.BrandedColorScheme; import org.chromium.components.browser_ui.styles.SemanticColorUtils; import org.chromium.ui.base.TestActivity; @@ -50,7 +47,6 @@ public ActivityScenarioRule<TestActivity> mActivityScenarioRule = new ActivityScenarioRule<>(TestActivity.class); - @Mock private ToggleTabStackButton mTabSwitcherButton; private Activity mActivity; private NewBackgroundTabAnimationHostView mHostView; @@ -93,48 +89,59 @@ NewBackgroundTabAnimationHostView.calculateAnimationType( /* tabSwitcherButtonIsVisible= */ true, /* isNtp= */ false, - /* ntpToolbarTransitionPercentage= */ 0f)); + /* ntpToolbarTransitionPercentage= */ 0f, + /* bottomBarVisible= */ false)); assertEquals( AnimationType.DEFAULT, NewBackgroundTabAnimationHostView.calculateAnimationType( /* tabSwitcherButtonIsVisible= */ true, /* isNtp= */ true, - /* ntpToolbarTransitionPercentage= */ 0f)); + /* ntpToolbarTransitionPercentage= */ 0f, + /* bottomBarVisible= */ false)); assertEquals( AnimationType.NTP_PARTIAL_SCROLL, NewBackgroundTabAnimationHostView.calculateAnimationType( /* tabSwitcherButtonIsVisible= */ false, /* isNtp= */ true, - /* ntpToolbarTransitionPercentage= */ 0f)); + /* ntpToolbarTransitionPercentage= */ 0f, + /* bottomBarVisible= */ false)); assertEquals( AnimationType.NTP_PARTIAL_SCROLL, NewBackgroundTabAnimationHostView.calculateAnimationType( /* tabSwitcherButtonIsVisible= */ false, /* isNtp= */ true, - /* ntpToolbarTransitionPercentage= */ 0.5f)); + /* ntpToolbarTransitionPercentage= */ 0.5f, + /* bottomBarVisible= */ false)); assertEquals( AnimationType.NTP_FULL_SCROLL, NewBackgroundTabAnimationHostView.calculateAnimationType( /* tabSwitcherButtonIsVisible= */ false, /* isNtp= */ true, - /* ntpToolbarTransitionPercentage= */ 1f)); + /* ntpToolbarTransitionPercentage= */ 1f, + /* bottomBarVisible= */ false)); + assertEquals( + AnimationType.DEFAULT, + NewBackgroundTabAnimationHostView.calculateAnimationType( + /* tabSwitcherButtonIsVisible= */ false, + /* isNtp= */ true, + /* ntpToolbarTransitionPercentage= */ 0.5f, + /* bottomBarVisible= */ true)); } @Test public void testSetUpAnimation_Default() { - when(mTabSwitcherButton.shouldShowNotificationIcon()).thenReturn(true); mHostView.setUpAnimation( - mTabSwitcherButton, - mTabSwitcherRect, + /* shouldShowNotificationIcon= */ true, + new Rect(50, 30, 120, 130), /* isIncognito= */ false, - /* isTopToolbar= */ false, + /* isTargetOnTop= */ false, /* backgroundColor= */ Color.CYAN, NewBackgroundTabAnimationHostView.AnimationType.DEFAULT, BrandedColorScheme.APP_DEFAULT, /* tabCount= */ 12, - /* toolbarHeight= */ 30, /* statusBarHeight= */ 5, - /* xOffset= */ 3); + /* xOffset= */ 3, + /* iconTint= */ ColorStateList.valueOf(Color.BLACK)); assertDefaultSettings( /* tabCount= */ 12, @@ -142,33 +149,32 @@ /* leftMargin= */ 47, /* showNotificationIcon= */ true); - when(mTabSwitcherButton.shouldShowNotificationIcon()).thenReturn(false); mHostView.setUpAnimation( - mTabSwitcherButton, - mTabSwitcherRect, + /* shouldShowNotificationIcon= */ false, + new Rect(50, 7, 120, 107), /* isIncognito= */ false, - /* isTopToolbar= */ false, + /* isTargetOnTop= */ false, /* backgroundColor= */ Color.CYAN, NewBackgroundTabAnimationHostView.AnimationType.DEFAULT, BrandedColorScheme.APP_DEFAULT, /* tabCount= */ 12, - /* toolbarHeight= */ 7, /* statusBarHeight= */ 10, - /* xOffset= */ 3); + /* xOffset= */ 3, + /* iconTint= */ ColorStateList.valueOf(Color.BLACK)); assertFalse(mFakeTabSwitcherButton.getShowIconNotificationStatusForTesting()); mHostView.setUpAnimation( - mTabSwitcherButton, - mTabSwitcherRect, + /* shouldShowNotificationIcon= */ false, + new Rect(50, 94, 120, 194), /* isIncognito= */ false, - /* isTopToolbar= */ false, + /* isTargetOnTop= */ false, /* backgroundColor= */ Color.CYAN, NewBackgroundTabAnimationHostView.AnimationType.DEFAULT, BrandedColorScheme.APP_DEFAULT, /* tabCount= */ 56, - /* toolbarHeight= */ 94, /* statusBarHeight= */ 10, - /* xOffset= */ 5); + /* xOffset= */ 5, + /* iconTint= */ ColorStateList.valueOf(Color.BLACK)); assertDefaultSettings( /* tabCount= */ 56, @@ -180,17 +186,17 @@ @Test public void testSetUpAnimation_NtpPartialScroll() { mHostView.setUpAnimation( - mTabSwitcherButton, - mTabSwitcherRect, + /* shouldShowNotificationIcon= */ false, + new Rect(50, 7, 120, 107), /* isIncognito= */ false, - /* isTopToolbar= */ false, + /* isTargetOnTop= */ false, /* backgroundColor= */ Color.CYAN, NewBackgroundTabAnimationHostView.AnimationType.NTP_PARTIAL_SCROLL, BrandedColorScheme.APP_DEFAULT, /* tabCount= */ 38, - /* toolbarHeight= */ 7, /* statusBarHeight= */ 3, - /* xOffset= */ 1); + /* xOffset= */ 1, + /* iconTint= */ ColorStateList.valueOf(Color.BLACK)); assertNtpSettings( AnimationType.NTP_PARTIAL_SCROLL, @@ -202,17 +208,17 @@ @Test public void testSetUpAnimation_NtpFullScroll() { mHostView.setUpAnimation( - mTabSwitcherButton, - mTabSwitcherRect, + /* shouldShowNotificationIcon= */ false, + new Rect(50, 12, 120, 112), /* isIncognito= */ false, - /* isTopToolbar= */ false, + /* isTargetOnTop= */ false, /* backgroundColor= */ Color.CYAN, NewBackgroundTabAnimationHostView.AnimationType.NTP_FULL_SCROLL, BrandedColorScheme.APP_DEFAULT, /* tabCount= */ 9, - /* toolbarHeight= */ 12, /* statusBarHeight= */ 10, - /* xOffset= */ 15); + /* xOffset= */ 15, + /* iconTint= */ ColorStateList.valueOf(Color.BLACK)); assertNtpSettings( AnimationType.NTP_FULL_SCROLL, /* tabCount= */ 9, @@ -228,17 +234,17 @@ @Test public void testGetAnimatorSet_Default() { mHostView.setUpAnimation( - mTabSwitcherButton, + /* shouldShowNotificationIcon= */ false, mTabSwitcherRect, /* isIncognito= */ false, - /* isTopToolbar= */ false, + /* isTargetOnTop= */ false, /* backgroundColor= */ Color.CYAN, NewBackgroundTabAnimationHostView.AnimationType.DEFAULT, BrandedColorScheme.APP_DEFAULT, /* tabCount= */ 9, - /* toolbarHeight= */ 0, /* statusBarHeight= */ 0, - /* xOffset= */ 0); + /* xOffset= */ 0, + /* iconTint= */ ColorStateList.valueOf(Color.BLACK)); AnimatorSet animatorSet = mHostView.getAnimatorSet(/* originX= */ -1, /* originY= */ -1); ArrayList<Animator> animators = animatorSet.getChildAnimations(); @@ -250,17 +256,17 @@ @Test public void testGetAnimatorSet_NtpPartialScroll() { mHostView.setUpAnimation( - mTabSwitcherButton, + /* shouldShowNotificationIcon= */ false, mTabSwitcherRect, /* isIncognito= */ false, - /* isTopToolbar= */ false, + /* isTargetOnTop= */ false, /* backgroundColor= */ Color.CYAN, NewBackgroundTabAnimationHostView.AnimationType.NTP_PARTIAL_SCROLL, BrandedColorScheme.APP_DEFAULT, /* tabCount= */ 9, - /* toolbarHeight= */ 0, /* statusBarHeight= */ 0, - /* xOffset= */ 0); + /* xOffset= */ 0, + /* iconTint= */ ColorStateList.valueOf(Color.BLACK)); AnimatorSet animatorSet = mHostView.getAnimatorSet(/* originX= */ -1, /* originY= */ -1); ArrayList<Animator> animators = animatorSet.getChildAnimations(); @@ -272,17 +278,17 @@ @Test public void testGetAnimatorSet_NtpFullScroll() { mHostView.setUpAnimation( - mTabSwitcherButton, + /* shouldShowNotificationIcon= */ false, mTabSwitcherRect, /* isIncognito= */ false, - /* isTopToolbar= */ false, + /* isTargetOnTop= */ false, /* backgroundColor= */ Color.CYAN, NewBackgroundTabAnimationHostView.AnimationType.NTP_FULL_SCROLL, BrandedColorScheme.APP_DEFAULT, /* tabCount= */ 9, - /* toolbarHeight= */ 0, /* statusBarHeight= */ 0, - /* xOffset= */ 0); + /* xOffset= */ 0, + /* iconTint= */ ColorStateList.valueOf(Color.BLACK)); AnimatorSet animatorSet = mHostView.getAnimatorSet(/* originX= */ -1, /* originY= */ -1); ArrayList<Animator> animators = animatorSet.getChildAnimations(); assertEquals(4, animators.size()); @@ -294,17 +300,17 @@ @Config(qualifiers = "night") public void testNightMode() { mHostView.setUpAnimation( - mTabSwitcherButton, + /* shouldShowNotificationIcon= */ false, mTabSwitcherRect, /* isIncognito= */ false, - /* isTopToolbar= */ false, + /* isTargetOnTop= */ false, /* backgroundColor= */ Color.CYAN, NewBackgroundTabAnimationHostView.AnimationType.DEFAULT, BrandedColorScheme.APP_DEFAULT, /* tabCount= */ 0, - /* toolbarHeight= */ 0, /* statusBarHeight= */ 0, - /* xOffset= */ 0); + /* xOffset= */ 0, + /* iconTint= */ ColorStateList.valueOf(Color.BLACK)); GradientDrawable roundedRect = (GradientDrawable) mLinkIconView.getBackground(); assertEquals(
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/phone/NewBackgroundTabFakeTabSwitcherButton.java b/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/phone/NewBackgroundTabFakeTabSwitcherButton.java index a4d5f26..8840d5d0 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/phone/NewBackgroundTabFakeTabSwitcherButton.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/phone/NewBackgroundTabFakeTabSwitcherButton.java
@@ -11,6 +11,7 @@ import android.animation.AnimatorSet; import android.animation.ObjectAnimator; import android.content.Context; +import android.content.res.ColorStateList; import android.content.res.Resources; import android.graphics.drawable.ColorDrawable; import android.util.AttributeSet; @@ -25,7 +26,6 @@ import org.chromium.build.BuildConfig; import org.chromium.build.annotations.NullMarked; import org.chromium.chrome.R; -import org.chromium.chrome.browser.theme.ThemeUtils; import org.chromium.chrome.browser.ui.android.bars_common.TabSwitcherDrawable; import org.chromium.chrome.browser.ui.android.bars_common.TabSwitcherDrawable.TabSwitcherDrawableLocation; import org.chromium.chrome.browser.ui.theme.BrandedColorScheme; @@ -86,7 +86,6 @@ getContext(), BrandedColorScheme.LIGHT_BRANDED_THEME, TabSwitcherDrawableLocation.TAB_TOOLBAR); - setBrandedColorScheme(BrandedColorScheme.LIGHT_BRANDED_THEME); setTabCount(mTabCount, mIsIncognito); setNotificationIconStatus(false); @@ -95,12 +94,24 @@ mTabSwitcherButtonView.setImageDrawable(mTabSwitcherDrawable); } - /* package */ void setBrandedColorScheme(@BrandedColorScheme int brandedColorScheme) { - mTabSwitcherDrawable.setTint( - ThemeUtils.getThemedToolbarIconTint(getContext(), brandedColorScheme)); + /** + * Sets the branded color scheme and icon tint. + * + * @param brandedColorScheme The {@link BrandedColorScheme} to use. + * @param iconTint The {@link ColorStateList} for the icon tint. + */ + /* package */ void setBrandedColorScheme( + @BrandedColorScheme int brandedColorScheme, ColorStateList iconTint) { + mTabSwitcherDrawable.setTintList(iconTint); mTabSwitcherDrawable.setNotificationBackground(brandedColorScheme); } + /** + * Sets the tab count and incognito status. + * + * @param tabCount The number of tabs to display. + * @param isIncognito True if in incognito mode. + */ /* package */ void setTabCount(int tabCount, boolean isIncognito) { mTabCount = tabCount; mIsIncognito = isIncognito; @@ -108,6 +119,11 @@ mTabSwitcherDrawable.setIncognitoStatus(isIncognito); } + /** + * Sets whether the notification icon should be shown. + * + * @param shouldShow True to show the notification icon. + */ /* package */ void setNotificationIconStatus(boolean shouldShow) { mTabSwitcherDrawable.setNotificationIconStatus(shouldShow); } @@ -122,6 +138,11 @@ mInnerContainer.setBackgroundColor(color); } + /** + * Returns the animator set for shrinking the button. + * + * @param incrementCount True if the tab count should be incremented at start. + */ /* package */ AnimatorSet getShrinkAnimator(boolean incrementCount) { mTabSwitcherButtonView.setAlpha(1f); @@ -152,6 +173,14 @@ return animatorSet; } + /** + * Returns the animator set for translating the button. + * + * <p>The translation distance is calculated as {@code (containerHeight + viewHeight) / 2}, + * ensuring the button translates to the correct position relative to the container. + * + * @param direction The {@link TranslateDirection} (UP or DOWN). + */ /* package */ AnimatorSet getTranslateAnimator(@TranslateDirection int direction) { float containerHeight = mInnerContainer.getHeight(); float viewHeight = mTabSwitcherButtonView.getHeight(); @@ -177,6 +206,7 @@ return animatorSet; } + /** Returns the animator set for scaling and fading the button. */ /* package */ AnimatorSet getScaleFadeAnimator() { ObjectAnimator fadeAnimator = ObjectAnimator.ofFloat(mTabSwitcherButtonView, View.ALPHA, 0f, 1f); @@ -199,6 +229,7 @@ return animatorSet; } + /** Returns the animator set for scaling down the button. */ /* package */ AnimatorSet getScaleDownAnimator() { ObjectAnimator scaleXAnimator = ObjectAnimator.ofFloat(mTabSwitcherButtonView, View.SCALE_X, 1.15f, 1f); @@ -241,6 +272,11 @@ location[1] += mTabSwitcherButtonView.getMeasuredHeight() / 2 - yOffset; } + /** + * Prepares the button for the NTP animation variant (bottom bar disabled). + * + * @param incrementCount True if the tab count should be incremented. + */ /* package */ void setUpNtpAnimation(boolean incrementCount) { if (incrementCount) incrementTabCount(); @@ -260,6 +296,15 @@ mTabSwitcherButtonView.setLayoutParams(tabSwitcherParams); } + /** + * Sets the margin for the fake tab switcher button container and the inner container. + * + * <p>The vertical margin is applied to the outer container and the horizontal margin to the + * inner container to position the fake button. + * + * @param vertical The vertical margin to set on the outer container. + * @param horizontal The horizontal margin to set on the inner container. + */ /* package */ void setMargin(int vertical, int horizontal) { FrameLayout.LayoutParams containerParams = (FrameLayout.LayoutParams) getLayoutParams(); containerParams.topMargin = vertical; @@ -271,6 +316,30 @@ mInnerContainer.setLayoutParams(innerContainerParams); } + /** + * Sets the size of the fake tab switcher button. + * + * <p>Both the inner container and the button view are set to these dimensions. This establishes + * a consistent baseline size that {@link #getTranslateAnimator} relies on to calculate the + * exact distance needed to translate the button to the correct position. + * + * @param width The width of the fake tab switcher button. + * @param height The height of the fake tab switcher button. + */ + /* package */ void setSize(int width, int height) { + FrameLayout.LayoutParams innerContainerParams = + (FrameLayout.LayoutParams) mInnerContainer.getLayoutParams(); + innerContainerParams.width = width; + innerContainerParams.height = height; + mInnerContainer.setLayoutParams(innerContainerParams); + + FrameLayout.LayoutParams tabSwitcherParams = + (FrameLayout.LayoutParams) mTabSwitcherButtonView.getLayoutParams(); + tabSwitcherParams.width = width; + tabSwitcherParams.height = height; + mTabSwitcherButtonView.setLayoutParams(tabSwitcherParams); + } + private void incrementTabCount() { setTabCount(mTabCount + 1, mIsIncognito); }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/phone/NewTabAnimationLayout.java b/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/phone/NewTabAnimationLayout.java index 9433bb6d..ab4241c 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/phone/NewTabAnimationLayout.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/phone/NewTabAnimationLayout.java
@@ -9,6 +9,7 @@ import android.animation.Animator; import android.animation.AnimatorSet; import android.content.Context; +import android.content.res.ColorStateList; import android.graphics.Color; import android.graphics.Insets; import android.graphics.Point; @@ -60,10 +61,10 @@ import org.chromium.chrome.browser.tabmodel.OverridableTabCount; import org.chromium.chrome.browser.tabmodel.TabModel; import org.chromium.chrome.browser.tabmodel.TabModelUtils; -import org.chromium.chrome.browser.theme.ThemeUtils; import org.chromium.chrome.browser.toolbar.ToolbarManager; import org.chromium.chrome.browser.toolbar.ToolbarPositionController; import org.chromium.chrome.browser.toolbar.top.ToggleTabStackButton; +import org.chromium.chrome.browser.ui.android.bars_common.TabSwitcherButtonView; import org.chromium.chrome.browser.ui.edge_to_edge.TopInsetProvider; import org.chromium.chrome.browser.ui.theme.BrandedColorScheme; import org.chromium.components.browser_ui.widget.animation.CancelAwareAnimatorListener; @@ -95,10 +96,12 @@ private final Handler mHandler; private final ToolbarManager mToolbarManager; private final NonNullObservableSupplier<Boolean> mScrimVisibilitySupplier; + private final NonNullObservableSupplier<Float> mNtpSearchBoxTransitionPercentageSupplier; private final OverridableTabCount mOverridableTabCount; private final BrowserStateBrowserControlsVisibilityDelegate mBrowserVisibilityDelegate; private final TopInsetProvider mTopInsetProvider; private final TopInsetProvider.Observer mTopInsetProviderObserver; + private final NewBackgroundTabAnimationData mNewBackgroundTabAnimationData; private @Nullable StaticTabSceneLayer mSceneLayer; private @Nullable NewBackgroundTabAnimationHostView mBackgroundHostView; @@ -155,9 +158,13 @@ mHandler = new Handler(); mToolbarManager = toolbarManager; mScrimVisibilitySupplier = scrimVisibilitySupplier; + mNtpSearchBoxTransitionPercentageSupplier = + toolbarManager.getNtpSearchBoxTransitionPercentageSupplier(); mOverridableTabCount = mToolbarManager.getOverridableTabCount(); mBrowserVisibilityDelegate = browserControlsManager.getBrowserVisibilityDelegate(); mTopInsetProvider = topInsetProvider; + mNewBackgroundTabAnimationData = + new NewBackgroundTabAnimationData(animationHostView, toolbarManager); // Set up observer to handle edge-to-edge changes. mTopInsetProviderObserver = this::onToEdgeChange; @@ -670,12 +677,11 @@ mBrowserControlsVisibilityToken = mBrowserVisibilityDelegate.showControlsPersistent(); } - ToggleTabStackButton tabSwitcherButton = - mAnimationHostView.findViewById(R.id.tab_switcher_button); - assert tabSwitcherButton != null; - Rect tabSwitcherRect = new Rect(); - boolean tabSwitcherButtonIsVisible = - tabSwitcherButton.getGlobalVisibleRect(tabSwitcherRect); + Rect compositorViewRect = new Rect(); + mCompositorViewHolder.getGlobalVisibleRect(compositorViewRect); + + int expectedToolbarTop = compositorViewRect.top + getTopInsetIfNeeded(animationTab); + mNewBackgroundTabAnimationData.captureState(animationTab, isRegularNtp, expectedToolbarTop); Context context = getContext(); mBackgroundHostView = @@ -689,57 +695,32 @@ int prevTabCount = mTabModelSelector.getModel(isIncognito).getCount() - 1; mOverridableTabCountToken = mOverridableTabCount.setCount(prevTabCount); - @ColorInt - int toolbarColor = - isRegularNtp - ? NewTabAnimationUtils.getBackgroundColor(context, isIncognito) - : mToolbarManager.getPrimaryColor(); - int[] toolbarPosition = new int[2]; - mAnimationHostView.findViewById(R.id.toolbar).getLocationInWindow(toolbarPosition); - boolean isTopToolbar = - isRegularNtp || ToolbarPositionController.shouldShowToolbarOnTop(animationTab); - int toolbarHeight = toolbarPosition[1] + getTopInsetIfNeeded(animationTab); + Rect tabSwitcherRect = mNewBackgroundTabAnimationData.getTabSwitcherButtonRect(); + View tabSwitcherButton = mNewBackgroundTabAnimationData.getTabSwitcherButton(); + int buttonColor = mNewBackgroundTabAnimationData.getPrimaryColor(); - Rect compositorViewRect = new Rect(); - mCompositorViewHolder.getGlobalVisibleRect(compositorViewRect); - - NonNullObservableSupplier<Float> ntpSearchBoxTransitionPercentageSupplier = - mToolbarManager.getNtpSearchBoxTransitionPercentageSupplier(); - - @AnimationType - int animationType = - NewBackgroundTabAnimationHostView.calculateAnimationType( - tabSwitcherButtonIsVisible, - isRegularNtp, - ntpSearchBoxTransitionPercentageSupplier.get()); - - @BrandedColorScheme int brandedColorScheme; - if (isRegularNtp - && animationType == AnimationType.DEFAULT - && NtpCustomizationUtils.shouldAdjustIconTintForNtp(/* isTablet= */ false)) { - brandedColorScheme = BrandedColorScheme.DARK_BRANDED_THEME; - } else { - brandedColorScheme = - ThemeUtils.getBrandedColorScheme(context, toolbarColor, isIncognito); - } + int animationType = mNewBackgroundTabAnimationData.getAnimationType(); + @BrandedColorScheme + int brandedColorScheme = mNewBackgroundTabAnimationData.getBrandedColorScheme(); + ColorStateList iconTint = mNewBackgroundTabAnimationData.getIconTint(); if (mTopInsetProviderAvailable && animationType == AnimationType.DEFAULT) { mTabSwitcherButton = tabSwitcherButton; - toolbarColor = Color.TRANSPARENT; + buttonColor = Color.TRANSPARENT; } mBackgroundHostView.setUpAnimation( - tabSwitcherButton, + shouldShowNotificationIcon(tabSwitcherButton), tabSwitcherRect, isIncognito, - isTopToolbar, - toolbarColor, + mNewBackgroundTabAnimationData.isPositionOnTop(), + buttonColor, animationType, brandedColorScheme, prevTabCount, - toolbarHeight, compositorViewRect.top, - compositorViewRect.left); + compositorViewRect.left, + iconTint); // {@link View#INVISIBLE} is needed to generate the geometry information. mBackgroundHostView.setVisibility(View.INVISIBLE); @@ -771,14 +752,16 @@ assumeNonNull(mTabModelSelector); assumeNonNull(mBackgroundHostView); boolean shouldObserveNtp = - isRegularNtp && animationType == AnimationType.DEFAULT; + isRegularNtp + && animationType == AnimationType.DEFAULT + && !mNewBackgroundTabAnimationData.isBottomBarVisible(); AnimationInterruptor interruptor = new AnimationInterruptor( mLayoutStateProvider, mTabModelSelector.getCurrentTabSupplier(), animationTab, mScrimVisibilitySupplier, - ntpSearchBoxTransitionPercentageSupplier, + mNtpSearchBoxTransitionPercentageSupplier, shouldObserveNtp, this::forceAnimationToFinish); assumeNonNull(mBackgroundHostView); @@ -874,6 +857,15 @@ } } + private static boolean shouldShowNotificationIcon(View tabSwitcherButton) { + if (tabSwitcherButton instanceof ToggleTabStackButton toggleTabStackButton) { + return toggleTabStackButton.shouldShowNotificationIcon(); + } else if (tabSwitcherButton instanceof TabSwitcherButtonView tabSwitcherButtonView) { + return tabSwitcherButtonView.isNotificationDotVisible(); + } + return false; + } + private int getTopInsetIfNeeded(@Nullable Tab tab) { if (mTopInsetProviderAvailable && NtpCustomizationUtils.supportsEnableEdgeToEdgeOnTop(tab)) {
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/phone/NewTabAnimationLayoutUnitTest.java b/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/phone/NewTabAnimationLayoutUnitTest.java index 13f92ed..a77e827 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/phone/NewTabAnimationLayoutUnitTest.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/phone/NewTabAnimationLayoutUnitTest.java
@@ -228,9 +228,8 @@ mTransitiveTopInsetProvider)); mNewTabAnimationLayout.setTabModelSelector(mTabModelSelector); mNewTabAnimationLayout.setTabContentManager(mTabContentManager); - when(mAnimationHostView.findViewById(R.id.tab_switcher_button)) - .thenReturn(mTabSwitcherButton); when(mAnimationHostView.findViewById(R.id.toolbar)).thenReturn(mToolbar); + when(mToolbar.findViewById(R.id.tab_switcher_button)).thenReturn(mTabSwitcherButton); when(mAnimationHostView.getWidth()).thenReturn(40); when(mAnimationHostView.getHeight()).thenReturn(40); mNewTabAnimationLayout.onFinishNativeInitialization();
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/compositor/overlay_panel/OverlayPanelBase.java b/chrome/android/java/src/org/chromium/chrome/browser/compositor/overlay_panel/OverlayPanelBase.java index 12fe419..3254f2e82 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/compositor/overlay_panel/OverlayPanelBase.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/compositor/overlay_panel/OverlayPanelBase.java
@@ -193,6 +193,16 @@ boolean bottomControlsMinHeightChanged, boolean requestNewFrame, boolean isVisibilityForced) { + maybeUpdatePanelForHeight(); + } + + @Override + public void onBottomControlsHeightChanged( + int bottomControlsHeight, int bottomControlsMinHeight) { + maybeUpdatePanelForHeight(); + } + + private void maybeUpdatePanelForHeight() { if (mPanelState > PanelState.CLOSED) { updatePanelForHeight(mHeight); }
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 970842a..aa07292 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
@@ -877,7 +877,7 @@ if (!mIncognito && (ChromeFeatureList.sGlic.isEnabled() || ChromeFeatureList.sContextualTasks.isEnabled())) { - mStripTabUnderlineManager = new StripTabUnderlineManager(this); + mStripTabUnderlineManager = new StripTabUnderlineManager(this, windowAndroid); } mIsFirstLayoutPass = true;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/compositor/overlays/strip/StripLayoutHelperManager.java b/chrome/android/java/src/org/chromium/chrome/browser/compositor/overlays/strip/StripLayoutHelperManager.java index 1cd86c8..9b64611 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/compositor/overlays/strip/StripLayoutHelperManager.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/compositor/overlays/strip/StripLayoutHelperManager.java
@@ -491,7 +491,7 @@ * space mode, false otherwise. * @param backPressManager The {@link BackPressManager} for handling back press. * @param snackbarManager The {@link SnackbarManager} used to show snackbar UI. - * @param glicClickHandler The click handler for the Glic button. + * @param glicClickHandler The {@link Callback<Boolean>} for the Glic button. */ // TODO(crbug.com/484116872): Suppressing to observe SharedPreferences, which is discouraged; // should use another messaging channel instead. @@ -522,7 +522,7 @@ @Nullable NonNullObservableSupplier<Boolean> xrSpaceModeObservableSupplier, BackPressManager backPressManager, SnackbarManager snackbarManager, - Runnable glicClickHandler, + Callback<Boolean> glicClickHandler, @Nullable GlicKeyedService glicKeyedService) { mContext = context; mWindowAndroid = windowAndroid; @@ -609,6 +609,7 @@ glicKeyedService, ChromeAndroidTaskTrackerFactory.getInstance(), () -> mIsIncognito, + () -> mTabModelSelector, this::updateButtonMargins); if (!IncognitoUtils.shouldOpenIncognitoAsWindow()) { @@ -658,7 +659,7 @@ @Override public void dismissContextMenu() { - mTrailingButtonsCoordinator.dismissGlicContextMenu(); + mTrailingButtonsCoordinator.dismissTrailingButtonsMenu(); } @Override
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/compositor/overlays/strip/StripLayoutTrailingButtonsCoordinator.java b/chrome/android/java/src/org/chromium/chrome/browser/compositor/overlays/strip/StripLayoutTrailingButtonsCoordinator.java index fbb650c..edb79d7 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/compositor/overlays/strip/StripLayoutTrailingButtonsCoordinator.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/compositor/overlays/strip/StripLayoutTrailingButtonsCoordinator.java
@@ -13,12 +13,15 @@ import androidx.annotation.ColorInt; import androidx.annotation.VisibleForTesting; +import org.chromium.base.Callback; import org.chromium.base.ContextUtils; import org.chromium.build.annotations.EnsuresNonNullIf; import org.chromium.build.annotations.NullMarked; import org.chromium.build.annotations.Nullable; import org.chromium.build.annotations.RequiresNonNull; import org.chromium.chrome.R; +import org.chromium.chrome.browser.actor.ActorKeyedServiceFactory; +import org.chromium.chrome.browser.actor.ActorTask; import org.chromium.chrome.browser.compositor.LayerTitleCache; import org.chromium.chrome.browser.compositor.layouts.LayoutRenderHost; import org.chromium.chrome.browser.compositor.layouts.LayoutUpdateHost; @@ -32,10 +35,12 @@ import org.chromium.chrome.browser.glic.GlicKeyedService; import org.chromium.chrome.browser.glic.GlicKeyedService.GlobalShowHideObserver; import org.chromium.chrome.browser.glic.GlicPrefNames; +import org.chromium.chrome.browser.glic.GlicTaskMenuCoordinator; import org.chromium.chrome.browser.glic.GlicUtils; import org.chromium.chrome.browser.layouts.animation.CompositorAnimator; import org.chromium.chrome.browser.layouts.components.VirtualView; import org.chromium.chrome.browser.profiles.Profile; +import org.chromium.chrome.browser.tabmodel.TabModelSelector; import org.chromium.chrome.browser.ui.browser_window.ChromeAndroidTaskTracker; import org.chromium.chrome.browser.ui.side_panel.AndroidSidePanelEnabledFn; import org.chromium.components.browser_ui.styles.SemanticColorUtils; @@ -118,11 +123,12 @@ private final StripLayoutTrailingButtonsObserver mObserver; private final float mDensity; private final float mStripEndPadding; - private final Runnable mGlicClickHandler; + private final Callback<Boolean> mGlicClickHandler; private final @Nullable GlicKeyedService mGlicKeyedService; private final @Nullable GlobalShowHideObserver mGlicUiObserver; private final @Nullable ChromeAndroidTaskTracker mTaskTracker; private final Supplier<Boolean> mIsIncognitoSupplier; + private final Supplier<@Nullable TabModelSelector> mTabModelSelectorSupplier; // Lifecycle & Caching Objects private @Nullable Profile mProfile; @@ -134,6 +140,7 @@ private @Nullable TintedCompositorButton mGlicDismissNudgeButton; private @Nullable TintedCompositorTextButton mGlicActorButton; private @Nullable GlicButtonContextMenuCoordinator mGlicButtonContextMenuCoordinator; + private @Nullable GlicTaskMenuCoordinator mGlicTaskMenuCoordinator; private final View mToolbarControlContainer; // Layout & State Parameters @@ -151,7 +158,7 @@ * @param updateHost The {@link LayoutUpdateHost} for requesting handles layout. * @param renderHost The {@link LayoutRenderHost} for requesting renders. * @param windowAndroid The {@link WindowAndroid} for the activity. - * @param glicClickHandler The {@link Runnable} to execute on Glic button click. + * @param glicClickHandler The {@link Callback<Boolean>} to execute on Glic button click. * @param density The display density. * @param stripEndPadding The end padding of the tab strip. * @param toolbarControlContainer The view containing toolbar controls. @@ -167,7 +174,7 @@ LayoutUpdateHost updateHost, LayoutRenderHost renderHost, WindowAndroid windowAndroid, - Runnable glicClickHandler, + Callback<Boolean> glicClickHandler, float density, float stripEndPadding, View toolbarControlContainer, @@ -177,6 +184,7 @@ @Nullable GlicKeyedService glicKeyedService, @Nullable ChromeAndroidTaskTracker taskTracker, Supplier<Boolean> isIncognitoSupplier, + Supplier<@Nullable TabModelSelector> tabModelSelectorSupplier, StripLayoutTrailingButtonsObserver observer) { mContext = context; mUpdateHost = updateHost; @@ -187,6 +195,7 @@ mGlicKeyedService = glicKeyedService; mTaskTracker = taskTracker; mIsIncognitoSupplier = isIncognitoSupplier; + mTabModelSelectorSupplier = tabModelSelectorSupplier; mObserver = observer; mWindowAndroid = windowAndroid; mToolbarControlContainer = toolbarControlContainer; @@ -199,7 +208,8 @@ } StripLayoutViewOnClickHandler glicClickHandlerOnButton = - (time, view, motionEventButtonState, modifiers) -> mGlicClickHandler.run(); + (time, view, motionEventButtonState, modifiers) -> + mGlicClickHandler.onResult(/* result= */ false); if (ChromeFeatureList.isEnabled(ChromeFeatureList.GLIC) && AndroidSidePanelEnabledFn.isEnabled()) { @@ -292,7 +302,8 @@ GLIC_BUTTON_BACKGROUND_WIDTH_DP, GLIC_BUTTON_BACKGROUND_HEIGHT_DP, /* tooltipHandler= */ null, - (time, view, motionEventButtonState, modifiers) -> {}, + (time, view, motionEventButtonState, modifiers) -> + toggleActorTaskMenu(), keyboardFocusHandler, R.drawable.ic_arrow_selector_spark_16dp, GLIC_BUTTON_CLICK_SLOP_DP, @@ -329,6 +340,10 @@ mGlicButtonContextMenuCoordinator.dismiss(); mGlicButtonContextMenuCoordinator = null; } + if (mGlicTaskMenuCoordinator != null) { + mGlicTaskMenuCoordinator.dismiss(); + mGlicTaskMenuCoordinator = null; + } } /** @@ -442,8 +457,9 @@ updateActorButtonState(); updateGlicButtonPosition(); - // Dismiss Glic context menu, similar to how the app menu is dismissed on orientation change - dismissGlicContextMenu(); + // Dismiss trailing buttons' menus, similar to how the app menu is dismissed on + // orientation change + dismissTrailingButtonsMenu(); } private void updateActorButtonState() { @@ -466,17 +482,54 @@ } } - /** Returns true if the trailing button context menu is showing. */ + /** Returns true if the trailing buttons' menus are showing. */ public boolean isMenuShowing() { - return mGlicButtonContextMenuCoordinator != null - && mGlicButtonContextMenuCoordinator.isShowing(); + return (mGlicButtonContextMenuCoordinator != null + && mGlicButtonContextMenuCoordinator.isShowing()) + || (mGlicTaskMenuCoordinator != null && mGlicTaskMenuCoordinator.isShowing()); } - /** Dismisses the trailing button context menu if it is showing. */ - public void dismissGlicContextMenu() { + /** Dismisses the trailing buttons' menus if they are showing. */ + public void dismissTrailingButtonsMenu() { if (mGlicButtonContextMenuCoordinator != null) { mGlicButtonContextMenuCoordinator.dismiss(); } + if (mGlicTaskMenuCoordinator != null) { + mGlicTaskMenuCoordinator.dismiss(); + } + } + + private void toggleActorTaskMenu() { + if (mGlicTaskMenuCoordinator != null && mGlicTaskMenuCoordinator.isShowing()) { + mGlicTaskMenuCoordinator.dismiss(); + return; + } + + if (mProfile == null || mGlicActorButton == null) return; + var actorService = ActorKeyedServiceFactory.getForProfile(mProfile); + if (actorService == null) return; + + List<ActorTask> tasks = actorService.getActiveTasks(); + if (tasks.isEmpty()) return; + + RectProvider anchorRectProvider = new RectProvider(); + mGlicActorButton.getAnchorRect(anchorRectProvider.getRect()); + StripLayoutUtils.getAdjustedAnchorRect( + mContext, + mToolbarControlContainer, + mProfile.isOffTheRecord(), + mTopPadding, + anchorRectProvider); + + // TabModelSelector is pulled lazily via supplier. This is safe from race conditions because + // Glic buttons require an initialized profile to be displayed/interacted with. + if (mGlicTaskMenuCoordinator == null) { + mGlicTaskMenuCoordinator = + new GlicTaskMenuCoordinator( + mContext, mTabModelSelectorSupplier, mGlicClickHandler::onResult); + } + mGlicTaskMenuCoordinator.show( + anchorRectProvider, mToolbarControlContainer.getRootView(), tasks); } /** @@ -816,7 +869,7 @@ */ public boolean onUpOrCancel() { if (mGlicButton != null && mGlicButton.onUpOrCancel()) { - mGlicClickHandler.run(); + mGlicClickHandler.onResult(/* result= */ false); return true; } else if (mGlicActorButton != null && mGlicActorButton.onUpOrCancel()) { return true;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/compositor/overlays/strip/StripTabUnderlineManager.java b/chrome/android/java/src/org/chromium/chrome/browser/compositor/overlays/strip/StripTabUnderlineManager.java index f1e4a4d..470ddf16 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/compositor/overlays/strip/StripTabUnderlineManager.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/compositor/overlays/strip/StripTabUnderlineManager.java
@@ -8,8 +8,15 @@ import org.jni_zero.JNINamespace; import org.jni_zero.NativeMethods; +import org.chromium.base.Callback; import org.chromium.build.annotations.NullMarked; +import org.chromium.chrome.browser.contextual_tasks.ContextualTasksBridge; +import org.chromium.chrome.browser.flags.ChromeFeatureList; import org.chromium.chrome.browser.tab.Tab; +import org.chromium.ui.base.WindowAndroid; + +import java.util.HashSet; +import java.util.Set; /** Manages the native C++ TabUnderlineController objects for the strip. */ @JNINamespace("android") @@ -17,29 +24,65 @@ public class StripTabUnderlineManager { private long mNativePtr; private final StripLayoutHelper mStripLayoutHelper; + private final WindowAndroid mWindowAndroid; + private final Set<Tab> mTabsPendingContextualTasksBridge = new HashSet<>(); + private final Callback<ContextualTasksBridge> mContextualTasksBridgeObserver; - public StripTabUnderlineManager(StripLayoutHelper stripLayoutHelper) { + private boolean mContextualTasksBridgeInitialized; + + public StripTabUnderlineManager( + StripLayoutHelper stripLayoutHelper, WindowAndroid windowAndroid) { mStripLayoutHelper = stripLayoutHelper; + mWindowAndroid = windowAndroid; + mContextualTasksBridgeObserver = this::onContextualTasksBridgeReady; mNativePtr = StripTabUnderlineManagerJni.get().init(this); + + if (ChromeFeatureList.sContextualTasks.isEnabled()) { + ContextualTasksBridge.getSupplier(mWindowAndroid) + .addSyncObserverAndCallIfNonNull(mContextualTasksBridgeObserver); + } else { + mContextualTasksBridgeInitialized = true; + } } public void destroy() { + if (ChromeFeatureList.sContextualTasks.isEnabled()) { + ContextualTasksBridge.getSupplier(mWindowAndroid) + .removeObserver(mContextualTasksBridgeObserver); + } if (mNativePtr != 0) { StripTabUnderlineManagerJni.get().destroy(mNativePtr); mNativePtr = 0; } } + private void onContextualTasksBridgeReady(ContextualTasksBridge bridge) { + mContextualTasksBridgeInitialized = true; + for (Tab tab : mTabsPendingContextualTasksBridge) { + registerTab(tab); + } + mTabsPendingContextualTasksBridge.clear(); + ContextualTasksBridge.getSupplier(mWindowAndroid) + .removeObserver(mContextualTasksBridgeObserver); + } + /** Track a tab in the native manager. */ public void registerTab(Tab tab) { if (mNativePtr == 0 || tab == null) return; + if (!mContextualTasksBridgeInitialized) { + mTabsPendingContextualTasksBridge.add(tab); + return; + } StripTabUnderlineManagerJni.get().registerTab(mNativePtr, tab); } /** Stop tracking a tab. */ public void unregisterTab(int tabId) { if (mNativePtr == 0) return; - StripTabUnderlineManagerJni.get().unregisterTab(mNativePtr, tabId); + mTabsPendingContextualTasksBridge.removeIf(tab -> tab.getId() == tabId); + if (mContextualTasksBridgeInitialized) { + StripTabUnderlineManagerJni.get().unregisterTab(mNativePtr, tabId); + } } @CalledByNative
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabBottomBarDelegate.java b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabBottomBarDelegate.java index 0990d3b..3eae1e9 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabBottomBarDelegate.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabBottomBarDelegate.java
@@ -238,7 +238,7 @@ if (mCustomButtonsUpdater != null && mCustomButtonsUpdater.updateBottomBarButton(params)) { return; } - ImageButton button = (ImageButton) getBottomBarView().findViewById(params.getId()); + ImageButton button = getBottomBarView().findViewById(params.getId()); button.setContentDescription(params.getDescription()); button.setImageDrawable(params.getIcon(mActivity, getButtonIconTint())); }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/content/DefaultCustomTabIntentHandlingStrategy.java b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/content/DefaultCustomTabIntentHandlingStrategy.java index dd80529..36b667b8 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/content/DefaultCustomTabIntentHandlingStrategy.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/content/DefaultCustomTabIntentHandlingStrategy.java
@@ -8,9 +8,12 @@ import static org.chromium.build.NullUtil.assumeNonNull; import android.app.Activity; +import android.content.Intent; import android.text.TextUtils; +import org.chromium.base.IntentUtils; import org.chromium.build.annotations.NullMarked; +import org.chromium.chrome.browser.IntentHandler; import org.chromium.chrome.browser.browserservices.intents.BrowserServicesIntentDataProvider; import org.chromium.chrome.browser.browserservices.ui.controller.CurrentPageVerifier; import org.chromium.chrome.browser.browserservices.ui.controller.Verifier; @@ -51,6 +54,13 @@ @Override public void handleInitialIntent(BrowserServicesIntentDataProvider intentDataProvider) { + Intent intent = intentDataProvider.getIntent(); + assertNonNull(intent); + if (IntentUtils.safeGetBooleanExtra( + intent, IntentHandler.EXTRA_SKIP_LOAD_ON_REPARENTING, false)) { + return; + } + @TabCreationMode int initialTabCreationMode = mTabProvider.getInitialTabCreationMode(); if (mTabProvider.getTab() != null) { CustomTabAuthUrlHeuristics.setFirstCctPageLoadForMetrics(mTabProvider.getTab());
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/hub/HubLayoutPublicTransitTest.java b/chrome/android/java/src/org/chromium/chrome/browser/hub/HubLayoutPublicTransitTest.java index 1a41989..3a22128e 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/hub/HubLayoutPublicTransitTest.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/hub/HubLayoutPublicTransitTest.java
@@ -162,6 +162,7 @@ @Test @LargeTest + @DisableIf.Device(DeviceFormFactor.DESKTOP_FREEFORM) // crbug.com/444482498 public void testIncognitoTabSwitcherStation_newTabGroup() { WebPageStation firstPage = mCtaTestRule.startOnBlankPage(); IncognitoNewTabPageStation incognitoNewTabPageStation =
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/media/MediaCapturePickerItemViewBinder.java b/chrome/android/java/src/org/chromium/chrome/browser/media/MediaCapturePickerItemViewBinder.java index 95761ba..e4d96668 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/media/MediaCapturePickerItemViewBinder.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/media/MediaCapturePickerItemViewBinder.java
@@ -19,7 +19,7 @@ if (MediaCapturePickerItemProperties.CLICK_LISTENER == propertyKey) { view.setOnClickListener(model.get(MediaCapturePickerItemProperties.CLICK_LISTENER)); } else if (MediaCapturePickerItemProperties.TAB_NAME == propertyKey) { - TextView titleView = (TextView) view.findViewById(R.id.tab_title); + TextView titleView = view.findViewById(R.id.tab_title); String text = model.get(MediaCapturePickerItemProperties.TAB_NAME); titleView.setText(text); } else if (MediaCapturePickerItemProperties.SELECTED == propertyKey) {
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/multiwindow/TabbedCrashRecoveryDelegate.java b/chrome/android/java/src/org/chromium/chrome/browser/multiwindow/TabbedCrashRecoveryDelegate.java new file mode 100644 index 0000000..0a56dd4 --- /dev/null +++ b/chrome/android/java/src/org/chromium/chrome/browser/multiwindow/TabbedCrashRecoveryDelegate.java
@@ -0,0 +1,52 @@ +// Copyright 2026 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +package org.chromium.chrome.browser.multiwindow; + +import org.chromium.base.supplier.MonotonicObservableSupplier; +import org.chromium.build.annotations.NullMarked; +import org.chromium.build.annotations.Nullable; +import org.chromium.chrome.browser.ChromeTabbedActivity; +import org.chromium.ui.modaldialog.ModalDialogManager; + +/** + * Delegate to help recover ChromeTabbedActivity windows from a previous session during app launch + * after a crash. + */ +@NullMarked +public class TabbedCrashRecoveryDelegate { + private static @Nullable TabbedCrashRecoveryDelegate sInstance; + + private TabbedCrashRecoveryDelegate() {} + + /* package */ static TabbedCrashRecoveryDelegate getInstance() { + if (sInstance == null) { + sInstance = new TabbedCrashRecoveryDelegate(); + } + return sInstance; + } + + /** + * Shows a crash recovery prompt if applicable, when the {@link ModalDialogManager} for the host + * activity is available. + * + * @param modalDialogManagerSupplier Supplier for ModalDialogManager. + * @param hostActivity The host activity where the prompt will be displayed. + */ + /* package */ void initiateCrashRecovery( + MonotonicObservableSupplier<ModalDialogManager> modalDialogManagerSupplier, + ChromeTabbedActivity hostActivity) { + // TODO: Implement this method. + } + + /** + * Registers successful recovery of a window after a crash. + * + * @param activity The activity that was created when a window was successfully recovered after + * a crash. + */ + /* package */ void registerRecovery(ChromeTabbedActivity activity) { + // TODO: Implement this method. + } +}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/native_page/NativePageFactory.java b/chrome/android/java/src/org/chromium/chrome/browser/native_page/NativePageFactory.java index eb1f593d..65836ed 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/native_page/NativePageFactory.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/native_page/NativePageFactory.java
@@ -557,12 +557,13 @@ BrowserControlsManager browserControlsManager, TabModelSelector tabModelSelector, Activity activity) { - if (sTestPage instanceof PdfPage) { + if (sTestPage != null) { return sTestPage; } return new PdfPage( new TabShim(tab, browserControlsManager, tabModelSelector, null), tab.getProfile(), + tab.getProfile().isOffTheRecord(), activity, url, pdfInfo, @@ -643,7 +644,7 @@ if (mNewTabPageCreationTracker != null) mNewTabPageCreationTracker.destroy(); } - public static void setPdfPageForTesting(PdfPage pdfPage) { + public static void setPdfPageForTesting(NativePage pdfPage) { sTestPage = pdfPage; } }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/notifications/tips/TipsPromoCoordinator.java b/chrome/android/java/src/org/chromium/chrome/browser/notifications/tips/TipsPromoCoordinator.java index 1ce8079..84e6012 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/notifications/tips/TipsPromoCoordinator.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/notifications/tips/TipsPromoCoordinator.java
@@ -255,9 +255,9 @@ /* attachToRoot= */ false); stepView.setBackgroundResource(TipsUtils.getDetailStepBackground(i, steps.size())); // TODO(crbug.com/454724965): Translate the step number set for all languages. - TextView stepNumber = (TextView) stepView.findViewById(R.id.step_number); + TextView stepNumber = stepView.findViewById(R.id.step_number); stepNumber.setText(String.valueOf(i + 1)); - TextView stepContent = (TextView) stepView.findViewById(R.id.step_content); + TextView stepContent = stepView.findViewById(R.id.step_content); stepContent.setText(steps.get(i)); stepsContainer.addView(stepView); }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/settings/search/SettingsSearchCoordinator.java b/chrome/android/java/src/org/chromium/chrome/browser/settings/search/SettingsSearchCoordinator.java index 7337ba83..7c811563 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/settings/search/SettingsSearchCoordinator.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/settings/search/SettingsSearchCoordinator.java
@@ -584,6 +584,7 @@ @Override public void onTransitionEnd(Transition transition) { searchBox.setOnClickListener(v -> onClickSearchBox(v)); + updateSearchUiWidth(); } }); var parentView = (ViewGroup) mActivity.findViewById(R.id.settings_activity);
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/sync/settings/IdentityErrorCardPreference.java b/chrome/android/java/src/org/chromium/chrome/browser/sync/settings/IdentityErrorCardPreference.java index 3a66ab77ab..7881371f 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/sync/settings/IdentityErrorCardPreference.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/sync/settings/IdentityErrorCardPreference.java
@@ -123,7 +123,7 @@ private void setupIdentityErrorCardView(View card) { Context context = getContext(); - ImageView image = (ImageView) card.findViewById(R.id.signin_settings_card_icon); + ImageView image = card.findViewById(R.id.signin_settings_card_icon); image.setContentDescription( context.getString(R.string.accessibility_account_management_row_account_error)); image.setImageDrawable(AppCompatResources.getDrawable(context, R.drawable.ic_error));
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/tabmodel/TabCollectionTabModelImpl.java b/chrome/android/java/src/org/chromium/chrome/browser/tabmodel/TabCollectionTabModelImpl.java index f36e9ec..4283533 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/tabmodel/TabCollectionTabModelImpl.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/tabmodel/TabCollectionTabModelImpl.java
@@ -718,9 +718,10 @@ } @Override - public void moveTabGroupToWindow(Token tabGroupId, Activity activity, int newIndex) { + public boolean moveTabGroupToWindow(Token tabGroupId, Activity activity, int newIndex) { try (ScopedStorageBatch ignored = mBatchFactory.get()) { - mModelDelegate.moveTabGroupToWindow(tabGroupId, activity, newIndex, isIncognito()); + return mModelDelegate.moveTabGroupToWindow( + tabGroupId, activity, newIndex, isIncognito()); } }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/tabmodel/TabModelDelegate.java b/chrome/android/java/src/org/chromium/chrome/browser/tabmodel/TabModelDelegate.java index bf289e7..fe1510c 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/tabmodel/TabModelDelegate.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/tabmodel/TabModelDelegate.java
@@ -82,7 +82,10 @@ * @param activity The activity to move the tab group to. * @param newIndex The index to move the tab group to. * @param isIncognito Whether the tab group is in the incognito model. + * @return Whether the move succeeded. */ - default void moveTabGroupToWindow( - Token tabGroupId, Activity activity, int newIndex, boolean isIncognito) {} + default boolean moveTabGroupToWindow( + Token tabGroupId, Activity activity, int newIndex, boolean isIncognito) { + return false; + } }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/tabmodel/TabModelJniBridge.java b/chrome/android/java/src/org/chromium/chrome/browser/tabmodel/TabModelJniBridge.java index 155e6d8e..740e61c9 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/tabmodel/TabModelJniBridge.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/tabmodel/TabModelJniBridge.java
@@ -203,9 +203,9 @@ mNativeTabModelJniBridge, tab, nativeAndroidBrowserWindow, newIndex); } - protected void moveTabGroupToWindowForTesting( + protected boolean moveTabGroupToWindowForTesting( Token tabGroupId, long nativeAndroidBrowserWindow, int newIndex) { - TabModelJniBridgeJni.get() + return TabModelJniBridgeJni.get() .moveTabGroupToWindowForTesting( // IN-TEST mNativeTabModelJniBridge, tabGroupId, nativeAndroidBrowserWindow, newIndex); } @@ -652,15 +652,15 @@ @JniType("TabAndroid*") Tab tab, Activity activity, int newIndex); @CalledByNative - private void moveTabGroupToWindowInternal( + private boolean moveTabGroupToWindowInternal( @JniType("base::Token") Token tabGroupId, @Nullable Activity activity, int newIndex) { - if (activity == null) return; - moveTabGroupToWindow(tabGroupId, activity, newIndex); + if (activity == null) return false; + return moveTabGroupToWindow(tabGroupId, activity, newIndex); } // TODO(https://crbug.com/495795228): add `bringToFront` parameter to indicate // if the destination activity should be activated. See MultiInstanceOrchestrator. - protected abstract void moveTabGroupToWindow( + protected abstract boolean moveTabGroupToWindow( @JniType("base::Token") Token tabGroupId, Activity activity, int newIndex); @Override @@ -757,7 +757,7 @@ long nativeAndroidBrowserWindow, int newIndex); - void moveTabGroupToWindowForTesting( // IN-TEST + boolean moveTabGroupToWindowForTesting( // IN-TEST long nativeTabModelJniBridge, @JniType("base::Token") Token tabGroupId, long nativeAndroidBrowserWindow,
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/tabmodel/TabModelSelectorImpl.java b/chrome/android/java/src/org/chromium/chrome/browser/tabmodel/TabModelSelectorImpl.java index 9e53857..17b67c03 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/tabmodel/TabModelSelectorImpl.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/tabmodel/TabModelSelectorImpl.java
@@ -371,15 +371,15 @@ } @Override - public void moveTabGroupToWindow( + public boolean moveTabGroupToWindow( Token tabGroupId, Activity activity, int newIndex, boolean isIncognito) { TabModel tabModel = getModel(isIncognito); - if (!tabModel.tabGroupExists(tabGroupId)) return; + if (!tabModel.tabGroupExists(tabGroupId)) return false; Tab currentTab = tabModel.getCurrentTabSupplier().get(); assert currentTab != null; Activity currentActivity = ContextUtils.activityFromContext(currentTab.getContext()); - if (currentActivity == null) return; + if (currentActivity == null) return false; String collaborationId = null; if (!isIncognito) { @@ -395,12 +395,13 @@ TabWindowManagerSingleton.getInstance().getIdForWindow(currentActivity), currentTab.getId(), TabShareUtils.isCollaborationIdValid(collaborationId)); - if (tabGroupMetadata == null) return; + if (tabGroupMetadata == null) return false; int destWindowId = TabWindowManagerSingleton.getInstance().getIdForWindow(activity); MultiInstanceOrchestratorFactory.getInstance() .moveTabGroupToWindowByIdChecked( destWindowId, tabGroupMetadata, newIndex, /* bringToFront= */ false); + return true; } /**
diff --git a/chrome/android/javatests/BUILD.gn b/chrome/android/javatests/BUILD.gn index 7b6b17a5..0204ef8 100644 --- a/chrome/android/javatests/BUILD.gn +++ b/chrome/android/javatests/BUILD.gn
@@ -2009,6 +2009,7 @@ deps = [ ":common_unit_test_deps_java", "//chrome/browser/ui/android/actions:java", + "//chrome/browser/ui/android/pdf:pdf_utils_java", "//components/omnibox/common:features_java", ] }
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/ExampleReusedCtaTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/ExampleReusedCtaTest.java index 8429af0..f4577666 100644 --- a/chrome/android/javatests/src/org/chromium/chrome/browser/ExampleReusedCtaTest.java +++ b/chrome/android/javatests/src/org/chromium/chrome/browser/ExampleReusedCtaTest.java
@@ -4,6 +4,9 @@ package org.chromium.chrome.browser; +import org.chromium.base.test.util.DisableIf; +import org.chromium.ui.base.DeviceFormFactor; + import androidx.test.filters.LargeTest; import org.junit.Rule; @@ -25,6 +28,7 @@ @RunWith(ChromeJUnit4ClassRunner.class) @CommandLineFlags.Add({ChromeSwitches.DISABLE_FIRST_RUN_EXPERIENCE}) @Batch(Batch.PER_CLASS) +@DisableIf.Device(DeviceFormFactor.DESKTOP_FREEFORM) // crbug.com/444482498 public class ExampleReusedCtaTest { @Rule public ReusedCtaTransitTestRule<RegularNewTabPageStation> mActivityTestRule =
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/PopupPTTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/PopupPTTest.java index ddb9dc5..e7c6340a 100644 --- a/chrome/android/javatests/src/org/chromium/chrome/browser/PopupPTTest.java +++ b/chrome/android/javatests/src/org/chromium/chrome/browser/PopupPTTest.java
@@ -22,6 +22,7 @@ import org.chromium.base.test.util.Batch; import org.chromium.base.test.util.CommandLineFlags; +import org.chromium.base.test.util.DisableIf; import org.chromium.base.test.util.Features; import org.chromium.base.test.util.RequiresRestart; import org.chromium.chrome.browser.flags.ChromeFeatureList; @@ -35,6 +36,7 @@ import org.chromium.chrome.test.transit.testhtmls.PopupOnClickPageStation; import org.chromium.chrome.test.transit.testhtmls.PopupOnLoadPageStation; import org.chromium.components.safe_browsing.SafeBrowsingApiBridge; +import org.chromium.ui.base.DeviceFormFactor; /** Tests whether popup windows appear or get blocked as expected. */ @RunWith(ChromeJUnit4ClassRunner.class) @@ -98,6 +100,7 @@ @Test @MediumTest + @DisableIf.Device(DeviceFormFactor.DESKTOP_FREEFORM) // crbug.com/444482498 public void test020SafeGestureTabNotBlocked() { PopupOnClickPageStation page = PopupOnClickPageStation.loadInCurrentTab( @@ -136,6 +139,7 @@ @Test @MediumTest + @DisableIf.Device(DeviceFormFactor.DESKTOP_FREEFORM) // crbug.com/444482498 public void test030AbusiveGesturePopupBlocked() { MockSafeBrowsingApiHandler.addMockResponse( mCtaTestRule.getTestServer().getURL(PopupOnClickPageStation.PATH), @@ -155,6 +159,7 @@ @Test @MediumTest @RequiresRestart + @DisableIf.Device(DeviceFormFactor.DESKTOP_FREEFORM) // crbug.com/444482498 public void testBlankPopupLaunchedFromBlockedChip() { PopupBlockedMessageFacility popupBlockedMessage = BlankPopupOnLoadPageStation.loadInCurrentTabExpectBlocked(
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/TabsTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/TabsTest.java index 1c1a4b0d..f5e24d0 100644 --- a/chrome/android/javatests/src/org/chromium/chrome/browser/TabsTest.java +++ b/chrome/android/javatests/src/org/chromium/chrome/browser/TabsTest.java
@@ -546,6 +546,7 @@ @Test @MediumTest @Feature({"Android-TabSwitcher"}) + @DisableIf.Device(DeviceFormFactor.DESKTOP_FREEFORM) // crbug.com/444482498 public void testOrientationChangeCausesLiveTabReflowInNormalView() throws InterruptedException, TimeoutException { mActivityTestRule
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/ToolbarSwipeTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/ToolbarSwipeTest.java index 99e8b35f..3ac3468 100644 --- a/chrome/android/javatests/src/org/chromium/chrome/browser/ToolbarSwipeTest.java +++ b/chrome/android/javatests/src/org/chromium/chrome/browser/ToolbarSwipeTest.java
@@ -24,6 +24,7 @@ import org.chromium.base.test.util.Batch; import org.chromium.base.test.util.CommandLineFlags; import org.chromium.base.test.util.CriteriaHelper; +import org.chromium.base.test.util.DisableIf; import org.chromium.base.test.util.Feature; import org.chromium.base.test.util.Restriction; import org.chromium.base.test.util.UrlUtils; @@ -162,6 +163,7 @@ @Test @MediumTest @Feature({"Android-TabSwitcher"}) + @DisableIf.Device(DeviceFormFactor.DESKTOP_FREEFORM) // crbug.com/444482498 public void testToolbarSwipeNextThenPrevTabIncognito() { WebPageStation pageStation = initToolbarSwipeTest(
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/app/ContextMenuDragTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/app/ContextMenuDragTest.java index ff62f92..497cede 100644 --- a/chrome/android/javatests/src/org/chromium/chrome/browser/app/ContextMenuDragTest.java +++ b/chrome/android/javatests/src/org/chromium/chrome/browser/app/ContextMenuDragTest.java
@@ -4,6 +4,9 @@ package org.chromium.chrome.browser.app; +import org.chromium.base.test.util.DisableIf; +import org.chromium.ui.base.DeviceFormFactor; + import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.mock; @@ -56,6 +59,7 @@ }) @EnableFeatures({ContentFeatures.TOUCH_DRAG_AND_CONTEXT_MENU}) @Batch(Batch.PER_CLASS) +@DisableIf.Device(DeviceFormFactor.DESKTOP_FREEFORM) // crbug.com/444482498 public class ContextMenuDragTest { // Test distance
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/compositor/layouts/LayoutManagerTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/compositor/layouts/LayoutManagerTest.java index 41da6a26..57a31faa 100644 --- a/chrome/android/javatests/src/org/chromium/chrome/browser/compositor/layouts/LayoutManagerTest.java +++ b/chrome/android/javatests/src/org/chromium/chrome/browser/compositor/layouts/LayoutManagerTest.java
@@ -4,6 +4,8 @@ package org.chromium.chrome.browser.compositor.layouts; +import static org.mockito.Mockito.when; + import static org.chromium.base.test.util.Batch.PER_CLASS; import static org.chromium.ui.test.util.ViewUtils.createMotionEvent; @@ -62,6 +64,7 @@ import org.chromium.chrome.browser.tab.Tab; import org.chromium.chrome.browser.tab_ui.TabContentManager; import org.chromium.chrome.browser.tab_ui.TabSwitcher; +import org.chromium.chrome.browser.tabmodel.OverridableTabCount; import org.chromium.chrome.browser.tabmodel.TabClosureParams; import org.chromium.chrome.browser.tabmodel.TabModel; import org.chromium.chrome.browser.tabmodel.TabModelSelector; @@ -102,6 +105,7 @@ @Mock private ToolbarManager mToolbarManager; @Mock private ViewGroup mContentView; @Mock private CompositorViewHolder mCompositorViewHolder; + @Mock private OverridableTabCount mOverridableTabCount; private NonNullObservableSupplier<CompositorViewHolder> mCompositorViewHolderSupplier; private TabModelSelector mTabModelSelector; @@ -218,6 +222,10 @@ ObservableSuppliers.createMonotonic(); mTabSwitcherSupplier = new OneshotSupplierImpl<>(); + mContentView = new FrameLayout(context); + when(mToolbarManager.getNtpSearchBoxTransitionPercentageSupplier()) + .thenReturn(ObservableSuppliers.createNonNull(0f)); + when(mToolbarManager.getOverridableTabCount()).thenReturn(mOverridableTabCount); mManagerPhone = new LayoutManagerChromePhone( layoutManagerHost,
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/compositor/layouts/phone/NewBackgroundTabFakeTabSwitcherButtonRenderTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/compositor/layouts/phone/NewBackgroundTabFakeTabSwitcherButtonRenderTest.java index 5e8fcf2..13fc576 100644 --- a/chrome/android/javatests/src/org/chromium/chrome/browser/compositor/layouts/phone/NewBackgroundTabFakeTabSwitcherButtonRenderTest.java +++ b/chrome/android/javatests/src/org/chromium/chrome/browser/compositor/layouts/phone/NewBackgroundTabFakeTabSwitcherButtonRenderTest.java
@@ -36,6 +36,7 @@ import org.chromium.base.test.util.Feature; import org.chromium.chrome.R; import org.chromium.chrome.browser.compositor.layouts.phone.NewBackgroundTabFakeTabSwitcherButton.TranslateDirection; +import org.chromium.chrome.browser.theme.ThemeUtils; import org.chromium.chrome.browser.ui.theme.BrandedColorScheme; import org.chromium.ui.interpolators.Interpolators; import org.chromium.ui.test.util.BlankUiTestActivity; @@ -134,7 +135,10 @@ public void testRenderFakeTabSwitcherButton_Incognito() throws Exception { Runnable updateRunnable = () -> { - mFakeTabSwitcherButton.setBrandedColorScheme(BrandedColorScheme.INCOGNITO); + mFakeTabSwitcherButton.setBrandedColorScheme( + BrandedColorScheme.INCOGNITO, + ThemeUtils.getThemedToolbarIconTint( + sActivity, BrandedColorScheme.INCOGNITO)); mFakeTabSwitcherButton.setTabCount(/* tabCount= */ 4, /* isIncognito= */ true); }; runUpdateOnUiThreadAndPerformRender(updateRunnable, "fake_tab_switcher_button_4_incognito");
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/compositor/overlay_panel/OverlayPanelBaseTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/compositor/overlay_panel/OverlayPanelBaseTest.java index 8830ecc..1e3c14d 100644 --- a/chrome/android/javatests/src/org/chromium/chrome/browser/compositor/overlay_panel/OverlayPanelBaseTest.java +++ b/chrome/android/javatests/src/org/chromium/chrome/browser/compositor/overlay_panel/OverlayPanelBaseTest.java
@@ -543,4 +543,35 @@ mExpandPanel.getOffsetY(), MathUtils.EPSILON); } + + @Test + @SmallTest + @Feature({"OverlayPanelBase"}) + @UiThreadTest + public void testResizeOnBottomControlsHeightChange() { + final float tabHeight = 1000; + mExpandPanel.onLayoutChanged(400, tabHeight, 100); + mExpandPanel.setIsFullWidthSizePanelForTesting(true); + + when(mBrowserControlsStateProvider.getControlsPosition()).thenReturn(ControlsPosition.TOP); + + float peekHeight = mExpandPanel.getPeekedHeight(); + mExpandPanel.setPanelState(PanelState.PEEKED, StateChangeReason.UNKNOWN); + mExpandPanel.setPanelHeight(peekHeight); + Assert.assertEquals(tabHeight - peekHeight, mExpandPanel.getOffsetY(), MathUtils.EPSILON); + + when(mBrowserControlsStateProvider.getControlsPosition()) + .thenReturn(ControlsPosition.BOTTOM); + when(mBottomControlsStacker.getTotalHeight()).thenReturn(MOCK_TOOLBAR_HEIGHT + CHIN_HEIGHT); + when(mBottomControlsStacker.getHeightFromLayerToBottom(LayerType.BOTTOM_CHIN)) + .thenReturn(CHIN_HEIGHT); + + mBrowserControlsStateProviderObserverCaptor + .getValue() + .onBottomControlsHeightChanged(MOCK_TOOLBAR_HEIGHT + CHIN_HEIGHT, 0); + Assert.assertEquals( + tabHeight - peekHeight - (MOCK_TOOLBAR_HEIGHT * mExpandPanel.mPxToDp), + mExpandPanel.getOffsetY(), + MathUtils.EPSILON); + } }
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/device_dialog/ItemChooserDialogTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/device_dialog/ItemChooserDialogTest.java index 8609828..6ebf8e1a 100644 --- a/chrome/android/javatests/src/org/chromium/chrome/browser/device_dialog/ItemChooserDialogTest.java +++ b/chrome/android/javatests/src/org/chromium/chrome/browser/device_dialog/ItemChooserDialogTest.java
@@ -167,11 +167,11 @@ } private ImageView getIconImageView(Dialog dialog, int position) { - return (ImageView) getRowView(dialog, position).findViewById(R.id.icon); + return getRowView(dialog, position).findViewById(R.id.icon); } private TextView getDescriptionTextView(Dialog dialog, int position) { - return (TextView) getRowView(dialog, position).findViewById(R.id.description); + return getRowView(dialog, position).findViewById(R.id.description); } @Test @@ -944,7 +944,7 @@ return dialog1; }); - Button cancelButton = (Button) dialog.findViewById(R.id.negative); + Button cancelButton = dialog.findViewById(R.id.negative); CriteriaHelper.pollUiThread( () -> Criteria.checkThat(cancelButton.getVisibility(), Matchers.is(View.VISIBLE))); CriteriaHelper.pollUiThread(
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/fullscreen/FullscreenManagerTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/fullscreen/FullscreenManagerTest.java index 1022447..d025c71f 100644 --- a/chrome/android/javatests/src/org/chromium/chrome/browser/fullscreen/FullscreenManagerTest.java +++ b/chrome/android/javatests/src/org/chromium/chrome/browser/fullscreen/FullscreenManagerTest.java
@@ -569,7 +569,7 @@ FullscreenManagerTestUtils.waitForBrowserControlsToBeMoveable(activity); // Check that the URL bar has not grabbed focus (http://crbug.com/40315594) - UrlBar urlBar = (UrlBar) activity.findViewById(R.id.url_bar); + UrlBar urlBar = activity.findViewById(R.id.url_bar); Assert.assertFalse("Url bar grabbed focus", urlBar.hasFocus()); } @@ -597,7 +597,7 @@ FullscreenManagerTestUtils.waitForBrowserControlsToBeMoveable(activity); // Check that the URL bar has not grabbed focus (http://crbug.com/40315594) - UrlBar urlBar = (UrlBar) activity.findViewById(R.id.url_bar); + UrlBar urlBar = activity.findViewById(R.id.url_bar); Assert.assertFalse("Url bar grabbed focus", urlBar.hasFocus()); }
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/infobar/InfoBarContainerTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/infobar/InfoBarContainerTest.java index 5a33cab..081e337 100644 --- a/chrome/android/javatests/src/org/chromium/chrome/browser/infobar/InfoBarContainerTest.java +++ b/chrome/android/javatests/src/org/chromium/chrome/browser/infobar/InfoBarContainerTest.java
@@ -201,7 +201,7 @@ mActivityTestRule.loadUrl(mTestServer.getURL("/chrome/test/data/android/about.html")); List<InfoBar> infoBars = mActivityTestRule.getInfoBars(); Assert.assertEquals(1, infoBars.size()); - TextView message = (TextView) infoBars.get(0).getView().findViewById(R.id.infobar_message); + TextView message = infoBars.get(0).getView().findViewById(R.id.infobar_message); Assert.assertEquals(MESSAGE_TEXT, message.getText().toString()); // Close the infobar.
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/ntp/RecentTabsPageTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/ntp/RecentTabsPageTest.java index eb08a720..ea150ad7 100644 --- a/chrome/android/javatests/src/org/chromium/chrome/browser/ntp/RecentTabsPageTest.java +++ b/chrome/android/javatests/src/org/chromium/chrome/browser/ntp/RecentTabsPageTest.java
@@ -222,7 +222,7 @@ final String title = group.getTitle(); waitForView(title); - ImageView iconView = (ImageView) mPage.getView().findViewById(R.id.row_icon); + ImageView iconView = mPage.getView().findViewById(R.id.row_icon); assertNotNull(iconView.getBackground()); assertEquals(View.VISIBLE, iconView.getVisibility()); assertThat(iconView.getBackground(), instanceOf(GradientDrawable.class)); @@ -272,7 +272,7 @@ waitForViewToDisappear(groupString); // Check that the remaining show history row item does not have an icon visible. - ImageView iconView = (ImageView) mPage.getView().findViewById(R.id.row_icon); + ImageView iconView = mPage.getView().findViewById(R.id.row_icon); assertEquals(View.GONE, iconView.getVisibility()); }
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/omnibox/LocationBarLayoutTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/omnibox/LocationBarLayoutTest.java index 5032bdc..0a86728 100644 --- a/chrome/android/javatests/src/org/chromium/chrome/browser/omnibox/LocationBarLayoutTest.java +++ b/chrome/android/javatests/src/org/chromium/chrome/browser/omnibox/LocationBarLayoutTest.java
@@ -84,11 +84,11 @@ } private UrlBar getUrlBar() { - return (UrlBar) mActivityTestRule.getActivity().findViewById(R.id.url_bar); + return mActivityTestRule.getActivity().findViewById(R.id.url_bar); } private LocationBarLayout getLocationBar() { - return (LocationBarLayout) mActivityTestRule.getActivity().findViewById(R.id.location_bar); + return mActivityTestRule.getActivity().findViewById(R.id.location_bar); } private LocationBarMediator getLocationBarMediator() {
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/omnibox/OmniboxTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/omnibox/OmniboxTest.java index 8696091..2977dc83 100644 --- a/chrome/android/javatests/src/org/chromium/chrome/browser/omnibox/OmniboxTest.java +++ b/chrome/android/javatests/src/org/chromium/chrome/browser/omnibox/OmniboxTest.java
@@ -363,7 +363,7 @@ locationBar.getStatusCoordinatorForTesting(); final int firstIcon = statusCoordinator.getSecurityIconResourceIdForTesting(); - UrlBar urlBar = (UrlBar) mActivityTestRule.getActivity().findViewById(R.id.url_bar); + UrlBar urlBar = mActivityTestRule.getActivity().findViewById(R.id.url_bar); ThreadUtils.runOnUiThreadBlocking(() -> urlBar.requestFocus()); CriteriaHelper.pollUiThread( () -> statusCoordinator.getSecurityIconResourceIdForTesting() != firstIcon);
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/password_manager/TouchToFillMainFlowIntegrationTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/password_manager/TouchToFillMainFlowIntegrationTest.java index 8a90e92..aa3d03c8 100644 --- a/chrome/android/javatests/src/org/chromium/chrome/browser/password_manager/TouchToFillMainFlowIntegrationTest.java +++ b/chrome/android/javatests/src/org/chromium/chrome/browser/password_manager/TouchToFillMainFlowIntegrationTest.java
@@ -22,6 +22,7 @@ import org.chromium.base.ThreadUtils; import org.chromium.base.test.util.CommandLineFlags; import org.chromium.base.test.util.CriteriaHelper; +import org.chromium.base.test.util.DisableIf; import org.chromium.base.test.util.DoNotBatch; import org.chromium.base.test.util.Features.DisableFeatures; import org.chromium.chrome.R; @@ -40,6 +41,7 @@ import org.chromium.content_public.browser.test.util.DOMUtils; import org.chromium.net.test.EmbeddedTestServer; import org.chromium.net.test.ServerCertificate; +import org.chromium.ui.base.DeviceFormFactor; import org.chromium.url.GURL; import java.util.concurrent.TimeoutException; @@ -116,6 +118,7 @@ @Test @MediumTest @DisableFeatures(ChromeFeatureList.AUTOFILL_ANDROID_KEYBOARD_ACCESSORY_DYNAMIC_POSITIONING) + @DisableIf.Device(DeviceFormFactor.DESKTOP_FREEFORM) // crbug.com/444482498 public void testClickingSuggestionPopulatesForm() throws TimeoutException, InterruptedException { // Fill the password store.
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/printing/PrintingControllerTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/printing/PrintingControllerTest.java index 3b879af..f653c4e0 100644 --- a/chrome/android/javatests/src/org/chromium/chrome/browser/printing/PrintingControllerTest.java +++ b/chrome/android/javatests/src/org/chromium/chrome/browser/printing/PrintingControllerTest.java
@@ -390,6 +390,38 @@ @Test @SmallTest @Feature({"Printing"}) + public void testGetFileDescriptorAfterFinish() throws Exception { + WebPageStation page = mActivityTestRule.startOnUrl(URL); + final PrintingControllerImpl controller = createControllerOnUiThread(); + + startControllerOnUiThread(controller, page.getTab()); + + try (TemporaryFileHandler handler = new TemporaryFileHandler()) { + ThreadUtils.runOnUiThreadBlocking( + () -> { + Assert.assertNull(controller.getParcelFileDescriptor()); + controller.onStart(); + + // Simulate onWrite to set the file descriptor + controller.onWrite( + new PageRange[] {PageRange.ALL_PAGES}, + handler.getFileDescriptor(), + new CancellationSignal(), + new WriteResultCallbackWrapperMock()); + + // Check that it is now a valid FD (non-null ParcelFileDescriptor) + Assert.assertNotNull(controller.getParcelFileDescriptor()); + + controller.onFinish(); + // Verify it goes back to invalid after finish + Assert.assertNull(controller.getParcelFileDescriptor()); + }); + } + } + + @Test + @SmallTest + @Feature({"Printing"}) public void testTabPrinterCanPrintHiddenTab() { WebPageStation page = mActivityTestRule.startOnUrl(URL); ChromeTabbedActivity cta = page.getActivity();
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/tabmodel/TabModelMultiWindowTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/tabmodel/TabModelMultiWindowTest.java index 641c8a9..9d0e16f 100644 --- a/chrome/android/javatests/src/org/chromium/chrome/browser/tabmodel/TabModelMultiWindowTest.java +++ b/chrome/android/javatests/src/org/chromium/chrome/browser/tabmodel/TabModelMultiWindowTest.java
@@ -6,8 +6,10 @@ import static org.hamcrest.CoreMatchers.notNullValue; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotEquals; import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.when; @@ -134,9 +136,12 @@ long nativeBrowserWindow2 = getNativeBrowserWindow(activity2); runOnUiThreadBlocking( - () -> - mTabModelJni.moveTabGroupToWindowForTesting( - groupId, nativeBrowserWindow2, 0)); + () -> { + boolean moved = + mTabModelJni.moveTabGroupToWindowForTesting( + groupId, nativeBrowserWindow2, /* newIndex= */ 0); + assertTrue(moved); + }); assertEquals(initialTabCount - 2, getTabCountOnUiThread(mTabModelJni)); TabModel model2 = activity2.getTabModelSelector().getModel(false); @@ -147,6 +152,34 @@ assertEquals(groupId, group.get(1).getTabGroupId()); } + @Test + @DisableIf.Device(DeviceFormFactor.DESKTOP) // https://crbug.com/481443908 + @LargeTest + public void testMoveTabGroupToWindowFailure() { + // Create a tab group. + ChromeTabbedActivity activity1 = mActivityTestRule.getActivity(); + TabModel tabModel = activity1.getTabModelSelector().getModel(false); + createTabGroup(2, tabModel); + + // Create an invalid group ID. + Token invalidGroupId = new Token(0, 0); + + // Create a new window. + ChromeTabbedActivity activity2 = createNewWindow(activity1); + assertNotNull(activity2); + assertNotEquals(activity1, activity2); + long nativeBrowserWindow2 = getNativeBrowserWindow(activity2); + + // Try to move an invalid group. + runOnUiThreadBlocking( + () -> { + boolean moved = + mTabModelJni.moveTabGroupToWindowForTesting( + invalidGroupId, nativeBrowserWindow2, /* newIndex= */ 0); + assertFalse(moved); + }); + } + private long getNativeBrowserWindow(ChromeTabbedActivity activity) { return runOnUiThreadBlocking( () -> {
diff --git a/chrome/android/junit/BUILD.gn b/chrome/android/junit/BUILD.gn index 71b981e..7c32ec2 100644 --- a/chrome/android/junit/BUILD.gn +++ b/chrome/android/junit/BUILD.gn
@@ -439,7 +439,6 @@ "src/org/chromium/chrome/browser/TabGroupUsageTrackerUnitTest.java", "src/org/chromium/chrome/browser/TabStateThemeResourceProviderTest.java", "src/org/chromium/chrome/browser/UndoRefocusHelperTest.java", - "src/org/chromium/chrome/browser/WebSearchDelegateUnitTest.java", "src/org/chromium/chrome/browser/document/ChromeLauncherActivityTest.java", "src/org/chromium/chrome/browser/handoff/HandoffControllerUnitTest.java", ] @@ -616,6 +615,7 @@ "//chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/LayoutManagerChromeUnitTest.java", "//chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/phone/AnimationFreezeCheckerUnitTest.java", "//chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/phone/AnimationInterruptorUnitTest.java", + "//chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/phone/NewBackgroundTabAnimationDataUnitTest.java", "//chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/phone/NewBackgroundTabAnimationHostViewUnitTest.java", "//chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/phone/NewTabAnimationLayoutUnitTest.java", "src/org/chromium/chrome/browser/compositor/CompositorSurfaceManagerImplTest.java", @@ -1093,8 +1093,8 @@ "//chrome/browser/share/android/javatests/src/org/chromium/chrome/browser/share/android_share_sheet/AndroidShareSheetControllerUnitTest.java", "//chrome/browser/share/android/javatests/src/org/chromium/chrome/browser/share/link_to_text/LinkToTextCoordinatorTest.java", "//chrome/browser/share/android/javatests/src/org/chromium/chrome/browser/share/link_to_text/LinkToTextHelperTest.java", - "//chrome/browser/share/android/javatests/src/org/chromium/chrome/browser/share/long_screenshots/LongScreenshotErrorUtilsTest.java", "//chrome/browser/share/android/javatests/src/org/chromium/chrome/browser/share/long_screenshots/LongScreenshotsCoordinatorTest.java", + "//chrome/browser/share/android/javatests/src/org/chromium/chrome/browser/share/long_screenshots/LongScreenshotsUtilsTest.java", "//chrome/browser/share/android/javatests/src/org/chromium/chrome/browser/share/long_screenshots/bitmap_generation/EntryManagerTest.java", "//chrome/browser/share/android/javatests/src/org/chromium/chrome/browser/share/long_screenshots/bitmap_generation/LongScreenshotsCompositorTest.java", "//chrome/browser/share/android/javatests/src/org/chromium/chrome/browser/share/long_screenshots/bitmap_generation/LongScreenshotsEntryTest.java", @@ -1121,6 +1121,7 @@ deps = [ ":chrome_junit_tests_helper", + "//chrome/browser/ui/android/pdf:pdf_utils_java", "//chrome/browser/ui/android/toolbar:adaptive_features_java", "//components/paint_preview/common/mojom:mojom_java", "//components/strings:components_strings_grd", @@ -1201,7 +1202,10 @@ "src/org/chromium/chrome/browser/tab/tab_restore/HistoricalTabSaverImplUnitTest.java", ] - deps = [ ":chrome_junit_tests_helper" ] + deps = [ + ":chrome_junit_tests_helper", + "//chrome/browser/ui/android/pdf:pdf_info_java", + ] } robolectric_library( @@ -1403,6 +1407,7 @@ deps = [ ":chrome_junit_tests_helper", + "//chrome/browser/ui/android/pdf:pdf_utils_java", "//chrome/browser/ui/android/toolbar:adaptive_features_java", "//chrome/browser/ui/android/toolbar:optional_button_java", ] @@ -1556,6 +1561,9 @@ "//chrome/browser/actor/android:java", "//chrome/browser/notifications:java", "//chrome/browser/privacy:privacy_preference_manager_java", + "//chrome/browser/ui/android/pdf:pdf_info_java", + "//chrome/browser/ui/android/pdf:pdf_page_java", + "//chrome/browser/ui/android/pdf:pdf_utils_java", "//chrome/browser/ui/android/toolbar:adaptive_features_java", "//components/dom_distiller/content/browser/android:dom_distiller_content_java", "//components/dom_distiller/core/android:dom_distiller_core_java",
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/compositor/overlays/strip/StripLayoutHelperManagerTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/compositor/overlays/strip/StripLayoutHelperManagerTest.java index 64e7460..fe66524 100644 --- a/chrome/android/junit/src/org/chromium/chrome/browser/compositor/overlays/strip/StripLayoutHelperManagerTest.java +++ b/chrome/android/junit/src/org/chromium/chrome/browser/compositor/overlays/strip/StripLayoutHelperManagerTest.java
@@ -281,7 +281,7 @@ /* xrSpaceModeObservableSupplier= */ null, mBackPressManager, mSnackbarManager, - () -> {}, + CallbackUtils.emptyCallback(), mGlicKeyedService); mStripLayoutHelperManager.setTabStripTreeProviderForTesting(mTabStripTreeProvider); mStripLayoutHelperManager.setTabModelSelector(mTabModelSelector, mTabCreatorManager);
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/compositor/overlays/strip/StripLayoutTrailingButtonsCoordinatorTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/compositor/overlays/strip/StripLayoutTrailingButtonsCoordinatorTest.java index 23e2b3ea..7c324c2 100644 --- a/chrome/android/junit/src/org/chromium/chrome/browser/compositor/overlays/strip/StripLayoutTrailingButtonsCoordinatorTest.java +++ b/chrome/android/junit/src/org/chromium/chrome/browser/compositor/overlays/strip/StripLayoutTrailingButtonsCoordinatorTest.java
@@ -31,11 +31,14 @@ import org.mockito.junit.MockitoRule; import org.robolectric.Robolectric; +import org.chromium.base.Callback; import org.chromium.base.MathUtils; import org.chromium.base.test.BaseRobolectricTestRunner; import org.chromium.base.test.util.Features.DisableFeatures; import org.chromium.base.test.util.Features.EnableFeatures; import org.chromium.chrome.R; +import org.chromium.chrome.browser.actor.ActorKeyedService; +import org.chromium.chrome.browser.actor.ActorKeyedServiceFactory; import org.chromium.chrome.browser.compositor.LayerTitleCache; import org.chromium.chrome.browser.compositor.layouts.LayoutRenderHost; import org.chromium.chrome.browser.compositor.layouts.LayoutUpdateHost; @@ -68,7 +71,7 @@ @Mock private LayoutRenderHost mRenderHost; @Mock private LayerTitleCache mLayerTitleCache; @Mock private GlicKeyedService mGlicKeyedService; - @Mock private Runnable mGlicClickHandler; + @Mock private Callback<Boolean> mGlicClickHandler; @Mock private View mToolbarContainerView; @Mock private WindowAndroid mWindowAndroid; @Mock private Profile mProfile; @@ -78,6 +81,7 @@ @Mock private StripLayoutTrailingButtonsObserver mObserver; @Mock private ChromeAndroidTaskTracker mTaskTracker; @Mock private ChromeAndroidTask mTask; + @Mock private ActorKeyedService mActorKeyedService; private Activity mActivity; private StripLayoutTrailingButtonsCoordinator mCoordinator; @@ -94,8 +98,12 @@ when(mUserPrefsJniMock.get(mProfile)).thenReturn(mPrefService); when(mPrefService.getBoolean(GlicPrefNames.GLIC_PINNED_TO_TABSTRIP)).thenReturn(true); + ActorKeyedServiceFactory.setForTesting(mActorKeyedService); + when(mActorKeyedService.getActiveTasks()).thenReturn(java.util.Collections.emptyList()); + mActivity = Robolectric.buildActivity(TestActivity.class).setup().get(); mActivity.setTheme(R.style.Theme_BrowserUI_DayNight); + when(mWindowAndroid.getActivity()).thenReturn(new WeakReference<>(mActivity)); when(mTaskTracker.get(anyInt())).thenReturn(mTask); when(mTask.getNativeBrowserWindowPtr(any(), any())).thenReturn(mBwiPtr); @@ -122,6 +130,7 @@ mGlicKeyedService, mTaskTracker, () -> mIsIncognito, + () -> null, mObserver); mCoordinator.onProfileAvailable(mProfile); mCoordinator.setLayerTitleCache(mLayerTitleCache); @@ -355,8 +364,6 @@ /* leftPadding= */ 10f, /* topPadding= */ 10f); - when(mWindowAndroid.getActivity()).thenReturn(new WeakReference<>(mActivity)); - float x = mGlicButton.getDrawX() + mGlicButton.getWidth() / 2; float y = mGlicButton.getDrawY() + mGlicButton.getHeight() / 2; @@ -380,7 +387,7 @@ float glicY = mGlicButton.getDrawY() + mGlicButton.getHeight() / 2; boolean glicHandled = mCoordinator.click(0L, glicX, glicY, 0, 0, /* tabWidthDp= */ 100f); assertTrue("Click on Glic coordinates should be handled.", glicHandled); - verify(mGlicClickHandler, Mockito.times(1)).run(); + verify(mGlicClickHandler, Mockito.times(1)).onResult(/* result= */ false); // 2. Test click routing on Glic Actor Button coordinates float actorX = mGlicActorButton.getDrawX() + mGlicActorButton.getWidth() / 2;
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/dom_distiller/ReaderModePrefsViewUnitTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/dom_distiller/ReaderModePrefsViewUnitTest.java index e4a30c0..3105b5ca 100644 --- a/chrome/android/junit/src/org/chromium/chrome/browser/dom_distiller/ReaderModePrefsViewUnitTest.java +++ b/chrome/android/junit/src/org/chromium/chrome/browser/dom_distiller/ReaderModePrefsViewUnitTest.java
@@ -90,7 +90,7 @@ assertTrue( ((MaterialButton) mReaderModePrefsView.findViewById(R.id.font_sans_serif)) .isChecked()); - Slider slider = (Slider) mReaderModePrefsView.findViewById(R.id.font_size_slider); + Slider slider = mReaderModePrefsView.findViewById(R.id.font_size_slider); assertEquals(1.0f, slider.getValue(), 0.0f); } @@ -236,7 +236,7 @@ .expectIntRecord( "DomDistiller.Android.FontScalingSelected", expectedMaxPercent) .build(); - Slider slider = (Slider) mReaderModePrefsView.findViewById(R.id.font_size_slider); + Slider slider = mReaderModePrefsView.findViewById(R.id.font_size_slider); // Manually force abritary size on the slider for the purposes of simulating drag motion. slider.measure(
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/feed/FeedSurfaceCoordinatorTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/feed/FeedSurfaceCoordinatorTest.java index 21c3070..db31b21 100644 --- a/chrome/android/junit/src/org/chromium/chrome/browser/feed/FeedSurfaceCoordinatorTest.java +++ b/chrome/android/junit/src/org/chromium/chrome/browser/feed/FeedSurfaceCoordinatorTest.java
@@ -114,7 +114,7 @@ /** Tests for {@link FeedSurfaceCoordinator}. */ @RunWith(BaseRobolectricTestRunner.class) @Config(manifest = Config.NONE) -@DisableFeatures({ChromeFeatureList.WEB_FEED_ONBOARDING, ChromeFeatureList.FEED_CONTAINMENT}) +@DisableFeatures({ChromeFeatureList.FEED_CONTAINMENT}) @EnableFeatures({SigninFeatures.ENABLE_SEAMLESS_SIGNIN}) public class FeedSurfaceCoordinatorTest { private static final @SurfaceType int SURFACE_TYPE = SurfaceType.NEW_TAB_PAGE;
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/tabbed_mode/TabbedAppMenuPropertiesDelegateUnitTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/tabbed_mode/TabbedAppMenuPropertiesDelegateUnitTest.java index 64d164d..469c168 100644 --- a/chrome/android/junit/src/org/chromium/chrome/browser/tabbed_mode/TabbedAppMenuPropertiesDelegateUnitTest.java +++ b/chrome/android/junit/src/org/chromium/chrome/browser/tabbed_mode/TabbedAppMenuPropertiesDelegateUnitTest.java
@@ -151,6 +151,7 @@ import java.util.Arrays; import java.util.Iterator; import java.util.List; +import java.util.function.BiConsumer; /** Unit tests for {@link TabbedAppMenuPropertiesDelegate}. */ // TODO(crbug.com/376238770): Removes ChromeFeatureList.NEW_TAB_PAGE_CUSTOMIZATION from @@ -271,6 +272,17 @@ // Used to ensure all the combinations are tested. private final boolean[] mFlagCombinations = new boolean[1 << 5]; + // Represents a hierarchical menu item used for structural testing and assertions. + public static class MenuItem { + public final int id; + public final MenuItem[] children; + + public MenuItem(int id, MenuItem... children) { + this.id = id; + this.children = children; + } + } + @Before public void setUp() { Context context = @@ -393,60 +405,111 @@ return null; } + private void assertMenuTreesAreEqual( + Iterable<MVCListAdapter.ListItem> items, + List<MenuItem> expectedNodes, + BiConsumer<MVCListAdapter.ListItem, Integer> assertionLogic) { + List<MVCListAdapter.ListItem> itemList = new ArrayList<>(); + for (MVCListAdapter.ListItem item : items) { + itemList.add(item); + } + + Assert.assertEquals("Mismatched item count.", expectedNodes.size(), itemList.size()); + + for (int i = 0; i < expectedNodes.size(); i++) { + assertMenuTreesAreEqualRecursively( + itemList.get(i), expectedNodes.get(i), assertionLogic); + } + } + + private void assertMenuTreesAreEqualRecursively( + MVCListAdapter.ListItem item, + MenuItem expectedNode, + BiConsumer<MVCListAdapter.ListItem, Integer> assertionLogic) { + assertionLogic.accept(item, expectedNode.id); + + boolean hasSubItems = + item.model.containsKey(AppMenuItemWithSubmenuProperties.SUBMENU_ITEMS); + Assert.assertEquals("Mismatched children.", expectedNode.children.length > 0, hasSubItems); + + if (!hasSubItems) return; + + List<MVCListAdapter.ListItem> subItems = + item.model.get(AppMenuItemWithSubmenuProperties.SUBMENU_ITEMS); + Assert.assertNotNull(subItems); + + Assert.assertEquals( + "Mismatched children count.", expectedNode.children.length, subItems.size()); + + for (int i = 0; i < subItems.size(); i++) { + assertMenuTreesAreEqualRecursively( + subItems.get(i), expectedNode.children[i], assertionLogic); + } + } + private void assertMenuItemsAreEqual( + Iterable<MVCListAdapter.ListItem> items, List<MenuItem> expectedItems) { + + assertMenuTreesAreEqual( + items, + expectedItems, + (item, expectedId) -> { + Assert.assertEquals( + "We got " + getMenuTitle(item) + ", which was unexpected.", + (int) expectedId, + item.model.get(AppMenuItemProperties.MENU_ITEM_ID)); + }); + } + + private void assertMenuTitlesAreEqual( + Iterable<MVCListAdapter.ListItem> items, List<MenuItem> expectedTitles) { + Context context = ContextUtils.getApplicationContext(); + assertMenuTreesAreEqual( + items, + expectedTitles, + (item, expectedTitleRes) -> { + CharSequence title = + item.model.containsKey(AppMenuItemProperties.TITLE) + ? item.model.get(AppMenuItemProperties.TITLE) + : null; + String expectedTitleString = + ((int) expectedTitleRes == 0) + ? null + : context.getString((int) expectedTitleRes); + Assert.assertEquals("Mismatched title:", expectedTitleString, title); + }); + } + + private void assertMenuItemsHaveIcons( MVCListAdapter.ModelList modelList, Integer... expectedItems) { List<Integer> actualItems = new ArrayList<>(); for (MVCListAdapter.ListItem item : modelList) { - actualItems.add(item.model.get(AppMenuItemProperties.MENU_ITEM_ID)); - } - - assertThat( - "Populated menu items were:" + getMenuTitles(modelList), - actualItems, - Matchers.containsInAnyOrder(expectedItems)); - } - - private void assertHasSubMenuItemIds( - MVCListAdapter.ListItem parentItem, Integer... expectedItems) { - assertNotNull("Parent item is null", parentItem); - assertTrue( - "Parent item is not a submenu", - parentItem.model.containsKey(AppMenuItemWithSubmenuProperties.SUBMENU_ITEMS)); - List<MVCListAdapter.ListItem> subItems = - parentItem.model.get(AppMenuItemWithSubmenuProperties.SUBMENU_ITEMS); - assertNotNull("Submenu item list is null", subItems); - - List<Integer> actualItems = new ArrayList<>(); - for (MVCListAdapter.ListItem item : subItems) { - actualItems.add(item.model.get(AppMenuItemProperties.MENU_ITEM_ID)); - } - - // Create a new ModelList and add the sub-items to it just for the error message. - MVCListAdapter.ModelList subModelList = new MVCListAdapter.ModelList(); - if (subItems != null) { - for (MVCListAdapter.ListItem item : subItems) { - subModelList.add(item); + if (item.model.containsKey(AppMenuItemProperties.ICON) + && item.model.get(AppMenuItemProperties.ICON) != null) { + actualItems.add(item.model.get(AppMenuItemProperties.MENU_ITEM_ID)); } } assertThat( - "Populated submenu items were:" + getMenuTitles(subModelList), + "menu items with icons were:" + getMenuTitles(modelList), actualItems, - Matchers.contains(expectedItems)); + Matchers.containsInAnyOrder(expectedItems)); } - private void assertMenuTitlesAreEqual( - MVCListAdapter.ModelList modelList, Integer... expectedTitles) { - Context context = ContextUtils.getApplicationContext(); - for (int i = 0; i < modelList.size(); i++) { - MVCListAdapter.ListItem listItem = modelList.get(i); - CharSequence title = - listItem.model.containsKey(AppMenuItemProperties.TITLE) - ? listItem.model.get(AppMenuItemProperties.TITLE) - : null; - Assert.assertEquals( - expectedTitles[i] == 0 ? null : context.getString(expectedTitles[i]), title); - } + private void assertMenuItemsHaveIcons( + Iterable<MVCListAdapter.ListItem> items, List<MenuItem> expectedItems) { + + assertMenuTreesAreEqual( + items, + expectedItems, + (item, expectedId) -> { + if (item.type != AppMenuHandler.AppMenuItemType.BUTTON_ROW + && item.type != AppMenuHandler.AppMenuItemType.DIVIDER) { + Assert.assertNotNull( + "Item should have an icon: " + getMenuTitle(item), + item.model.get(AppMenuItemProperties.ICON)); + } + }); } private void assertActionBarItemsAreEqual( @@ -466,22 +529,6 @@ Matchers.containsInAnyOrder(expectedItems)); } - private void assertMenuItemsHaveIcons( - MVCListAdapter.ModelList modelList, Integer... expectedItems) { - List<Integer> actualItems = new ArrayList<>(); - for (MVCListAdapter.ListItem item : modelList) { - if (item.model.containsKey(AppMenuItemProperties.ICON) - && item.model.get(AppMenuItemProperties.ICON) != null) { - actualItems.add(item.model.get(AppMenuItemProperties.MENU_ITEM_ID)); - } - } - - assertThat( - "menu items with icons were:" + getMenuTitles(modelList), - actualItems, - Matchers.containsInAnyOrder(expectedItems)); - } - private void setUpMocksForOverviewMenu() { when(mLayoutStateProvider.isLayoutVisible(LayoutType.TAB_SWITCHER)).thenReturn(true); when(mTabModelSelector.getTotalTabCount()).thenReturn(1); @@ -573,39 +620,41 @@ assertEquals(MenuGroup.PAGE_MENU, mTabbedAppMenuPropertiesDelegate.getMenuGroup()); MVCListAdapter.ModelList modelList = mTabbedAppMenuPropertiesDelegate.getMenuItems(); - List<Integer> expectedItems = + List<MenuItem> expectedItems = new ArrayList<>( Arrays.asList( - R.id.icon_row_menu_id, - R.id.new_tab_menu_id, - R.id.new_incognito_tab_menu_id, - R.id.add_to_group_menu_id, - R.id.divider_line_id, - R.id.history_parent_menu_id, - R.id.info_menu_id, - R.id.page_info_divider_line_id, - R.id.downloads_menu_id, - R.id.all_bookmarks_menu_id, - R.id.divider_line_id, - R.id.preferences_id, - R.id.ntp_customization_id, - R.id.help_id)); + item(R.id.icon_row_menu_id), + item(R.id.new_tab_menu_id), + item(R.id.new_incognito_tab_menu_id), + item(R.id.add_to_group_menu_id), + item(R.id.divider_line_id), + item( + R.id.history_parent_menu_id, + item(R.id.open_history_menu_id), + item(R.id.recent_tabs_menu_id), + item(R.id.quick_delete_menu_id)), + item(R.id.info_menu_id), + item(R.id.page_info_divider_line_id), + item(R.id.downloads_menu_id), + item(R.id.all_bookmarks_menu_id))); if (ExtensionsBuildflags.ENABLE_DESKTOP_ANDROID_EXTENSIONS) { - expectedItems.add(R.id.extensions_parent_menu_id); + expectedItems.add( + item( + R.id.extensions_parent_menu_id, + item(R.id.extensions_menu_menu_id), + item(R.id.manage_extensions_menu_id), + item(R.id.extensions_webstore_menu_id))); } - List<Integer> historyExpectedItems = - new ArrayList<>( - Arrays.asList( - R.id.open_history_menu_id, - R.id.recent_tabs_menu_id, - R.id.quick_delete_menu_id)); + expectedItems.addAll( + Arrays.asList( + item(R.id.divider_line_id), + item(R.id.preferences_id), + item(R.id.ntp_customization_id), + item(R.id.help_id))); - assertMenuItemsAreEqual(modelList, expectedItems.toArray(new Integer[0])); - assertMenuItemsAreEqual( - createModelList(getSubmenuItems(modelList, R.id.history_parent_menu_id)), - historyExpectedItems.toArray(new Integer[0])); + assertMenuItemsAreEqual(modelList, expectedItems); } @Test @@ -624,42 +673,44 @@ assertEquals(MenuGroup.PAGE_MENU, mTabbedAppMenuPropertiesDelegate.getMenuGroup()); MVCListAdapter.ModelList modelList = mTabbedAppMenuPropertiesDelegate.getMenuItems(); - List<Integer> expectedItems = + List<MenuItem> expectedItems = new ArrayList<>( Arrays.asList( - R.id.icon_row_menu_id, - R.id.new_tab_menu_id, - R.id.new_incognito_tab_menu_id, - R.id.add_to_group_menu_id, - R.id.divider_line_id, - R.id.history_parent_menu_id, - R.id.info_menu_id, - R.id.page_info_divider_line_id, - R.id.downloads_menu_id, - R.id.all_bookmarks_menu_id, - R.id.divider_line_id, - R.id.share_menu_id, - R.id.find_in_page_id, - R.id.open_with_id, - R.id.divider_line_id, - R.id.preferences_id, - R.id.help_id)); + item(R.id.icon_row_menu_id), + item(R.id.new_tab_menu_id), + item(R.id.new_incognito_tab_menu_id), + item(R.id.add_to_group_menu_id), + item(R.id.divider_line_id), + item( + R.id.history_parent_menu_id, + item(R.id.open_history_menu_id), + item(R.id.recent_tabs_menu_id), + item(R.id.quick_delete_menu_id)), + item(R.id.info_menu_id), + item(R.id.page_info_divider_line_id), + item(R.id.downloads_menu_id), + item(R.id.all_bookmarks_menu_id))); if (ExtensionsBuildflags.ENABLE_DESKTOP_ANDROID_EXTENSIONS) { - expectedItems.add(R.id.extensions_parent_menu_id); + expectedItems.add( + item( + R.id.extensions_parent_menu_id, + item(R.id.extensions_menu_menu_id), + item(R.id.manage_extensions_menu_id), + item(R.id.extensions_webstore_menu_id))); } - List<Integer> historyExpectedItems = - new ArrayList<>( - Arrays.asList( - R.id.open_history_menu_id, - R.id.recent_tabs_menu_id, - R.id.quick_delete_menu_id)); + expectedItems.addAll( + Arrays.asList( + item(R.id.divider_line_id), + item(R.id.share_menu_id), + item(R.id.find_in_page_id), + item(R.id.open_with_id), + item(R.id.divider_line_id), + item(R.id.preferences_id), + item(R.id.help_id))); - assertMenuItemsAreEqual(modelList, expectedItems.toArray(new Integer[0])); - assertMenuItemsAreEqual( - createModelList(getSubmenuItems(modelList, R.id.history_parent_menu_id)), - historyExpectedItems.toArray(new Integer[0])); + assertMenuItemsAreEqual(modelList, expectedItems); } private void testPageMenuItems_RegularPage() { @@ -674,73 +725,100 @@ assertEquals(MenuGroup.PAGE_MENU, mTabbedAppMenuPropertiesDelegate.getMenuGroup()); MVCListAdapter.ModelList modelList = mTabbedAppMenuPropertiesDelegate.getMenuItems(); - List<Integer> expectedItems = new ArrayList<>(); - List<Integer> expectedTitles = new ArrayList<>(); + List<MenuItem> expectedItems = new ArrayList<>(); + List<MenuItem> expectedTitles = new ArrayList<>(); - List<Integer> saveAndPrintExpectedItems = new ArrayList<>(); - List<Integer> saveAndPrintExpectedTitles = new ArrayList<>(); + expectedItems.add(item(R.id.icon_row_menu_id)); + expectedTitles.add(item(0)); - List<Integer> historyExpectedItems = new ArrayList<>(); - List<Integer> historyExpectedTitles = new ArrayList<>(); + expectedItems.add(item(R.id.new_tab_menu_id)); + expectedTitles.add(item(R.string.menu_new_tab)); - expectedItems.add(R.id.icon_row_menu_id); - expectedTitles.add(0); - expectedItems.add(R.id.new_tab_menu_id); - expectedTitles.add(R.string.menu_new_tab); if (!IncognitoUtils.shouldOpenIncognitoAsWindow()) { - expectedItems.add(R.id.new_incognito_tab_menu_id); - expectedTitles.add(R.string.menu_new_incognito_tab); + expectedItems.add(item(R.id.new_incognito_tab_menu_id)); + expectedTitles.add(item(R.string.menu_new_incognito_tab)); } - expectedItems.add(R.id.add_to_group_menu_id); - expectedTitles.add(R.string.menu_add_tab_to_new_group); - expectedItems.add(R.id.divider_line_id); - expectedTitles.add(0); - expectedItems.add(R.id.history_parent_menu_id); - expectedTitles.add(R.string.menu_history); - historyExpectedItems.add(R.id.open_history_menu_id); - historyExpectedTitles.add(R.string.menu_history); - historyExpectedItems.add(R.id.recent_tabs_menu_id); - historyExpectedTitles.add(R.string.menu_recent_tabs); - historyExpectedItems.add(R.id.quick_delete_menu_id); - historyExpectedTitles.add(R.string.menu_quick_delete); + + expectedItems.add(item(R.id.add_to_group_menu_id)); + expectedTitles.add(item(R.string.menu_add_tab_to_new_group)); + + expectedItems.add(item(R.id.divider_line_id)); + expectedTitles.add(item(0)); + + expectedItems.add( + item( + R.id.history_parent_menu_id, + item(R.id.open_history_menu_id), + item(R.id.recent_tabs_menu_id), + item(R.id.quick_delete_menu_id))); + expectedTitles.add( + item( + R.string.menu_history, + item(R.string.menu_history), + item(R.string.menu_recent_tabs), + item(R.string.menu_quick_delete))); + if (!mTabbedAppMenuPropertiesDelegate.isTabletSizeScreen()) { - expectedItems.add(R.id.info_menu_id); - expectedTitles.add(R.string.menu_site_controls); - expectedItems.add(R.id.page_info_divider_line_id); - expectedTitles.add(0); + expectedItems.add(item(R.id.info_menu_id)); + expectedTitles.add(item(R.string.menu_site_controls)); + + expectedItems.add(item(R.id.page_info_divider_line_id)); + expectedTitles.add(item(0)); } - expectedItems.add(R.id.downloads_menu_id); - expectedTitles.add(R.string.menu_downloads); - expectedItems.add(R.id.all_bookmarks_menu_id); - expectedTitles.add(R.string.menu_bookmarks); + + expectedItems.add(item(R.id.downloads_menu_id)); + expectedTitles.add(item(R.string.menu_downloads)); + + expectedItems.add(item(R.id.all_bookmarks_menu_id)); + expectedTitles.add(item(R.string.menu_bookmarks)); + if (ExtensionsBuildflags.ENABLE_DESKTOP_ANDROID_EXTENSIONS) { - expectedItems.add(R.id.extensions_parent_menu_id); - expectedTitles.add(R.string.menu_extensions); + expectedItems.add( + item( + R.id.extensions_parent_menu_id, + item(R.id.extensions_menu_menu_id), + item(R.id.manage_extensions_menu_id), + item(R.id.extensions_webstore_menu_id))); + expectedTitles.add( + item( + R.string.menu_extensions, + item(R.string.menu_extensions_menu), + item(R.string.menu_manage_extensions), + item(R.string.menu_chrome_webstore))); } - expectedItems.add(R.id.divider_line_id); - expectedTitles.add(0); - expectedItems.add(R.id.share_menu_id); - expectedTitles.add(R.string.menu_share_page); - expectedItems.add(R.id.save_and_print_parent_menu_id); - expectedTitles.add(R.string.menu_save_and_print); - saveAndPrintExpectedItems.add(R.id.universal_install); - saveAndPrintExpectedTitles.add(R.string.menu_add_to_homescreen); - expectedItems.add(R.id.find_in_page_id); - expectedTitles.add(R.string.menu_find_in_page); - expectedItems.add(R.id.translate_id); - expectedTitles.add(R.string.menu_translate); + + expectedItems.add(item(R.id.divider_line_id)); + expectedTitles.add(item(0)); + + expectedItems.add(item(R.id.share_menu_id)); + expectedTitles.add(item(R.string.menu_share_page)); + + expectedItems.add(item(R.id.save_and_print_parent_menu_id, item(R.id.universal_install))); + expectedTitles.add( + item(R.string.menu_save_and_print, item(R.string.menu_add_to_homescreen))); + + expectedItems.add(item(R.id.find_in_page_id)); + expectedTitles.add(item(R.string.menu_find_in_page)); + + expectedItems.add(item(R.id.translate_id)); + expectedTitles.add(item(R.string.menu_translate)); + if (!DeviceInfo.isDesktop()) { - expectedItems.add(R.id.request_desktop_site_id); - expectedTitles.add(R.string.menu_request_desktop_site); + expectedItems.add(item(R.id.request_desktop_site_id)); + expectedTitles.add(item(R.string.menu_request_desktop_site)); } - expectedItems.add(R.id.auto_dark_web_contents_id); - expectedTitles.add(R.string.menu_auto_dark_web_contents); - expectedItems.add(R.id.divider_line_id); - expectedTitles.add(0); - expectedItems.add(R.id.preferences_id); - expectedTitles.add(R.string.menu_settings); - expectedItems.add(R.id.help_id); - expectedTitles.add(R.string.menu_help); + + expectedItems.add(item(R.id.auto_dark_web_contents_id)); + expectedTitles.add(item(R.string.menu_auto_dark_web_contents)); + + expectedItems.add(item(R.id.divider_line_id)); + expectedTitles.add(item(0)); + + expectedItems.add(item(R.id.preferences_id)); + expectedTitles.add(item(R.string.menu_settings)); + + expectedItems.add(item(R.id.help_id)); + expectedTitles.add(item(R.string.menu_help)); Integer[] expectedActionBarItems = ChromeFeatureList.sThreeDotMenuBackButton.isEnabled() @@ -759,20 +837,8 @@ R.id.reload_menu_id }; - assertMenuItemsAreEqual(modelList, expectedItems.toArray(new Integer[0])); - assertMenuTitlesAreEqual(modelList, expectedTitles.toArray(new Integer[0])); - assertMenuItemsAreEqual( - createModelList(getSubmenuItems(modelList, R.id.save_and_print_parent_menu_id)), - saveAndPrintExpectedItems.toArray(new Integer[0])); - assertMenuTitlesAreEqual( - createModelList(getSubmenuItems(modelList, R.id.save_and_print_parent_menu_id)), - saveAndPrintExpectedTitles.toArray(new Integer[0])); - assertMenuItemsAreEqual( - createModelList(getSubmenuItems(modelList, R.id.history_parent_menu_id)), - historyExpectedItems.toArray(new Integer[0])); - assertMenuTitlesAreEqual( - createModelList(getSubmenuItems(modelList, R.id.history_parent_menu_id)), - historyExpectedTitles.toArray(new Integer[0])); + assertMenuItemsAreEqual(modelList, expectedItems); + assertMenuTitlesAreEqual(modelList, expectedTitles); assertActionBarItemsAreEqual(modelList, expectedActionBarItems); } @@ -798,61 +864,91 @@ assertEquals(MenuGroup.PAGE_MENU, mTabbedAppMenuPropertiesDelegate.getMenuGroup()); MVCListAdapter.ModelList modelList = mTabbedAppMenuPropertiesDelegate.getMenuItems(); - List<Integer> expectedItems = new ArrayList<>(); - List<Integer> expectedTitles = new ArrayList<>(); + List<MenuItem> expectedItems = new ArrayList<>(); + List<MenuItem> expectedTitles = new ArrayList<>(); - expectedItems.add(R.id.icon_row_menu_id); - expectedTitles.add(0); + expectedItems.add(item(R.id.icon_row_menu_id)); + expectedTitles.add(item(0)); if (!IncognitoUtils.shouldOpenIncognitoAsWindow()) { - expectedItems.add(R.id.new_tab_menu_id); - expectedTitles.add(R.string.menu_new_tab); + expectedItems.add(item(R.id.new_tab_menu_id)); + expectedTitles.add(item(R.string.menu_new_tab)); } - expectedItems.add(R.id.new_incognito_tab_menu_id); - expectedTitles.add(R.string.menu_new_incognito_tab); - expectedItems.add(R.id.add_to_group_menu_id); - expectedTitles.add(R.string.menu_add_tab_to_new_group); - expectedItems.add(R.id.divider_line_id); - expectedTitles.add(0); + expectedItems.add(item(R.id.new_incognito_tab_menu_id)); + expectedTitles.add(item(R.string.menu_new_incognito_tab)); + + expectedItems.add(item(R.id.add_to_group_menu_id)); + expectedTitles.add(item(R.string.menu_add_tab_to_new_group)); + + expectedItems.add(item(R.id.divider_line_id)); + expectedTitles.add(item(0)); + if (!IncognitoUtils.shouldOpenIncognitoAsWindow()) { - expectedItems.add(R.id.history_parent_menu_id); - expectedTitles.add(R.string.menu_history); + expectedItems.add(item(R.id.history_parent_menu_id, item(R.id.open_history_menu_id))); + expectedTitles.add(item(R.string.menu_history, item(R.string.menu_history))); } + if (!mTabbedAppMenuPropertiesDelegate.isTabletSizeScreen()) { - expectedItems.add(R.id.info_menu_id); - expectedTitles.add(R.string.menu_site_controls); - expectedItems.add(R.id.page_info_divider_line_id); - expectedTitles.add(0); + expectedItems.add(item(R.id.info_menu_id)); + expectedTitles.add(item(R.string.menu_site_controls)); + + expectedItems.add(item(R.id.page_info_divider_line_id)); + expectedTitles.add(item(0)); } - expectedItems.add(R.id.downloads_menu_id); - expectedTitles.add(R.string.menu_downloads); - expectedItems.add(R.id.all_bookmarks_menu_id); - expectedTitles.add(R.string.menu_bookmarks); + + expectedItems.add(item(R.id.downloads_menu_id)); + expectedTitles.add(item(R.string.menu_downloads)); + + expectedItems.add(item(R.id.all_bookmarks_menu_id)); + expectedTitles.add(item(R.string.menu_bookmarks)); + if (ExtensionsBuildflags.ENABLE_DESKTOP_ANDROID_EXTENSIONS) { - expectedItems.add(R.id.extensions_parent_menu_id); - expectedTitles.add(R.string.menu_extensions); + expectedItems.add( + item( + R.id.extensions_parent_menu_id, + item(R.id.extensions_menu_menu_id), + item(R.id.manage_extensions_menu_id), + item(R.id.extensions_webstore_menu_id))); + expectedTitles.add( + item( + R.string.menu_extensions, + item(R.string.menu_extensions_menu), + item(R.string.menu_manage_extensions), + item(R.string.menu_chrome_webstore))); } - expectedItems.add(R.id.divider_line_id); - expectedTitles.add(0); - expectedItems.add(R.id.share_menu_id); - expectedTitles.add(R.string.menu_share_page); - expectedItems.add(R.id.find_in_page_id); - expectedTitles.add(R.string.menu_find_in_page); - expectedItems.add(R.id.translate_id); - expectedTitles.add(R.string.menu_translate); + + expectedItems.add(item(R.id.divider_line_id)); + expectedTitles.add(item(0)); + + expectedItems.add(item(R.id.share_menu_id)); + expectedTitles.add(item(R.string.menu_share_page)); + + expectedItems.add(item(R.id.find_in_page_id)); + expectedTitles.add(item(R.string.menu_find_in_page)); + + expectedItems.add(item(R.id.translate_id)); + expectedTitles.add(item(R.string.menu_translate)); + if (!DeviceInfo.isDesktop()) { - expectedItems.add(R.id.request_desktop_site_id); - expectedTitles.add(R.string.menu_request_desktop_site); + expectedItems.add(item(R.id.request_desktop_site_id)); + expectedTitles.add(item(R.string.menu_request_desktop_site)); } - expectedItems.add(R.id.auto_dark_web_contents_id); - expectedTitles.add(R.string.menu_auto_dark_web_contents); - expectedItems.add(R.id.divider_line_id); - expectedTitles.add(0); - expectedItems.add(R.id.preferences_id); - expectedTitles.add(R.string.menu_settings); - expectedItems.add(R.id.help_id); - expectedTitles.add(R.string.menu_help); + + expectedItems.add(item(R.id.auto_dark_web_contents_id)); + expectedTitles.add(item(R.string.menu_auto_dark_web_contents)); + + expectedItems.add(item(R.id.divider_line_id)); + expectedTitles.add(item(0)); + + expectedItems.add(item(R.id.preferences_id)); + expectedTitles.add(item(R.string.menu_settings)); + + expectedItems.add(item(R.id.help_id)); + expectedTitles.add(item(R.string.menu_help)); + + assertMenuItemsAreEqual(modelList, expectedItems); + assertMenuTitlesAreEqual(modelList, expectedTitles); Integer[] expectedActionBarItems = ChromeFeatureList.sThreeDotMenuBackButton.isEnabled() @@ -870,8 +966,6 @@ R.id.info_menu_id, R.id.reload_menu_id }; - assertMenuItemsAreEqual(modelList, expectedItems.toArray(new Integer[0])); - assertMenuTitlesAreEqual(modelList, expectedTitles.toArray(new Integer[0])); assertActionBarItemsAreEqual(modelList, expectedActionBarItems); } @@ -901,69 +995,96 @@ assertEquals(MenuGroup.PAGE_MENU, mTabbedAppMenuPropertiesDelegate.getMenuGroup()); MVCListAdapter.ModelList modelList = mTabbedAppMenuPropertiesDelegate.getMenuItems(); - List<Integer> expectedItems = new ArrayList<>(); - List<Integer> expectedTitles = new ArrayList<>(); + List<MenuItem> expectedItems = new ArrayList<>(); + List<MenuItem> expectedTitles = new ArrayList<>(); - List<Integer> saveAndPrintExpectedItems = new ArrayList<>(); - List<Integer> saveAndPrintExpectedTitles = new ArrayList<>(); + expectedItems.add(item(R.id.icon_row_menu_id)); + expectedTitles.add(item(0)); - List<Integer> historyExpectedItems = new ArrayList<>(); - List<Integer> historyExpectedTitles = new ArrayList<>(); + expectedItems.add(item(R.id.new_tab_menu_id)); + expectedTitles.add(item(R.string.menu_new_tab)); - expectedItems.add(R.id.icon_row_menu_id); - expectedTitles.add(0); - expectedItems.add(R.id.new_tab_menu_id); - expectedTitles.add(R.string.menu_new_tab); - expectedItems.add(R.id.new_incognito_tab_menu_id); - expectedTitles.add(R.string.menu_new_incognito_tab); - expectedItems.add(R.id.add_to_group_menu_id); - expectedTitles.add(R.string.menu_add_tab_to_new_group); - expectedItems.add(R.id.divider_line_id); - expectedTitles.add(0); - expectedItems.add(R.id.history_parent_menu_id); - expectedTitles.add(R.string.menu_history); - historyExpectedItems.add(R.id.open_history_menu_id); - historyExpectedTitles.add(R.string.menu_history); - historyExpectedItems.add(R.id.recent_tabs_menu_id); - historyExpectedTitles.add(R.string.menu_recent_tabs); - historyExpectedItems.add(R.id.quick_delete_menu_id); - historyExpectedTitles.add(R.string.menu_quick_delete); - expectedItems.add(R.id.info_menu_id); - expectedTitles.add(R.string.menu_site_controls); - expectedItems.add(R.id.page_info_divider_line_id); - expectedTitles.add(0); - expectedItems.add(R.id.downloads_menu_id); - expectedTitles.add(R.string.menu_downloads); - expectedItems.add(R.id.all_bookmarks_menu_id); - expectedTitles.add(R.string.menu_bookmarks); + expectedItems.add(item(R.id.new_incognito_tab_menu_id)); + expectedTitles.add(item(R.string.menu_new_incognito_tab)); + + expectedItems.add(item(R.id.add_to_group_menu_id)); + expectedTitles.add(item(R.string.menu_add_tab_to_new_group)); + + expectedItems.add(item(R.id.divider_line_id)); + expectedTitles.add(item(0)); + + expectedItems.add( + item( + R.id.history_parent_menu_id, + item(R.id.open_history_menu_id), + item(R.id.recent_tabs_menu_id), + item(R.id.quick_delete_menu_id))); + expectedTitles.add( + item( + R.string.menu_history, + item(R.string.menu_history), + item(R.string.menu_recent_tabs), + item(R.string.menu_quick_delete))); + + expectedItems.add(item(R.id.info_menu_id)); + expectedTitles.add(item(R.string.menu_site_controls)); + + expectedItems.add(item(R.id.page_info_divider_line_id)); + expectedTitles.add(item(0)); + + expectedItems.add(item(R.id.downloads_menu_id)); + expectedTitles.add(item(R.string.menu_downloads)); + + expectedItems.add(item(R.id.all_bookmarks_menu_id)); + expectedTitles.add(item(R.string.menu_bookmarks)); + if (ExtensionsBuildflags.ENABLE_DESKTOP_ANDROID_EXTENSIONS) { - expectedItems.add(R.id.extensions_parent_menu_id); - expectedTitles.add(R.string.menu_extensions); + expectedItems.add( + item( + R.id.extensions_parent_menu_id, + item(R.id.extensions_menu_menu_id), + item(R.id.manage_extensions_menu_id), + item(R.id.extensions_webstore_menu_id))); + expectedTitles.add( + item( + R.string.menu_extensions, + item(R.string.menu_extensions_menu), + item(R.string.menu_manage_extensions), + item(R.string.menu_chrome_webstore))); } - expectedItems.add(R.id.divider_line_id); - expectedTitles.add(0); - expectedItems.add(R.id.share_menu_id); - expectedTitles.add(R.string.menu_share_page); - expectedItems.add(R.id.save_and_print_parent_menu_id); - expectedTitles.add(R.string.menu_save_and_print); - saveAndPrintExpectedItems.add(R.id.universal_install); - saveAndPrintExpectedTitles.add(R.string.menu_add_to_homescreen); - expectedItems.add(R.id.find_in_page_id); - expectedTitles.add(R.string.menu_find_in_page); - expectedItems.add(R.id.translate_id); - expectedTitles.add(R.string.menu_translate); + + expectedItems.add(item(R.id.divider_line_id)); + expectedTitles.add(item(0)); + + expectedItems.add(item(R.id.share_menu_id)); + expectedTitles.add(item(R.string.menu_share_page)); + + expectedItems.add(item(R.id.save_and_print_parent_menu_id, item(R.id.universal_install))); + expectedTitles.add( + item(R.string.menu_save_and_print, item(R.string.menu_add_to_homescreen))); + + expectedItems.add(item(R.id.find_in_page_id)); + expectedTitles.add(item(R.string.menu_find_in_page)); + + expectedItems.add(item(R.id.translate_id)); + expectedTitles.add(item(R.string.menu_translate)); + if (!DeviceInfo.isDesktop()) { - expectedItems.add(R.id.request_desktop_site_id); - expectedTitles.add(R.string.menu_request_desktop_site); + expectedItems.add(item(R.id.request_desktop_site_id)); + expectedTitles.add(item(R.string.menu_request_desktop_site)); } - expectedItems.add(R.id.auto_dark_web_contents_id); - expectedTitles.add(R.string.menu_auto_dark_web_contents); - expectedItems.add(R.id.divider_line_id); - expectedTitles.add(0); - expectedItems.add(R.id.preferences_id); - expectedTitles.add(R.string.menu_settings); - expectedItems.add(R.id.help_id); - expectedTitles.add(R.string.menu_help); + + expectedItems.add(item(R.id.auto_dark_web_contents_id)); + expectedTitles.add(item(R.string.menu_auto_dark_web_contents)); + + expectedItems.add(item(R.id.divider_line_id)); + expectedTitles.add(item(0)); + + expectedItems.add(item(R.id.preferences_id)); + expectedTitles.add(item(R.string.menu_settings)); + + expectedItems.add(item(R.id.help_id)); + expectedTitles.add(item(R.string.menu_help)); Integer[] expectedActionBarItems = ChromeFeatureList.sThreeDotMenuBackButton.isEnabled() @@ -982,20 +1103,8 @@ R.id.reload_menu_id }; - assertMenuItemsAreEqual(modelList, expectedItems.toArray(new Integer[0])); - assertMenuTitlesAreEqual(modelList, expectedTitles.toArray(new Integer[0])); - assertMenuItemsAreEqual( - createModelList(getSubmenuItems(modelList, R.id.save_and_print_parent_menu_id)), - saveAndPrintExpectedItems.toArray(new Integer[0])); - assertMenuTitlesAreEqual( - createModelList(getSubmenuItems(modelList, R.id.save_and_print_parent_menu_id)), - saveAndPrintExpectedTitles.toArray(new Integer[0])); - assertMenuItemsAreEqual( - createModelList(getSubmenuItems(modelList, R.id.history_parent_menu_id)), - historyExpectedItems.toArray(new Integer[0])); - assertMenuTitlesAreEqual( - createModelList(getSubmenuItems(modelList, R.id.history_parent_menu_id)), - historyExpectedTitles.toArray(new Integer[0])); + assertMenuItemsAreEqual(modelList, expectedItems); + assertMenuTitlesAreEqual(modelList, expectedTitles); assertActionBarItemsAreEqual(modelList, expectedActionBarItems); } @@ -1014,49 +1123,47 @@ assertEquals(MenuGroup.PAGE_MENU, mTabbedAppMenuPropertiesDelegate.getMenuGroup()); MVCListAdapter.ModelList modelList = mTabbedAppMenuPropertiesDelegate.getMenuItems(); - List<Integer> expectedItems = + List<MenuItem> expectedItems = new ArrayList<>( Arrays.asList( - R.id.icon_row_menu_id, - R.id.new_tab_menu_id, - R.id.new_incognito_tab_menu_id, - R.id.add_to_group_menu_id, - R.id.divider_line_id, - R.id.history_parent_menu_id, - R.id.info_menu_id, - R.id.page_info_divider_line_id, - R.id.downloads_menu_id, - R.id.all_bookmarks_menu_id, - R.id.divider_line_id, - R.id.share_menu_id, - R.id.save_and_print_parent_menu_id, - R.id.find_in_page_id, - R.id.translate_id, - // Request desktop site is hidden. - R.id.auto_dark_web_contents_id, - R.id.divider_line_id, - R.id.preferences_id, - R.id.help_id)); + item(R.id.icon_row_menu_id), + item(R.id.new_tab_menu_id), + item(R.id.new_incognito_tab_menu_id), + item(R.id.add_to_group_menu_id), + item(R.id.divider_line_id), + item( + R.id.history_parent_menu_id, + item(R.id.open_history_menu_id), + item(R.id.recent_tabs_menu_id), + item(R.id.quick_delete_menu_id)), + item(R.id.info_menu_id), + item(R.id.page_info_divider_line_id), + item(R.id.downloads_menu_id), + item(R.id.all_bookmarks_menu_id))); - List<Integer> saveAndPrintExpectedItems = - new ArrayList<>(Arrays.asList(R.id.universal_install)); if (ExtensionsBuildflags.ENABLE_DESKTOP_ANDROID_EXTENSIONS) { - expectedItems.add(R.id.extensions_parent_menu_id); + expectedItems.add( + item( + R.id.extensions_parent_menu_id, + item(R.id.extensions_menu_menu_id), + item(R.id.manage_extensions_menu_id), + item(R.id.extensions_webstore_menu_id))); } - List<Integer> historyExpectedItems = - new ArrayList<>( - Arrays.asList( - R.id.open_history_menu_id, - R.id.recent_tabs_menu_id, - R.id.quick_delete_menu_id)); - assertMenuItemsAreEqual(modelList, expectedItems.toArray(new Integer[0])); - assertMenuItemsAreEqual( - createModelList(getSubmenuItems(modelList, R.id.save_and_print_parent_menu_id)), - saveAndPrintExpectedItems.toArray(new Integer[0])); - assertMenuItemsAreEqual( - createModelList(getSubmenuItems(modelList, R.id.history_parent_menu_id)), - historyExpectedItems.toArray(new Integer[0])); + expectedItems.addAll( + Arrays.asList( + item(R.id.divider_line_id), + item(R.id.share_menu_id), + item(R.id.save_and_print_parent_menu_id, item(R.id.universal_install)), + item(R.id.find_in_page_id), + item(R.id.translate_id), + // Request desktop site is hidden. + item(R.id.auto_dark_web_contents_id), + item(R.id.divider_line_id), + item(R.id.preferences_id), + item(R.id.help_id))); + + assertMenuItemsAreEqual(modelList, expectedItems); } @Test @@ -1089,45 +1196,48 @@ assertEquals(MenuGroup.PAGE_MENU, mTabbedAppMenuPropertiesDelegate.getMenuGroup()); MVCListAdapter.ModelList modelList = mTabbedAppMenuPropertiesDelegate.getMenuItems(); - List<Integer> expectedItems = + List<MenuItem> expectedItems = new ArrayList<>( Arrays.asList( - R.id.update_menu_id, - R.id.new_tab_menu_id, - R.id.new_incognito_tab_menu_id, - R.id.add_to_group_menu_id, - R.id.history_parent_menu_id, - R.id.info_menu_id, - R.id.downloads_menu_id, - R.id.all_bookmarks_menu_id, - R.id.translate_id, - R.id.share_menu_id, - R.id.save_and_print_parent_menu_id, - R.id.find_in_page_id, - R.id.reader_mode_prefs_id, - R.id.auto_dark_web_contents_id, - R.id.preferences_id, - R.id.help_id)); + item(R.id.icon_row_menu_id), + item(R.id.update_menu_id), + item(R.id.new_tab_menu_id), + item(R.id.new_incognito_tab_menu_id), + item(R.id.add_to_group_menu_id), + item(R.id.divider_line_id), + item( + R.id.history_parent_menu_id, + item(R.id.open_history_menu_id), + item(R.id.recent_tabs_menu_id), + item(R.id.quick_delete_menu_id)), + item(R.id.info_menu_id), + item(R.id.page_info_divider_line_id), + item(R.id.downloads_menu_id), + item(R.id.all_bookmarks_menu_id))); + if (ExtensionsBuildflags.ENABLE_DESKTOP_ANDROID_EXTENSIONS) { - expectedItems.add(R.id.extensions_parent_menu_id); + expectedItems.add( + item( + R.id.extensions_parent_menu_id, + item(R.id.extensions_menu_menu_id), + item(R.id.manage_extensions_menu_id), + item(R.id.extensions_webstore_menu_id))); } - List<Integer> historyExpectedItems = - new ArrayList<>( - Arrays.asList( - R.id.open_history_menu_id, - R.id.recent_tabs_menu_id, - R.id.quick_delete_menu_id)); - List<Integer> saveAndPrintExpectedItems = - new ArrayList<>(Arrays.asList(R.id.universal_install)); + expectedItems.addAll( + Arrays.asList( + item(R.id.divider_line_id), + item(R.id.share_menu_id), + item(R.id.save_and_print_parent_menu_id, item(R.id.universal_install)), + item(R.id.find_in_page_id), + item(R.id.translate_id), + item(R.id.auto_dark_web_contents_id), + item(R.id.reader_mode_prefs_id), + item(R.id.divider_line_id), + item(R.id.preferences_id), + item(R.id.help_id))); - assertMenuItemsHaveIcons(modelList, expectedItems.toArray(new Integer[0])); - assertMenuItemsAreEqual( - createModelList(getSubmenuItems(modelList, R.id.save_and_print_parent_menu_id)), - saveAndPrintExpectedItems.toArray(new Integer[0])); - assertMenuItemsAreEqual( - createModelList(getSubmenuItems(modelList, R.id.history_parent_menu_id)), - historyExpectedItems.toArray(new Integer[0])); + assertMenuItemsHaveIcons(modelList, expectedItems); } @Test @@ -1147,16 +1257,17 @@ MVCListAdapter.ModelList modelList = mTabbedAppMenuPropertiesDelegate.getMenuItems(); - Integer[] expectedItems = { - R.id.new_tab_group_menu_id, - R.id.new_tab_menu_id, - R.id.new_window_menu_id, - R.id.new_incognito_window_menu_id, - R.id.close_all_tabs_menu_id, - R.id.menu_select_tabs, - R.id.quick_delete_menu_id, - R.id.preferences_id - }; + List<MenuItem> expectedItems = + Arrays.asList( + item(R.id.new_tab_menu_id), + item(R.id.new_window_menu_id), + item(R.id.new_incognito_window_menu_id), + item(R.id.new_tab_group_menu_id), + item(R.id.close_all_tabs_menu_id), + item(R.id.menu_select_tabs), + item(R.id.quick_delete_menu_id), + item(R.id.preferences_id)); + assertMenuItemsAreEqual(modelList, expectedItems); } @@ -1176,14 +1287,15 @@ MVCListAdapter.ModelList modelList = mTabbedAppMenuPropertiesDelegate.getMenuItems(); - Integer[] expectedItems = { - R.id.new_tab_menu_id, - R.id.new_incognito_tab_menu_id, - R.id.new_tab_group_menu_id, - R.id.close_all_incognito_tabs_menu_id, - R.id.menu_select_tabs, - R.id.preferences_id - }; + List<MenuItem> expectedItems = + Arrays.asList( + item(R.id.new_tab_menu_id), + item(R.id.new_incognito_tab_menu_id), + item(R.id.new_tab_group_menu_id), + item(R.id.close_all_incognito_tabs_menu_id), + item(R.id.menu_select_tabs), + item(R.id.preferences_id)); + assertMenuItemsAreEqual(modelList, expectedItems); } @@ -1205,15 +1317,16 @@ MVCListAdapter.ModelList modelList = mTabbedAppMenuPropertiesDelegate.getMenuItems(); - Integer[] expectedItems = { - R.id.new_incognito_tab_menu_id, - R.id.new_tab_group_menu_id, - R.id.new_window_menu_id, - R.id.new_incognito_window_menu_id, - R.id.close_all_incognito_tabs_menu_id, - R.id.menu_select_tabs, - R.id.preferences_id - }; + List<MenuItem> expectedItems = + Arrays.asList( + item(R.id.new_incognito_tab_menu_id), + item(R.id.new_window_menu_id), + item(R.id.new_incognito_window_menu_id), + item(R.id.new_tab_group_menu_id), + item(R.id.close_all_incognito_tabs_menu_id), + item(R.id.menu_select_tabs), + item(R.id.preferences_id)); + assertMenuItemsAreEqual(modelList, expectedItems); } @@ -1229,20 +1342,19 @@ MVCListAdapter.ModelList modelList = mTabbedAppMenuPropertiesDelegate.getMenuItems(); - Integer[] expectedItems = { - R.id.new_tab_menu_id, - R.id.new_tab_group_menu_id, - R.id.new_incognito_tab_menu_id, - R.id.close_all_tabs_menu_id, - R.id.menu_select_tabs, - R.id.quick_delete_menu_id, - R.id.preferences_id - }; + List<MenuItem> expectedItems = + Arrays.asList( + item(R.id.new_tab_menu_id), + item(R.id.new_incognito_tab_menu_id), + item(R.id.new_tab_group_menu_id), + item(R.id.close_all_tabs_menu_id), + item(R.id.menu_select_tabs), + item(R.id.quick_delete_menu_id), + item(R.id.preferences_id)); + assertMenuItemsAreEqual(modelList, expectedItems); - PropertyModel closeAllTabsModel = modelList.get(3).model; - assertEquals( - R.id.close_all_tabs_menu_id, - closeAllTabsModel.get(AppMenuItemProperties.MENU_ITEM_ID)); + PropertyModel closeAllTabsModel = + findItemById(modelList, R.id.close_all_tabs_menu_id).model; assertFalse(closeAllTabsModel.get(AppMenuItemProperties.ENABLED)); } @@ -1259,19 +1371,19 @@ MVCListAdapter.ModelList modelList = mTabbedAppMenuPropertiesDelegate.getMenuItems(); - Integer[] expectedItems = { - R.id.new_tab_menu_id, - R.id.new_incognito_tab_menu_id, - R.id.new_tab_group_menu_id, - R.id.close_all_incognito_tabs_menu_id, - R.id.menu_select_tabs, - R.id.preferences_id - }; + List<MenuItem> expectedItems = + Arrays.asList( + item(R.id.new_tab_menu_id), + item(R.id.new_incognito_tab_menu_id), + item(R.id.new_tab_group_menu_id), + item(R.id.close_all_incognito_tabs_menu_id), + item(R.id.menu_select_tabs), + item(R.id.preferences_id)); + assertMenuItemsAreEqual(modelList, expectedItems); - PropertyModel closeAllTabsModel = modelList.get(3).model; - assertEquals( - R.id.close_all_incognito_tabs_menu_id, - closeAllTabsModel.get(AppMenuItemProperties.MENU_ITEM_ID)); + + PropertyModel closeAllTabsModel = + findItemById(modelList, R.id.close_all_incognito_tabs_menu_id).model; assertFalse(closeAllTabsModel.get(AppMenuItemProperties.ENABLED)); } @@ -1286,15 +1398,16 @@ MVCListAdapter.ModelList modelList = mTabbedAppMenuPropertiesDelegate.getMenuItems(); - Integer[] expectedItems = { - R.id.new_tab_menu_id, - R.id.new_incognito_tab_menu_id, - R.id.new_tab_group_menu_id, - R.id.close_all_tabs_menu_id, - R.id.menu_select_tabs, - R.id.quick_delete_menu_id, - R.id.preferences_id - }; + List<MenuItem> expectedItems = + Arrays.asList( + item(R.id.new_tab_menu_id), + item(R.id.new_incognito_tab_menu_id), + item(R.id.new_tab_group_menu_id), + item(R.id.close_all_tabs_menu_id), + item(R.id.menu_select_tabs), + item(R.id.quick_delete_menu_id), + item(R.id.preferences_id)); + assertMenuItemsAreEqual(modelList, expectedItems); } @@ -1311,14 +1424,15 @@ MVCListAdapter.ModelList modelList = mTabbedAppMenuPropertiesDelegate.getMenuItems(); - Integer[] expectedItems = { - R.id.new_tab_menu_id, - R.id.new_incognito_tab_menu_id, - R.id.new_tab_group_menu_id, - R.id.close_all_tabs_menu_id, - R.id.quick_delete_menu_id, - R.id.preferences_id - }; + List<MenuItem> expectedItems = + Arrays.asList( + item(R.id.new_tab_menu_id), + item(R.id.new_incognito_tab_menu_id), + item(R.id.new_tab_group_menu_id), + item(R.id.close_all_tabs_menu_id), + item(R.id.quick_delete_menu_id), + item(R.id.preferences_id)); + assertMenuItemsAreEqual(modelList, expectedItems); } @@ -1333,19 +1447,21 @@ MVCListAdapter.ModelList modelList = mTabbedAppMenuPropertiesDelegate.getMenuItems(); - List<Integer> expectedItems = new ArrayList<>(List.of(R.id.new_tab_menu_id)); + List<MenuItem> expectedItems = new ArrayList<>(); + + expectedItems.add(item(R.id.new_tab_menu_id)); if (newIncognitoWindowEnabled) { - expectedItems.add(R.id.new_window_menu_id); - expectedItems.add(R.id.new_incognito_window_menu_id); + expectedItems.add(item(R.id.new_window_menu_id)); + expectedItems.add(item(R.id.new_incognito_window_menu_id)); } else { - expectedItems.add(R.id.new_incognito_tab_menu_id); + expectedItems.add(item(R.id.new_incognito_tab_menu_id)); } - expectedItems.add(R.id.preferences_id); - expectedItems.add(R.id.quick_delete_menu_id); + expectedItems.add(item(R.id.preferences_id)); + expectedItems.add(item(R.id.quick_delete_menu_id)); - assertMenuItemsAreEqual(modelList, expectedItems.toArray(new Integer[0])); + assertMenuItemsAreEqual(modelList, expectedItems); } @Test @@ -1387,52 +1503,53 @@ MVCListAdapter.ModelList modelList = mTabbedAppMenuPropertiesDelegate.getMenuItems(); - ArrayList<Integer> expectedItems = + List<MenuItem> expectedItems = new ArrayList<>( Arrays.asList( - R.id.icon_row_menu_id, - R.id.new_tab_menu_id, - R.id.new_incognito_tab_menu_id, - R.id.add_to_group_menu_id, - R.id.divider_line_id, - R.id.history_parent_menu_id, - R.id.info_menu_id, - R.id.page_info_divider_line_id, - R.id.downloads_menu_id, - R.id.all_bookmarks_menu_id, - R.id.divider_line_id, - R.id.share_menu_id, - R.id.save_and_print_parent_menu_id, - R.id.get_image_descriptions_id, - R.id.find_in_page_id, - R.id.auto_dark_web_contents_id, - R.id.divider_line_id, - R.id.preferences_id, - R.id.help_id)); - if (!DeviceInfo.isDesktop()) { - expectedItems.add(R.id.request_desktop_site_id); - } + item(R.id.icon_row_menu_id), + item(R.id.new_tab_menu_id), + item(R.id.new_incognito_tab_menu_id), + item(R.id.add_to_group_menu_id), + item(R.id.divider_line_id), + item( + R.id.history_parent_menu_id, + item(R.id.open_history_menu_id), + item(R.id.recent_tabs_menu_id), + item(R.id.quick_delete_menu_id)), + item(R.id.info_menu_id), + item(R.id.page_info_divider_line_id), + item(R.id.downloads_menu_id), + item(R.id.all_bookmarks_menu_id))); + if (ExtensionsBuildflags.ENABLE_DESKTOP_ANDROID_EXTENSIONS) { - expectedItems.add(R.id.extensions_parent_menu_id); + expectedItems.add( + item( + R.id.extensions_parent_menu_id, + item(R.id.extensions_menu_menu_id), + item(R.id.manage_extensions_menu_id), + item(R.id.extensions_webstore_menu_id))); } - List<Integer> saveAndPrintExpectedItems = - new ArrayList<>(Arrays.asList(R.id.universal_install)); + expectedItems.addAll( + Arrays.asList( + item(R.id.divider_line_id), + item(R.id.share_menu_id), + item(R.id.save_and_print_parent_menu_id, item(R.id.universal_install)), + item(R.id.find_in_page_id))); - List<Integer> historyExpectedItems = - new ArrayList<>( - Arrays.asList( - R.id.open_history_menu_id, - R.id.recent_tabs_menu_id, - R.id.quick_delete_menu_id)); + if (!DeviceInfo.isDesktop()) { + expectedItems.add(item(R.id.request_desktop_site_id)); + } - assertMenuItemsAreEqual(modelList, expectedItems.toArray(new Integer[0])); - assertMenuItemsAreEqual( - createModelList(getSubmenuItems(modelList, R.id.save_and_print_parent_menu_id)), - saveAndPrintExpectedItems.toArray(new Integer[0])); - assertMenuItemsAreEqual( - createModelList(getSubmenuItems(modelList, R.id.history_parent_menu_id)), - historyExpectedItems.toArray(new Integer[0])); + expectedItems.addAll( + Arrays.asList( + item(R.id.auto_dark_web_contents_id), + item(R.id.get_image_descriptions_id), + item(R.id.divider_line_id), + item(R.id.preferences_id), + item(R.id.help_id))); + + assertMenuItemsAreEqual(modelList, expectedItems); // Ensure the text of the menu item is correct assertEquals( @@ -1479,54 +1596,54 @@ MVCListAdapter.ModelList modelList = mTabbedAppMenuPropertiesDelegate.getMenuItems(); - ArrayList<Integer> expectedItems = + List<MenuItem> expectedItems = new ArrayList<>( Arrays.asList( - R.id.icon_row_menu_id, - R.id.new_tab_menu_id, - R.id.new_incognito_tab_menu_id, - R.id.add_to_group_menu_id, - R.id.divider_line_id, - R.id.history_parent_menu_id, - R.id.info_menu_id, - R.id.page_info_divider_line_id, - R.id.downloads_menu_id, - R.id.all_bookmarks_menu_id, - R.id.divider_line_id, - R.id.share_menu_id, - R.id.save_and_print_parent_menu_id, - R.id.find_in_page_id, - R.id.auto_dark_web_contents_id, - R.id.divider_line_id, - R.id.preferences_id, - R.id.help_id, - R.id.managed_by_divider_line_id, - R.id.managed_by_menu_id)); + item(R.id.icon_row_menu_id), + item(R.id.new_tab_menu_id), + item(R.id.new_incognito_tab_menu_id), + item(R.id.add_to_group_menu_id), + item(R.id.divider_line_id), + item( + R.id.history_parent_menu_id, + item(R.id.open_history_menu_id), + item(R.id.recent_tabs_menu_id), + item(R.id.quick_delete_menu_id)), + item(R.id.info_menu_id), + item(R.id.page_info_divider_line_id), + item(R.id.downloads_menu_id), + item(R.id.all_bookmarks_menu_id))); + + if (ExtensionsBuildflags.ENABLE_DESKTOP_ANDROID_EXTENSIONS) { + expectedItems.add( + item( + R.id.extensions_parent_menu_id, + item(R.id.extensions_menu_menu_id), + item(R.id.manage_extensions_menu_id), + item(R.id.extensions_webstore_menu_id))); + } + + expectedItems.addAll( + Arrays.asList( + item(R.id.divider_line_id), + item(R.id.share_menu_id), + item(R.id.save_and_print_parent_menu_id, item(R.id.universal_install)), + item(R.id.find_in_page_id))); if (!DeviceInfo.isDesktop()) { - expectedItems.add(R.id.request_desktop_site_id); - } - if (ExtensionsBuildflags.ENABLE_DESKTOP_ANDROID_EXTENSIONS) { - expectedItems.add(R.id.extensions_parent_menu_id); + expectedItems.add(item(R.id.request_desktop_site_id)); } - List<Integer> saveAndPrintExpectedItems = - new ArrayList<>(Arrays.asList(R.id.universal_install)); + expectedItems.addAll( + Arrays.asList( + item(R.id.auto_dark_web_contents_id), + item(R.id.divider_line_id), + item(R.id.preferences_id), + item(R.id.help_id), + item(R.id.managed_by_divider_line_id), + item(R.id.managed_by_menu_id))); - List<Integer> historyExpectedItems = - new ArrayList<>( - Arrays.asList( - R.id.open_history_menu_id, - R.id.recent_tabs_menu_id, - R.id.quick_delete_menu_id)); - - assertMenuItemsAreEqual(modelList, expectedItems.toArray(new Integer[0])); - assertMenuItemsAreEqual( - createModelList(getSubmenuItems(modelList, R.id.save_and_print_parent_menu_id)), - saveAndPrintExpectedItems.toArray(new Integer[0])); - assertMenuItemsAreEqual( - createModelList(getSubmenuItems(modelList, R.id.history_parent_menu_id)), - historyExpectedItems.toArray(new Integer[0])); + assertMenuItemsAreEqual(modelList, expectedItems); } @Test @@ -1546,54 +1663,53 @@ // TODO(crbug.com/427240031): Stop asserting on menu items that are not subject of this // test. - ArrayList<Integer> expectedItems = + List<MenuItem> expectedItems = new ArrayList<>( Arrays.asList( - R.id.icon_row_menu_id, - R.id.new_tab_menu_id, - R.id.new_incognito_tab_menu_id, - R.id.add_to_group_menu_id, - R.id.divider_line_id, - R.id.history_parent_menu_id, - R.id.info_menu_id, - R.id.page_info_divider_line_id, - R.id.downloads_menu_id, - R.id.all_bookmarks_menu_id, - R.id.divider_line_id, - R.id.share_menu_id, - R.id.save_and_print_parent_menu_id, - R.id.find_in_page_id, - R.id.auto_dark_web_contents_id, - R.id.divider_line_id, - R.id.preferences_id, - R.id.help_id, - R.id.menu_item_content_filter_divider_line_id, - R.id.menu_item_content_filter_help_center_id)); + item(R.id.icon_row_menu_id), + item(R.id.new_tab_menu_id), + item(R.id.new_incognito_tab_menu_id), + item(R.id.add_to_group_menu_id), + item(R.id.divider_line_id), + item( + R.id.history_parent_menu_id, + item(R.id.open_history_menu_id), + item(R.id.recent_tabs_menu_id), + item(R.id.quick_delete_menu_id)), + item(R.id.info_menu_id), + item(R.id.page_info_divider_line_id), + item(R.id.downloads_menu_id), + item(R.id.all_bookmarks_menu_id))); + + if (ExtensionsBuildflags.ENABLE_DESKTOP_ANDROID_EXTENSIONS) { + expectedItems.add( + item( + R.id.extensions_parent_menu_id, + item(R.id.extensions_menu_menu_id), + item(R.id.manage_extensions_menu_id), + item(R.id.extensions_webstore_menu_id))); + } + + expectedItems.addAll( + Arrays.asList( + item(R.id.divider_line_id), + item(R.id.share_menu_id), + item(R.id.save_and_print_parent_menu_id, item(R.id.universal_install)), + item(R.id.find_in_page_id))); if (!DeviceInfo.isDesktop()) { - expectedItems.add(R.id.request_desktop_site_id); - } - if (ExtensionsBuildflags.ENABLE_DESKTOP_ANDROID_EXTENSIONS) { - expectedItems.add(R.id.extensions_parent_menu_id); + expectedItems.add(item(R.id.request_desktop_site_id)); } - List<Integer> saveAndPrintExpectedItems = - new ArrayList<>(Arrays.asList(R.id.universal_install)); - - List<Integer> historyExpectedItems = - new ArrayList<>( - Arrays.asList( - R.id.open_history_menu_id, - R.id.recent_tabs_menu_id, - R.id.quick_delete_menu_id)); - - assertMenuItemsAreEqual(modelList, expectedItems.toArray(new Integer[0])); - assertMenuItemsAreEqual( - createModelList(getSubmenuItems(modelList, R.id.save_and_print_parent_menu_id)), - saveAndPrintExpectedItems.toArray(new Integer[0])); - assertMenuItemsAreEqual( - createModelList(getSubmenuItems(modelList, R.id.history_parent_menu_id)), - historyExpectedItems.toArray(new Integer[0])); + expectedItems.addAll( + Arrays.asList( + item(R.id.auto_dark_web_contents_id), + item(R.id.divider_line_id), + item(R.id.preferences_id), + item(R.id.help_id), + item(R.id.menu_item_content_filter_divider_line_id), + item(R.id.menu_item_content_filter_help_center_id))); + assertMenuItemsAreEqual(modelList, expectedItems); } @Test @@ -2461,54 +2577,27 @@ findItemById(modelList, R.id.extensions_parent_menu_id); assertNotNull("Extensions parent menu item not found.", parentItem); - assertEquals( - ContextUtils.getApplicationContext().getString(R.string.menu_extensions), - parentItem.model.get(AppMenuItemProperties.TITLE)); - assertNotNull( - "Parent extension item should have an icon.", - parentItem.model.get(AppMenuItemProperties.ICON)); + List<MVCListAdapter.ListItem> isolatedExtensionMenu = Arrays.asList(parentItem); - assertHasSubMenuItemIds( - parentItem, - R.id.extensions_menu_menu_id, - R.id.manage_extensions_menu_id, - R.id.extensions_webstore_menu_id); + List<MenuItem> expectedItems = + Arrays.asList( + item( + R.id.extensions_parent_menu_id, + item(R.id.extensions_menu_menu_id), + item(R.id.manage_extensions_menu_id), + item(R.id.extensions_webstore_menu_id))); - List<MVCListAdapter.ListItem> subItems = - parentItem.model.get(AppMenuItemWithSubmenuProperties.SUBMENU_ITEMS); + List<MenuItem> expectedTitles = + Arrays.asList( + item( + R.string.menu_extensions, + item(R.string.menu_extensions_menu), + item(R.string.menu_manage_extensions), + item(R.string.menu_chrome_webstore))); - MVCListAdapter.ListItem menuItem = subItems.get(0); - assertEquals( - R.id.extensions_menu_menu_id, - menuItem.model.get(AppMenuItemProperties.MENU_ITEM_ID)); - assertEquals( - ContextUtils.getApplicationContext().getString(R.string.menu_extensions_menu), - menuItem.model.get(AppMenuItemProperties.TITLE)); - assertNotNull( - "Manage Extensions item should have an icon.", - menuItem.model.get(AppMenuItemProperties.ICON)); - - MVCListAdapter.ListItem manageItem = subItems.get(1); - assertEquals( - R.id.manage_extensions_menu_id, - manageItem.model.get(AppMenuItemProperties.MENU_ITEM_ID)); - assertEquals( - ContextUtils.getApplicationContext().getString(R.string.menu_manage_extensions), - manageItem.model.get(AppMenuItemProperties.TITLE)); - assertNotNull( - "Manage Extensions item should have an icon.", - manageItem.model.get(AppMenuItemProperties.ICON)); - - MVCListAdapter.ListItem webstoreItem = subItems.get(2); - assertEquals( - R.id.extensions_webstore_menu_id, - webstoreItem.model.get(AppMenuItemProperties.MENU_ITEM_ID)); - assertEquals( - ContextUtils.getApplicationContext().getString(R.string.menu_chrome_webstore), - webstoreItem.model.get(AppMenuItemProperties.TITLE)); - assertNotNull( - "Visit Chrome Web Store item should have an icon.", - webstoreItem.model.get(AppMenuItemProperties.ICON)); + assertMenuItemsAreEqual(isolatedExtensionMenu, expectedItems); + assertMenuTitlesAreEqual(isolatedExtensionMenu, expectedTitles); + assertMenuItemsHaveIcons(isolatedExtensionMenu, expectedItems); } @Test @@ -2751,7 +2840,23 @@ return menuItem.model.get(AppMenuItemProperties.TITLE) == expectedTitle; } - private String getMenuTitles(MVCListAdapter.ModelList modelList) { + private String getMenuTitle(MVCListAdapter.ListItem item) { + CharSequence title = + item.model.containsKey(AppMenuItemProperties.TITLE) + ? item.model.get(AppMenuItemProperties.TITLE) + : null; + if (title == null) { + if (item.type == AppMenuHandler.AppMenuItemType.BUTTON_ROW) { + title = "Icon Row"; + } else if (item.type == AppMenuHandler.AppMenuItemType.DIVIDER) { + title = "Divider"; + } + } + + return title.toString(); + } + + private String getMenuTitles(Iterable<MVCListAdapter.ListItem> modelList) { StringBuilder items = new StringBuilder(); for (MVCListAdapter.ListItem item : modelList) { CharSequence title = @@ -2991,4 +3096,8 @@ item = findItemById(modelList, R.id.lens_overlay_menu_id); assertTrue(item.model.get(AppMenuItemProperties.ENABLED)); } + + private static MenuItem item(int id, MenuItem... subItems) { + return new MenuItem(id, subItems); + } }
diff --git a/chrome/android/modules/on_demand/BUILD.gn b/chrome/android/modules/on_demand/BUILD.gn index 4b2121b..c0a4e57 100644 --- a/chrome/android/modules/on_demand/BUILD.gn +++ b/chrome/android/modules/on_demand/BUILD.gn
@@ -13,6 +13,8 @@ deps = [ ":provider_java", "//base:service_loader_java", + "//chrome/browser/ui/android/pdf:java", + "//chrome/browser/ui/android/pdf/internal:java", ] } @@ -20,6 +22,7 @@ android_library("provider_java") { sources = [ "java/src/org/chromium/chrome/modules/on_demand/OnDemandModuleEntryPoints.java" ] deps = [ + "//chrome/browser/ui/android/pdf:java", "//components/module_installer/android:module_installer_java", "//components/module_installer/android:module_interface_java", ]
diff --git a/chrome/android/modules/on_demand/java/src/org/chromium/chrome/modules/on_demand/OnDemandModuleEntryPoints.java b/chrome/android/modules/on_demand/java/src/org/chromium/chrome/modules/on_demand/OnDemandModuleEntryPoints.java index 6c31758..5e00d5e 100644 --- a/chrome/android/modules/on_demand/java/src/org/chromium/chrome/modules/on_demand/OnDemandModuleEntryPoints.java +++ b/chrome/android/modules/on_demand/java/src/org/chromium/chrome/modules/on_demand/OnDemandModuleEntryPoints.java
@@ -6,6 +6,7 @@ import org.chromium.build.annotations.NullMarked; import org.chromium.build.annotations.Nullable; +import org.chromium.chrome.browser.pdf.PdfEntryPoint; import org.chromium.components.module_installer.builder.ModuleInterface; /** @@ -19,4 +20,7 @@ public interface OnDemandModuleEntryPoints { /** Used by internal Chrome builds. */ @Nullable Object getInternalEntryPoints(); + + /** Returns the PDF entry point. */ + PdfEntryPoint getPdfEntryPoint(); }
diff --git a/chrome/android/modules/on_demand/java/src/org/chromium/chrome/modules/on_demand/OnDemandModuleEntryPointsImpl.java b/chrome/android/modules/on_demand/java/src/org/chromium/chrome/modules/on_demand/OnDemandModuleEntryPointsImpl.java index a6574e6..ef2f5a98 100644 --- a/chrome/android/modules/on_demand/java/src/org/chromium/chrome/modules/on_demand/OnDemandModuleEntryPointsImpl.java +++ b/chrome/android/modules/on_demand/java/src/org/chromium/chrome/modules/on_demand/OnDemandModuleEntryPointsImpl.java
@@ -8,13 +8,22 @@ import org.chromium.build.annotations.NullMarked; import org.chromium.build.annotations.Nullable; import org.chromium.build.annotations.UsedByReflection; +import org.chromium.chrome.browser.pdf.PdfEntryPoint; +import org.chromium.chrome.browser.pdf.PdfEntryPointImpl; /** Entry points implementation that lives inside the apk split. */ @NullMarked @UsedByReflection("SplitChromeApplication.java") public class OnDemandModuleEntryPointsImpl implements OnDemandModuleEntryPoints { + private final PdfEntryPoint mPdfEntryPoint = new PdfEntryPointImpl(); + @Override public @Nullable Object getInternalEntryPoints() { return ServiceLoaderUtil.maybeCreate(OnDemandModuleInternalEntryPoints.class); } + + @Override + public PdfEntryPoint getPdfEntryPoint() { + return mPdfEntryPoint; + } }
diff --git a/chrome/android/modules/on_demand/on_demand_module.gni b/chrome/android/modules/on_demand/on_demand_module.gni index 166bc8a..f0cfb469 100644 --- a/chrome/android/modules/on_demand/on_demand_module.gni +++ b/chrome/android/modules/on_demand/on_demand_module.gni
@@ -9,5 +9,8 @@ uses_split = "chrome" android_manifest = "//chrome/android/modules/on_demand/java/AndroidManifest.xml" - java_deps = [ "//chrome/android/modules/on_demand:impl_java" ] + java_deps = [ + "//chrome/android/modules/on_demand:impl_java", + "//chrome/browser/ui/android/pdf/internal:java", + ] }
diff --git a/chrome/android/profiles/arm.newest.txt b/chrome/android/profiles/arm.newest.txt index af4ecff5..ddbfdf64 100644 --- a/chrome/android/profiles/arm.newest.txt +++ b/chrome/android/profiles/arm.newest.txt
@@ -1 +1 @@ -chromeos-chrome-arm-150.0.7828.0_pre1625187_rc-r1-merged.afdo.bz2 +chromeos-chrome-arm-150.0.7830.0_pre1626253_rc-r1-merged.afdo.bz2
diff --git a/chrome/app/generated_resources.grd b/chrome/app/generated_resources.grd index 8dff7ba..66aa917 100644 --- a/chrome/app/generated_resources.grd +++ b/chrome/app/generated_resources.grd
@@ -19384,6 +19384,9 @@ <message name="IDS_OVERFLOW_MENU_ITEM_TEXT_CONTEXTUAL_TASKS" desc="Text of overflow menu item of contextual tasks button"> Contextual Tasks </message> + <message name="IDS_OVERFLOW_MENU_ITEM_TEXT_ENERGY_SAVER" desc="Text of overflow menu item of energy saver button"> + Energy Saver + </message> <!-- Promise icon status labels. --> <message name="IDS_PROMISE_STATUS_WAITING" desc="Text label for a promise icon in the Launcher or Shelf to indicate that the app is pending installation">
diff --git a/chrome/app/generated_resources_grd/IDS_OVERFLOW_MENU_ITEM_TEXT_ENERGY_SAVER.png.sha1 b/chrome/app/generated_resources_grd/IDS_OVERFLOW_MENU_ITEM_TEXT_ENERGY_SAVER.png.sha1 new file mode 100644 index 0000000..a4cee64 --- /dev/null +++ b/chrome/app/generated_resources_grd/IDS_OVERFLOW_MENU_ITEM_TEXT_ENERGY_SAVER.png.sha1
@@ -0,0 +1 @@ +22d311744c5ac02664de59dd0cd4d6bfb8780974 \ No newline at end of file
diff --git a/chrome/app/glic_strings_grdp/OWNERS b/chrome/app/glic_strings_grdp/OWNERS index 7a6985e..891f96d 100644 --- a/chrome/app/glic_strings_grdp/OWNERS +++ b/chrome/app/glic_strings_grdp/OWNERS
@@ -1,4 +1,5 @@ file://chrome/browser/glic/OWNERS +file://chrome/browser/glic/android/OWNERS haileywang@google.com ritikagup@google.com
diff --git a/chrome/app/settings_chromium_strings.grdp b/chrome/app/settings_chromium_strings.grdp index 9acda3ac..8fffd90 100644 --- a/chrome/app/settings_chromium_strings.grdp +++ b/chrome/app/settings_chromium_strings.grdp
@@ -200,9 +200,6 @@ <message name="IDS_SETTINGS_PRIVACY_GUIDE_SAFE_BROWSING_CARD_STANDARD_PROTECTION_PRIVACY_DESCRIPTION1" desc="A part of the privacy description of the standard protection section of the safe browsing card in the 'Privacy Guide'."> If a site tries to steal your password, or when you download a harmful file, Chromium may also send URLs, including bits of page content, to Safe Browsing </message> - <message name="IDS_SETTINGS_PRIVACY_GUIDE_COMPLETION_CARD_PRIVACY_SANDBOX_SUB_LABEL" desc="Text of the Privacy Sandbox sublabel in the completion card of the 'Privacy Guide'."> - Chromium gives you more control over the ads you see and limits what sites can learn about you when they show you personalized ads - </message> <message name="IDS_SETTINGS_PRIVACY_GUIDE_COMPLETION_CARD_WAA_SUB_LABEL" desc="Text of the Web and App Activity sublabel in the completion card of the 'Privacy Guide'."> Choose whether to include Chromium history for more personalized experiences in Google services </message>
diff --git a/chrome/app/settings_chromium_strings_grdp/IDS_SETTINGS_PRIVACY_GUIDE_COMPLETION_CARD_PRIVACY_SANDBOX_SUB_LABEL.png.sha1 b/chrome/app/settings_chromium_strings_grdp/IDS_SETTINGS_PRIVACY_GUIDE_COMPLETION_CARD_PRIVACY_SANDBOX_SUB_LABEL.png.sha1 deleted file mode 100644 index c2abf53..0000000 --- a/chrome/app/settings_chromium_strings_grdp/IDS_SETTINGS_PRIVACY_GUIDE_COMPLETION_CARD_PRIVACY_SANDBOX_SUB_LABEL.png.sha1 +++ /dev/null
@@ -1 +0,0 @@ -63b2bf6dc0c4d3f02f0989fe55f462181f5acbb8 \ No newline at end of file
diff --git a/chrome/app/settings_google_chrome_strings.grdp b/chrome/app/settings_google_chrome_strings.grdp index e7bb20e..a98de1b 100644 --- a/chrome/app/settings_google_chrome_strings.grdp +++ b/chrome/app/settings_google_chrome_strings.grdp
@@ -190,9 +190,6 @@ <message name="IDS_SETTINGS_PRIVACY_GUIDE_SAFE_BROWSING_CARD_STANDARD_PROTECTION_PRIVACY_DESCRIPTION1" desc="A part of the privacy description of the standard protection section of the safe browsing card in the 'Privacy Guide'."> If a site tries to steal your password, or when you download a harmful file, Chrome may also send URLs, including bits of page content, to Safe Browsing </message> - <message name="IDS_SETTINGS_PRIVACY_GUIDE_COMPLETION_CARD_PRIVACY_SANDBOX_SUB_LABEL" desc="Text of the Privacy Sandbox sublabel in the completion card of the 'Privacy Guide'."> - Chrome gives you more control over the ads you see and limits what sites can learn about you when they show you personalized ads - </message> <message name="IDS_SETTINGS_PRIVACY_GUIDE_COMPLETION_CARD_WAA_SUB_LABEL" desc="Text of the Web and App Activity sublabel in the completion card of the 'Privacy Guide'."> Choose whether to include Chrome history for more personalized experiences in Google services </message>
diff --git a/chrome/app/settings_google_chrome_strings_grdp/IDS_SETTINGS_PRIVACY_GUIDE_COMPLETION_CARD_PRIVACY_SANDBOX_SUB_LABEL.png.sha1 b/chrome/app/settings_google_chrome_strings_grdp/IDS_SETTINGS_PRIVACY_GUIDE_COMPLETION_CARD_PRIVACY_SANDBOX_SUB_LABEL.png.sha1 deleted file mode 100644 index c2abf53..0000000 --- a/chrome/app/settings_google_chrome_strings_grdp/IDS_SETTINGS_PRIVACY_GUIDE_COMPLETION_CARD_PRIVACY_SANDBOX_SUB_LABEL.png.sha1 +++ /dev/null
@@ -1 +0,0 @@ -63b2bf6dc0c4d3f02f0989fe55f462181f5acbb8 \ No newline at end of file
diff --git a/chrome/app/theme/theme_resources.grd b/chrome/app/theme/theme_resources.grd index c0ea5654..18f8199b 100644 --- a/chrome/app/theme/theme_resources.grd +++ b/chrome/app/theme/theme_resources.grd
@@ -108,6 +108,7 @@ <if expr="not is_android"> <if expr="_google_chrome"> <structure type="chrome_scaled_image" name="IDR_GOOGLE_G_GRADIENT_16" file="google_chrome/google_g_gradient_16.png" /> + <structure type="chrome_scaled_image" name="IDR_GOOGLE_G_GRADIENT_16_ALT" file="google_chrome/google_g_gradient_16_alt.png" /> <structure type="chrome_scaled_image" name="IDR_GOOGLE_G_GRADIENT_20" file="google_chrome/google_g_gradient_20.png" /> <structure type="chrome_scaled_image" name="IDR_GOOGLE_G" file="google_chrome/google_icon.png" /> </if>
diff --git a/chrome/browser/BUILD.gn b/chrome/browser/BUILD.gn index a9ff46a..e1adec45 100644 --- a/chrome/browser/BUILD.gn +++ b/chrome/browser/BUILD.gn
@@ -851,6 +851,7 @@ "//chrome/browser/performance_monitor", "//chrome/browser/permissions", "//chrome/browser/persisted_state_db", + "//chrome/browser/personal_context", "//chrome/browser/picture_in_picture", "//chrome/browser/picture_in_picture:impl", "//chrome/browser/plugins:impl", @@ -1104,8 +1105,8 @@ "//chrome/browser/ui/webui/actor_internals", "//chrome/browser/ui/webui/bluetooth_internals", "//chrome/browser/ui/webui/bluetooth_internals:mojo_bindings", - "//chrome/browser/ui/webui/intro:mojo_bindings", "//chrome/browser/ui/webui/internal_debug_pages_disabled", + "//chrome/browser/ui/webui/intro:mojo_bindings", "//chrome/browser/ui/zoom", "//chrome/browser/updater", "//chrome/browser/updates/announcement_notification", @@ -1217,6 +1218,7 @@ "//components/enterprise/data_protection", "//components/enterprise/device_trust", "//components/enterprise/encryption/cache", + "//components/enterprise/isolated_mode", "//components/enterprise/networking", "//components/enterprise/obfuscation/core:enterprise_obfuscation", "//components/error_page/common", @@ -1664,7 +1666,13 @@ if (is_android) { deps += [ "//chrome/browser/ntp_customization", + "//chrome/browser/ui/android", + "//chrome/browser/ui/android:context_menu_helper", + "//chrome/browser/ui/android/autofill", + "//chrome/browser/ui/android/exclusive_access", "//chrome/browser/ui/android/lens", + "//chrome/browser/ui/android/logo", + "//chrome/browser/ui/android/webid", "//chrome/browser/webapps:android", "//chrome/browser/webapps:android_impl", "//chrome/browser/webapps/installable:android", @@ -3003,12 +3011,6 @@ if (is_chromeos) { sources += [ - "apps/digital_goods/digital_goods_factory_impl.cc", - "apps/digital_goods/digital_goods_factory_impl.h", - "apps/digital_goods/digital_goods_impl.cc", - "apps/digital_goods/digital_goods_impl.h", - "apps/digital_goods/util.cc", - "apps/digital_goods/util.h", "apps/intent_helper/chromeos_disabled_apps_throttle.cc", "apps/intent_helper/chromeos_disabled_apps_throttle.h", "browser_process_platform_part_ash.cc", @@ -3104,6 +3106,8 @@ "//chrome/browser/apps/app_service/app_install:implementation", "//chrome/browser/apps/app_service/app_install:navigation_throttle", "//chrome/browser/apps/browser_instance", + "//chrome/browser/apps/digital_goods", + "//chrome/browser/apps/digital_goods:impl", "//chrome/browser/ash/accessibility", "//chrome/browser/ash/accessibility/live_caption", "//chrome/browser/ash/account_manager", @@ -4717,6 +4721,7 @@ deps += [ "//chrome/browser/extensions", + "//chrome/browser/ui/android/extensions", "//extensions/browser:keepalive", ] @@ -4727,7 +4732,10 @@ # TODO(https://crbug.com/356905053): Remove the circular dependency once # we port ExtensionService and migrate away from # extensions/desktop_android/desktop_android_extension_system.cc. - allow_circular_includes_from += [ "//chrome/browser/extensions" ] + allow_circular_includes_from += [ + "//chrome/browser/extensions", + "//chrome/browser/ui/android:context_menu_helper", + ] } if (enable_library_cdms) {
diff --git a/chrome/browser/about_flags.cc b/chrome/browser/about_flags.cc index fdbe5a1e..b91c534 100644 --- a/chrome/browser/about_flags.cc +++ b/chrome/browser/about_flags.cc
@@ -2731,15 +2731,11 @@ switches::kWebOtpBackendAuto}, }; -const FeatureEntry::FeatureParam kEnableAndroidSidePanel_Enabled[] = { - {"disable_animations", "false"}}; const FeatureEntry::FeatureParam kEnableAndroidSidePanel_NoAnimations[] = { {"disable_animations", "true"}}; const FeatureEntry::FeatureVariation kEnableAndroidSidePanelVariations[] = { - {"Enabled", kEnableAndroidSidePanel_Enabled, nullptr}, - {"Enabled without animations", kEnableAndroidSidePanel_NoAnimations, - nullptr}}; + {"Without Animations", kEnableAndroidSidePanel_NoAnimations, nullptr}}; #endif // BUILDFLAG(IS_ANDROID) // The choices for the Send Tab To Self enhanced handoff experiment. @@ -3426,6 +3422,8 @@ "enable-extension-ai-data-collection"; #endif // BUILDFLAG(ENABLE_EXTENSIONS) +constexpr char kGemma4ForBuiltInAIInternalName[] = "gemma4-for-built-in-ai"; + const FeatureEntry::FeatureParam kDiscountOnShoppyPage[] = { {commerce::kDiscountOnShoppyPageParam, "true"}}; @@ -4583,6 +4581,8 @@ {"Stage3", kBookmarksEncryptionStage3, nullptr}}; #if BUILDFLAG(IS_ANDROID) +const FeatureEntry::FeatureParam kGestureUserEducationPageDelay2Seconds[] = { + {"gesture-user-education-page-delay", "2000"}}; const FeatureEntry::FeatureParam kGestureUserEducationPageDelay4Seconds[] = { {"gesture-user-education-page-delay", "4000"}}; const FeatureEntry::FeatureParam kGestureUserEducationPageDelay6Seconds[] = { @@ -4593,6 +4593,7 @@ {"gesture-user-education-page-delay", "10000"}}; const FeatureEntry::FeatureVariation kGestureUserEducationVariations[] = { + {"2000ms delay", kGestureUserEducationPageDelay2Seconds, nullptr}, {"4000ms delay", kGestureUserEducationPageDelay4Seconds, nullptr}, {"6000ms delay", kGestureUserEducationPageDelay6Seconds, nullptr}, {"8000ms delay", kGestureUserEducationPageDelay8Seconds, nullptr}, @@ -5555,6 +5556,11 @@ FEATURE_VALUE_TYPE(media::kCastStreamingWinHardwareH264)}, #endif + {"enable-cast-streaming-offer-hardware-first", + flag_descriptions::kCastStreamingOfferHardwareFirstName, + flag_descriptions::kCastStreamingOfferHardwareFirstDescription, kOsDesktop, + FEATURE_VALUE_TYPE(mirroring::features::kCastStreamingOfferHardwareFirst)}, + {"enable-cast-streaming-vp8", flag_descriptions::kCastStreamingVp8Name, flag_descriptions::kCastStreamingVp8Description, kOsDesktop, FEATURE_VALUE_TYPE(media::kCastStreamingVp8)}, @@ -5824,9 +5830,6 @@ flag_descriptions::kFeedSignedOutViewDemotionName, flag_descriptions::kFeedSignedOutViewDemotionDescription, kOsAndroid, FEATURE_VALUE_TYPE(feed::kFeedSignedOutViewDemotion)}, - {"web-feed-onboarding", flag_descriptions::kWebFeedOnboardingName, - flag_descriptions::kWebFeedOnboardingDescription, kOsAndroid, - FEATURE_VALUE_TYPE(feed::kWebFeedOnboarding)}, {"xsurface-metrics-reporting", flag_descriptions::kXsurfaceMetricsReportingName, flag_descriptions::kXsurfaceMetricsReportingDescription, kOsAndroid, @@ -5843,9 +5846,6 @@ {"feed-audio-overviews", flag_descriptions::kFeedAudioOverviewsName, flag_descriptions::kFeedAudioOverviewsDescription, kOsAndroid, FEATURE_VALUE_TYPE(feed::kFeedAudioOverviews)}, - {"web-feed-deprecation", flag_descriptions::kWebFeedDeprecationName, - flag_descriptions::kWebFeedDeprecationDescription, kOsAndroid, - FEATURE_VALUE_TYPE(feed::kWebFeedKillSwitch)}, #endif // BUILDFLAG(IS_ANDROID) {"enable-force-dark", flag_descriptions::kAutoWebContentsDarkModeName, flag_descriptions::kAutoWebContentsDarkModeDescription, kOsAll, @@ -9688,12 +9688,6 @@ FEATURE_VALUE_TYPE( autofill::features::kAutofillEnableFpanRiskBasedAuthentication)}, - {"ack-copy-output-request-early-for-view-transition", - flag_descriptions::kAckCopyOutputRequestEarlyForViewTransitionName, - flag_descriptions::kAckCopyOutputRequestEarlyForViewTransitionDescription, - kOsAll, - FEATURE_VALUE_TYPE(features::kAckCopyOutputRequestEarlyForViewTransition)}, - #if BUILDFLAG(IS_MAC) {"enable-mac-pwas-notification-attribution", flag_descriptions::kMacPWAsNotificationAttributionName, @@ -10125,7 +10119,8 @@ MULTI_VALUE_TYPE(kAIClassifierChoices), flag_descriptions::kAIAPIsForGeminiNanoLinks}, - {"gemma4-for-built-in-ai", flag_descriptions::kGemma4ForBuiltInAIName, + {kGemma4ForBuiltInAIInternalName, + flag_descriptions::kGemma4ForBuiltInAIName, flag_descriptions::kGemma4ForBuiltInAIDescription, kOsDesktop, MULTI_VALUE_TYPE(kGemma4Choices), flag_descriptions::kGemma4ForBuiltInAILinks}, @@ -12980,6 +12975,13 @@ FEATURE_VALUE_TYPE( payments::features::kPaymentRequestRejectTooSmallWindows)}, +#if BUILDFLAG(IS_ANDROID) + {"long-screenshots-lenient-memory-check", + flag_descriptions::kLongScreenshotsLenientMemoryCheckName, + flag_descriptions::kLongScreenshotsLenientMemoryCheckDescription, + kOsAndroid, + FEATURE_VALUE_TYPE(chrome::android::kLongScreenshotsLenientMemoryCheck)}, +#endif // Add new entries above this line. // NOTE: Adding a new flag requires adding a corresponding entry to enum // "LoginCustomFlags" in tools/metrics/histograms/enums.xml. See "Flag @@ -13082,9 +13084,8 @@ bool ShouldSkipConditionalFeatureEntry(const flags_ui::FlagsStorage* storage, const FeatureEntry& entry) { -#if BUILDFLAG(IS_CHROMEOS) version_info::Channel channel = chrome::GetChannel(); - +#if BUILDFLAG(IS_CHROMEOS) // enable-ui-devtools is only available on for non Stable channels. if (std::string_view(ui_devtools::switches::kEnableUiDevTools) == entry.internal_name && @@ -13178,20 +13179,18 @@ } #endif // BUILDFLAG(IS_CHROMEOS) #if BUILDFLAG(ENABLE_EXTENSIONS) - version_info::Channel chrome_channel = chrome::GetChannel(); // Only show extension AI data flag in non-stable channels. if (std::string_view(kExtensionAiDataInternalName) == entry.internal_name) { - return chrome_channel != version_info::Channel::BETA && - chrome_channel != version_info::Channel::DEV && - chrome_channel != version_info::Channel::CANARY && - chrome_channel != version_info::Channel::UNKNOWN; + return channel != version_info::Channel::BETA && + channel != version_info::Channel::DEV && + channel != version_info::Channel::CANARY && + channel != version_info::Channel::UNKNOWN; } #endif #if BUILDFLAG(IS_ANDROID) // Only show the payments test flag to disable merchant allowlists if channel // is one of Beta/Dev/Canary/ Unknown. - version_info::Channel channel = chrome::GetChannel(); if (std::string_view( kDisableFacilitatedPaymentsMerchantAllowlistInternalName) == entry.internal_name) { @@ -13205,11 +13204,20 @@ #if !BUILDFLAG(IS_ANDROID) // Only show Webium flag for Canary channel and developer builds. if (std::string_view(kWebiumFlag) == entry.internal_name) { - return chrome::GetChannel() != version_info::Channel::CANARY && + return channel != version_info::Channel::CANARY && version_info::IsOfficialBuild(); } #endif // !BUILDFLAG(IS_ANDROID) + // Only show the Gemma4 flag on Canary/Dev/Unknown and unofficial builds. + if (std::string_view(kGemma4ForBuiltInAIInternalName) == + entry.internal_name) { + return channel != version_info::Channel::CANARY && + channel != version_info::Channel::DEV && + channel != version_info::Channel::UNKNOWN && + version_info::IsOfficialBuild(); + } + if (flags::IsFlagExpired(storage, entry.internal_name)) { return true; }
diff --git a/chrome/browser/actor/BUILD.gn b/chrome/browser/actor/BUILD.gn index bbd2b0a..ebfe6b6b 100644 --- a/chrome/browser/actor/BUILD.gn +++ b/chrome/browser/actor/BUILD.gn
@@ -324,6 +324,7 @@ "actor_keyed_service_unittest.cc", "actor_metrics_unittest.cc", "actor_navigation_throttle_unittest.cc", + "actor_tab_data_unittest.cc", "actor_task_metadata_unittest.cc", "actor_test_util_unittest.cc", "aggregated_journal_unittest.cc",
diff --git a/chrome/browser/actor/actor_keyed_service.cc b/chrome/browser/actor/actor_keyed_service.cc index 400b889f..17eceda9 100644 --- a/chrome/browser/actor/actor_keyed_service.cc +++ b/chrome/browser/actor/actor_keyed_service.cc
@@ -493,7 +493,8 @@ if (tab) { actor::ActorTabData::From(tab.get())->DidObserveContent( - fetch_result.annotated_page_content_result->proto); + fetch_result.annotated_page_content_result->proto, + actor::ApcSource::kActor); } }
diff --git a/chrome/browser/actor/actor_metrics.cc b/chrome/browser/actor/actor_metrics.cc index 07a324f8..b68f17a 100644 --- a/chrome/browser/actor/actor_metrics.cc +++ b/chrome/browser/actor/actor_metrics.cc
@@ -4,6 +4,7 @@ #include "chrome/browser/actor/actor_metrics.h" +#include <string_view> #include <utility> #include "base/metrics/histogram_functions.h" @@ -17,6 +18,7 @@ namespace actor { namespace { + std::string_view ToString(ActorTask::StoppedReason stopped_reason) { switch (stopped_reason) { case ActorTask::StoppedReason::kStoppedByUser: @@ -40,6 +42,17 @@ } NOTREACHED(); } + +std::string_view ToString(ApcSource source) { + switch (source) { + case ApcSource::kActor: + return "Actor"; + case ApcSource::kGlic: + return "Glic"; + } + NOTREACHED(); +} + } // namespace void RecordActorTaskStateTransitionActionCount(size_t action_count, @@ -136,6 +149,13 @@ base::UmaHistogramBoolean("Actor.Download.SaveAsDialogTriggered", success); } +void RecordApcComparisonIdentical(ApcSource source, bool identical) { + base::UmaHistogramBoolean( + base::StrCat({"Actor.PageContext.APC.Comparison.", ToString(source), + ".IsIdenticalToPreviousFetch"}), + identical); +} + void RecordScriptToolActionResultCode( actor::mojom::ActionResultCode action_result_code) { base::UmaHistogramSparse("Actor.Tools.ScriptTool.ActionResultCode",
diff --git a/chrome/browser/actor/actor_metrics.h b/chrome/browser/actor/actor_metrics.h index 29e43e0..bb0a368d 100644 --- a/chrome/browser/actor/actor_metrics.h +++ b/chrome/browser/actor/actor_metrics.h
@@ -19,6 +19,12 @@ namespace actor { +// The source or feature that triggered an Annotated Page Content (APC) fetch. +enum class ApcSource { + kActor, + kGlic, +}; + // Records the number of actions taken in `from_state` before transitioning to // `to_state`. void RecordActorTaskStateTransitionActionCount(size_t action_count, @@ -76,6 +82,10 @@ // Recorded when a 'save as' download dialog is triggered by an ActorTask. void RecordDownloadSaveAsDialogTriggered(bool success); +// Records whether the APC is identical to the one from the previous fetch +// from ANY source. +void RecordApcComparisonIdentical(ApcSource source, bool identical); + // Records script tool specific metrics. void RecordScriptToolActionResultCode( actor::mojom::ActionResultCode action_result_code);
diff --git a/chrome/browser/actor/actor_proto_conversion.cc b/chrome/browser/actor/actor_proto_conversion.cc index 89600656..f8dcc5d2 100644 --- a/chrome/browser/actor/actor_proto_conversion.cc +++ b/chrome/browser/actor/actor_proto_conversion.cc
@@ -50,6 +50,7 @@ #include "chrome/common/actor/actor_constants.h" #include "chrome/common/actor/actor_logging.h" #include "chrome/common/actor/journal_details_builder.h" +#include "chrome/common/buildflags.h" #include "chrome/common/chrome_features.h" #include "components/actor/core/actor_features.h" #include "components/actor/core/shared_types.h"
diff --git a/chrome/browser/actor/actor_tab_data.cc b/chrome/browser/actor/actor_tab_data.cc index 9d5fd9fc..ed583bc 100644 --- a/chrome/browser/actor/actor_tab_data.cc +++ b/chrome/browser/actor/actor_tab_data.cc
@@ -4,14 +4,35 @@ #include "chrome/browser/actor/actor_tab_data.h" +#include <algorithm> +#include <optional> + +#include "base/feature_list.h" +#include "base/rand_util.h" +#include "chrome/browser/actor/actor_metrics.h" #include "chrome/browser/actor/ui/dom_node_geometry.h" #include "chrome/browser/ui/tabs/public/tab_features.h" #include "chrome/common/chrome_features.h" +#include "components/optimization_guide/proto/features/common_quality_data.equal.h" #include "ui/base/metadata/metadata_impl_macros.h" #include "ui/base/unowned_user_data/scoped_unowned_user_data.h" namespace actor { +namespace { + +double ShouldRecordApcComparison() { + if (!base::FeatureList::IsEnabled(features::kGlicActorApcComparison)) { + return false; + } + + double sampling_rate = + std::clamp(features::kGlicActorApcComparisonSamplingRate.Get(), 0.0, 1.0); + return base::ShouldRecordSubsampledMetric(sampling_rate); +} + +} // namespace + DEFINE_USER_DATA(ActorTabData); ActorTabData::ActorTabData(tabs::TabInterface* tab) @@ -24,7 +45,15 @@ } void ActorTabData::DidObserveContent( - optimization_guide::proto::AnnotatedPageContent& content) { + const optimization_guide::proto::AnnotatedPageContent& content, + ApcSource source) { + if (last_observed_page_content_ && ShouldRecordApcComparison()) { + // TODO(b/508314200): Consider granularizing this metric by the tool to see + // if certain tools are more likely to result in identical content. + RecordApcComparisonIdentical(source, + *last_observed_page_content_ == content); + } + last_observed_page_content_.emplace(content); last_observed_dom_node_geometry_.reset(); }
diff --git a/chrome/browser/actor/actor_tab_data.h b/chrome/browser/actor/actor_tab_data.h index 2aa473e..2d68648 100644 --- a/chrome/browser/actor/actor_tab_data.h +++ b/chrome/browser/actor/actor_tab_data.h
@@ -15,6 +15,9 @@ #include "ui/gfx/geometry/point.h" namespace actor { + +enum class ApcSource; + namespace ui { class DomNodeGeometry; } @@ -29,7 +32,8 @@ static ActorTabData* From(tabs::TabInterface* tab); void DidObserveContent( - optimization_guide::proto::AnnotatedPageContent& content); + const optimization_guide::proto::AnnotatedPageContent& content, + ApcSource source); // Returns last observed page content, nullptr if no observation has been // made.
diff --git a/chrome/browser/actor/actor_tab_data_unittest.cc b/chrome/browser/actor/actor_tab_data_unittest.cc new file mode 100644 index 0000000..ef32b9f --- /dev/null +++ b/chrome/browser/actor/actor_tab_data_unittest.cc
@@ -0,0 +1,122 @@ +// Copyright 2026 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chrome/browser/actor/actor_tab_data.h" + +#include <memory> +#include <string_view> + +#include "base/test/metrics/histogram_tester.h" +#include "base/test/scoped_feature_list.h" +#include "chrome/browser/actor/actor_metrics.h" +#include "chrome/common/chrome_features.h" +#include "components/optimization_guide/proto/features/common_quality_data.pb.h" +#include "components/tabs/public/mock_tab_interface.h" +#include "testing/gmock/include/gmock/gmock.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "ui/base/unowned_user_data/unowned_user_data_host.h" + +namespace actor { +namespace { + +using ::testing::ReturnRef; + +constexpr std::string_view + kActorPageContextAPCComparisonActorIsIdenticalToPreviousFetchMetricName = + "Actor.PageContext.APC.Comparison.Actor.IsIdenticalToPreviousFetch"; +constexpr std::string_view + kActorPageContextAPCComparisonGlicIsIdenticalToPreviousFetchMetricName = + "Actor.PageContext.APC.Comparison.Glic.IsIdenticalToPreviousFetch"; + +class ActorTabDataTest : public testing::Test { + public: + ActorTabDataTest() = default; + ~ActorTabDataTest() override = default; + + void SetUp() override { + ON_CALL(mock_tab_, GetUnownedUserDataHost()) + .WillByDefault(ReturnRef(user_data_host_)); + } + + protected: + tabs::MockTabInterface mock_tab_; + ::ui::UnownedUserDataHost user_data_host_; +}; + +TEST_F(ActorTabDataTest, DidObserveContentRecordsMetrics) { + base::test::ScopedFeatureList scoped_feature_list; + scoped_feature_list.InitAndEnableFeatureWithParameters( + features::kGlicActorApcComparison, {{"sampling-rate", "1.0"}}); + + base::HistogramTester histogram_tester; + auto tab_data_ptr = std::make_unique<ActorTabData>(&mock_tab_); + ActorTabData* tab_data = ActorTabData::From(&mock_tab_); + ASSERT_TRUE(tab_data); + + optimization_guide::proto::AnnotatedPageContent content1; + content1.set_tab_id(1); + content1.mutable_main_frame_data()->set_title("title1"); + + // First observation should not record any metrics. + tab_data->DidObserveContent(content1, ApcSource::kActor); + histogram_tester.ExpectTotalCount( + kActorPageContextAPCComparisonActorIsIdenticalToPreviousFetchMetricName, + 0); + + // Second observation with identical content should record 'true'. + tab_data->DidObserveContent(content1, ApcSource::kActor); + histogram_tester.ExpectUniqueSample( + kActorPageContextAPCComparisonActorIsIdenticalToPreviousFetchMetricName, + true, 1); + + // Third observation with different content should record 'false'. + optimization_guide::proto::AnnotatedPageContent content2; + content2.set_tab_id(1); + content2.mutable_main_frame_data()->set_title("title2"); + tab_data->DidObserveContent(content2, ApcSource::kActor); + histogram_tester.ExpectBucketCount( + kActorPageContextAPCComparisonActorIsIdenticalToPreviousFetchMetricName, + false, 1); + + // Observation from different source (Glic) should compare with the last fetch + // (Actor). Current last content is content2. + tab_data->DidObserveContent(content2, ApcSource::kGlic); + histogram_tester.ExpectUniqueSample( + kActorPageContextAPCComparisonGlicIsIdenticalToPreviousFetchMetricName, + true, 1); + + // Another fetch from Glic, different from previous Glic (which was same as + // Actor's content2). + tab_data->DidObserveContent(content1, ApcSource::kGlic); + histogram_tester.ExpectBucketCount( + kActorPageContextAPCComparisonGlicIsIdenticalToPreviousFetchMetricName, + false, 1); +} + +TEST_F(ActorTabDataTest, DidObserveContentRecordsNoMetricsWhenDisabled) { + base::test::ScopedFeatureList scoped_feature_list; + scoped_feature_list.InitAndDisableFeature(features::kGlicActorApcComparison); + + base::HistogramTester histogram_tester; + auto tab_data_ptr = std::make_unique<ActorTabData>(&mock_tab_); + ActorTabData* tab_data = ActorTabData::From(&mock_tab_); + + optimization_guide::proto::AnnotatedPageContent content; + content.set_tab_id(1); + content.mutable_main_frame_data()->set_title("title1"); + + tab_data->DidObserveContent(content, ApcSource::kActor); + tab_data->DidObserveContent(content, ApcSource::kActor); + tab_data->DidObserveContent(content, ApcSource::kGlic); + + histogram_tester.ExpectTotalCount( + kActorPageContextAPCComparisonActorIsIdenticalToPreviousFetchMetricName, + 0); + histogram_tester.ExpectTotalCount( + kActorPageContextAPCComparisonGlicIsIdenticalToPreviousFetchMetricName, + 0); +} + +} // namespace +} // namespace actor
diff --git a/chrome/browser/actor/android/BUILD.gn b/chrome/browser/actor/android/BUILD.gn index 7829007..a7a0317 100644 --- a/chrome/browser/actor/android/BUILD.gn +++ b/chrome/browser/actor/android/BUILD.gn
@@ -130,14 +130,16 @@ android_resources("java_resources") { sources = [ + "java/res/color/actor_control_icon_button_bg.xml", "java/res/drawable/actor_gts_tab_indicator_background.xml", "java/res/drawable/actor_thumbnail_overlay_outline.xml", "java/res/layout/actor_control_layout.xml", "java/res/layout/actor_gts_tab_indicator.xml", "java/res/layout/actor_overlay.xml", "java/res/layout/actor_picture_in_picture_overlay.xml", - "java/res/values/dimens.xml", + "java/res/values-night/colors.xml", "java/res/values/colors.xml", + "java/res/values/dimens.xml", ] deps = [ "//chrome/app:java_strings_grd",
diff --git a/chrome/browser/actor/android/java/res/color/actor_control_icon_button_bg.xml b/chrome/browser/actor/android/java/res/color/actor_control_icon_button_bg.xml new file mode 100644 index 0000000..54910ae1 --- /dev/null +++ b/chrome/browser/actor/android/java/res/color/actor_control_icon_button_bg.xml
@@ -0,0 +1,10 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +Copyright 2026 The Chromium Authors +Use of this source code is governed by a BSD-style license that can be +found in the LICENSE file. +--> + +<selector xmlns:android="http://schemas.android.com/apk/res/android"> + <item android:color="?attr/colorSurfaceContainer" /> +</selector>
diff --git a/chrome/browser/actor/android/java/res/layout/actor_control_layout.xml b/chrome/browser/actor/android/java/res/layout/actor_control_layout.xml index 228d1941..93c0db4 100644 --- a/chrome/browser/actor/android/java/res/layout/actor_control_layout.xml +++ b/chrome/browser/actor/android/java/res/layout/actor_control_layout.xml
@@ -11,6 +11,7 @@ android:layout_height="wrap_content" android:paddingStart="@dimen/actor_control_padding_start" android:paddingEnd="@dimen/actor_control_padding_end" + android:paddingTop="@dimen/actor_control_padding_top" android:gravity="center_vertical"> <ImageView @@ -21,9 +22,8 @@ android:layout_marginEnd="@dimen/actor_control_icon_margin_end" android:layout_alignParentStart="true" android:layout_centerVertical="true" - android:src="@drawable/ic_spark_24dp" - app:tint="@color/default_icon_color_tint_list" - android:scaleType="centerInside" + android:src="@drawable/ic_spark_blue_16dp" + android:scaleType="fitCenter" android:importantForAccessibility="no"/> <org.chromium.ui.widget.ChromeImageButton @@ -47,13 +47,15 @@ android:layout_toStartOf="@id/actor_control_close_button" android:layout_centerVertical="true" android:layout_marginEnd="@dimen/actor_control_button_margin_end" - android:textAppearance="@style/TextAppearance.TextSmall.Secondary" + android:textAppearance="@style/TextAppearance.Button.Text.Filled" android:visibility="gone" - app:tint="@color/default_icon_color_tint_list" + android:insetTop="0dp" + android:insetBottom="0dp" + android:insetLeft="0dp" + android:insetRight="0dp" app:iconPadding="0dp" app:iconGravity="textStart" - app:materialSizeOverlay="@style/SizeOverlay.Material3Expressive.Button.IconButton.Small" - style="?attr/materialIconButtonFilledStyle" /> + style="?attr/materialButtonStyle"/> <LinearLayout android:id="@+id/actor_control_text_container" @@ -61,6 +63,7 @@ android:layout_height="wrap_content" android:minHeight="?attr/minInteractTargetSize" android:orientation="vertical" + android:gravity="center_vertical" android:layout_toEndOf="@id/actor_icon" android:layout_toStartOf="@id/actor_control_button" android:layout_centerVertical="true" @@ -69,9 +72,8 @@ <TextView android:id="@+id/actor_control_title" android:layout_width="match_parent" - android:layout_height="0dp" - android:layout_weight="1" - android:gravity="start|center_vertical" + android:layout_height="wrap_content" + android:gravity="start" android:textAppearance="@style/TextAppearance.TextMediumThick.Primary" android:ellipsize="end" android:singleLine="true" /> @@ -81,7 +83,7 @@ android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginTop="@dimen/actor_control_description_margin_top" - android:gravity="start|center_vertical" + android:gravity="start" android:textAppearance="@style/TextAppearance.TextSmall.Secondary" android:ellipsize="end" android:singleLine="true" />
diff --git a/chrome/browser/actor/android/java/res/values-night/colors.xml b/chrome/browser/actor/android/java/res/values-night/colors.xml new file mode 100644 index 0000000..2be775c --- /dev/null +++ b/chrome/browser/actor/android/java/res/values-night/colors.xml
@@ -0,0 +1,9 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +Copyright 2026 The Chromium Authors +Use of this source code is governed by a BSD-style license that can be +found in the LICENSE file. +--> +<resources> + <color name="actor_view_button_background_color">@color/baseline_primary_80</color> +</resources>
diff --git a/chrome/browser/actor/android/java/res/values/colors.xml b/chrome/browser/actor/android/java/res/values/colors.xml index e740d9ee..f25f4a2 100644 --- a/chrome/browser/actor/android/java/res/values/colors.xml +++ b/chrome/browser/actor/android/java/res/values/colors.xml
@@ -6,4 +6,5 @@ --> <resources> <color name="actor_overlay_hover_color">@color/baseline_primary_40_alpha_24</color> + <color name="actor_view_button_background_color">@color/baseline_primary_40</color> </resources>
diff --git a/chrome/browser/actor/android/java/res/values/dimens.xml b/chrome/browser/actor/android/java/res/values/dimens.xml index a0b361a8..0c96a419 100644 --- a/chrome/browser/actor/android/java/res/values/dimens.xml +++ b/chrome/browser/actor/android/java/res/values/dimens.xml
@@ -12,19 +12,18 @@ <dimen name="actor_pip_status_margin_top">4dp</dimen> <!-- Actor Control Button Dimensions --> - <dimen name="actor_control_button_corner_radius_waiting">16dp</dimen> - <dimen name="actor_control_button_corner_radius_play_pause">999dp</dimen> - <dimen name="actor_control_button_horizontal_padding_waiting">24dp</dimen> <dimen name="actor_control_button_margin_end">4dp</dimen> + <dimen name="actor_control_view_button_horizontal_padding">24dp</dimen> <!-- Actor Control View --> <dimen name="actor_control_padding_start">12dp</dimen> - <dimen name="actor_control_padding_end">4dp</dimen> + <dimen name="actor_control_padding_end">3dp</dimen> + <dimen name="actor_control_padding_top">3dp</dimen> <dimen name="actor_control_icon_size">24dp</dimen> <dimen name="actor_control_icon_margin_start">6dp</dimen> <dimen name="actor_control_icon_margin_end">12dp</dimen> <dimen name="actor_control_text_container_margin_end">8dp</dimen> - <dimen name="actor_control_description_margin_top">2dp</dimen> + <dimen name="actor_control_description_margin_top">-2dp</dimen> <!-- Actor Border Glow --> <dimen name="actor_glow_soft_blur">80dp</dimen>
diff --git a/chrome/browser/actor/android/java/src/org/chromium/chrome/browser/actor/ui/ActorControlView.java b/chrome/browser/actor/android/java/src/org/chromium/chrome/browser/actor/ui/ActorControlView.java index d0dadfa..b04a1702 100644 --- a/chrome/browser/actor/android/java/src/org/chromium/chrome/browser/actor/ui/ActorControlView.java +++ b/chrome/browser/actor/android/java/src/org/chromium/chrome/browser/actor/ui/ActorControlView.java
@@ -90,16 +90,15 @@ void configurePeekViewForState(PeekViewUiState state) { Context context = getContext(); setDescriptionView(state.getDescription(context), state.getDescriptionVisibility()); - mTitleView.setTextAppearance(state.getTitleTextAppearanceResId()); + mTitleView.setTextAppearance(context, state.getTitleTextAppearanceResId()); mActorControlButton.setVisibility(state.getButtonVisibility()); mActorControlButton.setText(state.getButtonText(context)); mActorControlButton.setIconResource(state.buttonIconResId); mActorControlButton.setBackgroundTintList(state.getButtonBackgroundTint(context)); - mActorControlButton.setCornerRadius(state.getButtonCornerRadius(context)); mActorControlButton.setIconTint(state.getIconTint(context)); int horizontalPadding = state.getButtonHorizontalPadding(context); - mActorControlButton.setPadding(horizontalPadding, 0, horizontalPadding, 0); + mActorControlButton.setPaddingRelative(horizontalPadding, 0, horizontalPadding, 0); } String getStepDescriptionForTesting() {
diff --git a/chrome/browser/actor/android/java/src/org/chromium/chrome/browser/actor/ui/PeekViewUiState.java b/chrome/browser/actor/android/java/src/org/chromium/chrome/browser/actor/ui/PeekViewUiState.java index 42be951e..9f17ee9 100644 --- a/chrome/browser/actor/android/java/src/org/chromium/chrome/browser/actor/ui/PeekViewUiState.java +++ b/chrome/browser/actor/android/java/src/org/chromium/chrome/browser/actor/ui/PeekViewUiState.java
@@ -9,7 +9,6 @@ import android.content.res.Resources; import android.view.View; -import androidx.annotation.AttrRes; import androidx.annotation.ColorRes; import androidx.annotation.DimenRes; import androidx.annotation.DrawableRes; @@ -17,8 +16,7 @@ import androidx.annotation.StringRes; import androidx.annotation.StyleRes; import androidx.appcompat.content.res.AppCompatResources; - -import com.google.android.material.color.MaterialColors; +import androidx.core.content.ContextCompat; import org.chromium.build.annotations.NullMarked; import org.chromium.build.annotations.Nullable; @@ -56,21 +54,19 @@ // UI properties for the actor control button public final @DrawableRes int buttonIconResId; - public final @AttrRes int buttonBackgroundAttr; - public final @DimenRes int buttonCornerRadiusResId; public final @DimenRes int buttonHorizontalPaddingResId; public final @ColorRes int iconTintResId; public final @Visibility int buttonVisibility; public final @StringRes int buttonTextResId; public final @Visibility int descriptionVisibility; public final @StyleRes int titleTextAppearanceResId; + public final @ColorRes int buttonBackgroundResId; private PeekViewUiState( @StateType int type, @StringRes int descriptionResId, @DrawableRes int buttonIconResId, - @AttrRes int buttonBackgroundAttr, - @DimenRes int buttonCornerRadiusResId, + @ColorRes int buttonBackgroundResId, @DimenRes int buttonHorizontalPaddingResId, @ColorRes int iconTintResId, @Visibility int buttonVisibility, @@ -80,8 +76,7 @@ this.type = type; this.descriptionResId = descriptionResId; this.buttonIconResId = buttonIconResId; - this.buttonBackgroundAttr = buttonBackgroundAttr; - this.buttonCornerRadiusResId = buttonCornerRadiusResId; + this.buttonBackgroundResId = buttonBackgroundResId; this.buttonHorizontalPaddingResId = buttonHorizontalPaddingResId; this.iconTintResId = iconTintResId; this.buttonVisibility = buttonVisibility; @@ -96,21 +91,11 @@ * @param context The {@link Context} to use for retrieving resources. * @return The background color tint list for the actor control button. */ - public ColorStateList getButtonBackgroundTint(Context context) { - return ColorStateList.valueOf( - MaterialColors.getColor(context, buttonBackgroundAttr, /* defaultValue= */ 0)); - } - - /** - * Returns the corner radius for the actor control button. - * - * @param context The {@link Context} to use for retrieving resources. - * @return The corner radius for the actor control button. - */ - public int getButtonCornerRadius(Context context) { - return (buttonCornerRadiusResId != Resources.ID_NULL) - ? context.getResources().getDimensionPixelSize(buttonCornerRadiusResId) - : 0; + public @Nullable ColorStateList getButtonBackgroundTint(Context context) { + if (buttonBackgroundResId == Resources.ID_NULL) { + return null; + } + return ColorStateList.valueOf(ContextCompat.getColor(context, buttonBackgroundResId)); } /** @@ -196,8 +181,7 @@ return type == that.type && descriptionResId == that.descriptionResId && buttonIconResId == that.buttonIconResId - && buttonBackgroundAttr == that.buttonBackgroundAttr - && buttonCornerRadiusResId == that.buttonCornerRadiusResId + && buttonBackgroundResId == that.buttonBackgroundResId && buttonHorizontalPaddingResId == that.buttonHorizontalPaddingResId && iconTintResId == that.iconTintResId && buttonVisibility == that.buttonVisibility @@ -212,8 +196,7 @@ type, descriptionResId, buttonIconResId, - buttonBackgroundAttr, - buttonCornerRadiusResId, + buttonBackgroundResId, buttonHorizontalPaddingResId, iconTintResId, buttonVisibility, @@ -228,9 +211,7 @@ /* type= */ StateType.ACTING, /* descriptionResId= */ R.string.peek_state_acting, /* buttonIconResId= */ ICON_ACTING, - /* buttonBackgroundAttr= */ R.attr.colorSurfaceContainer, - /* buttonCornerRadiusResId= */ R.dimen - .actor_control_button_corner_radius_play_pause, + /* buttonBackgroundResId= */ R.color.actor_control_icon_button_bg, /* buttonHorizontalPaddingResId= */ Resources.ID_NULL, /* iconTintResId= */ R.color.default_icon_color_tint_list, /* buttonVisibility= */ View.VISIBLE, @@ -243,9 +224,7 @@ /* type= */ StateType.PAUSED, /* descriptionResId= */ R.string.peek_state_paused, /* buttonIconResId= */ ICON_PAUSED, - /* buttonBackgroundAttr= */ R.attr.colorSurfaceContainerLow, - /* buttonCornerRadiusResId= */ R.dimen - .actor_control_button_corner_radius_play_pause, + /* buttonBackgroundResId= */ R.color.actor_control_icon_button_bg, /* buttonHorizontalPaddingResId= */ Resources.ID_NULL, /* iconTintResId= */ R.color.default_icon_color_tint_list, /* buttonVisibility= */ View.VISIBLE, @@ -258,11 +237,9 @@ /* type= */ StateType.WAITING, /* descriptionResId= */ R.string.peek_state_waiting, /* buttonIconResId= */ ICON_WAITING, - /* buttonBackgroundAttr= */ R.attr.colorPrimary, - /* buttonCornerRadiusResId= */ R.dimen - .actor_control_button_corner_radius_waiting, + /* buttonBackgroundResId= */ R.color.actor_view_button_background_color, /* buttonHorizontalPaddingResId= */ R.dimen - .actor_control_button_horizontal_padding_waiting, + .actor_control_view_button_horizontal_padding, /* iconTintResId= */ Resources.ID_NULL, /* buttonVisibility= */ View.VISIBLE, /* buttonTextResId= */ R.string.peek_state_view_button_label, @@ -274,12 +251,11 @@ /* type= */ StateType.DEFAULT, /* descriptionResId= */ Resources.ID_NULL, /* buttonIconResId= */ ICON_DEFAULT, - /* buttonBackgroundAttr= */ Resources.ID_NULL, - /* buttonCornerRadiusResId= */ Resources.ID_NULL, + /* buttonBackgroundResId= */ Resources.ID_NULL, /* buttonHorizontalPaddingResId= */ Resources.ID_NULL, /* iconTintResId= */ Resources.ID_NULL, /* buttonVisibility= */ View.GONE, /* buttonTextResId= */ Resources.ID_NULL, /* descriptionVisibility= */ View.GONE, - /* titleTextAppearanceResId= */ R.style.TextAppearance_TextLarge_Primary); + /* titleTextAppearanceResId= */ R.style.TextAppearance_Headline2Thick); }
diff --git a/chrome/browser/actor/execution_engine_origin_gating_browsertest.cc b/chrome/browser/actor/execution_engine_origin_gating_browsertest.cc index 6367967..451fb6d 100644 --- a/chrome/browser/actor/execution_engine_origin_gating_browsertest.cc +++ b/chrome/browser/actor/execution_engine_origin_gating_browsertest.cc
@@ -6,6 +6,7 @@ #include "base/task/current_thread.h" #include "base/test/bind.h" #include "base/test/metrics/histogram_tester.h" +#include "base/test/run_until.h" #include "base/test/scoped_feature_list.h" #include "base/test/test_future.h" #include "base/test/values_test_util.h" @@ -239,8 +240,16 @@ base::expected<int32_t, glic::mojom::CreateTaskErrorReason>> create_task_future; ASSERT_TRUE(GetGlicInstanceImpl()); - GetGlicInstanceImpl()->CreateTask(nullptr, nullptr, - create_task_future.GetCallback()); + ASSERT_TRUE(base::test::RunUntil([&]() { + return GetGlicInstanceImpl() + ->GetActorTaskManager() + ->GetClientSessionForTesting() != nullptr; + })); + GetGlicInstanceImpl() + ->GetActorTaskManager() + ->GetClientSessionForTesting() + ->CreateTask(actor::webui::mojom::TaskOptions::New(), + create_task_future.GetCallback()); auto result = create_task_future.Get(); ASSERT_TRUE(result.has_value()); task_id_ = TaskId(result.value()); @@ -1751,12 +1760,12 @@ StopAllTasks(); if (recording_metrics_enabled()) { - base::test::RunUntil([&]() { + ASSERT_TRUE(base::test::RunUntil([&]() { return histogram_tester .GetAllSamples( "Actor.NavigationGating.ActionNavigationsApprovedByServer") .size() == 1; - }); + })); histogram_tester.ExpectUniqueSample( "Actor.NavigationGating.ActionNavigationsApprovedByServer", true, 1); } else { @@ -1787,12 +1796,12 @@ StopAllTasks(); if (recording_metrics_enabled()) { - base::test::RunUntil([&]() { + ASSERT_TRUE(base::test::RunUntil([&]() { return histogram_tester .GetAllSamples( "Actor.NavigationGating.ActionNavigationsApprovedByServer") .size() == 1; - }); + })); histogram_tester.ExpectUniqueSample( "Actor.NavigationGating.ActionNavigationsApprovedByServer", true, 1); } else { @@ -1830,12 +1839,12 @@ StopAllTasks(); if (recording_metrics_enabled()) { - base::test::RunUntil([&]() { + ASSERT_TRUE(base::test::RunUntil([&]() { return histogram_tester .GetAllSamples( "Actor.NavigationGating.ActionNavigationsApprovedByServer") .size() == 1; - }); + })); histogram_tester.ExpectUniqueSample( "Actor.NavigationGating.ActionNavigationsApprovedByServer", false, 1); } else { @@ -1885,12 +1894,12 @@ StopAllTasks(); if (recording_metrics_enabled()) { - base::test::RunUntil([&]() { + ASSERT_TRUE(base::test::RunUntil([&]() { return histogram_tester .GetAllSamples( "Actor.NavigationGating.ActionNavigationsApprovedByServer") .size() == 1; - }); + })); histogram_tester.ExpectUniqueSample( "Actor.NavigationGating.ActionNavigationsApprovedByServer", true, 1); } else { @@ -1932,12 +1941,12 @@ StopAllTasks(); if (recording_metrics_enabled()) { - base::test::RunUntil([&]() { + ASSERT_TRUE(base::test::RunUntil([&]() { return histogram_tester .GetAllSamples( "Actor.NavigationGating.ActionNavigationsApprovedByServer") .size() == 1; - }); + })); histogram_tester.ExpectUniqueSample( "Actor.NavigationGating.ActionNavigationsApprovedByServer", true, 1); } else { @@ -1979,12 +1988,12 @@ StopAllTasks(); if (recording_metrics_enabled()) { - base::test::RunUntil([&]() { + ASSERT_TRUE(base::test::RunUntil([&]() { return histogram_tester .GetAllSamples( "Actor.NavigationGating.ActionNavigationsApprovedByServer") .size() == 1; - }); + })); histogram_tester.ExpectUniqueSample( "Actor.NavigationGating.ActionNavigationsApprovedByServer", true, 1); } else {
diff --git a/chrome/browser/actor/tools/attempt_form_filling_tool.cc b/chrome/browser/actor/tools/attempt_form_filling_tool.cc index ba25d63a..9864441d 100644 --- a/chrome/browser/actor/tools/attempt_form_filling_tool.cc +++ b/chrome/browser/actor/tools/attempt_form_filling_tool.cc
@@ -25,41 +25,15 @@ #include "components/actor/public/mojom/actor_types.mojom.h" #include "components/autofill/core/browser/integrators/actor/actor_form_filling_types.h" #include "components/autofill/core/common/unique_ids.h" -#include "components/optimization_guide/content/browser/page_content_proto_util.h" #include "content/public/browser/render_frame_host.h" #include "content/public/browser/web_contents.h" namespace actor { using autofill::FieldGlobalId; -using content::RenderFrameHost; -using content::WebContents; -using optimization_guide::TargetNodeInfo; -using optimization_guide::proto::AnnotatedPageContent; namespace { -FieldGlobalId GetFieldIdFromPageTarget( - const optimization_guide::proto::AnnotatedPageContent* last_observation, - tabs::TabInterface* tab, - const PageTarget& target) { - if (std::optional<TargetNodeInfo> node_info = - FindLastObservedNodeForActionTarget(last_observation, target)) { - if (WebContents* web_contents = tab->GetContents()) { - if (RenderFrameHost* rfh = - optimization_guide::GetRenderFrameForDocumentIdentifier( - *web_contents, - node_info->document_identifier.serialized_token())) { - return FieldGlobalId( - autofill::LocalFrameToken(rfh->GetFrameToken().value()), - autofill::FieldRendererId(node_info->node->content_attributes() - .common_ancestor_dom_node_id())); - } - } - } - return {}; -} - mojom::ActionResultPtr FromServiceError(autofill::ActorFormFillingError error) { switch (error) { case autofill::ActorFormFillingError::kAutofillNotAvailable:
diff --git a/chrome/browser/actor/tools/attempt_login_tool_interactive_ui_test.cc b/chrome/browser/actor/tools/attempt_login_tool_interactive_ui_test.cc index 10dc4af..c9bad1a 100644 --- a/chrome/browser/actor/tools/attempt_login_tool_interactive_ui_test.cc +++ b/chrome/browser/actor/tools/attempt_login_tool_interactive_ui_test.cc
@@ -202,8 +202,16 @@ base::expected<int32_t, glic::mojom::CreateTaskErrorReason>> create_task_future; ASSERT_TRUE(GetGlicInstanceImpl()); - GetGlicInstanceImpl()->CreateTask(nullptr, nullptr, - create_task_future.GetCallback()); + ASSERT_TRUE(base::test::RunUntil([&]() { + return GetGlicInstanceImpl() + ->GetActorTaskManager() + ->GetClientSessionForTesting() != nullptr; + })); + GetGlicInstanceImpl() + ->GetActorTaskManager() + ->GetClientSessionForTesting() + ->CreateTask(actor::webui::mojom::TaskOptions::New(), + create_task_future.GetCallback()); auto create_task_result = create_task_future.Get(); ASSERT_TRUE(create_task_result.has_value()); task_id_ = TaskId(create_task_result.value());
diff --git a/chrome/browser/actor/tools/page_target_util.cc b/chrome/browser/actor/tools/page_target_util.cc index 6da7ff0..d630288 100644 --- a/chrome/browser/actor/tools/page_target_util.cc +++ b/chrome/browser/actor/tools/page_target_util.cc
@@ -118,9 +118,8 @@ return optimization_guide::FindNodeAtPoint(*apc, target_blink_pixels); } -std::optional<optimization_guide::TargetNodeInfo> -FindLastObservedNodeForActionTarget( - const optimization_guide::proto::AnnotatedPageContent* apc, +std::optional<TargetNodeInfo> FindLastObservedNodeForActionTarget( + const AnnotatedPageContent* apc, const PageTarget& target) { return std::visit( absl::Overload{ @@ -134,4 +133,25 @@ target); } +autofill::FieldGlobalId GetFieldIdFromPageTarget( + const AnnotatedPageContent* last_observation, + tabs::TabInterface* tab, + const PageTarget& target) { + if (std::optional<TargetNodeInfo> node_info = + FindLastObservedNodeForActionTarget(last_observation, target)) { + if (content::WebContents* web_contents = tab->GetContents()) { + if (RenderFrameHost* rfh = + optimization_guide::GetRenderFrameForDocumentIdentifier( + *web_contents, + node_info->document_identifier.serialized_token())) { + return autofill::FieldGlobalId( + autofill::LocalFrameToken(rfh->GetFrameToken().value()), + autofill::FieldRendererId(node_info->node->content_attributes() + .common_ancestor_dom_node_id())); + } + } + } + return {}; +} + } // namespace actor
diff --git a/chrome/browser/actor/tools/page_target_util.h b/chrome/browser/actor/tools/page_target_util.h index afb3b7d..3c1fe998 100644 --- a/chrome/browser/actor/tools/page_target_util.h +++ b/chrome/browser/actor/tools/page_target_util.h
@@ -9,6 +9,7 @@ #include <string_view> #include "components/actor/core/shared_types.h" +#include "components/autofill/core/common/unique_ids.h" #include "components/optimization_guide/content/browser/page_content_proto_util.h" #include "components/optimization_guide/proto/features/common_quality_data.pb.h" #include "components/tabs/public/tab_interface.h" @@ -51,6 +52,13 @@ const optimization_guide::proto::AnnotatedPageContent* apc, const PageTarget& target); +// Returns the `autofill::FieldGlobalId` for a `PageTarget` given the last +// observed APC and the tab. +autofill::FieldGlobalId GetFieldIdFromPageTarget( + const optimization_guide::proto::AnnotatedPageContent* last_observation, + tabs::TabInterface* tab, + const PageTarget& target); + } // namespace actor #endif // CHROME_BROWSER_ACTOR_TOOLS_PAGE_TARGET_UTIL_H_
diff --git a/chrome/browser/actor/tools/tools_test_util.cc b/chrome/browser/actor/tools/tools_test_util.cc index d4ed11f8..243d8e0 100644 --- a/chrome/browser/actor/tools/tools_test_util.cc +++ b/chrome/browser/actor/tools/tools_test_util.cc
@@ -11,6 +11,7 @@ #include "base/test/bind.h" #include "base/test/test_timeouts.h" #include "chrome/browser/actor/actor_keyed_service.h" +#include "chrome/browser/actor/actor_metrics.h" #include "chrome/browser/actor/actor_tab_data.h" #include "chrome/browser/actor/actor_test_util.h" #include "chrome/browser/actor/execution_engine.h" @@ -283,7 +284,7 @@ optimization_guide::AIPageContentResultOrError page_content) { auto apc = std::move(page_content->proto); auto* tab_data = ActorTabData::From(active_tab()); - tab_data->DidObserveContent(apc); + tab_data->DidObserveContent(apc, ApcSource::kActor); std::move(quit_closure).Run(); }
diff --git a/chrome/browser/actor/ui/dom_node_geometry_browsertest.cc b/chrome/browser/actor/ui/dom_node_geometry_browsertest.cc index aba011ff..500fb39 100644 --- a/chrome/browser/actor/ui/dom_node_geometry_browsertest.cc +++ b/chrome/browser/actor/ui/dom_node_geometry_browsertest.cc
@@ -9,6 +9,7 @@ #include "base/test/bind.h" #include "base/test/scoped_feature_list.h" #include "base/test/test_future.h" +#include "chrome/browser/actor/actor_metrics.h" #include "chrome/browser/actor/actor_tab_data.h" #include "chrome/browser/ui/browser.h" #include "chrome/browser/ui/views/frame/browser_view.h" @@ -155,7 +156,7 @@ tab_data_ = ActorTabData::From(browser()->tab_strip_model()->GetActiveTab()); - tab_data_->DidObserveContent(apc); + tab_data_->DidObserveContent(apc, ApcSource::kActor); std::move(quit_closure).Run(); }
diff --git a/chrome/browser/ai/ai_data_keyed_service.cc b/chrome/browser/ai/ai_data_keyed_service.cc index f232366..9807c30 100644 --- a/chrome/browser/ai/ai_data_keyed_service.cc +++ b/chrome/browser/ai/ai_data_keyed_service.cc
@@ -31,7 +31,6 @@ #include "base/unguessable_token.h" #include "chrome/browser/history_embeddings/history_embeddings_service_factory.h" #include "chrome/browser/profiles/profile.h" -#include "chrome/browser/ui/browser_finder.h" #include "chrome/browser/ui/tabs/public/tab_features.h" #include "chrome/common/chrome_switches.h" #include "components/autofill/content/browser/content_autofill_driver.h"
diff --git a/chrome/browser/ai/ai_language_model.cc b/chrome/browser/ai/ai_language_model.cc index e6303d76..a35583d2 100644 --- a/chrome/browser/ai/ai_language_model.cc +++ b/chrome/browser/ai/ai_language_model.cc
@@ -29,6 +29,7 @@ #include "components/on_device_ai/ai_utils.h" #include "components/optimization_guide/core/model_execution/multimodal_message.h" #include "components/optimization_guide/core/model_execution/on_device_capability.h" +#include "components/optimization_guide/core/model_execution/on_device_telemetry_logger.h" #include "components/optimization_guide/core/model_execution/optimization_guide_model_execution_error.h" #include "components/optimization_guide/core/model_execution/substitution.h" #include "components/optimization_guide/core/optimization_guide_enums.h" @@ -239,6 +240,9 @@ } ~PromptState() override { + if (!completed_ && telemetry_logger_.has_value()) { + telemetry_logger_->RecordDestroyedWhileWaiting(); + } OnError(blink::mojom::ModelStreamingResponseStatus::kErrorCancelled); } @@ -250,7 +254,8 @@ uint32_t available_context_tokens, std::optional<uint32_t> configured_max_output_tokens, base::OnceClosure callback) { - start_ = base::TimeTicks::Now(); + telemetry_logger_.emplace( + optimization_guide::mojom::OnDeviceFeature::kPromptApi); callback_ = std::move(callback); // Subtract 1 to make sure the model's max tokens is never actually reached. max_output_tokens_ = @@ -271,6 +276,7 @@ void OnError(blink::mojom::ModelStreamingResponseStatus error, blink::mojom::QuotaErrorInfoPtr quota_error_info = nullptr) { + completed_ = true; if (responder_) { on_device_ai::SendStreamingStatus(responder_, error, std::move(quota_error_info)); @@ -315,6 +321,8 @@ Mode mode() const { return mode_; } private: + bool completed_ = false; + void OnDisconnect(uint32_t custom_reason, const std::string& description) { VLOG(1) << "LanguageModel ModelStreamingResponder disconnect; reason: " << custom_reason << ", description: " << description; @@ -334,8 +342,11 @@ void OnComplete(uint32_t tokens_processed) override { base::UmaHistogramCounts10000("AI.Session.LanguageModel.ContextTokens", tokens_processed); - base::UmaHistogramMediumTimes("AI.Session.LanguageModel.ContextTime", - base::TimeTicks::Now() - start_); + CHECK(telemetry_logger_.has_value()); + telemetry_logger_->RecordContextTime(); + base::UmaHistogramMediumTimes( + "AI.Session.LanguageModel.ContextTime", + telemetry_logger_->GetTimeToContextProcessing()); if (logger_ && logger_->ShouldEnableDebugLogs()) { OPTIMIZATION_GUIDE_LOGGER( optimization_guide_common::mojom::LogSource::MODEL_EXECUTION, @@ -348,16 +359,19 @@ context_receiver_.reset(); token_count_ = tokens_processed; if (mode_ == Mode::kAppendOnly) { + completed_ = true; RunCallback(); } } // on_device_model::mojom::StreamingResponder: void OnResponse(on_device_model::mojom::ResponseChunkPtr chunk) override { - if (full_response_.empty()) { + if (output_tokens_ == 0) { + CHECK(telemetry_logger_.has_value()); + telemetry_logger_->RecordFirstResponse(); base::UmaHistogramMediumTimes( "AI.Session.LanguageModel.FirstResponseTime", - base::TimeTicks::Now() - start_); + telemetry_logger_->GetTimeToFirstResponse()); } output_tokens_++; full_response_ += chunk->text; @@ -380,6 +394,17 @@ } void OnComplete(on_device_model::mojom::ResponseSummaryPtr summary) override { + completed_ = true; + token_count_ += summary->output_token_count; + + CHECK(telemetry_logger_.has_value()); + telemetry_logger_->RecordCompletion(summary->output_token_count); + base::UmaHistogramMediumTimes( + "AI.Session.LanguageModel.ResponseCompleteTime", + base::TimeTicks::Now() - generate_start_); + base::UmaHistogramCounts10000("AI.Session.LanguageModel.ResponseTokens", + summary->output_token_count); + // The `OnComplete()` method on `responder_` will be called in // `AILanguageModel::OnPromptOutputComplete()` after adding the response to // the session and handling overflow. @@ -439,7 +464,9 @@ auto generate_options = on_device_model::mojom::GenerateOptions::New(); generate_options->constraint = std::move(constraint_); generate_options->max_output_tokens = max_output_tokens_; - generate_options->add_output_tokens_to_context = true; + generate_options->add_output_tokens_to_context = + base::FeatureList::IsEnabled( + features::kAILanguageModelAppendOutputTokensToContext); session_->Generate(std::move(generate_options), response_receiver_.BindNewPipeAndPassRemote()); response_receiver_.set_disconnect_with_reason_handler( @@ -468,12 +495,6 @@ if (HandleSafetyError(std::move(safety_result))) { return; } - token_count_ += summary->output_token_count; - base::UmaHistogramMediumTimes( - "AI.Session.LanguageModel.ResponseCompleteTime", - base::TimeTicks::Now() - generate_start_); - base::UmaHistogramCounts10000("AI.Session.LanguageModel.ResponseTokens", - summary->output_token_count); if (logger_ && logger_->ShouldEnableDebugLogs()) { OPTIMIZATION_GUIDE_LOGGER( @@ -544,7 +565,8 @@ uint32_t max_output_tokens_ = 0; Mode mode_; - base::TimeTicks start_; + std::optional<optimization_guide::OnDeviceRequestTelemetryLogger> + telemetry_logger_; base::TimeTicks generate_start_; base::WeakPtrFactory<PromptState> weak_factory_{this}; @@ -1127,6 +1149,17 @@ // here. HandleOverflow(); responder->OnContextOverflow(); + } else if (!base::FeatureList::IsEnabled( + features::kAILanguageModelAppendOutputTokensToContext) && + model_output) { + // Add the output to the session since this is not added automatically from + // the Generate() call. The previous token will be a kModel token from + // ConvertToInputForExecute(). + current_session_->Append( + MakeAppendOptions( + std::move(model_output), + on_device_model::mojom::InputSource::kModelOutputFeedback), + {}); } uint32_t total_tokens = context_->non_evictable_tokens() + context_->evictable_tokens();
diff --git a/chrome/browser/ai/ai_language_model_unittest.cc b/chrome/browser/ai/ai_language_model_unittest.cc index 44cc141..6e276666 100644 --- a/chrome/browser/ai/ai_language_model_unittest.cc +++ b/chrome/browser/ai/ai_language_model_unittest.cc
@@ -21,6 +21,7 @@ #include "base/strings/stringprintf.h" #include "base/task/current_thread.h" #include "base/test/gmock_expected_support.h" +#include "base/test/metrics/histogram_tester.h" #include "base/test/mock_callback.h" #include "base/test/scoped_feature_list.h" #include "base/test/task_environment.h" @@ -256,6 +257,7 @@ {{blink::features::kAIPromptAPIMultimodalInput, {}}, {features::kAILanguageModelOverrideConfiguration, {{"ai_language_model_output_buffer", "100"}}}, + {features::kAILanguageModelAppendOutputTokensToContext, {}}, {optimization_guide::features::kOptimizationGuideOnDeviceModel, {}}, {optimization_guide::features::kAIModelUnloadableProgress, {{"ai_model_unloadable_progress_bytes", "0"}}}}, @@ -376,6 +378,29 @@ EXPECT_THAT(Prompt(*session, MakeInput("foo")), ElementsAreArray({"UfooEM"})); } +TEST_F(AILanguageModelTest, PromptTelemetry) { + base::HistogramTester histogram_tester; + auto session = CreateSession(); + Prompt(*session, MakeInput("foo")); + + histogram_tester.ExpectTotalCount( + "OptimizationGuide.ModelExecution." + "OnDeviceFirstResponseTime.PromptApi", + 1); + histogram_tester.ExpectTotalCount( + "OptimizationGuide.ModelExecution." + "OnDeviceResponseCompleteTime.PromptApi", + 1); + histogram_tester.ExpectTotalCount( + "OptimizationGuide.ModelExecution." + "OnDeviceResponseCompleteTokens.PromptApi", + 1); + histogram_tester.ExpectTotalCount( + "OptimizationGuide.ModelExecution." + "OnDeviceResponseTokensTimeToNextToken.PromptApi", + 1); +} + TEST_F(AILanguageModelTest, MultiplePrompts) { auto session = CreateSession(); EXPECT_THAT(Prompt(*session, MakeInput("foo")), ElementsAreArray({"UfooEM"})); @@ -405,6 +430,19 @@ ElementsAre("UfoobarE", "UbazEM")); } +TEST_F(AILanguageModelTest, AppendDoesNotLogDestroyedMetric) { + base::HistogramTester histogram_tester; + auto session = CreateSession(); + Append(*session, MakeInput("foo")); + + session.reset(); + + histogram_tester.ExpectTotalCount( + "OptimizationGuide.ModelExecution." + "OnDeviceDestroyedWhileWaitingForResponseTime.PromptApi", + 0); +} + TEST_F(AILanguageModelTest, PromptTokenCounts) { fake_broker_->settings().set_execute_result({"hi"}); auto session = CreateSession();
diff --git a/chrome/browser/ai/ai_manager.cc b/chrome/browser/ai/ai_manager.cc index b729523..440c485 100644 --- a/chrome/browser/ai/ai_manager.cc +++ b/chrome/browser/ai/ai_manager.cc
@@ -23,6 +23,8 @@ #include "base/types/expected.h" #include "base/types/optional_ref.h" #include "base/types/pass_key.h" +#include "base/version_info/channel.h" +#include "base/version_info/version_info.h" #include "chrome/browser/ai/ai_classifier.h" #include "chrome/browser/ai/ai_context_bound_object.h" #include "chrome/browser/ai/ai_context_bound_object_set.h" @@ -38,6 +40,7 @@ #include "chrome/browser/optimization_guide/optimization_guide_keyed_service.h" #include "chrome/browser/optimization_guide/optimization_guide_keyed_service_factory.h" #include "chrome/browser/profiles/profile.h" +#include "chrome/common/channel_info.h" #include "components/language/core/common/locale_util.h" #include "components/on_device_ai/ai_utils.h" #include "components/optimization_guide/core/delivery/model_util.h" @@ -292,6 +295,15 @@ template <typename FeatureConfigProto> std::optional<std::string> GetExperimentalUseCaseByModelVersion( const FeatureConfigProto& feature_config) { + // Support experimental use cases on Canary/Dev/Unknown and unofficial builds. + version_info::Channel channel = chrome::GetChannel(); + if (channel != version_info::Channel::CANARY && + channel != version_info::Channel::DEV && + channel != version_info::Channel::UNKNOWN && + version_info::IsOfficialBuild()) { + return std::nullopt; + } + if (base::FeatureList::IsEnabled(kAIApiFoundationalModel)) { std::string model_version = base::GetFieldTrialParamValueByFeature( kAIApiFoundationalModel, kModelVersionParam);
diff --git a/chrome/browser/ai/ai_summarizer_unittest.cc b/chrome/browser/ai/ai_summarizer_unittest.cc index e1903fd..5a4988df 100644 --- a/chrome/browser/ai/ai_summarizer_unittest.cc +++ b/chrome/browser/ai/ai_summarizer_unittest.cc
@@ -13,10 +13,13 @@ #include "base/test/task_environment.h" #include "base/test/test_future.h" #include "base/types/expected.h" +#include "base/version_info/channel.h" +#include "base/version_info/version_info.h" #include "chrome/browser/ai/ai_test_utils.h" #include "chrome/browser/ai/features.h" #include "chrome/browser/optimization_guide/mock_optimization_guide_keyed_service.h" #include "chrome/browser/optimization_guide/optimization_guide_keyed_service_factory.h" +#include "chrome/common/channel_info.h" #include "components/optimization_guide/core/model_execution/manifest_broker/test/fake_manifest_broker.h" #include "components/optimization_guide/core/model_execution/manifest_broker/test/scenario_builder.h" #include "components/optimization_guide/core/model_execution/test/mock_on_device_capability.h" @@ -1193,6 +1196,15 @@ } TEST_F(AISummarizerManifestTest, CanCreateAndCreateWithManifestGemma4) { + version_info::Channel channel = chrome::GetChannel(); + if (channel != version_info::Channel::CANARY && + channel != version_info::Channel::DEV && + channel != version_info::Channel::UNKNOWN && + version_info::IsOfficialBuild()) { + GTEST_SKIP() << "Experimental use case support is limited to " + "Canary/Dev/Unknown channels and unofficial builds."; + } + base::test::ScopedFeatureList scoped_feature_list; scoped_feature_list.InitAndEnableFeatureWithParameters( kAIApiFoundationalModel, {{"model_version", "v4"}}); @@ -1217,6 +1229,15 @@ } TEST_F(AISummarizerManifestTest, CanCreateBeforeDownloadGemma4) { + version_info::Channel channel = chrome::GetChannel(); + if (channel != version_info::Channel::CANARY && + channel != version_info::Channel::DEV && + channel != version_info::Channel::UNKNOWN && + version_info::IsOfficialBuild()) { + GTEST_SKIP() << "Experimental use case support is limited to " + "Canary/Dev/Unknown channels and unofficial builds."; + } + base::test::ScopedFeatureList scoped_feature_list; scoped_feature_list.InitAndEnableFeatureWithParameters( kAIApiFoundationalModel, {{"model_version", "v4"}});
diff --git a/chrome/browser/ai/features.cc b/chrome/browser/ai/features.cc index 14bd2b8d..200359d 100644 --- a/chrome/browser/ai/features.cc +++ b/chrome/browser/ai/features.cc
@@ -7,7 +7,9 @@ namespace features { BASE_FEATURE(kAILanguageModelOverrideConfiguration, - "kAILanguageModelOverrideConfiguration", + base::FEATURE_ENABLED_BY_DEFAULT); + +BASE_FEATURE(kAILanguageModelAppendOutputTokensToContext, base::FEATURE_ENABLED_BY_DEFAULT); // The number of tokens to use as a buffer for generating output. At least this
diff --git a/chrome/browser/ai/features.h b/chrome/browser/ai/features.h index 7cb48ac..e2b614c 100644 --- a/chrome/browser/ai/features.h +++ b/chrome/browser/ai/features.h
@@ -11,6 +11,7 @@ namespace features { BASE_DECLARE_FEATURE(kAILanguageModelOverrideConfiguration); +BASE_DECLARE_FEATURE(kAILanguageModelAppendOutputTokensToContext); extern const base::FeatureParam<int> kAILanguageModelOverrideConfigurationOutputBuffer;
diff --git a/chrome/browser/android/background_task_scheduler/chrome_background_task_factory.cc b/chrome/browser/android/background_task_scheduler/chrome_background_task_factory.cc index 9b40704a..281d1df 100644 --- a/chrome/browser/android/background_task_scheduler/chrome_background_task_factory.cc +++ b/chrome/browser/android/background_task_scheduler/chrome_background_task_factory.cc
@@ -19,6 +19,8 @@ Java_ChromeBackgroundTaskFactory_setAsDefault(env); } +// TODO(crbug.com/407797637): Remove RefreshTaskId enum since kRefreshForYouFeed +// is the only remaining value. std::unique_ptr<background_task::BackgroundTask> ChromeBackgroundTaskFactory::GetNativeBackgroundTaskFromTaskId(int task_id) { // Add your tasks here with mappings to the given task_id. @@ -26,9 +28,6 @@ case static_cast<int>(background_task::TaskIds::FEEDV2_REFRESH_JOB_ID): return std::make_unique<feed::BackgroundRefreshTask>( feed::RefreshTaskId::kRefreshForYouFeed); - case static_cast<int>(background_task::TaskIds::WEBFEEDS_REFRESH_JOB_ID): - return std::make_unique<feed::BackgroundRefreshTask>( - feed::RefreshTaskId::kRefreshWebFeed); case static_cast<int>(background_task::TaskIds::UMA_UPLOAD_JOB_ID): case static_cast<int>(background_task::TaskIds::UKM_UPLOAD_JOB_ID): case static_cast<int>(background_task::TaskIds::DWA_UPLOAD_JOB_ID):
diff --git a/chrome/browser/android/examples/custom_tabs_client/src/java/org/chromium/customtabsclient/MainActivity.java b/chrome/browser/android/examples/custom_tabs_client/src/java/org/chromium/customtabsclient/MainActivity.java index e9fb320cc..08700b28 100644 --- a/chrome/browser/android/examples/custom_tabs_client/src/java/org/chromium/customtabsclient/MainActivity.java +++ b/chrome/browser/android/examples/custom_tabs_client/src/java/org/chromium/customtabsclient/MainActivity.java
@@ -663,7 +663,7 @@ } private void initializeColorSpinner() { - Spinner colorSpinner = (Spinner) findViewById(R.id.color_spinner); + Spinner colorSpinner = findViewById(R.id.color_spinner); HashMap<String, String> colors = new HashMap<>(); colors.put("Default", ""); colors.put("White (AGA Light)", "#ffffff"); @@ -913,7 +913,7 @@ mOverflowContextualMenuItemCheckbox.setChecked( mSharedPref.getInt(SHARED_PREF_OVERFLOW_CONTEXTUAL_MENU_ITEM_BUTTON, CHECKED) == CHECKED); - EditText customSchemeEdit = (EditText) findViewById(R.id.custom_scheme); + EditText customSchemeEdit = findViewById(R.id.custom_scheme); customSchemeEdit.setText(mCustomScheme, TextView.BufferType.NORMAL); mInitialIntentCanLeaveBrowser = findViewById(R.id.allow_initial_navigation_to_leave); mInitialIntentCanLeaveBrowser.setChecked( @@ -921,7 +921,7 @@ } private void initializeCctSpinner() { - Spinner cctSpinner = (Spinner) findViewById(R.id.cct_spinner); + Spinner cctSpinner = findViewById(R.id.cct_spinner); String[] cctOptions = new String[] { CCT_OPTION_REGULAR, CCT_OPTION_PARTIAL, CCT_OPTION_INCOGNITO, CCT_OPTION_AUTHTAB
diff --git a/chrome/browser/android/omnibox/BUILD.gn b/chrome/browser/android/omnibox/BUILD.gn index 0005290..ead860b 100644 --- a/chrome/browser/android/omnibox/BUILD.gn +++ b/chrome/browser/android/omnibox/BUILD.gn
@@ -14,6 +14,7 @@ deps = [ ":omnibox", "//chrome/browser/profiles:profile", + "//chrome/browser/ui/android/omnibox", "//chrome/browser/ui/android/omnibox:jni_headers", "//url", ]
diff --git a/chrome/browser/app_controller_mac_browsertest.mm b/chrome/browser/app_controller_mac_browsertest.mm index b4bcbb4..5e5ab8a 100644 --- a/chrome/browser/app_controller_mac_browsertest.mm +++ b/chrome/browser/app_controller_mac_browsertest.mm
@@ -55,7 +55,6 @@ #include "chrome/browser/shortcuts/chrome_webloc_file.h" #include "chrome/browser/signin/signin_util.h" #include "chrome/browser/ui/browser.h" -#include "chrome/browser/ui/browser_finder.h" #include "chrome/browser/ui/browser_window.h" #include "chrome/browser/ui/browser_window/public/browser_window_interface.h" #include "chrome/browser/ui/browser_window/public/global_browser_collection.h"
diff --git a/chrome/browser/app_controller_mac_interactive_uitest.mm b/chrome/browser/app_controller_mac_interactive_uitest.mm index 5eca05d..13f8171 100644 --- a/chrome/browser/app_controller_mac_interactive_uitest.mm +++ b/chrome/browser/app_controller_mac_interactive_uitest.mm
@@ -15,7 +15,6 @@ #include "chrome/browser/profiles/profile_test_util.h" #include "chrome/browser/ui/browser.h" #include "chrome/browser/ui/browser_element_identifiers.h" -#include "chrome/browser/ui/browser_finder.h" #include "chrome/browser/ui/browser_window.h" #include "chrome/browser/ui/browser_window/public/browser_window_interface.h" #include "chrome/browser/ui/browser_window/public/browser_window_interface_iterator.h"
diff --git a/chrome/browser/apps/app_service/metrics/app_platform_input_metrics.cc b/chrome/browser/apps/app_service/metrics/app_platform_input_metrics.cc index f21c7942..e79d03a3 100644 --- a/chrome/browser/apps/app_service/metrics/app_platform_input_metrics.cc +++ b/chrome/browser/apps/app_service/metrics/app_platform_input_metrics.cc
@@ -13,7 +13,6 @@ #include "chrome/browser/apps/app_service/web_contents_app_id_utils.h" #include "chrome/browser/profiles/profile.h" #include "chrome/browser/ui/browser.h" -#include "chrome/browser/ui/browser_finder.h" #include "chrome/browser/ui/tabs/tab_strip_model.h" #include "chromeos/components/mgs/managed_guest_session_utils.h" #include "components/app_constants/constants.h"
diff --git a/chrome/browser/apps/app_service/metrics/website_metrics_browsertest.cc b/chrome/browser/apps/app_service/metrics/website_metrics_browsertest.cc index fbcc410..d8db6f8 100644 --- a/chrome/browser/apps/app_service/metrics/website_metrics_browsertest.cc +++ b/chrome/browser/apps/app_service/metrics/website_metrics_browsertest.cc
@@ -19,7 +19,6 @@ #include "chrome/browser/profiles/profile_manager.h" #include "chrome/browser/ui/browser.h" #include "chrome/browser/ui/browser_commands.h" -#include "chrome/browser/ui/browser_finder.h" #include "chrome/browser/ui/browser_window.h" #include "chrome/browser/ui/navigator/browser_navigator.h" #include "chrome/browser/ui/tabs/tab_enums.h"
diff --git a/chrome/browser/apps/digital_goods/BUILD.gn b/chrome/browser/apps/digital_goods/BUILD.gn new file mode 100644 index 0000000..5a9c897 --- /dev/null +++ b/chrome/browser/apps/digital_goods/BUILD.gn
@@ -0,0 +1,43 @@ +# Copyright 2026 The Chromium Authors +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +import("//build/config/chromeos/ui_mode.gni") + +assert(is_chromeos) + +source_set("digital_goods") { + public = [ "digital_goods_factory_impl.h" ] + + public_deps = [ + "//content/public/browser", + "//mojo/public/cpp/bindings", + "//third_party/blink/public/mojom:android_mojo_bindings", + ] +} + +source_set("impl") { + sources = [ + "digital_goods_factory_impl.cc", + "digital_goods_impl.cc", + "digital_goods_impl.h", + "util.cc", + "util.h", + ] + + deps = [ + ":digital_goods", + "//base", + "//chrome/browser/ash/apps", + "//chrome/browser/profiles:profile", + "//chrome/browser/ui/browser_window", + "//chrome/browser/web_applications", + "//chrome/browser/web_applications/proto", + "//chromeos/ash/experiences/arc", + "//components/digital_goods/mojom", + "//components/payments/core", + "//components/tabs:public", + "//content/public/browser", + "//services/network/public/mojom", + ] +}
diff --git a/chrome/browser/apps/guest_view/web_view_browsertest.cc b/chrome/browser/apps/guest_view/web_view_browsertest.cc index 5d7ea6e..5373cc50 100644 --- a/chrome/browser/apps/guest_view/web_view_browsertest.cc +++ b/chrome/browser/apps/guest_view/web_view_browsertest.cc
@@ -6810,8 +6810,10 @@ // Ensure the guest SiteInstance reflects the proper site and actually uses // site isolation. - EXPECT_EQ("http://a.test/", - main_frame->GetSiteInstance()->GetSiteURL().spec()); + EXPECT_EQ("http://a.test/", main_frame->GetSiteInstance() + ->GetSecurityPrincipal() + .GetDeprecatedSiteURL() + .spec()); EXPECT_TRUE(main_frame->GetSiteInstance()->RequiresDedicatedProcess()); EXPECT_TRUE(main_frame->GetProcess()->IsProcessLockedToSiteForTesting()); @@ -6838,7 +6840,10 @@ .GetStoragePartitionConfig()); EXPECT_EQ(subframe->GetProcess()->GetStoragePartition(), main_frame->GetProcess()->GetStoragePartition()); - EXPECT_EQ("http://b.test/", subframe->GetSiteInstance()->GetSiteURL().spec()); + EXPECT_EQ("http://b.test/", subframe->GetSiteInstance() + ->GetSecurityPrincipal() + .GetDeprecatedSiteURL() + .spec()); EXPECT_TRUE(subframe->GetSiteInstance()->RequiresDedicatedProcess()); EXPECT_TRUE(subframe->GetProcess()->IsProcessLockedToSiteForTesting()); }
diff --git a/chrome/browser/apps/platform_apps/platform_app_navigation_redirector.cc b/chrome/browser/apps/platform_apps/platform_app_navigation_redirector.cc index e510937..f2f4e5d2 100644 --- a/chrome/browser/apps/platform_apps/platform_app_navigation_redirector.cc +++ b/chrome/browser/apps/platform_apps/platform_app_navigation_redirector.cc
@@ -9,7 +9,6 @@ #include "base/logging.h" #include "chrome/browser/preloading/prefetch/no_state_prefetch/chrome_no_state_prefetch_contents_delegate.h" #include "chrome/browser/profiles/profile.h" -#include "chrome/browser/ui/browser_finder.h" #include "chrome/common/extensions/api/url_handlers/url_handlers_parser.h" #include "components/navigation_interception/intercept_navigation_throttle.h" #include "components/no_state_prefetch/browser/no_state_prefetch_contents.h"
diff --git a/chrome/browser/ash/app_list/DEPS b/chrome/browser/ash/app_list/DEPS index 696a440..dbb128c 100644 --- a/chrome/browser/ash/app_list/DEPS +++ b/chrome/browser/ash/app_list/DEPS
@@ -52,7 +52,6 @@ "+chrome/browser/trusted_vault", "+chrome/browser/ui/ash", "+chrome/browser/ui/browser_commands.h", - "+chrome/browser/ui/browser_finder.h", "+chrome/browser/ui/browser.h", "+chrome/browser/ui/browser_window.h", "+chrome/browser/ui/browser_window/public/browser_window_interface.h",
diff --git a/chrome/browser/ash/app_list/app_list_client_impl_browsertest.cc b/chrome/browser/ash/app_list/app_list_client_impl_browsertest.cc index 9e47c851..e48443b 100644 --- a/chrome/browser/ash/app_list/app_list_client_impl_browsertest.cc +++ b/chrome/browser/ash/app_list/app_list_client_impl_browsertest.cc
@@ -78,7 +78,6 @@ #include "chrome/browser/ui/ash/shelf/shelf_controller_helper.h" #include "chrome/browser/ui/ash/system_web_apps/system_web_app_ui_utils.h" #include "chrome/browser/ui/browser.h" -#include "chrome/browser/ui/browser_finder.h" #include "chrome/browser/ui/browser_window.h" #include "chrome/browser/ui/browser_window/public/browser_window_interface.h" #include "chrome/browser/ui/browser_window/public/profile_browser_collection.h"
diff --git a/chrome/browser/ash/app_list/search/DEPS b/chrome/browser/ash/app_list/search/DEPS index 8ebef80..0738728 100644 --- a/chrome/browser/ash/app_list/search/DEPS +++ b/chrome/browser/ash/app_list/search/DEPS
@@ -41,7 +41,6 @@ "+chrome/browser/trusted_vault", "+chrome/browser/ui/ash", "+chrome/browser/ui/browser_commands.h", - "+chrome/browser/ui/browser_finder.h", "+chrome/browser/ui/browser.h", "+chrome/browser/ui/browser_window.h", "+chrome/browser/ui/webui/ash/settings/calculator",
diff --git a/chrome/browser/ash/app_list/search/test/app_list_search_test_helper.h b/chrome/browser/ash/app_list/search/test/app_list_search_test_helper.h index ef9fa86..0501e2c 100644 --- a/chrome/browser/ash/app_list/search/test/app_list_search_test_helper.h +++ b/chrome/browser/ash/app_list/search/test/app_list_search_test_helper.h
@@ -29,7 +29,6 @@ #include "chrome/browser/ash/file_manager/path_util.h" #include "chrome/browser/profiles/profile.h" #include "chrome/browser/ui/browser.h" -#include "chrome/browser/ui/browser_finder.h" #include "chrome/test/base/in_process_browser_test.h" #include "components/prefs/pref_service.h" #include "content/public/test/browser_test.h"
diff --git a/chrome/browser/ash/app_mode/DEPS b/chrome/browser/ash/app_mode/DEPS index df5c565..ae5a2c0c 100644 --- a/chrome/browser/ash/app_mode/DEPS +++ b/chrome/browser/ash/app_mode/DEPS
@@ -85,7 +85,6 @@ "+chrome/browser/ui/browser_window/public", ], "kiosk_troubleshooting_tools_browsertest\\.cc": [ - "+chrome/browser/ui/browser_finder.h", "+chrome/browser/ui/browser_window/public", "+chrome/browser/ui/views/task_manager_view.h", ],
diff --git a/chrome/browser/ash/app_mode/kiosk_troubleshooting_tools_browsertest.cc b/chrome/browser/ash/app_mode/kiosk_troubleshooting_tools_browsertest.cc index 8742602..6a72a7e6 100644 --- a/chrome/browser/ash/app_mode/kiosk_troubleshooting_tools_browsertest.cc +++ b/chrome/browser/ash/app_mode/kiosk_troubleshooting_tools_browsertest.cc
@@ -17,7 +17,6 @@ #include "chrome/browser/devtools/devtools_window_testing.h" #include "chrome/browser/policy/developer_tools_policy_handler.h" #include "chrome/browser/profiles/profile.h" -#include "chrome/browser/ui/browser_finder.h" #include "chrome/browser/ui/browser_window.h" #include "chrome/browser/ui/browser_window/public/browser_window_interface.h" #include "chrome/browser/ui/browser_window/public/browser_window_interface_iterator.h"
diff --git a/chrome/browser/ash/app_mode/test/DEPS b/chrome/browser/ash/app_mode/test/DEPS index 36c0363..1c67b60 100644 --- a/chrome/browser/ash/app_mode/test/DEPS +++ b/chrome/browser/ash/app_mode/test/DEPS
@@ -12,7 +12,6 @@ "+chrome/browser/ash/accessibility/accessibility_test_utils.h", "+chrome/browser/ash/accessibility/chromevox_test_utils.h", "+chrome/browser/device_identity", - "+chrome/browser/ui/browser_finder.h", "+chrome/browser/ui/browser_window/public", "+chrome/browser/ui/exclusive_access/exclusive_access_context.h", "+chrome/browser/ui/navigator/browser_navigator.h",
diff --git a/chrome/browser/ash/app_mode/test/kiosk_browsertest.cc b/chrome/browser/ash/app_mode/test/kiosk_browsertest.cc index c5aef53..b8675e2 100644 --- a/chrome/browser/ash/app_mode/test/kiosk_browsertest.cc +++ b/chrome/browser/ash/app_mode/test/kiosk_browsertest.cc
@@ -27,7 +27,6 @@ #include "chrome/browser/ash/app_mode/test/network_state_mixin.h" #include "chrome/browser/signin/identity_manager_factory.h" #include "chrome/browser/ui/ash/login/login_display_host.h" -#include "chrome/browser/ui/browser_finder.h" #include "chrome/browser/ui/browser_window.h" #include "chrome/browser/ui/browser_window/public/browser_window_interface_iterator.h" #include "chrome/browser/ui/browser_window/public/global_browser_collection.h"
diff --git a/chrome/browser/ash/app_mode/test/kiosk_test_utils.cc b/chrome/browser/ash/app_mode/test/kiosk_test_utils.cc index 850b122..d243444 100644 --- a/chrome/browser/ash/app_mode/test/kiosk_test_utils.cc +++ b/chrome/browser/ash/app_mode/test/kiosk_test_utils.cc
@@ -36,7 +36,6 @@ #include "chrome/browser/chromeos/app_mode/kiosk_web_app_install_util.h" #include "chrome/browser/profiles/profile_manager.h" #include "chrome/browser/ui/ash/login/login_display_host.h" -#include "chrome/browser/ui/browser_finder.h" #include "chrome/browser/ui/browser_window/public/browser_window_interface.h" #include "chrome/browser/ui/browser_window/public/browser_window_interface_iterator.h" #include "chrome/browser/ui/browser_window/public/global_browser_collection.h"
diff --git a/chrome/browser/ash/app_mode/test/new_windows_in_kiosk_allowed_browsertest.cc b/chrome/browser/ash/app_mode/test/new_windows_in_kiosk_allowed_browsertest.cc index cd7179ef..1ecf81e 100644 --- a/chrome/browser/ash/app_mode/test/new_windows_in_kiosk_allowed_browsertest.cc +++ b/chrome/browser/ash/app_mode/test/new_windows_in_kiosk_allowed_browsertest.cc
@@ -20,7 +20,6 @@ #include "chrome/browser/ash/app_mode/web_app/kiosk_web_app_manager.h" #include "chrome/browser/ash/ownership/fake_owner_settings_service.h" #include "chrome/browser/chromeos/app_mode/kiosk_web_app_install_util.h" -#include "chrome/browser/ui/browser_finder.h" #include "chrome/browser/ui/browser_window.h" #include "chrome/browser/ui/browser_window/public/browser_window_interface.h" #include "chrome/browser/ui/browser_window/public/browser_window_interface_iterator.h"
diff --git a/chrome/browser/ash/app_restore/DEPS b/chrome/browser/ash/app_restore/DEPS index 9ce0b53..041b9b38 100644 --- a/chrome/browser/ash/app_restore/DEPS +++ b/chrome/browser/ash/app_restore/DEPS
@@ -59,7 +59,6 @@ ".*test\\.cc": [ "+chrome/browser/browser_process.h", "+chrome/browser/ui/browser.h", - "+chrome/browser/ui/browser_finder.h", "+chrome/browser/ui/browser_tabstrip.h", "+chrome/browser/ui/browser_window.h", "+chrome/browser/ui/browser_window/public",
diff --git a/chrome/browser/ash/app_restore/full_restore_app_launch_handler_browsertest.cc b/chrome/browser/ash/app_restore/full_restore_app_launch_handler_browsertest.cc index 8496891..8e3ef3c 100644 --- a/chrome/browser/ash/app_restore/full_restore_app_launch_handler_browsertest.cc +++ b/chrome/browser/ash/app_restore/full_restore_app_launch_handler_browsertest.cc
@@ -57,7 +57,6 @@ #include "chrome/browser/ui/ash/desks/desks_client.h" #include "chrome/browser/ui/ash/device_scheduled_reboot/reboot_notification_controller.h" #include "chrome/browser/ui/browser.h" -#include "chrome/browser/ui/browser_finder.h" #include "chrome/browser/ui/browser_tabstrip.h" #include "chrome/browser/ui/browser_window.h" #include "chrome/browser/ui/browser_window/public/browser_window_interface.h"
diff --git a/chrome/browser/ash/app_restore/informed_restore_browsertest.cc b/chrome/browser/ash/app_restore/informed_restore_browsertest.cc index 2aab8f3f..2a6040f 100644 --- a/chrome/browser/ash/app_restore/informed_restore_browsertest.cc +++ b/chrome/browser/ash/app_restore/informed_restore_browsertest.cc
@@ -27,7 +27,6 @@ #include "chrome/browser/ash/app_restore/full_restore_service.h" #include "chrome/browser/profiles/profile_manager.h" #include "chrome/browser/ui/ash/system_web_apps/system_web_app_ui_utils.h" -#include "chrome/browser/ui/browser_finder.h" #include "chrome/browser/ui/browser_tabstrip.h" #include "chrome/browser/ui/browser_window.h" #include "chrome/browser/ui/browser_window/public/browser_window_interface.h"
diff --git a/chrome/browser/ash/arc/BUILD.gn b/chrome/browser/ash/arc/BUILD.gn index 0e4f43d..8cb5263c 100644 --- a/chrome/browser/ash/arc/BUILD.gn +++ b/chrome/browser/ash/arc/BUILD.gn
@@ -275,6 +275,7 @@ "//chrome/browser/profiles:profile", "//chrome/browser/profiles:profile_manager", "//chrome/browser/tab_contents", + "//chrome/browser/ui:simple_message_box_headers", "//chrome/browser/ui/ash/login", "//chrome/browser/ui/ash/shelf", "//chromeos/ash/components/browser_context_helper",
diff --git a/chrome/browser/ash/boca/on_task/DEPS b/chrome/browser/ash/boca/on_task/DEPS index ac5ac2f..6526da6 100644 --- a/chrome/browser/ash/boca/on_task/DEPS +++ b/chrome/browser/ash/boca/on_task/DEPS
@@ -20,7 +20,6 @@ ], "on_task_locked_session_navigation_throttle_interactive_ui_test\\.cc": [ "+chrome/browser/ui/browser_commands.h", - "+chrome/browser/ui/browser_finder.h", "+chrome/browser/ui/browser_window/public/global_browser_collection.h", "+chrome/browser/ui/navigator/browser_navigator_params.h", "+chrome/browser/ui/test/test_browser_closed_waiter.h", @@ -31,7 +30,6 @@ "+chrome/app/chrome_command_ids.h", "+chrome/browser/renderer_context_menu/render_view_context_menu_test_util.h", "+chrome/browser/ui/views/web_apps/frame_toolbar/web_app_frame_toolbar_view.h", - "+chrome/browser/ui/browser_finder.h", "+chrome/browser/ui/browser_window/public/global_browser_collection.h", "+chrome/common/chrome_paths.h", "+chrome/common/webui_url_constants.h",
diff --git a/chrome/browser/ash/boca/on_task/on_task_locked_session_navigation_throttle_interactive_ui_test.cc b/chrome/browser/ash/boca/on_task/on_task_locked_session_navigation_throttle_interactive_ui_test.cc index 3325e84..2dbfd22 100644 --- a/chrome/browser/ash/boca/on_task/on_task_locked_session_navigation_throttle_interactive_ui_test.cc +++ b/chrome/browser/ash/boca/on_task/on_task_locked_session_navigation_throttle_interactive_ui_test.cc
@@ -27,7 +27,6 @@ #include "chrome/browser/ui/ash/system_web_apps/system_web_app_ui_utils.h" #include "chrome/browser/ui/browser.h" #include "chrome/browser/ui/browser_commands.h" -#include "chrome/browser/ui/browser_finder.h" #include "chrome/browser/ui/browser_window/public/global_browser_collection.h" #include "chrome/browser/ui/navigator/browser_navigator_params.h" #include "chrome/test/base/in_process_browser_test.h"
diff --git a/chrome/browser/ash/boca/on_task/on_task_locked_session_window_tracker_browsertest.cc b/chrome/browser/ash/boca/on_task/on_task_locked_session_window_tracker_browsertest.cc index 82fea38..b7738786 100644 --- a/chrome/browser/ash/boca/on_task/on_task_locked_session_window_tracker_browsertest.cc +++ b/chrome/browser/ash/boca/on_task/on_task_locked_session_window_tracker_browsertest.cc
@@ -27,7 +27,6 @@ #include "chrome/browser/renderer_context_menu/render_view_context_menu_test_util.h" #include "chrome/browser/ui/ash/system_web_apps/system_web_app_ui_utils.h" #include "chrome/browser/ui/browser.h" -#include "chrome/browser/ui/browser_finder.h" #include "chrome/browser/ui/browser_window.h" #include "chrome/browser/ui/browser_window/public/global_browser_collection.h" #include "chrome/browser/ui/views/frame/browser_view.h"
diff --git a/chrome/browser/ash/browser_delegate/browser_controller_impl.cc b/chrome/browser/ash/browser_delegate/browser_controller_impl.cc index d12145c..6a36049 100644 --- a/chrome/browser/ash/browser_delegate/browser_controller_impl.cc +++ b/chrome/browser/ash/browser_delegate/browser_controller_impl.cc
@@ -17,7 +17,6 @@ #include "chrome/browser/profiles/profile.h" #include "chrome/browser/ui/autofill/chrome_autofill_client.h" #include "chrome/browser/ui/browser.h" -#include "chrome/browser/ui/browser_finder.h" #include "chrome/browser/ui/browser_window.h" #include "chrome/browser/ui/browser_window/public/browser_window_interface.h" #include "chrome/browser/ui/browser_window/public/browser_window_interface_iterator.h"
diff --git a/chrome/browser/ash/child_accounts/DEPS b/chrome/browser/ash/child_accounts/DEPS index 0e7c3e4..2956586 100644 --- a/chrome/browser/ash/child_accounts/DEPS +++ b/chrome/browser/ash/child_accounts/DEPS
@@ -38,7 +38,6 @@ "+chrome/browser/prefs", "+chrome/browser/ui/ash/shelf", "+chrome/browser/ui/browser.h", - "+chrome/browser/ui/browser_finder.h", "+chrome/browser/web_applications/test", "+chrome/browser/web_applications/web_app_command_scheduler.h", "+chrome/browser/web_applications/web_app_constants.h",
diff --git a/chrome/browser/ash/child_accounts/family_user_chrome_activity_metrics_unittest.cc b/chrome/browser/ash/child_accounts/family_user_chrome_activity_metrics_unittest.cc index 710c2288..2174af0 100644 --- a/chrome/browser/ash/child_accounts/family_user_chrome_activity_metrics_unittest.cc +++ b/chrome/browser/ash/child_accounts/family_user_chrome_activity_metrics_unittest.cc
@@ -22,7 +22,6 @@ #include "chrome/browser/ash/child_accounts/time_limits/app_types.h" #include "chrome/browser/extensions/extension_service.h" #include "chrome/browser/extensions/test_extension_system.h" -#include "chrome/browser/ui/browser_finder.h" #include "chrome/browser/ui/browser_window/public/global_browser_collection.h" #include "chrome/test/base/chrome_render_view_host_test_harness.h" #include "chrome/test/base/test_browser_window_aura.h"
diff --git a/chrome/browser/ash/crosapi/BUILD.gn b/chrome/browser/ash/crosapi/BUILD.gn index 86c9038..b40ddf690 100644 --- a/chrome/browser/ash/crosapi/BUILD.gn +++ b/chrome/browser/ash/crosapi/BUILD.gn
@@ -19,10 +19,6 @@ "crosapi_id.h", "crosapi_manager.cc", "crosapi_manager.h", - "document_scan_ash.cc", - "document_scan_ash.h", - "document_scan_ash_type_converters.cc", - "document_scan_ash_type_converters.h", "local_printer_ash.cc", "local_printer_ash.h", ] @@ -279,11 +275,7 @@ source_set("unit_tests") { testonly = true - sources = [ - "document_scan_ash_type_converters_unittest.cc", - "document_scan_ash_unittest.cc", - "local_printer_ash_unittest.cc", - ] + sources = [ "local_printer_ash_unittest.cc" ] deps = [ ":crosapi",
diff --git a/chrome/browser/ash/crosapi/crosapi_ash.cc b/chrome/browser/ash/crosapi/crosapi_ash.cc index f8ba9d5..2ebefe0 100644 --- a/chrome/browser/ash/crosapi/crosapi_ash.cc +++ b/chrome/browser/ash/crosapi/crosapi_ash.cc
@@ -13,7 +13,6 @@ #include "base/notimplemented.h" #include "chrome/browser/apps/app_service/app_service_proxy.h" #include "chrome/browser/apps/app_service/app_service_proxy_factory.h" -#include "chrome/browser/ash/crosapi/document_scan_ash.h" #include "chrome/browser/ash/crosapi/local_printer_ash.h" #include "chrome/browser/ash/login/quick_unlock/quick_unlock_factory.h" #include "chrome/browser/ash/profiles/profile_helper.h" @@ -71,8 +70,7 @@ } // namespace CrosapiAsh::CrosapiAsh() - : document_scan_ash_(std::make_unique<DocumentScanAsh>()), - local_printer_ash_(std::make_unique<LocalPrinterAsh>()), + : local_printer_ash_(std::make_unique<LocalPrinterAsh>()), telemetry_diagnostic_routine_service_ash_( std::make_unique<ash::TelemetryDiagnosticsRoutineServiceAsh>()), telemetry_management_service_ash_( @@ -113,11 +111,6 @@ std::move(receiver)); } -void CrosapiAsh::BindDocumentScan( - mojo::PendingReceiver<mojom::DocumentScan> receiver) { - document_scan_ash_->BindReceiver(std::move(receiver)); -} - void CrosapiAsh::BindHidManager( mojo::PendingReceiver<device::mojom::HidManager> receiver) { content::GetDeviceService().BindHidManager(std::move(receiver));
diff --git a/chrome/browser/ash/crosapi/crosapi_ash.h b/chrome/browser/ash/crosapi/crosapi_ash.h index 7a8e0c68..0afb1b2e 100644 --- a/chrome/browser/ash/crosapi/crosapi_ash.h +++ b/chrome/browser/ash/crosapi/crosapi_ash.h
@@ -32,7 +32,6 @@ namespace crosapi { -class DocumentScanAsh; class LocalPrinterAsh; // Implementation of Crosapi in Ash. It provides a set of APIs that @@ -55,8 +54,6 @@ void BindCfmServiceContext( mojo::PendingReceiver<chromeos::cfm::mojom::CfmServiceContext> receiver) override; - void BindDocumentScan( - mojo::PendingReceiver<mojom::DocumentScan> receiver) override; void BindHidManager( mojo::PendingReceiver<device::mojom::HidManager> receiver) override; void BindInSessionAuth( @@ -89,8 +86,6 @@ void BindTelemetryProbeService( mojo::PendingReceiver<mojom::TelemetryProbeService> receiver) override; - DocumentScanAsh* document_scan_ash() { return document_scan_ash_.get(); } - LocalPrinterAsh* local_printer_ash() { return local_printer_ash_.get(); } ash::ProbeServiceAsh* probe_service_ash() { return probe_service_ash_.get(); } @@ -99,7 +94,6 @@ // Called when a connection is lost. void OnDisconnected(); - std::unique_ptr<DocumentScanAsh> document_scan_ash_; std::unique_ptr<LocalPrinterAsh> local_printer_ash_; std::unique_ptr<ash::TelemetryDiagnosticsRoutineServiceAsh> telemetry_diagnostic_routine_service_ash_;
diff --git a/chrome/browser/ash/crosapi/document_scan_ash.cc b/chrome/browser/ash/crosapi/document_scan_ash.cc deleted file mode 100644 index 58cdc43..0000000 --- a/chrome/browser/ash/crosapi/document_scan_ash.cc +++ /dev/null
@@ -1,53 +0,0 @@ -// Copyright 2022 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/ash/crosapi/document_scan_ash.h" - -#include <memory> -#include <optional> -#include <string> -#include <utility> -#include <vector> - -#include "ash/constants/ash_features.h" -#include "base/memory/weak_ptr.h" -#include "chrome/browser/ash/crosapi/document_scan_ash_type_converters.h" -#include "chrome/browser/ash/scanning/lorgnette_scanner_manager.h" -#include "chrome/browser/ash/scanning/lorgnette_scanner_manager_factory.h" -#include "chrome/browser/profiles/profile_manager.h" -#include "chromeos/ash/components/dbus/lorgnette/lorgnette_service.pb.h" -#include "components/user_manager/user_manager.h" - -namespace crosapi { - -namespace { - -// Supports the static_cast() in ProtobufResultToMojoResult() below. -static_assert(lorgnette::SCAN_FAILURE_MODE_NO_FAILURE == - static_cast<int>(mojom::ScanFailureMode::kNoFailure)); -static_assert(lorgnette::SCAN_FAILURE_MODE_UNKNOWN == - static_cast<int>(mojom::ScanFailureMode::kUnknown)); -static_assert(lorgnette::SCAN_FAILURE_MODE_DEVICE_BUSY == - static_cast<int>(mojom::ScanFailureMode::kDeviceBusy)); -static_assert(lorgnette::SCAN_FAILURE_MODE_ADF_JAMMED == - static_cast<int>(mojom::ScanFailureMode::kAdfJammed)); -static_assert(lorgnette::SCAN_FAILURE_MODE_ADF_EMPTY == - static_cast<int>(mojom::ScanFailureMode::kAdfEmpty)); -static_assert(lorgnette::SCAN_FAILURE_MODE_FLATBED_OPEN == - static_cast<int>(mojom::ScanFailureMode::kFlatbedOpen)); -static_assert(lorgnette::SCAN_FAILURE_MODE_IO_ERROR == - static_cast<int>(mojom::ScanFailureMode::kIoError)); - -} // namespace - -DocumentScanAsh::DocumentScanAsh() = default; - -DocumentScanAsh::~DocumentScanAsh() = default; - -void DocumentScanAsh::BindReceiver( - mojo::PendingReceiver<mojom::DocumentScan> pending_receiver) { - receivers_.Add(this, std::move(pending_receiver)); -} - -} // namespace crosapi
diff --git a/chrome/browser/ash/crosapi/document_scan_ash.h b/chrome/browser/ash/crosapi/document_scan_ash.h deleted file mode 100644 index a2af795..0000000 --- a/chrome/browser/ash/crosapi/document_scan_ash.h +++ /dev/null
@@ -1,32 +0,0 @@ -// Copyright 2022 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_ASH_CROSAPI_DOCUMENT_SCAN_ASH_H_ -#define CHROME_BROWSER_ASH_CROSAPI_DOCUMENT_SCAN_ASH_H_ - -#include "chromeos/crosapi/mojom/document_scan.mojom.h" -#include "mojo/public/cpp/bindings/receiver_set.h" - -namespace crosapi { - -// Implements the crosapi interface for DocumentScan. Lives in Ash-Chrome on the -// UI thread. -class DocumentScanAsh : public mojom::DocumentScan { - public: - DocumentScanAsh(); - DocumentScanAsh(const DocumentScanAsh&) = delete; - DocumentScanAsh& operator=(const DocumentScanAsh&) = delete; - ~DocumentScanAsh() override; - - void BindReceiver(mojo::PendingReceiver<mojom::DocumentScan> receiver); - - private: - // This class supports any number of connections. This allows the client to - // have multiple, potentially thread-affine, remotes. - mojo::ReceiverSet<mojom::DocumentScan> receivers_; -}; - -} // namespace crosapi - -#endif // CHROME_BROWSER_ASH_CROSAPI_DOCUMENT_SCAN_ASH_H_
diff --git a/chrome/browser/ash/crosapi/document_scan_ash_type_converters.cc b/chrome/browser/ash/crosapi/document_scan_ash_type_converters.cc deleted file mode 100644 index a08dbd2a..0000000 --- a/chrome/browser/ash/crosapi/document_scan_ash_type_converters.cc +++ /dev/null
@@ -1,114 +0,0 @@ -// Copyright 2023 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/ash/crosapi/document_scan_ash_type_converters.h" - -#include <cstdint> - -#include "base/notreached.h" -#include "base/strings/strcat.h" - -namespace mojo { - -template <> -struct TypeConverter<crosapi::mojom::ScannerInfo_ConnectionType, - lorgnette::ConnectionType> { - static crosapi::mojom::ScannerInfo_ConnectionType Convert( - lorgnette::ConnectionType input) { - using ConnectionType = crosapi::mojom::ScannerInfo_ConnectionType; - - switch (input) { - default: - NOTREACHED(); - case lorgnette::ConnectionType::CONNECTION_UNSPECIFIED: - return ConnectionType::kUnspecified; - case lorgnette::ConnectionType::CONNECTION_USB: - return ConnectionType::kUsb; - case lorgnette::ConnectionType::CONNECTION_NETWORK: - return ConnectionType::kNetwork; - } - } -}; - -template <> -struct TypeConverter<crosapi::mojom::ScannerInfoPtr, lorgnette::ScannerInfo> { - static crosapi::mojom::ScannerInfoPtr Convert( - const lorgnette::ScannerInfo& input) { - auto output = crosapi::mojom::ScannerInfo::New(); - output->id = input.name(); - output->display_name = input.display_name(); - output->manufacturer = input.manufacturer(); - output->model = input.model(); - output->device_uuid = input.device_uuid(); - output->connection_type = - ConvertTo<crosapi::mojom::ScannerInfo_ConnectionType>( - input.connection_type()); - output->secure = input.secure(); - output->image_formats.reserve(input.image_format_size()); - for (const std::string& format : input.image_format()) { - output->image_formats.emplace_back(format); - } - output->protocol_type = input.protocol_type(); - return output; - } -}; - -crosapi::mojom::ScannerOperationResult TypeConverter< - crosapi::mojom::ScannerOperationResult, - lorgnette::OperationResult>::Convert(lorgnette::OperationResult input) { - switch (input) { - default: - NOTREACHED(); - case lorgnette::OPERATION_RESULT_UNKNOWN: - return crosapi::mojom::ScannerOperationResult::kUnknown; - case lorgnette::OPERATION_RESULT_SUCCESS: - return crosapi::mojom::ScannerOperationResult::kSuccess; - case lorgnette::OPERATION_RESULT_UNSUPPORTED: - return crosapi::mojom::ScannerOperationResult::kUnsupported; - case lorgnette::OPERATION_RESULT_CANCELLED: - return crosapi::mojom::ScannerOperationResult::kCancelled; - case lorgnette::OPERATION_RESULT_DEVICE_BUSY: - return crosapi::mojom::ScannerOperationResult::kDeviceBusy; - case lorgnette::OPERATION_RESULT_INVALID: - return crosapi::mojom::ScannerOperationResult::kInvalid; - case lorgnette::OPERATION_RESULT_WRONG_TYPE: - return crosapi::mojom::ScannerOperationResult::kWrongType; - case lorgnette::OPERATION_RESULT_EOF: - return crosapi::mojom::ScannerOperationResult::kEndOfData; - case lorgnette::OPERATION_RESULT_ADF_JAMMED: - return crosapi::mojom::ScannerOperationResult::kAdfJammed; - case lorgnette::OPERATION_RESULT_ADF_EMPTY: - return crosapi::mojom::ScannerOperationResult::kAdfEmpty; - case lorgnette::OPERATION_RESULT_COVER_OPEN: - return crosapi::mojom::ScannerOperationResult::kCoverOpen; - case lorgnette::OPERATION_RESULT_IO_ERROR: - return crosapi::mojom::ScannerOperationResult::kIoError; - case lorgnette::OPERATION_RESULT_ACCESS_DENIED: - return crosapi::mojom::ScannerOperationResult::kAccessDenied; - case lorgnette::OPERATION_RESULT_NO_MEMORY: - return crosapi::mojom::ScannerOperationResult::kNoMemory; - case lorgnette::OPERATION_RESULT_UNREACHABLE: - return crosapi::mojom::ScannerOperationResult::kDeviceUnreachable; - case lorgnette::OPERATION_RESULT_MISSING: - return crosapi::mojom::ScannerOperationResult::kDeviceMissing; - case lorgnette::OPERATION_RESULT_INTERNAL_ERROR: - return crosapi::mojom::ScannerOperationResult::kInternalError; - } -} - -crosapi::mojom::GetScannerListResponsePtr -TypeConverter<crosapi::mojom::GetScannerListResponsePtr, - lorgnette::ListScannersResponse>:: - Convert(const lorgnette::ListScannersResponse& input) { - auto output = crosapi::mojom::GetScannerListResponse::New(); - output->result = - ConvertTo<crosapi::mojom::ScannerOperationResult>(input.result()); - output->scanners.reserve(input.scanners().size()); - for (const auto& scanner : input.scanners()) { - output->scanners.emplace_back(crosapi::mojom::ScannerInfo::From(scanner)); - } - return output; -} - -} // namespace mojo
diff --git a/chrome/browser/ash/crosapi/document_scan_ash_type_converters.h b/chrome/browser/ash/crosapi/document_scan_ash_type_converters.h deleted file mode 100644 index a8f850bd..0000000 --- a/chrome/browser/ash/crosapi/document_scan_ash_type_converters.h +++ /dev/null
@@ -1,32 +0,0 @@ -// Copyright 2023 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_ASH_CROSAPI_DOCUMENT_SCAN_ASH_TYPE_CONVERTERS_H_ -#define CHROME_BROWSER_ASH_CROSAPI_DOCUMENT_SCAN_ASH_TYPE_CONVERTERS_H_ - -#include <optional> - -#include "chromeos/ash/components/dbus/lorgnette/lorgnette_service.pb.h" -#include "chromeos/crosapi/mojom/document_scan.mojom.h" -#include "mojo/public/cpp/bindings/type_converter.h" - -namespace mojo { - -template <> -struct TypeConverter<crosapi::mojom::GetScannerListResponsePtr, - lorgnette::ListScannersResponse> { - static crosapi::mojom::GetScannerListResponsePtr Convert( - const lorgnette::ListScannersResponse& input); -}; - -template <> -struct TypeConverter<crosapi::mojom::ScannerOperationResult, - lorgnette::OperationResult> { - static crosapi::mojom::ScannerOperationResult Convert( - lorgnette::OperationResult input); -}; - -} // namespace mojo - -#endif // CHROME_BROWSER_ASH_CROSAPI_DOCUMENT_SCAN_ASH_TYPE_CONVERTERS_H_
diff --git a/chrome/browser/ash/crosapi/document_scan_ash_type_converters_unittest.cc b/chrome/browser/ash/crosapi/document_scan_ash_type_converters_unittest.cc deleted file mode 100644 index 80098c11..0000000 --- a/chrome/browser/ash/crosapi/document_scan_ash_type_converters_unittest.cc +++ /dev/null
@@ -1,144 +0,0 @@ -// Copyright 2023 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/ash/crosapi/document_scan_ash_type_converters.h" - -#include "testing/gmock/include/gmock/gmock.h" -#include "testing/gtest/include/gtest/gtest.h" - -namespace mojo { -namespace { - -using ::testing::ElementsAre; -using ::testing::UnorderedElementsAre; - -TEST(DocumentScanAshTypeConvertersTest, OperationResult) { - EXPECT_EQ(ConvertTo<crosapi::mojom::ScannerOperationResult>( - lorgnette::OPERATION_RESULT_UNKNOWN), - crosapi::mojom::ScannerOperationResult::kUnknown); - EXPECT_EQ(ConvertTo<crosapi::mojom::ScannerOperationResult>( - lorgnette::OPERATION_RESULT_SUCCESS), - crosapi::mojom::ScannerOperationResult::kSuccess); - EXPECT_EQ(ConvertTo<crosapi::mojom::ScannerOperationResult>( - lorgnette::OPERATION_RESULT_UNSUPPORTED), - crosapi::mojom::ScannerOperationResult::kUnsupported); - EXPECT_EQ(ConvertTo<crosapi::mojom::ScannerOperationResult>( - lorgnette::OPERATION_RESULT_CANCELLED), - crosapi::mojom::ScannerOperationResult::kCancelled); - EXPECT_EQ(ConvertTo<crosapi::mojom::ScannerOperationResult>( - lorgnette::OPERATION_RESULT_DEVICE_BUSY), - crosapi::mojom::ScannerOperationResult::kDeviceBusy); - EXPECT_EQ(ConvertTo<crosapi::mojom::ScannerOperationResult>( - lorgnette::OPERATION_RESULT_INVALID), - crosapi::mojom::ScannerOperationResult::kInvalid); - EXPECT_EQ(ConvertTo<crosapi::mojom::ScannerOperationResult>( - lorgnette::OPERATION_RESULT_WRONG_TYPE), - crosapi::mojom::ScannerOperationResult::kWrongType); - EXPECT_EQ(ConvertTo<crosapi::mojom::ScannerOperationResult>( - lorgnette::OPERATION_RESULT_EOF), - crosapi::mojom::ScannerOperationResult::kEndOfData); - EXPECT_EQ(ConvertTo<crosapi::mojom::ScannerOperationResult>( - lorgnette::OPERATION_RESULT_ADF_JAMMED), - crosapi::mojom::ScannerOperationResult::kAdfJammed); - EXPECT_EQ(ConvertTo<crosapi::mojom::ScannerOperationResult>( - lorgnette::OPERATION_RESULT_ADF_EMPTY), - crosapi::mojom::ScannerOperationResult::kAdfEmpty); - EXPECT_EQ(ConvertTo<crosapi::mojom::ScannerOperationResult>( - lorgnette::OPERATION_RESULT_COVER_OPEN), - crosapi::mojom::ScannerOperationResult::kCoverOpen); - EXPECT_EQ(ConvertTo<crosapi::mojom::ScannerOperationResult>( - lorgnette::OPERATION_RESULT_IO_ERROR), - crosapi::mojom::ScannerOperationResult::kIoError); - EXPECT_EQ(ConvertTo<crosapi::mojom::ScannerOperationResult>( - lorgnette::OPERATION_RESULT_ACCESS_DENIED), - crosapi::mojom::ScannerOperationResult::kAccessDenied); - EXPECT_EQ(ConvertTo<crosapi::mojom::ScannerOperationResult>( - lorgnette::OPERATION_RESULT_NO_MEMORY), - crosapi::mojom::ScannerOperationResult::kNoMemory); - EXPECT_EQ(ConvertTo<crosapi::mojom::ScannerOperationResult>( - lorgnette::OPERATION_RESULT_UNREACHABLE), - crosapi::mojom::ScannerOperationResult::kDeviceUnreachable); - EXPECT_EQ(ConvertTo<crosapi::mojom::ScannerOperationResult>( - lorgnette::OPERATION_RESULT_MISSING), - crosapi::mojom::ScannerOperationResult::kDeviceMissing); - EXPECT_EQ(ConvertTo<crosapi::mojom::ScannerOperationResult>( - lorgnette::OPERATION_RESULT_INTERNAL_ERROR), - crosapi::mojom::ScannerOperationResult::kInternalError); -} - -TEST(DocumentScanAshTypeConvertersTest, - GetScannerListResponse_EmptyObjectSucceeds) { - lorgnette::ListScannersResponse input; - auto output = crosapi::mojom::GetScannerListResponse::From(input); - EXPECT_EQ(output->result, crosapi::mojom::ScannerOperationResult::kUnknown); - EXPECT_EQ(output->scanners.size(), 0U); -} - -TEST(DocumentScanAshTypeConvertersTest, GetScannerListResponse_UsbScanner) { - lorgnette::ListScannersResponse input; - input.set_result(lorgnette::OPERATION_RESULT_SUCCESS); - lorgnette::ScannerInfo* scanner = input.add_scanners(); - scanner->set_name("backend:usb:18d1:505e"); - scanner->set_manufacturer("GoogleTest"); - scanner->set_model("USB Scanner"); - scanner->set_display_name("GoogleTest USB Scanner (USB)"); - scanner->set_device_uuid("13245-67890"); - scanner->set_connection_type(lorgnette::CONNECTION_USB); - scanner->set_secure(true); - scanner->add_image_format("image/png"); - scanner->add_image_format("image/jpeg"); - scanner->set_protocol_type("backend"); - - auto output = crosapi::mojom::GetScannerListResponse::From(input); - EXPECT_EQ(output->result, crosapi::mojom::ScannerOperationResult::kSuccess); - ASSERT_EQ(output->scanners.size(), 1U); - crosapi::mojom::ScannerInfoPtr& scanner_out = output->scanners[0]; - EXPECT_EQ(scanner_out->id, "backend:usb:18d1:505e"); - EXPECT_EQ(scanner_out->manufacturer, "GoogleTest"); - EXPECT_EQ(scanner_out->model, "USB Scanner"); - EXPECT_EQ(scanner_out->display_name, "GoogleTest USB Scanner (USB)"); - EXPECT_EQ(scanner_out->device_uuid, "13245-67890"); - EXPECT_EQ(scanner_out->connection_type, - crosapi::mojom::ScannerInfo_ConnectionType::kUsb); - EXPECT_TRUE(scanner_out->secure); - EXPECT_THAT(scanner_out->image_formats, - UnorderedElementsAre("image/png", "image/jpeg")); - EXPECT_EQ(scanner_out->protocol_type, "backend"); -} - -TEST(DocumentScanAshTypeConvertersTest, GetScannerListResponse_NetworkScanner) { - lorgnette::ListScannersResponse input; - input.set_result(lorgnette::OPERATION_RESULT_NO_MEMORY); - lorgnette::ScannerInfo* scanner = input.add_scanners(); - scanner->set_name("backend:net:127.0.0.1"); - scanner->set_manufacturer("GoogleTest"); - scanner->set_model("Network Scanner"); - scanner->set_display_name("GoogleTest Network Scanner"); - scanner->set_device_uuid("13245-67890"); - scanner->set_connection_type(lorgnette::CONNECTION_NETWORK); - scanner->set_secure(false); - scanner->add_image_format("image/png"); - scanner->add_image_format("image/jpeg"); - scanner->set_protocol_type("backend"); - - auto output = crosapi::mojom::GetScannerListResponse::From(input); - EXPECT_EQ(output->result, crosapi::mojom::ScannerOperationResult::kNoMemory); - ASSERT_EQ(output->scanners.size(), 1U); - crosapi::mojom::ScannerInfoPtr& scanner_out = output->scanners[0]; - EXPECT_EQ(scanner_out->id, "backend:net:127.0.0.1"); - EXPECT_EQ(scanner_out->manufacturer, "GoogleTest"); - EXPECT_EQ(scanner_out->model, "Network Scanner"); - EXPECT_EQ(scanner_out->display_name, "GoogleTest Network Scanner"); - EXPECT_EQ(scanner_out->device_uuid, "13245-67890"); - EXPECT_EQ(scanner_out->connection_type, - crosapi::mojom::ScannerInfo_ConnectionType::kNetwork); - EXPECT_FALSE(scanner_out->secure); - EXPECT_THAT(scanner_out->image_formats, - UnorderedElementsAre("image/png", "image/jpeg")); - EXPECT_EQ(scanner_out->protocol_type, "backend"); -} - - -} // namespace -} // namespace mojo
diff --git a/chrome/browser/ash/crosapi/document_scan_ash_unittest.cc b/chrome/browser/ash/crosapi/document_scan_ash_unittest.cc deleted file mode 100644 index 87ce957..0000000 --- a/chrome/browser/ash/crosapi/document_scan_ash_unittest.cc +++ /dev/null
@@ -1,81 +0,0 @@ -// Copyright 2022 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/ash/crosapi/document_scan_ash.h" - -#include <memory> -#include <optional> -#include <string> -#include <tuple> -#include <vector> - -#include "base/test/bind.h" -#include "base/test/test_future.h" -#include "chrome/browser/ash/login/users/fake_chrome_user_manager.h" -#include "chrome/browser/ash/scanning/fake_lorgnette_scanner_manager.h" -#include "chrome/browser/ash/scanning/lorgnette_scanner_manager_factory.h" -#include "chrome/test/base/testing_profile.h" -#include "chromeos/crosapi/mojom/document_scan.mojom-shared.h" -#include "components/user_manager/scoped_user_manager.h" -#include "components/user_manager/test_helper.h" -#include "content/public/test/browser_task_environment.h" -#include "testing/gmock/include/gmock/gmock.h" -#include "testing/gtest/include/gtest/gtest.h" - -namespace crosapi { - -namespace { - -using testing::ElementsAre; - -// Creates a new FakeLorgnetteScannerManager for the given `context`. -std::unique_ptr<KeyedService> BuildLorgnetteScannerManager( - content::BrowserContext* context) { - return std::make_unique<ash::FakeLorgnetteScannerManager>(); -} - -} // namespace - -class DocumentScanAshTest : public testing::Test { - public: - void SetUp() override { - fake_user_manager_.Reset(std::make_unique<ash::FakeChromeUserManager>()); - ash::LorgnetteScannerManagerFactory::GetInstance()->SetTestingFactory( - &profile_, base::BindRepeating(&BuildLorgnetteScannerManager)); - - const AccountId account_id = - AccountId::FromUserEmail(profile_.GetProfileUserName()); - fake_user_manager_->AddUserWithAffiliationAndTypeAndProfile( - account_id, - /*is_affiliated=*/false, user_manager::UserType::kRegular, &profile_); - fake_user_manager_->UserLoggedIn( - account_id, user_manager::TestHelper::GetFakeUsernameHash(account_id)); - fake_user_manager_->SimulateUserProfileLoad(account_id); - } - - void TearDown() override { - fake_user_manager_.Reset(); - } - - ash::FakeLorgnetteScannerManager* GetLorgnetteScannerManager() { - return static_cast<ash::FakeLorgnetteScannerManager*>( - ash::LorgnetteScannerManagerFactory::GetForBrowserContext(&profile_)); - } - - DocumentScanAsh& document_scan_ash() { return document_scan_ash_; } - - private: - // Must outlive `profile_`. - content::BrowserTaskEnvironment task_environment_; - - user_manager::TypedScopedUserManager<ash::FakeChromeUserManager> - fake_user_manager_; - - // Must outlive `document_scan_ash_`. - TestingProfile profile_; - - DocumentScanAsh document_scan_ash_; -}; - -} // namespace crosapi
diff --git a/chrome/browser/ash/cryptauth/client_app_metadata_provider_service.cc b/chrome/browser/ash/cryptauth/client_app_metadata_provider_service.cc index b6b5a4b9..2fdba50 100644 --- a/chrome/browser/ash/cryptauth/client_app_metadata_provider_service.cc +++ b/chrome/browser/ash/cryptauth/client_app_metadata_provider_service.cc
@@ -20,7 +20,6 @@ #include "base/time/time.h" #include "base/version.h" #include "chrome/browser/ash/cryptauth/cryptauth_device_id_provider.h" -#include "chrome/common/pref_names.h" #include "chromeos/ash/components/multidevice/logging/logging.h" #include "chromeos/ash/components/network/network_state_handler.h" #include "chromeos/ash/components/network/network_type_pattern.h" @@ -127,8 +126,8 @@ // static void ClientAppMetadataProviderService::RegisterProfilePrefs( PrefRegistrySimple* registry) { - registry->RegisterStringPref(::prefs::kCryptAuthInstanceId, std::string()); - registry->RegisterStringPref(::prefs::kCryptAuthInstanceIdToken, + registry->RegisterStringPref(ash::prefs::kCryptAuthInstanceId, std::string()); + registry->RegisterStringPref(ash::prefs::kCryptAuthInstanceIdToken, std::string()); } @@ -252,12 +251,12 @@ const std::string& instance_id) { DCHECK(!instance_id.empty()); std::string previous_instance_id = - pref_service_->GetString(::prefs::kCryptAuthInstanceId); + pref_service_->GetString(ash::prefs::kCryptAuthInstanceId); if (!previous_instance_id.empty()) { base::UmaHistogramBoolean("CryptAuth.InstanceId.DidInstanceIdChange", previous_instance_id != instance_id); } - pref_service_->SetString(::prefs::kCryptAuthInstanceId, instance_id); + pref_service_->SetString(ash::prefs::kCryptAuthInstanceId, instance_id); GetInstanceId()->GetToken( device_sync:: @@ -307,12 +306,12 @@ DCHECK(!token.empty()); std::string previous_instance_id_token = - pref_service_->GetString(::prefs::kCryptAuthInstanceIdToken); + pref_service_->GetString(ash::prefs::kCryptAuthInstanceIdToken); if (!previous_instance_id_token.empty()) { base::UmaHistogramBoolean("CryptAuth.InstanceId.DidInstanceIdTokenChange", previous_instance_id_token != token); } - pref_service_->SetString(::prefs::kCryptAuthInstanceIdToken, token); + pref_service_->SetString(ash::prefs::kCryptAuthInstanceIdToken, token); cryptauthv2::ClientAppMetadata metadata;
diff --git a/chrome/browser/ash/cryptauth/cryptauth_device_id_provider.cc b/chrome/browser/ash/cryptauth/cryptauth_device_id_provider.cc index 29a639d..ff2ddbf 100644 --- a/chrome/browser/ash/cryptauth/cryptauth_device_id_provider.cc +++ b/chrome/browser/ash/cryptauth/cryptauth_device_id_provider.cc
@@ -4,22 +4,22 @@ #include "chrome/browser/ash/cryptauth/cryptauth_device_id_provider.h" +#include "ash/constants/ash_pref_names.h" #include "base/uuid.h" -#include "chrome/common/pref_names.h" #include "components/prefs/pref_registry_simple.h" #include "components/prefs/pref_service.h" namespace ash::cryptauth_device_id { void RegisterLocalPrefs(PrefRegistrySimple* registry) { - registry->RegisterStringPref(prefs::kCryptAuthDeviceId, std::string()); + registry->RegisterStringPref(ash::prefs::kCryptAuthDeviceId, std::string()); } std::string GetDeviceID(PrefService& local_state) { - std::string device_id = local_state.GetString(prefs::kCryptAuthDeviceId); + std::string device_id = local_state.GetString(ash::prefs::kCryptAuthDeviceId); if (device_id.empty()) { device_id = base::Uuid::GenerateRandomV4().AsLowercaseString(); - local_state.SetString(prefs::kCryptAuthDeviceId, device_id); + local_state.SetString(ash::prefs::kCryptAuthDeviceId, device_id); } return device_id; }
diff --git a/chrome/browser/ash/customization/customization_document.cc b/chrome/browser/ash/customization/customization_document.cc index 32b6176..800918b6 100644 --- a/chrome/browser/ash/customization/customization_document.cc +++ b/chrome/browser/ash/customization/customization_document.cc
@@ -10,6 +10,7 @@ #include <string_view> #include <utility> +#include "ash/constants/ash_features.h" #include "ash/constants/ash_paths.h" #include "ash/constants/ash_pref_names.h" #include "base/files/file_path.h" @@ -60,7 +61,9 @@ namespace ash { namespace { - // Manifest attributes names. +using ::extensions::ExternalProviderImpl; + +// Manifest attributes names. const char kVersionAttr[] = "version"; const char kDefaultAttr[] = "default"; const char kInitialLocaleAttr[] = "initial_locale"; @@ -753,9 +756,32 @@ prefs.clear(); break; } - if (!entry.Find(extensions::ExternalProviderImpl::kExternalUpdateUrl)) { - entry.Set(extensions::ExternalProviderImpl::kExternalUpdateUrl, - extension_urls::GetWebstoreUpdateUrl().spec()); + base::Value* update_url_value = + entry.Find(ExternalProviderImpl::kExternalUpdateUrl); + if (ash::features::IsOemAppsMustUpdateFromWebstoreEnabled()) { + // Fix for crbug.com/502771678. OEM apps must update from the webstore. + // Providing a non-webstore update URL is a fatal error. + if (update_url_value) { + CHECK(update_url_value->is_string()); + GURL update_url(update_url_value->GetString()); + CHECK(update_url.is_valid()); + if (!extension_urls::IsWebstoreUpdateUrl(update_url)) { + // Use log because official builds strip the stream passed to CHECK. + LOG(FATAL) << "Invalid update URL: " << update_url; + } + // If we get here, a extension provided a valid update URL. + } else { + // Provide the default update URL. + entry.Set(ExternalProviderImpl::kExternalUpdateUrl, + extension_urls::GetWebstoreUpdateUrl().spec()); + } + } else { + // Legacy behavior. Keep any provided external_update_url value, but + // override to web store if it's not set. + if (!update_url_value) { + entry.Set(ExternalProviderImpl::kExternalUpdateUrl, + extension_urls::GetWebstoreUpdateUrl().spec()); + } } prefs.SetByDottedPath(app_id, std::move(entry)); }
diff --git a/chrome/browser/ash/customization/customization_document.h b/chrome/browser/ash/customization/customization_document.h index b04b030..09b2733 100644 --- a/chrome/browser/ash/customization/customization_document.h +++ b/chrome/browser/ash/customization/customization_document.h
@@ -63,6 +63,10 @@ // Return true if the document was successfully fetched and parsed. bool IsReady() const { return root_.get(); } + void set_root_for_test(std::unique_ptr<base::DictValue> root) { + root_ = std::move(root); + } + protected: explicit CustomizationDocument(const std::string& accepted_version);
diff --git a/chrome/browser/ash/customization/customization_document_unittest.cc b/chrome/browser/ash/customization/customization_document_unittest.cc index 528bdfcd..ab6356c 100644 --- a/chrome/browser/ash/customization/customization_document_unittest.cc +++ b/chrome/browser/ash/customization/customization_document_unittest.cc
@@ -6,9 +6,14 @@ #include <utility> +#include "ash/constants/ash_features.h" #include "base/functional/bind.h" +#include "base/json/json_reader.h" #include "base/run_loop.h" #include "base/strings/stringprintf.h" +#include "base/test/gtest_util.h" +#include "base/test/scoped_feature_list.h" +#include "base/values.h" #include "chrome/browser/ash/app_list/app_list_syncable_service.h" #include "chrome/browser/ash/app_list/app_list_syncable_service_factory.h" #include "chrome/browser/ash/settings/scoped_cros_settings_test_helper.h" @@ -29,6 +34,7 @@ #include "content/public/test/browser_task_environment.h" #include "extensions/browser/external_install_info.h" #include "extensions/common/extension.h" +#include "extensions/common/extension_urls.h" #include "extensions/common/manifest.h" #include "net/http/http_response_headers.h" #include "net/http/http_status_code.h" @@ -82,31 +88,51 @@ const char kBadManifest[] = "{\"version\": \"1\"}"; -const char kGoodServicesManifest[] = - "{" - " \"version\": \"1.0\"," - " \"default_apps\": [\n" - " \"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\",\n" - " {\n" - " \"id\": \"bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\",\n" - " \"do_not_install_for_enterprise\": true\n" - " }\n" - " ],\n" - " \"localized_content\": {\n" - " \"en-US\": {\n" - " \"default_apps_folder_name\": \"EN-US OEM Name\"\n" - " },\n" - " \"en\": {\n" - " \"default_apps_folder_name\": \"EN OEM Name\"\n" - " },\n" - " \"default\": {\n" - " \"default_apps_folder_name\": \"Default OEM Name\"\n" - " }\n" - " }\n" - "}"; +// A manifest with a bad external_update_url. +const char kBadServicesManifest[] = R"( + { + "version": "1.0", + "default_apps": [ + { + "id": "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb", + "external_update_url": "http://evil.com/" + } + ] + })"; const char kDummyCustomizationID[] = "test-dummy"; +// Returns a good services manifest. One app provides a valid (webstore) update +// URL. +std::string GetGoodServicesManifest() { + const char kGoodServicesManifest[] = R"( + { + "version": "1.0", + "default_apps": [ + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + { + "id": "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb", + "do_not_install_for_enterprise": true, + "external_update_url": "%s" + } + ], + "localized_content": { + "en-US": { + "default_apps_folder_name": "EN-US OEM Name" + }, + "en": { + "default_apps_folder_name": "EN OEM Name" + }, + "default": { + "default_apps_folder_name": "Default OEM Name" + } + } + })"; + return base::StringPrintf( + kGoodServicesManifest, + extension_urls::GetWebstoreUpdateUrl().spec().c_str()); +} + } // anonymous namespace namespace ash { @@ -276,7 +302,7 @@ TEST_F(ServicesCustomizationDocumentTest, Basic) { AddCustomizationIdToVp(kDummyCustomizationID); - AddExpectedManifest(kDummyCustomizationID, kGoodServicesManifest); + AddExpectedManifest(kDummyCustomizationID, GetGoodServicesManifest()); ServicesCustomizationDocument* doc = ServicesCustomizationDocument::GetInstance(); @@ -352,7 +378,7 @@ TEST_F(ServicesCustomizationDocumentTest, DefaultApps) { AddCustomizationIdToVp(kDummyCustomizationID); - AddExpectedManifest(kDummyCustomizationID, kGoodServicesManifest); + AddExpectedManifest(kDummyCustomizationID, GetGoodServicesManifest()); ServicesCustomizationDocument* doc = ServicesCustomizationDocument::GetInstance(); @@ -439,4 +465,60 @@ EXPECT_TRUE(doc->IsReady()); } +// When `kOemAppsMustUpdateFromWebstore` is enabled (default), the bad update +// URL in `kBadServicesManifest` causes an assertion failure. This test is +// unlike the others (not a TEST_F) as the test suite helper objects interfere +// with EXPECT_NOTREACHED_DEATH. +TEST(ServicesCustomizationDocumentDeathTest, ExternalUpdateUrl_Default) { + base::test::ScopedFeatureList feature_list; + feature_list.InitAndEnableFeature( + ash::features::kOemAppsMustUpdateFromWebstore); + + // Manually parse the manifest to avoid the async reader. + std::optional<base::Value> manifest_json = + base::JSONReader::Read(kBadServicesManifest, base::JSON_PARSE_RFC); + ASSERT_TRUE(manifest_json.has_value()); + ASSERT_TRUE(manifest_json->is_dict()); + const base::DictValue& manifest_dict = manifest_json->GetDict(); + + // Provide the manifest directly to ServicesCustomizationDocument. + ServicesCustomizationDocument* doc = + ServicesCustomizationDocument::GetInstance(); + doc->set_root_for_test( + std::make_unique<base::DictValue>(manifest_dict.Clone())); + EXPECT_TRUE(doc->IsReady()); + + // Requesting the list of apps triggers an assertion failure. + EXPECT_DEATH(doc->GetDefaultApps(), ""); +} + +// When `kOemAppsMustUpdateFromWebstore` is disabled (kill switch), the bad +// update URL in `kBadServicesManifest` is permitted. +TEST_F(ServicesCustomizationDocumentTest, ExternalUpdateUrl_Legacy) { + base::test::ScopedFeatureList feature_list; + feature_list.InitAndDisableFeature( + ash::features::kOemAppsMustUpdateFromWebstore); + + AddCustomizationIdToVp(kDummyCustomizationID); + AddExpectedManifest(kDummyCustomizationID, kBadServicesManifest); + + ServicesCustomizationDocument* doc = + ServicesCustomizationDocument::GetInstance(); + EXPECT_FALSE(doc->IsReady()); + + doc->StartFetching(); + RunUntilIdle(); + EXPECT_TRUE(doc->IsReady()); + + auto default_apps = doc->GetDefaultApps(); + ASSERT_TRUE(default_apps); + const base::DictValue* app_entry = + default_apps->FindDict("bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb"); + ASSERT_TRUE(app_entry); + const std::string* url = app_entry->FindString( + extensions::ExternalProviderImpl::kExternalUpdateUrl); + ASSERT_TRUE(url); + EXPECT_EQ(*url, "http://evil.com/"); +} + } // namespace ash
diff --git a/chrome/browser/ash/eol/DEPS b/chrome/browser/ash/eol/DEPS index 4def002..1dc7410 100644 --- a/chrome/browser/ash/eol/DEPS +++ b/chrome/browser/ash/eol/DEPS
@@ -32,7 +32,6 @@ ".*test\.cc": [ "+chrome/browser/ash/profiles/profile_helper.h", "+chrome/browser/ui/browser.h", - "+chrome/browser/ui/browser_finder.h", "+chrome/browser/ui/browser_window/public/global_browser_collection.h", ],
diff --git a/chrome/browser/ash/extensions/default_keyboard_extension_browser_test.cc b/chrome/browser/ash/extensions/default_keyboard_extension_browser_test.cc index b2e05297..f1e7677 100644 --- a/chrome/browser/ash/extensions/default_keyboard_extension_browser_test.cc +++ b/chrome/browser/ash/extensions/default_keyboard_extension_browser_test.cc
@@ -15,6 +15,7 @@ #include "chrome/test/base/chrome_test_utils.h" #include "chrome/test/base/in_process_browser_test.h" #include "chrome/test/base/ui_test_utils.h" +#include "content/public/browser/security_principal.h" #include "content/public/browser/site_instance.h" #include "content/public/browser/web_contents.h" #include "content/public/test/browser_test.h" @@ -97,7 +98,10 @@ GURL url = extensions::Extension::GetBaseURLFromExtensionId(id); for (content::WebContents* wc : content::GetAllWebContents()) { - if (url == wc->GetPrimaryMainFrame()->GetSiteInstance()->GetSiteURL()) { + if (url == wc->GetPrimaryMainFrame() + ->GetSiteInstance() + ->GetSecurityPrincipal() + .GetDeprecatedSiteURL()) { // Waits for virtual keyboard to load. EXPECT_TRUE(content::WaitForLoadStop(wc)); return wc;
diff --git a/chrome/browser/ash/file_manager/DEPS b/chrome/browser/ash/file_manager/DEPS index 4250830..5ceca80a 100644 --- a/chrome/browser/ash/file_manager/DEPS +++ b/chrome/browser/ash/file_manager/DEPS
@@ -89,7 +89,6 @@ "+chrome/browser/extensions/extension_apitest.h", "+chrome/browser/extensions/mixin_based_extension_apitest.h", "+chrome/browser/extensions/test_extension_system.h", - "+chrome/browser/ui/browser_finder.h", "+chrome/browser/ui/browser.h", "+chrome/browser/ui/browser_window.h", "+chrome/browser/ui/browser_window/public/browser_window_interface_iterator.h",
diff --git a/chrome/browser/ash/input_method/DEPS b/chrome/browser/ash/input_method/DEPS index f5f7113..ace9d34e 100644 --- a/chrome/browser/ash/input_method/DEPS +++ b/chrome/browser/ash/input_method/DEPS
@@ -22,7 +22,6 @@ "+chrome/browser/ui/ash/input_method", "+chrome/browser/ui/ash/keyboard", "+chrome/browser/ui/aura/accessibility", - "+chrome/browser/ui/browser_finder.h", "+chrome/browser/ui/webui/ash/mako", "+chrome/browser/ui/webui/ash/settings/search", "+chrome/common/chrome_features.h",
diff --git a/chrome/browser/ash/input_method/native_input_method_engine_without_ime_service_browsertest.cc b/chrome/browser/ash/input_method/native_input_method_engine_without_ime_service_browsertest.cc index d0ca301..990ce61 100644 --- a/chrome/browser/ash/input_method/native_input_method_engine_without_ime_service_browsertest.cc +++ b/chrome/browser/ash/input_method/native_input_method_engine_without_ime_service_browsertest.cc
@@ -20,7 +20,6 @@ #include "chrome/browser/ash/input_method/textinput_test_helper.h" #include "chrome/browser/profiles/profile.h" #include "chrome/browser/signin/identity_manager_factory.h" -#include "chrome/browser/ui/browser_finder.h" #include "chrome/browser/ui/browser_window.h" #include "chrome/test/base/chrome_test_utils.h" #include "chrome/test/base/in_process_browser_test.h"
diff --git a/chrome/browser/ash/input_method/text_field_contextual_info_fetcher.h b/chrome/browser/ash/input_method/text_field_contextual_info_fetcher.h index ab2c5fe..e1c2a50e 100644 --- a/chrome/browser/ash/input_method/text_field_contextual_info_fetcher.h +++ b/chrome/browser/ash/input_method/text_field_contextual_info_fetcher.h
@@ -8,7 +8,6 @@ #include <optional> #include <string> -#include "chrome/browser/ui/browser_finder.h" #include "chromeos/ui/base/app_types.h" #include "url/gurl.h"
diff --git a/chrome/browser/ash/login/DEPS b/chrome/browser/ash/login/DEPS index cc66d4c..275e5d873 100644 --- a/chrome/browser/ash/login/DEPS +++ b/chrome/browser/ash/login/DEPS
@@ -108,7 +108,6 @@ "+chrome/browser/extensions/test_extension_system.h", "+chrome/browser/lifetime/termination_notification.h", "+chrome/browser/ui/browser_dialogs.h", - "+chrome/browser/ui/browser_finder.h", "+chrome/browser/ui/browser.h", "+chrome/browser/ui/browser_tabstrip.h", "+chrome/browser/ui/browser_window.h",
diff --git a/chrome/browser/ash/login/app_mode/test/kiosk_accelerator_browsertest.cc b/chrome/browser/ash/login/app_mode/test/kiosk_accelerator_browsertest.cc index 0e7e441..1308d20 100644 --- a/chrome/browser/ash/login/app_mode/test/kiosk_accelerator_browsertest.cc +++ b/chrome/browser/ash/login/app_mode/test/kiosk_accelerator_browsertest.cc
@@ -10,7 +10,6 @@ #include "chrome/browser/ash/app_mode/test/kiosk_mixin.h" #include "chrome/browser/ash/app_mode/test/kiosk_test_utils.h" #include "chrome/browser/ash/login/app_mode/test/ash_accelerator_helpers.h" -#include "chrome/browser/ui/browser_finder.h" #include "chrome/browser/ui/browser_window/public/global_browser_collection.h" #include "chrome/browser/ui/views/frame/browser_view.h" #include "chrome/test/base/in_process_browser_test.h"
diff --git a/chrome/browser/ash/login/demo_mode/demo_mode_idle_handler_unittest.cc b/chrome/browser/ash/login/demo_mode/demo_mode_idle_handler_unittest.cc index 56f10e5..caf92a7 100644 --- a/chrome/browser/ash/login/demo_mode/demo_mode_idle_handler_unittest.cc +++ b/chrome/browser/ash/login/demo_mode/demo_mode_idle_handler_unittest.cc
@@ -26,7 +26,6 @@ #include "chrome/browser/ash/login/demo_mode/demo_mode_window_closer.h" #include "chrome/browser/ash/login/users/fake_chrome_user_manager.h" #include "chrome/browser/ui/browser.h" -#include "chrome/browser/ui/browser_finder.h" #include "chrome/browser/ui/browser_window/public/global_browser_collection.h" #include "chrome/grit/generated_resources.h" #include "chrome/test/base/chrome_ash_test_base.h"
diff --git a/chrome/browser/ash/os_feedback/DEPS b/chrome/browser/ash/os_feedback/DEPS index 14e533f2..3347ed5 100644 --- a/chrome/browser/ash/os_feedback/DEPS +++ b/chrome/browser/ash/os_feedback/DEPS
@@ -23,7 +23,6 @@ specific_include_rules = { "chrome_os_feedback_delegate_browsertest\\.cc": [ - "+chrome/browser/ui/browser_finder.h", "+chrome/browser/ui/browser.h", "+chrome/browser/ui/browser_window.h", "+chrome/browser/ui/browser_window/public/global_browser_collection.h",
diff --git a/chrome/browser/ash/os_feedback/chrome_os_feedback_delegate_browsertest.cc b/chrome/browser/ash/os_feedback/chrome_os_feedback_delegate_browsertest.cc index 4cc9a2a1..5ec28bd 100644 --- a/chrome/browser/ash/os_feedback/chrome_os_feedback_delegate_browsertest.cc +++ b/chrome/browser/ash/os_feedback/chrome_os_feedback_delegate_browsertest.cc
@@ -36,7 +36,6 @@ #include "chrome/browser/signin/identity_test_environment_profile_adaptor.h" #include "chrome/browser/ui/ash/system_web_apps/system_web_app_ui_utils.h" #include "chrome/browser/ui/browser.h" -#include "chrome/browser/ui/browser_finder.h" #include "chrome/browser/ui/browser_window.h" #include "chrome/browser/ui/browser_window/public/global_browser_collection.h" #include "chrome/browser/ui/chrome_pages.h"
diff --git a/chrome/browser/ash/plugin_vm/BUILD.gn b/chrome/browser/ash/plugin_vm/BUILD.gn index be5692d..d1dd43c 100644 --- a/chrome/browser/ash/plugin_vm/BUILD.gn +++ b/chrome/browser/ash/plugin_vm/BUILD.gn
@@ -79,6 +79,7 @@ "//chrome/browser/notifications", "//chrome/browser/profiles", "//chrome/browser/signin", + "//chrome/browser/ui:simple_message_box_headers", "//chrome/common:chrome_features", "//chromeos/ash/components/dbus/cicerone", "//chromeos/ash/components/dbus/cicerone:cicerone_proto",
diff --git a/chrome/browser/ash/policy/core/DEPS b/chrome/browser/ash/policy/core/DEPS index ecf6236..3f0eb77 100644 --- a/chrome/browser/ash/policy/core/DEPS +++ b/chrome/browser/ash/policy/core/DEPS
@@ -80,7 +80,6 @@ "+chrome/browser/ui/ash/login", "+chrome/browser/ui/browser_commands.h", "+chrome/browser/ui/browser.h", - "+chrome/browser/ui/browser_finder.h", "+chrome/browser/ui/browser_window.h", "+chrome/browser/ui/browser_window/public", "+chrome/browser/ui/tabs",
diff --git a/chrome/browser/ash/policy/core/device_local_account_browsertest.cc b/chrome/browser/ash/policy/core/device_local_account_browsertest.cc index f0b8d0f4..efcbcdcd 100644 --- a/chrome/browser/ash/policy/core/device_local_account_browsertest.cc +++ b/chrome/browser/ash/policy/core/device_local_account_browsertest.cc
@@ -99,7 +99,6 @@ #include "chrome/browser/signin/identity_manager_factory.h" #include "chrome/browser/ui/ash/login/login_display_host.h" #include "chrome/browser/ui/browser_commands.h" -#include "chrome/browser/ui/browser_finder.h" #include "chrome/browser/ui/browser_window.h" #include "chrome/browser/ui/browser_window/public/browser_window_interface.h" #include "chrome/browser/ui/browser_window/public/browser_window_interface_iterator.h"
diff --git a/chrome/browser/ash/policy/core/user_cloud_policy_manager_ash_browsertest.cc b/chrome/browser/ash/policy/core/user_cloud_policy_manager_ash_browsertest.cc index fac900f..75ac26d9 100644 --- a/chrome/browser/ash/policy/core/user_cloud_policy_manager_ash_browsertest.cc +++ b/chrome/browser/ash/policy/core/user_cloud_policy_manager_ash_browsertest.cc
@@ -16,7 +16,6 @@ #include "chrome/browser/profiles/profile.h" #include "chrome/browser/profiles/profile_manager.h" #include "chrome/browser/ui/browser.h" -#include "chrome/browser/ui/browser_finder.h" #include "chrome/browser/ui/browser_window/public/browser_window_interface.h" #include "chrome/browser/ui/browser_window/public/global_browser_collection.h" #include "chrome/browser/ui/tabs/tab_strip_model.h"
diff --git a/chrome/browser/ash/policy/handlers/DEPS b/chrome/browser/ash/policy/handlers/DEPS index b99689d..5720e5ec 100644 --- a/chrome/browser/ash/policy/handlers/DEPS +++ b/chrome/browser/ash/policy/handlers/DEPS
@@ -63,7 +63,6 @@ "+chrome/browser/browser_process.h", "+chrome/browser/browser_process_platform_part.h", "+chrome/browser/ui/browser.h", - "+chrome/browser/ui/browser_finder.h", "+chrome/browser/ui/browser_window/public", "+chrome/browser/ui/tabs", "+chrome/test/base",
diff --git a/chrome/browser/ash/policy/handlers/restore_on_startup_browsertest.cc b/chrome/browser/ash/policy/handlers/restore_on_startup_browsertest.cc index 545e736..fa2a5ba 100644 --- a/chrome/browser/ash/policy/handlers/restore_on_startup_browsertest.cc +++ b/chrome/browser/ash/policy/handlers/restore_on_startup_browsertest.cc
@@ -11,7 +11,6 @@ #include "chrome/browser/ash/policy/login/login_policy_test_base.h" #include "chrome/browser/prefs/session_startup_pref.h" #include "chrome/browser/ui/browser.h" -#include "chrome/browser/ui/browser_finder.h" #include "chrome/browser/ui/browser_window/public/browser_window_interface.h" #include "chrome/browser/ui/browser_window/public/browser_window_interface_iterator.h" #include "chrome/browser/ui/browser_window/public/global_browser_collection.h"
diff --git a/chrome/browser/ash/policy/login/DEPS b/chrome/browser/ash/policy/login/DEPS index e6cb473..34725961 100644 --- a/chrome/browser/ash/policy/login/DEPS +++ b/chrome/browser/ash/policy/login/DEPS
@@ -29,7 +29,6 @@ "+chrome/browser/ui/ash/keyboard", "+chrome/browser/ui/ash/login", "+chrome/browser/ui/browser.h", - "+chrome/browser/ui/browser_finder.h", "+chrome/browser/ui/browser_window.h", "+chrome/browser/ui/browser_window/public", "+chrome/browser/ui/webui/ash/login",
diff --git a/chrome/browser/ash/policy/login/force_maximize_on_first_run_browsertest.cc b/chrome/browser/ash/policy/login/force_maximize_on_first_run_browsertest.cc index 05720980..c6acc6e 100644 --- a/chrome/browser/ash/policy/login/force_maximize_on_first_run_browsertest.cc +++ b/chrome/browser/ash/policy/login/force_maximize_on_first_run_browsertest.cc
@@ -13,7 +13,6 @@ #include "chrome/browser/ash/profiles/profile_helper.h" #include "chrome/browser/profiles/profile.h" #include "chrome/browser/ui/browser.h" -#include "chrome/browser/ui/browser_finder.h" #include "chrome/browser/ui/browser_window.h" #include "chrome/browser/ui/browser_window/public/browser_window_interface.h" #include "chrome/browser/ui/browser_window/public/browser_window_interface_iterator.h"
diff --git a/chrome/browser/ash/policy/networking/DEPS b/chrome/browser/ash/policy/networking/DEPS index 380cb04..d5290bd 100644 --- a/chrome/browser/ash/policy/networking/DEPS +++ b/chrome/browser/ash/policy/networking/DEPS
@@ -33,7 +33,6 @@ "+chrome/browser/ui/ash/login", "+chrome/browser/ui/ash/network", "+chrome/browser/ui/browser.h", - "+chrome/browser/ui/browser_finder.h", "+chrome/browser/ui/browser_window/public", "+chrome/browser/ui/webui/ash/login", "+chrome/test/base",
diff --git a/chrome/browser/ash/policy/status_collector/child_activity_storage.cc b/chrome/browser/ash/policy/status_collector/child_activity_storage.cc index 8278ae1..b760ade 100644 --- a/chrome/browser/ash/policy/status_collector/child_activity_storage.cc +++ b/chrome/browser/ash/policy/status_collector/child_activity_storage.cc
@@ -9,7 +9,6 @@ #include "ash/constants/ash_pref_names.h" #include "base/strings/string_number_conversions.h" #include "base/values.h" -#include "chrome/common/pref_names.h" #include "components/prefs/pref_service.h" #include "components/prefs/scoped_user_pref_update.h" #include "components/user_manager/user_manager.h" @@ -79,7 +78,7 @@ if (activity_day_start == today_start) { pref_service_->SetInteger(ash::prefs::kChildScreenTimeMilliseconds, (previous_activity + activity).InMilliseconds()); - pref_service_->SetTime(prefs::kLastChildScreenTimeSaved, now); + pref_service_->SetTime(ash::prefs::kLastChildScreenTimeSaved, now); pref_service_->CommitPendingWrite(); } }
diff --git a/chrome/browser/ash/policy/status_collector/child_status_collector.cc b/chrome/browser/ash/policy/status_collector/child_status_collector.cc index e3f4ed1..019560b 100644 --- a/chrome/browser/ash/policy/status_collector/child_status_collector.cc +++ b/chrome/browser/ash/policy/status_collector/child_status_collector.cc
@@ -236,8 +236,9 @@ if (reset_time > now) reset_time -= base::Days(1); // Reset screen time if it has not been reset today. - if (reset_time > pref_service_->GetTime(prefs::kLastChildScreenTimeReset)) { - pref_service_->SetTime(prefs::kLastChildScreenTimeReset, now); + if (reset_time > + pref_service_->GetTime(ash::prefs::kLastChildScreenTimeReset)) { + pref_service_->SetTime(ash::prefs::kLastChildScreenTimeReset, now); pref_service_->SetInteger(ash::prefs::kChildScreenTimeMilliseconds, 0); pref_service_->CommitPendingWrite(); }
diff --git a/chrome/browser/ash/policy/status_collector/child_status_collector_unittest.cc b/chrome/browser/ash/policy/status_collector/child_status_collector_unittest.cc index 07ba0d1..93040e7 100644 --- a/chrome/browser/ash/policy/status_collector/child_status_collector_unittest.cc +++ b/chrome/browser/ash/policy/status_collector/child_status_collector_unittest.cc
@@ -381,7 +381,7 @@ base::BindOnce( [](Time time, PrefService* profile_pref_service_) { EXPECT_EQ(time, profile_pref_service_->GetTime( - prefs::kLastChildScreenTimeReset)); + ash::prefs::kLastChildScreenTimeReset)); }, time, pref_service())); } @@ -557,7 +557,7 @@ // the results are stored in a pref. RestartStatusCollector(base::BindRepeating(&GetEmptyAndroidStatus)); // Avoid resetting to test accumulating screen time. - pref_service()->SetTime(prefs::kLastChildScreenTimeReset, Time::Now()); + pref_service()->SetTime(ash::prefs::kLastChildScreenTimeReset, Time::Now()); SimulateStateChanges(test_states, sizeof(test_states) / sizeof(DeviceStateTransitions));
diff --git a/chrome/browser/ash/policy/status_collector/status_collector.cc b/chrome/browser/ash/policy/status_collector/status_collector.cc index a4276d9..aa8fd1f 100644 --- a/chrome/browser/ash/policy/status_collector/status_collector.cc +++ b/chrome/browser/ash/policy/status_collector/status_collector.cc
@@ -86,8 +86,10 @@ // TODO(crbug.com/40569404): move to ChildStatusCollector after migration. registry->RegisterDictionaryPref(ash::prefs::kUserActivityTimes); - registry->RegisterTimePref(prefs::kLastChildScreenTimeReset, base::Time()); - registry->RegisterTimePref(prefs::kLastChildScreenTimeSaved, base::Time()); + registry->RegisterTimePref(ash::prefs::kLastChildScreenTimeReset, + base::Time()); + registry->RegisterTimePref(ash::prefs::kLastChildScreenTimeSaved, + base::Time()); registry->RegisterIntegerPref(ash::prefs::kChildScreenTimeMilliseconds, 0); AppInfoGenerator::RegisterProfilePrefs(registry);
diff --git a/chrome/browser/ash/system_web_apps/apps/camera_app/camera_app_integration_browsertest.cc b/chrome/browser/ash/system_web_apps/apps/camera_app/camera_app_integration_browsertest.cc index 2db0edf5..d68b3ce 100644 --- a/chrome/browser/ash/system_web_apps/apps/camera_app/camera_app_integration_browsertest.cc +++ b/chrome/browser/ash/system_web_apps/apps/camera_app/camera_app_integration_browsertest.cc
@@ -5,7 +5,6 @@ #include "chrome/browser/ash/system_web_apps/test_support/system_web_app_browsertest_base.h" #include "chrome/browser/ash/system_web_apps/test_support/system_web_app_integration_test.h" #include "chrome/browser/ui/browser.h" -#include "chrome/browser/ui/browser_finder.h" #include "chrome/browser/ui/browser_window/public/global_browser_collection.h" #include "chrome/browser/ui/views/frame/browser_view.h" #include "chrome/test/base/ui_test_utils.h"
diff --git a/chrome/browser/ash/system_web_apps/apps/media_app/media_app_integration_browsertest.cc b/chrome/browser/ash/system_web_apps/apps/media_app/media_app_integration_browsertest.cc index b9c4abb4..f23251a 100644 --- a/chrome/browser/ash/system_web_apps/apps/media_app/media_app_integration_browsertest.cc +++ b/chrome/browser/ash/system_web_apps/apps/media_app/media_app_integration_browsertest.cc
@@ -45,7 +45,6 @@ #include "chrome/browser/platform_util.h" #include "chrome/browser/ui/ash/system_web_apps/system_web_app_ui_utils.h" #include "chrome/browser/ui/browser.h" -#include "chrome/browser/ui/browser_finder.h" #include "chrome/browser/ui/browser_window.h" #include "chrome/browser/ui/browser_window/public/browser_window_interface.h" #include "chrome/browser/ui/browser_window/public/global_browser_collection.h"
diff --git a/chrome/browser/ash/system_web_apps/apps/media_app/media_app_ocr_integration_browsertest.cc b/chrome/browser/ash/system_web_apps/apps/media_app/media_app_ocr_integration_browsertest.cc index 1ea9e87a..7e4a92c 100644 --- a/chrome/browser/ash/system_web_apps/apps/media_app/media_app_ocr_integration_browsertest.cc +++ b/chrome/browser/ash/system_web_apps/apps/media_app/media_app_ocr_integration_browsertest.cc
@@ -19,7 +19,6 @@ #include "chrome/browser/ash/profiles/profile_helper.h" #include "chrome/browser/ash/system_web_apps/test_support/system_web_app_integration_test.h" #include "chrome/browser/ui/ash/system_web_apps/system_web_app_ui_utils.h" -#include "chrome/browser/ui/browser_finder.h" #include "chrome/browser/ui/browser_window/public/browser_window_interface.h" #include "chrome/browser/ui/browser_window/public/global_browser_collection.h" #include "chrome/common/chrome_paths.h"
diff --git a/chrome/browser/ash/system_web_apps/apps/os_feedback_app_integration_browsertest.cc b/chrome/browser/ash/system_web_apps/apps/os_feedback_app_integration_browsertest.cc index ff3923f..7749f19 100644 --- a/chrome/browser/ash/system_web_apps/apps/os_feedback_app_integration_browsertest.cc +++ b/chrome/browser/ash/system_web_apps/apps/os_feedback_app_integration_browsertest.cc
@@ -15,7 +15,6 @@ #include "chrome/browser/ash/system_web_apps/test_support/system_web_app_integration_test.h" #include "chrome/browser/ui/ash/system_web_apps/system_web_app_ui_utils.h" #include "chrome/browser/ui/browser.h" -#include "chrome/browser/ui/browser_finder.h" #include "chrome/browser/ui/browser_window.h" #include "chrome/browser/ui/browser_window/public/global_browser_collection.h" #include "chrome/browser/ui/web_applications/test/web_app_browsertest_util.h"
diff --git a/chrome/browser/ash/system_web_apps/system_web_app_manager_browsertest.cc b/chrome/browser/ash/system_web_apps/system_web_app_manager_browsertest.cc index 087c563..a2fa0677 100644 --- a/chrome/browser/ash/system_web_apps/system_web_app_manager_browsertest.cc +++ b/chrome/browser/ash/system_web_apps/system_web_app_manager_browsertest.cc
@@ -49,7 +49,6 @@ #include "chrome/browser/renderer_context_menu/render_view_context_menu_test_util.h" #include "chrome/browser/ui/ash/shelf/chrome_shelf_controller_util.h" #include "chrome/browser/ui/ash/system_web_apps/system_web_app_ui_utils.h" -#include "chrome/browser/ui/browser_finder.h" #include "chrome/browser/ui/browser_window.h" #include "chrome/browser/ui/web_applications/app_browser_controller.h" #include "chrome/browser/web_applications/proto/web_app.pb.h"
diff --git a/chrome/browser/ash/system_web_apps/test_support/DEPS b/chrome/browser/ash/system_web_apps/test_support/DEPS index ff15a83c..dc4a7ad 100644 --- a/chrome/browser/ash/system_web_apps/test_support/DEPS +++ b/chrome/browser/ash/system_web_apps/test_support/DEPS
@@ -19,7 +19,6 @@ "+chrome/browser/global_features.h", "+chrome/browser/profiles", "+chrome/browser/ui/ash/system_web_apps", - "+chrome/browser/ui/browser_finder.h", "+chrome/browser/ui/browser.h", "+chrome/browser/ui/browser_window.h", "+chrome/browser/ui/browser_window/public/global_browser_collection.h",
diff --git a/chrome/browser/attribution_reporting/DEPS b/chrome/browser/attribution_reporting/DEPS index 9b4bbff..67bb2f7c 100644 --- a/chrome/browser/attribution_reporting/DEPS +++ b/chrome/browser/attribution_reporting/DEPS
@@ -3,6 +3,5 @@ "+chrome/browser/privacy_sandbox/privacy_sandbox_attestations/privacy_sandbox_attestations_mixin.h", "+chrome/browser/privacy_sandbox/privacy_sandbox_settings_factory.h", "+chrome/browser/ui/browser.h", - "+chrome/browser/ui/browser_finder.h", "+chrome/browser/ui/browser_window/public/global_browser_collection.h", ]
diff --git a/chrome/browser/autofill/android/java/src/org/chromium/chrome/browser/autofill/AutofillEditorBase.java b/chrome/browser/autofill/android/java/src/org/chromium/chrome/browser/autofill/AutofillEditorBase.java index 78b5c2b5..104f5ae 100644 --- a/chrome/browser/autofill/android/java/src/org/chromium/chrome/browser/autofill/AutofillEditorBase.java +++ b/chrome/browser/autofill/android/java/src/org/chromium/chrome/browser/autofill/AutofillEditorBase.java
@@ -101,7 +101,7 @@ SettingsUtils.getShowShadowOnScrollListener( scrollView, baseView.findViewById(R.id.shadow))); // Inflate the editor and buttons into the "content" LinearLayout. - LinearLayout contentLayout = (LinearLayout) scrollView.findViewById(R.id.content); + LinearLayout contentLayout = scrollView.findViewById(R.id.content); inflater.inflate(getLayoutId(), contentLayout, true); inflater.inflate(R.layout.autofill_editor_base_buttons, contentLayout, true); @@ -149,7 +149,7 @@ /** Initializes the buttons within the layout. */ protected void initializeButtons(View layout) { - Button button = (Button) layout.findViewById(R.id.button_secondary); + Button button = layout.findViewById(R.id.button_secondary); button.setOnClickListener( new View.OnClickListener() { @Override
diff --git a/chrome/browser/autofill/android/java/src/org/chromium/chrome/browser/autofill/options/AutofillAiPreference.java b/chrome/browser/autofill/android/java/src/org/chromium/chrome/browser/autofill/options/AutofillAiPreference.java index fbaa92d36..234334c 100644 --- a/chrome/browser/autofill/android/java/src/org/chromium/chrome/browser/autofill/options/AutofillAiPreference.java +++ b/chrome/browser/autofill/android/java/src/org/chromium/chrome/browser/autofill/options/AutofillAiPreference.java
@@ -104,10 +104,10 @@ @StringRes int summaryId, @DrawableRes int iconId) { Context context = getContext(); - TextView titleView = (TextView) infoItem.findViewById(R.id.info_item_title); + TextView titleView = infoItem.findViewById(R.id.info_item_title); titleView.setText(context.getString(titleId)); - TextView summaryView = (TextView) infoItem.findViewById(R.id.info_item_summary); + TextView summaryView = infoItem.findViewById(R.id.info_item_summary); setTextAndIconToInfoDetails(summaryView, summaryId, iconId); } }
diff --git a/chrome/browser/background_sync/background_sync_browsertest.cc b/chrome/browser/background_sync/background_sync_browsertest.cc index 69eca02..6e96a87f 100644 --- a/chrome/browser/background_sync/background_sync_browsertest.cc +++ b/chrome/browser/background_sync/background_sync_browsertest.cc
@@ -8,7 +8,6 @@ #include "chrome/browser/lifetime/application_lifetime.h" #include "chrome/browser/lifetime/application_lifetime_desktop.h" #include "chrome/browser/ui/browser.h" -#include "chrome/browser/ui/browser_finder.h" #include "chrome/browser/ui/tabs/tab_strip_model.h" #include "chrome/test/base/in_process_browser_test.h" #include "chrome/test/base/ui_test_utils.h"
diff --git a/chrome/browser/bluetooth/BUILD.gn b/chrome/browser/bluetooth/BUILD.gn index ffe3e4f..08f4868 100644 --- a/chrome/browser/bluetooth/BUILD.gn +++ b/chrome/browser/bluetooth/BUILD.gn
@@ -46,7 +46,10 @@ } if (is_android) { sources += [ "android/bluetooth_bridge.cc" ] - deps += [ "//chrome/browser/bluetooth/android:jni_headers" ] + deps += [ + "//chrome/browser/bluetooth/android:jni_headers", + "//chrome/browser/ui/android/device_dialog", + ] } }
diff --git a/chrome/browser/bookmarks/android/javatests/src/org/chromium/chrome/browser/bookmarks/BookmarkPersonalizedSigninPromoTest.java b/chrome/browser/bookmarks/android/javatests/src/org/chromium/chrome/browser/bookmarks/BookmarkPersonalizedSigninPromoTest.java index 6d7f530..6d36a0c 100644 --- a/chrome/browser/bookmarks/android/javatests/src/org/chromium/chrome/browser/bookmarks/BookmarkPersonalizedSigninPromoTest.java +++ b/chrome/browser/bookmarks/android/javatests/src/org/chromium/chrome/browser/bookmarks/BookmarkPersonalizedSigninPromoTest.java
@@ -4,6 +4,9 @@ package org.chromium.chrome.browser.bookmarks; +import org.chromium.base.test.util.DisableIf; +import org.chromium.ui.base.DeviceFormFactor; + import static androidx.test.espresso.Espresso.onView; import static androidx.test.espresso.assertion.ViewAssertions.matches; import static androidx.test.espresso.matcher.ViewMatchers.isDisplayed; @@ -54,6 +57,7 @@ @RunWith(ChromeJUnit4ClassRunner.class) @CommandLineFlags.Add({ChromeSwitches.DISABLE_FIRST_RUN_EXPERIENCE}) @Batch(Batch.PER_CLASS) +@DisableIf.Device(DeviceFormFactor.DESKTOP_FREEFORM) // crbug.com/444482498 public class BookmarkPersonalizedSigninPromoTest { private static final String SHOWN_HISTOGRAM_NAME = "Signin.SyncPromo.Shown.Count.Bookmarks";
diff --git a/chrome/browser/browser_process_impl.cc b/chrome/browser/browser_process_impl.cc index 275331e..20551542 100644 --- a/chrome/browser/browser_process_impl.cc +++ b/chrome/browser/browser_process_impl.cc
@@ -209,7 +209,6 @@ #include "chrome/browser/intranet_redirect_detector.h" #include "chrome/browser/lifetime/application_lifetime_desktop.h" #include "chrome/browser/resource_coordinator/tab_manager.h" -#include "chrome/browser/ui/browser_finder.h" #include "chrome/browser/usb/usb_system_tray_icon.h" #include "chrome/browser/web_applications/isolated_web_apps/runtime_init.h" #include "chrome/browser/webapps/webapps_client_desktop.h"
diff --git a/chrome/browser/browser_process_platform_part_ash.cc b/chrome/browser/browser_process_platform_part_ash.cc index 56e391a..1b10fef 100644 --- a/chrome/browser/browser_process_platform_part_ash.cc +++ b/chrome/browser/browser_process_platform_part_ash.cc
@@ -47,7 +47,6 @@ #include "chrome/browser/sessions/session_service_utils.h" #include "chrome/browser/sync/device_info_sync_service_factory.h" #include "chrome/browser/ui/browser.h" -#include "chrome/browser/ui/browser_finder.h" #include "chrome/browser/ui/browser_window.h" #include "chrome/browser/ui/navigator/browser_navigator.h" #include "chrome/browser/ui/navigator/browser_navigator_params.h"
diff --git a/chrome/browser/browser_process_platform_part_ash_browsertest.cc b/chrome/browser/browser_process_platform_part_ash_browsertest.cc index 2061c2a0..e8bf984 100644 --- a/chrome/browser/browser_process_platform_part_ash_browsertest.cc +++ b/chrome/browser/browser_process_platform_part_ash_browsertest.cc
@@ -24,7 +24,6 @@ #include "chrome/browser/sessions/session_restore_test_utils.h" #include "chrome/browser/signin/identity_manager_factory.h" #include "chrome/browser/ui/browser_commands.h" -#include "chrome/browser/ui/browser_finder.h" #include "chrome/browser/ui/browser_window.h" #include "chrome/browser/ui/browser_window/public/browser_window_interface.h" #include "chrome/browser/ui/browser_window/public/browser_window_interface_iterator.h"
diff --git a/chrome/browser/browsing_data/DEPS b/chrome/browser/browsing_data/DEPS index 28944c2..170dcce0 100644 --- a/chrome/browser/browsing_data/DEPS +++ b/chrome/browser/browsing_data/DEPS
@@ -143,7 +143,6 @@ "+chrome/browser/ui/android/tab_model/tab_model_test_helper.h", "+chrome/browser/ui/browser.h", "+chrome/browser/ui/browser_commands.h", - "+chrome/browser/ui/browser_finder.h", "+chrome/browser/ui/browser_window/public/browser_window_interface.h", "+chrome/browser/ui/browser_window/public/browser_window_interface_iterator.h", "+chrome/browser/ui/browser_window/public/global_browser_collection.h",
diff --git a/chrome/browser/chrome_browser_interface_binders.cc b/chrome/browser/chrome_browser_interface_binders.cc index 7e78d75..fbc34a9 100644 --- a/chrome/browser/chrome_browser_interface_binders.cc +++ b/chrome/browser/chrome_browser_interface_binders.cc
@@ -133,6 +133,7 @@ #if BUILDFLAG(IS_WIN) #include "chrome/browser/media/media_foundation_service_monitor.h" +#include "content/public/browser/security_principal.h" #include "content/public/browser/site_instance.h" #include "media/mojo/mojom/media_foundation_preferences.mojom.h" #include "media/mojo/services/media_foundation_preferences.h" @@ -366,7 +367,9 @@ content::RenderFrameHost* frame_host, mojo::PendingReceiver<media::mojom::MediaFoundationPreferences> receiver) { MediaFoundationPreferencesImpl::Create( - frame_host->GetSiteInstance()->GetSiteURL(), + frame_host->GetSiteInstance() + ->GetSecurityPrincipal() + .GetDeprecatedSiteURL(), base::BindRepeating(&MediaFoundationServiceMonitor:: IsHardwareSecureDecryptionAllowedForSite), std::move(receiver));
diff --git a/chrome/browser/chrome_browser_interface_binders_webui_parts_desktop.cc b/chrome/browser/chrome_browser_interface_binders_webui_parts_desktop.cc index 40dd3d5db..db74836 100644 --- a/chrome/browser/chrome_browser_interface_binders_webui_parts_desktop.cc +++ b/chrome/browser/chrome_browser_interface_binders_webui_parts_desktop.cc
@@ -590,7 +590,7 @@ drive_picker_host::mojom::DrivePickerHostHandler, DrivePickerHostUI>( map); RegisterWebUIControllerInterfaceBinder< - drive_picker_host_untrusted::mojom::DrivePickerUntrustedHostHandler, + drive_picker_host_untrusted::mojom::PageHandlerFactory, DrivePickerUntrustedHostUI>(map); } @@ -749,8 +749,7 @@ if (base::FeatureList::IsEnabled( omnibox::kComposeboxDriveContextMenuOption)) { registry.ForWebUI<DrivePickerUntrustedHostUI>() - .Add<drive_picker_host_untrusted::mojom:: - DrivePickerUntrustedHostHandler>(); + .Add<drive_picker_host_untrusted::mojom::PageHandlerFactory>(); } }
diff --git a/chrome/browser/chrome_content_browser_client.cc b/chrome/browser/chrome_content_browser_client.cc index 3fb55d4..dacc7aa6 100644 --- a/chrome/browser/chrome_content_browser_client.cc +++ b/chrome/browser/chrome_content_browser_client.cc
@@ -2482,8 +2482,9 @@ #if !BUILDFLAG(IS_ANDROID) // Remember the ID of the Instant process to signal the renderer process // on startup in |AppendExtraCommandLineSwitches| below. - if (search::ShouldAssignURLToInstantRenderer(site_instance->GetSiteURL(), - profile)) { + if (search::ShouldAssignURLToInstantRenderer( + site_instance->GetSecurityPrincipal().GetDeprecatedSiteURL(), + profile)) { InstantService* instant_service = InstantServiceFactory::GetForProfile(profile); if (instant_service) {
diff --git a/chrome/browser/chrome_content_browser_client_browsertest.cc b/chrome/browser/chrome_content_browser_client_browsertest.cc index fa6bca66..4404987 100644 --- a/chrome/browser/chrome_content_browser_client_browsertest.cc +++ b/chrome/browser/chrome_content_browser_client_browsertest.cc
@@ -238,16 +238,22 @@ InstantServiceFactory::GetForProfile(browser()->profile()); EXPECT_TRUE(instant_service->IsInstantProcess( contents->GetPrimaryMainFrame()->GetProcess()->GetDeprecatedID())); - EXPECT_EQ(contents->GetPrimaryMainFrame()->GetSiteInstance()->GetSiteURL(), - ntp_site_instance->GetSiteURL()); + EXPECT_EQ(contents->GetPrimaryMainFrame() + ->GetSiteInstance() + ->GetSecurityPrincipal() + .GetDeprecatedSiteURL(), + ntp_site_instance->GetSecurityPrincipal().GetDeprecatedSiteURL()); // Navigating to a non-NTP URL on ntp.com should not result in an Instant // process. ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), isolated_url)); EXPECT_FALSE(instant_service->IsInstantProcess( contents->GetPrimaryMainFrame()->GetProcess()->GetDeprecatedID())); - EXPECT_EQ(contents->GetPrimaryMainFrame()->GetSiteInstance()->GetSiteURL(), - site_instance->GetSiteURL()); + EXPECT_EQ(contents->GetPrimaryMainFrame() + ->GetSiteInstance() + ->GetSecurityPrincipal() + .GetDeprecatedSiteURL(), + site_instance->GetSecurityPrincipal().GetDeprecatedSiteURL()); } // Helper class to test window creation from NTP.
diff --git a/chrome/browser/chrome_content_browser_client_navigation_throttles.cc b/chrome/browser/chrome_content_browser_client_navigation_throttles.cc index 3955b96..f3b17084 100644 --- a/chrome/browser/chrome_content_browser_client_navigation_throttles.cc +++ b/chrome/browser/chrome_content_browser_client_navigation_throttles.cc
@@ -121,7 +121,6 @@ #endif // BUILDFLAG(ENABLE_PLATFORM_APPS) #if !BUILDFLAG(IS_ANDROID) -#include "chrome/browser/ui/browser_finder.h" #include "chrome/browser/ui/web_applications/app_browser_controller.h" #endif // !BUILDFLAG(IS_ANDROID)
diff --git a/chrome/browser/chrome_content_browser_client_receiver_bindings.cc b/chrome/browser/chrome_content_browser_client_receiver_bindings.cc index ffa633e..575ab3d 100644 --- a/chrome/browser/chrome_content_browser_client_receiver_bindings.cc +++ b/chrome/browser/chrome_content_browser_client_receiver_bindings.cc
@@ -326,7 +326,9 @@ return; } - const GURL& site = render_frame_host->GetSiteInstance()->GetSiteURL(); + const GURL& site = render_frame_host->GetSiteInstance() + ->GetSecurityPrincipal() + .GetDeprecatedSiteURL(); content::BrowserContext* browser_context = render_frame_host->GetProcess()->GetBrowserContext(); auto* extension = extensions::ExtensionRegistry::Get(browser_context)
diff --git a/chrome/browser/chrome_content_browser_client_unittest.cc b/chrome/browser/chrome_content_browser_client_unittest.cc index c39ddb78..48e0058 100644 --- a/chrome/browser/chrome_content_browser_client_unittest.cc +++ b/chrome/browser/chrome_content_browser_client_unittest.cc
@@ -45,6 +45,7 @@ #include "chrome/browser/ui/ui_features.h" #include "chrome/browser/web_applications/web_app_helpers.h" #include "chrome/browser/webauthn/webauthn_pref_names.h" +#include "chrome/common/buildflags.h" #include "chrome/common/chrome_features.h" #include "chrome/common/chrome_switches.h" #include "chrome/common/pref_names.h" @@ -77,6 +78,7 @@ #include "content/public/browser/navigation_controller.h" #include "content/public/browser/navigation_entry.h" #include "content/public/browser/render_frame_host.h" +#include "content/public/browser/security_principal.h" #include "content/public/browser/site_instance.h" #include "content/public/browser/site_isolation_policy.h" #include "content/public/browser/storage_partition.h" @@ -377,13 +379,15 @@ GURL("chrome-search://remote-ntp")); EXPECT_TRUE(client.ShouldStayInParentProcessForNTP( GURL("chrome-search://most-visited/title.html"), - site_instance->GetSiteURL())); + site_instance->GetSecurityPrincipal().GetDeprecatedSiteURL())); // Only the most visited tiles host is allowed to stay in the 3P NTP. EXPECT_FALSE(client.ShouldStayInParentProcessForNTP( - GURL("chrome-search://foo/"), site_instance->GetSiteURL())); + GURL("chrome-search://foo/"), + site_instance->GetSecurityPrincipal().GetDeprecatedSiteURL())); EXPECT_FALSE(client.ShouldStayInParentProcessForNTP( - GURL("chrome://new-tab-page"), site_instance->GetSiteURL())); + GURL("chrome://new-tab-page"), + site_instance->GetSecurityPrincipal().GetDeprecatedSiteURL())); site_instance = content::SiteInstance::CreateForURL( browser()->profile(), GURL("chrome://new-tab-page")); @@ -392,13 +396,15 @@ // ShouldStayInParentProcessForNTP() should only return true for NTPs hosted // under the chrome-search: scheme. EXPECT_FALSE(client.ShouldStayInParentProcessForNTP( - GURL("chrome://new-tab-page"), site_instance->GetSiteURL())); + GURL("chrome://new-tab-page"), + site_instance->GetSecurityPrincipal().GetDeprecatedSiteURL())); // For now, we also allow chrome-search://most-visited to stay in 1P NTP, // chrome://new-tab-page. We should consider tightening this to only allow // most-visited tiles to stay in 3P NTP. EXPECT_TRUE(client.ShouldStayInParentProcessForNTP( - GURL("chrome-search://most-visited"), site_instance->GetSiteURL())); + GURL("chrome-search://most-visited"), + site_instance->GetSecurityPrincipal().GetDeprecatedSiteURL())); } TEST_F(ChromeContentBrowserClientWindowTest, OverrideNavigationParams) {
diff --git a/chrome/browser/chrome_main_process_singleton_browsertest.cc b/chrome/browser/chrome_main_process_singleton_browsertest.cc index e494af0c..632486e 100644 --- a/chrome/browser/chrome_main_process_singleton_browsertest.cc +++ b/chrome/browser/chrome_main_process_singleton_browsertest.cc
@@ -17,7 +17,6 @@ #include "chrome/browser/profiles/profile_test_util.h" #include "chrome/browser/ui/browser.h" #include "chrome/browser/ui/browser_commands.h" -#include "chrome/browser/ui/browser_finder.h" #include "chrome/browser/ui/browser_window/public/browser_window_interface.h" #include "chrome/browser/ui/browser_window/public/global_browser_collection.h" #include "chrome/browser/ui/browser_window/public/profile_browser_collection.h"
diff --git a/chrome/browser/chrome_navigation_browsertest.cc b/chrome/browser/chrome_navigation_browsertest.cc index 4dddcc11..a15238b6 100644 --- a/chrome/browser/chrome_navigation_browsertest.cc +++ b/chrome/browser/chrome_navigation_browsertest.cc
@@ -346,9 +346,14 @@ // SiteInstance and BrowsingInstance from the old contents. EXPECT_EQ(contents1->GetPrimaryMainFrame()->GetProcess(), contents2->GetPrimaryMainFrame()->GetProcess()); - EXPECT_EQ( - contents1->GetPrimaryMainFrame()->GetSiteInstance()->GetSiteURL(), - contents2->GetPrimaryMainFrame()->GetSiteInstance()->GetSiteURL()); + EXPECT_EQ(contents1->GetPrimaryMainFrame() + ->GetSiteInstance() + ->GetSecurityPrincipal() + .GetDeprecatedSiteURL(), + contents2->GetPrimaryMainFrame() + ->GetSiteInstance() + ->GetSecurityPrincipal() + .GetDeprecatedSiteURL()); EXPECT_FALSE(contents1->GetSiteInstance()->IsRelatedSiteInstance( contents2->GetSiteInstance())); } @@ -687,9 +692,11 @@ ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), url)); EXPECT_FALSE(observer.last_navigation_succeeded()); EXPECT_EQ(url, observer.last_navigation_url()); - EXPECT_EQ( - GURL(content::kUnreachableWebDataURL), - web_contents->GetPrimaryMainFrame()->GetSiteInstance()->GetSiteURL()); + EXPECT_EQ(GURL(content::kUnreachableWebDataURL), + web_contents->GetPrimaryMainFrame() + ->GetSiteInstance() + ->GetSecurityPrincipal() + .GetDeprecatedSiteURL()); } // Install an extension, which will redirect all navigations to a.com URLs to @@ -730,9 +737,11 @@ observer.Wait(); EXPECT_TRUE(observer.last_navigation_succeeded()); EXPECT_EQ(GURL(url::kAboutBlankURL), observer.last_navigation_url()); - EXPECT_NE( - GURL(content::kUnreachableWebDataURL), - web_contents->GetPrimaryMainFrame()->GetSiteInstance()->GetSiteURL()); + EXPECT_NE(GURL(content::kUnreachableWebDataURL), + web_contents->GetPrimaryMainFrame() + ->GetSiteInstance() + ->GetSecurityPrincipal() + .GetDeprecatedSiteURL()); } // In the above setup, the reload was carried out with the error page being @@ -749,9 +758,10 @@ // and process selection should still honor the initiator, rather than end up // in an unlocked process and an unassigned SiteInstance. See // https://crbug.com/40261555. - EXPECT_EQ( - "http://a.com/", - web_contents->GetPrimaryMainFrame()->GetSiteInstance()->GetSiteURL()); + EXPECT_EQ("http://a.com/", web_contents->GetPrimaryMainFrame() + ->GetSiteInstance() + ->GetSecurityPrincipal() + .GetDeprecatedSiteURL()); EXPECT_TRUE(web_contents->GetPrimaryMainFrame() ->GetProcess() ->IsProcessLockedToSiteForTesting());
diff --git a/chrome/browser/chrome_security_exploit_browsertest.cc b/chrome/browser/chrome_security_exploit_browsertest.cc index ab854ea..cd55efb 100644 --- a/chrome/browser/chrome_security_exploit_browsertest.cc +++ b/chrome/browser/chrome_security_exploit_browsertest.cc
@@ -28,6 +28,7 @@ #include "content/public/browser/blob_handle.h" #include "content/public/browser/render_frame_host.h" #include "content/public/browser/render_process_host.h" +#include "content/public/browser/security_principal.h" #include "content/public/browser/site_isolation_policy.h" #include "content/public/browser/web_contents_observer.h" #include "content/public/common/content_switches.h" @@ -412,10 +413,13 @@ // page. if (content::SiteIsolationPolicy::IsErrorPageIsolationEnabled( !rfh->GetParent())) { - EXPECT_EQ(GURL(content::kUnreachableWebDataURL), - rfh->GetSiteInstance()->GetSiteURL()); + EXPECT_EQ( + GURL(content::kUnreachableWebDataURL), + rfh->GetSiteInstance()->GetSecurityPrincipal().GetDeprecatedSiteURL()); } else { - EXPECT_EQ(GURL(target_origin), rfh->GetSiteInstance()->GetSiteURL()); + EXPECT_EQ( + GURL(target_origin), + rfh->GetSiteInstance()->GetSecurityPrincipal().GetDeprecatedSiteURL()); } std::string script = R"( var textContent = document.body.innerText.replace(/\n+/g, '\n'); @@ -497,10 +501,13 @@ // page. if (content::SiteIsolationPolicy::IsErrorPageIsolationEnabled( !rfh->GetParent())) { - EXPECT_EQ(GURL(content::kUnreachableWebDataURL), - rfh->GetSiteInstance()->GetSiteURL()); + EXPECT_EQ( + GURL(content::kUnreachableWebDataURL), + rfh->GetSiteInstance()->GetSecurityPrincipal().GetDeprecatedSiteURL()); } else { - EXPECT_EQ(GURL(target_origin), rfh->GetSiteInstance()->GetSiteURL()); + EXPECT_EQ( + GURL(target_origin), + rfh->GetSiteInstance()->GetSecurityPrincipal().GetDeprecatedSiteURL()); } std::string body; std::string script = R"(
diff --git a/chrome/browser/chromeos/app_mode/DEPS b/chrome/browser/chromeos/app_mode/DEPS index d2965d0..2267b4e 100644 --- a/chrome/browser/chromeos/app_mode/DEPS +++ b/chrome/browser/chromeos/app_mode/DEPS
@@ -34,7 +34,6 @@ "+chrome/browser/ui/ash/system_web_apps", "+chrome/browser/ui/browser.h", "+chrome/browser/ui/browser_commands.h", - "+chrome/browser/ui/browser_finder.h", "+chrome/browser/ui/browser_window.h", "+chrome/browser/ui/browser_window/public", "+chrome/browser/ui/exclusive_access",
diff --git a/chrome/browser/chromeos/extensions/odfs_config_private/DEPS b/chrome/browser/chromeos/extensions/odfs_config_private/DEPS index 039e4e55..8566d541 100644 --- a/chrome/browser/chromeos/extensions/odfs_config_private/DEPS +++ b/chrome/browser/chromeos/extensions/odfs_config_private/DEPS
@@ -30,7 +30,6 @@ "odfs_config_private_api_browsertest\.cc": [ "+chrome/browser/ui/browser.h", "+chrome/browser/ui/browser_commands.h", - "+chrome/browser/ui/browser_finder.h", "+chrome/browser/ui/browser_window/public/browser_window_interface_iterator.h", "+chrome/browser/ui/browser_window/public/global_browser_collection.h", "+chrome/browser/ui/web_applications/app_browser_controller.h",
diff --git a/chrome/browser/chromeos/extensions/odfs_config_private/odfs_config_private_api_browsertest.cc b/chrome/browser/chromeos/extensions/odfs_config_private/odfs_config_private_api_browsertest.cc index a8a66cc..a45c42b 100644 --- a/chrome/browser/chromeos/extensions/odfs_config_private/odfs_config_private_api_browsertest.cc +++ b/chrome/browser/chromeos/extensions/odfs_config_private/odfs_config_private_api_browsertest.cc
@@ -22,7 +22,6 @@ #include "chrome/browser/sessions/session_tab_helper_factory.h" #include "chrome/browser/ui/browser.h" #include "chrome/browser/ui/browser_commands.h" -#include "chrome/browser/ui/browser_finder.h" #include "chrome/browser/ui/browser_window/public/browser_window_interface_iterator.h" #include "chrome/browser/ui/browser_window/public/global_browser_collection.h" #include "chrome/browser/ui/web_applications/app_browser_controller.h"
diff --git a/chrome/browser/chromeos/network/DEPS b/chrome/browser/chromeos/network/DEPS index 9dc2753..ffb1087c 100644 --- a/chrome/browser/chromeos/network/DEPS +++ b/chrome/browser/chromeos/network/DEPS
@@ -14,7 +14,6 @@ "+chrome/browser/ash/net", "+chrome/browser/prefs", "+chrome/browser/profiles", - "+chrome/browser/ui/browser_finder.h", "+chrome/browser/ui/browser.h", "+chrome/browser/ui/browser_window/public/browser_window_interface.h", "+chrome/browser/ui/browser_window/public/global_browser_collection.h",
diff --git a/chrome/browser/commerce/shopping_service_factory.cc b/chrome/browser/commerce/shopping_service_factory.cc index 4dfee17..853d1a4 100644 --- a/chrome/browser/commerce/shopping_service_factory.cc +++ b/chrome/browser/commerce/shopping_service_factory.cc
@@ -28,7 +28,6 @@ #if !BUILDFLAG(IS_ANDROID) #include "chrome/browser/ui/browser.h" -#include "chrome/browser/ui/browser_finder.h" #include "components/commerce/core/proto/cart_db_content.pb.h" // nogncheck #include "components/commerce/core/proto/discounts_db_content.pb.h" // nogncheck #endif
diff --git a/chrome/browser/context_sharing/tab_bottom_sheet/android/java/src/org/chromium/chrome/browser/tab_bottom_sheet/TabBottomSheetContent.java b/chrome/browser/context_sharing/tab_bottom_sheet/android/java/src/org/chromium/chrome/browser/tab_bottom_sheet/TabBottomSheetContent.java index 4c5bc8d..f6bd019 100644 --- a/chrome/browser/context_sharing/tab_bottom_sheet/android/java/src/org/chromium/chrome/browser/tab_bottom_sheet/TabBottomSheetContent.java +++ b/chrome/browser/context_sharing/tab_bottom_sheet/android/java/src/org/chromium/chrome/browser/tab_bottom_sheet/TabBottomSheetContent.java
@@ -6,13 +6,13 @@ import android.content.Context; import android.view.View; -import android.view.ViewGroup; import androidx.annotation.ColorInt; import androidx.annotation.StringRes; import org.chromium.build.annotations.NullMarked; import org.chromium.build.annotations.Nullable; +import org.chromium.build.NullUtil; import org.chromium.chrome.browser.context_sharing.R; import org.chromium.chrome.browser.flags.ChromeFeatureList; import org.chromium.components.browser_ui.bottomsheet.BottomSheetContent; @@ -27,9 +27,10 @@ private final int mPeekViewHeight; private final @ColorInt int mBackgroundColor; private final @Nullable GlowSpec mGlowSpec; - private final ViewGroup mPeekContainer; /** + * Constructor. + * * @param contentView The inflated view for the bottom sheet. * @param fullHeightRatio The full height ratio for the bottom sheet. * @param backgroundColor The background color for the bottom sheet. @@ -54,9 +55,10 @@ mContentView .getResources() .getDimensionPixelSize(R.dimen.tab_bottom_sheet_peek_height_total); - mPeekContainer = (ViewGroup) mContentView.findViewById(R.id.actor_control_container); - assert mPeekContainer != null; - mPeekContainer.setBackgroundColor(mBackgroundColor); + + View view = mContentView.findViewById(R.id.actor_control_container); + View peekContainer = NullUtil.assertNonNull(view); + peekContainer.setBackgroundColor(mBackgroundColor); } @Override @@ -81,9 +83,7 @@ } @Override - public void destroy() { - mPeekContainer.removeAllViews(); - } + public void destroy() {} @Override public int getPriority() {
diff --git a/chrome/browser/context_sharing/tab_bottom_sheet/android/java/src/org/chromium/chrome/browser/tab_bottom_sheet/TabBottomSheetCoordinator.java b/chrome/browser/context_sharing/tab_bottom_sheet/android/java/src/org/chromium/chrome/browser/tab_bottom_sheet/TabBottomSheetCoordinator.java index 16714ca..67d1d0a 100644 --- a/chrome/browser/context_sharing/tab_bottom_sheet/android/java/src/org/chromium/chrome/browser/tab_bottom_sheet/TabBottomSheetCoordinator.java +++ b/chrome/browser/context_sharing/tab_bottom_sheet/android/java/src/org/chromium/chrome/browser/tab_bottom_sheet/TabBottomSheetCoordinator.java
@@ -230,7 +230,7 @@ mBottomSheetController.getMaxOffset()); if (startsExpanded) { if (mSheetContent != null && isSheetHeightSufficient) { - mBottomSheetController.expandSheet(); + mBottomSheetController.expandSheet(animate); } else { mSheetEventsCallback.onBottomSheetOpened(/* isExpanded= */ false); }
diff --git a/chrome/browser/context_sharing/tab_bottom_sheet/android/java/src/org/chromium/chrome/browser/tab_bottom_sheet/TabBottomSheetCoordinatorTest.java b/chrome/browser/context_sharing/tab_bottom_sheet/android/java/src/org/chromium/chrome/browser/tab_bottom_sheet/TabBottomSheetCoordinatorTest.java index 0d4aeac..3265e2b 100644 --- a/chrome/browser/context_sharing/tab_bottom_sheet/android/java/src/org/chromium/chrome/browser/tab_bottom_sheet/TabBottomSheetCoordinatorTest.java +++ b/chrome/browser/context_sharing/tab_bottom_sheet/android/java/src/org/chromium/chrome/browser/tab_bottom_sheet/TabBottomSheetCoordinatorTest.java
@@ -285,28 +285,6 @@ } @Test - public void testTabBottomSheetContentDestroyClearsContainer() { - simulateShowSuccessAndGetObserver(); - verify(mMockBottomSheetController) - .requestShowContent(mBottomSheetContentArgumentCaptor.capture(), eq(true)); - TabBottomSheetContent content = mBottomSheetContentArgumentCaptor.getValue(); - assertNotNull(content); - - ViewGroup peekContainer = mView.findViewById(R.id.actor_control_container); - assertNotNull(peekContainer); - - // Simulate a stale view. - View staleView = new View(mContext); - peekContainer.addView(staleView); - assertEquals(1, peekContainer.getChildCount()); - - content.destroy(); - - // Verify no children are left in the container. - assertEquals(0, peekContainer.getChildCount()); - } - - @Test public void testCorrectFullHeightRatio_WithoutKeyboard() { when(mKeyboardDelegate.isKeyboardShowing(eq(mView))).thenReturn(false); simulateShowSuccessAndGetObserver();
diff --git a/chrome/browser/contextual_cueing/BUILD.gn b/chrome/browser/contextual_cueing/BUILD.gn index 9742342..92d89149 100644 --- a/chrome/browser/contextual_cueing/BUILD.gn +++ b/chrome/browser/contextual_cueing/BUILD.gn
@@ -24,6 +24,10 @@ "//components/tabs:public", "//url", ] + + if (!is_android) { + public_deps += [ "//chrome/browser/ui/page_action" ] + } } source_set("impl") { @@ -72,8 +76,8 @@ if (!is_android) { deps += [ + "//chrome/browser/ui/page_action", "//chrome/browser/ui/user_education", - "//chrome/browser/ui/views/page_action", ] } } @@ -94,6 +98,7 @@ "//chrome/browser/optimization_guide:test_support", "//chrome/browser/sync", "//chrome/browser/ui/browser_window", + "//chrome/browser/ui/page_action", "//chrome/browser/ui/side_panel", "//chrome/browser/ui/side_panel:side_panel_ui_provider", "//chrome/browser/ui/side_panel:side_panel_views_dependent",
diff --git a/chrome/browser/contextual_cueing/contextual_cueing_controller.cc b/chrome/browser/contextual_cueing/contextual_cueing_controller.cc index f808218..cadbedc7a 100644 --- a/chrome/browser/contextual_cueing/contextual_cueing_controller.cc +++ b/chrome/browser/contextual_cueing/contextual_cueing_controller.cc
@@ -29,6 +29,7 @@ #include "chrome/browser/page_content_annotations/page_content_annotations_service_factory.h" #include "chrome/browser/profiles/profile.h" #include "chrome/browser/search_engines/template_url_service_factory.h" +#include "chrome/browser/signin/identity_manager_factory.h" #include "chrome/browser/sync/sync_service_factory.h" #include "chrome/browser/tab_list/tab_list_interface.h" #include "chrome/browser/ui/browser_actions.h" @@ -46,6 +47,9 @@ #include "components/optimization_guide/proto/features/contextual_cueing.pb.h" #include "components/search_engines/template_url_service.h" #include "components/sessions/content/session_tab_helper.h" +#include "components/signin/public/identity_manager/account_capabilities.h" +#include "components/signin/public/identity_manager/account_info.h" +#include "components/signin/public/identity_manager/identity_manager.h" #include "components/sync/service/sync_service.h" #include "components/sync/service/sync_service_utils.h" #include "components/sync/service/sync_user_settings.h" @@ -57,6 +61,7 @@ #if !BUILDFLAG(IS_ANDROID) #include "chrome/browser/ui/page_action/page_action_controller.h" +#include "chrome/browser/ui/page_action/page_action_observer.h" #include "chrome/browser/ui/user_education/browser_user_education_interface.h" #endif @@ -105,6 +110,25 @@ tab1.title() == tab2.title(); } +#if !BUILDFLAG(IS_ANDROID) +class ContextualCueingPageActionObserver + : public page_actions::PageActionObserver { + public: + explicit ContextualCueingPageActionObserver( + base::RepeatingClosure hidden_callback) + : page_actions::PageActionObserver(kActionAnchoredContextualCue), + hidden_callback_(hidden_callback) {} + + void OnPageActionIconHidden( + const page_actions::PageActionState& page_action) override { + hidden_callback_.Run(); + } + + private: + base::RepeatingClosure hidden_callback_; +}; +#endif + } // namespace ContextualCueingController::ContextualCueingController( @@ -123,7 +147,14 @@ sync_service_(SyncServiceFactory::GetForProfile( browser_window_interface_->GetProfile())), template_url_service_(TemplateURLServiceFactory::GetForProfile( + browser_window_interface_->GetProfile())), + identity_manager_(IdentityManagerFactory::GetForProfile( browser_window_interface_->GetProfile())) { +#if !BUILDFLAG(IS_ANDROID) + page_action_observer_ = std::make_unique<ContextualCueingPageActionObserver>( + base::BindRepeating(&ContextualCueingController::OnCueHidden, + weak_ptr_factory_.GetWeakPtr())); +#endif if (page_content_annotations_service_) { page_content_annotations_service_->AddObserver( page_content_annotations::AnnotationType::kCategoryClassifier, this); @@ -380,6 +411,12 @@ return; } + if (IsUserSubjectToAgeRestrictions()) { + RecordContextualCueingDecision( + ContextualCueingDecision::kAgeRestrictionEnforced); + return; + } + if (!target->IsEligible()) { CUEING_LOG(base::StringPrintf("Not eligible for '%s' cues", GetName(*target_type))); @@ -393,6 +430,28 @@ } } +bool ContextualCueingController::IsUserSubjectToAgeRestrictions() { + if (!base::FeatureList::IsEnabled(kContextualCueingV2EnforceAgeRestriction)) { + return false; + } + + // If the user is not signed in, we cannot check their age. Say that they are + // subject to age restrictions. + if (!identity_manager_) { + return true; + } + + // If the user is signed in, check if they are subject to age restrictions. + AccountCapabilities capabilities = + identity_manager_ + ->FindExtendedAccountInfo(identity_manager_->GetPrimaryAccountInfo( + signin::ConsentLevel::kSignin)) + .capabilities; + + return capabilities.can_use_model_execution_features() != + signin::Tribool::kTrue; +} + bool ContextualCueingController::IsUrlEligibleForCue(const GURL& url) { if (!url.SchemeIsHTTPOrHTTPS()) { return false; @@ -524,6 +583,10 @@ "Showing cue for CUJ %s: %s [%s]", response.suggested_cuj(), strings.anchored_message_text(), strings.action_text())); + page_action_observer_->RegisterAsPageActionObserver(*page_action_controller); + + current_cuj_ = response.suggested_cuj(); + contextual_cueing_service_->OnCueShown( tab->GetContents()->GetLastCommittedURL()); #endif @@ -534,6 +597,10 @@ RecordContextualCueingDecision(ContextualCueingDecision::kSuccess); } +void ContextualCueingController::OnCueHidden() { + current_cuj_.clear(); +} + void ContextualCueingController::OnCueClicked( CueTargetType cue_type, CueActionData data, @@ -546,7 +613,8 @@ } contextual_cueing_service_->OnCueClicked(cue_type); - RecordContextualCueingInteraction(ContextualCueingInteraction::kCueClicked); + RecordContextualCueingInteraction(ContextualCueingInteraction::kCueClicked, + current_cuj_); HideCue(); }
diff --git a/chrome/browser/contextual_cueing/contextual_cueing_controller.h b/chrome/browser/contextual_cueing/contextual_cueing_controller.h index 0ec3331..560f6ef 100644 --- a/chrome/browser/contextual_cueing/contextual_cueing_controller.h +++ b/chrome/browser/contextual_cueing/contextual_cueing_controller.h
@@ -29,6 +29,14 @@ struct OptimizationGuideModelExecutionResult; } // namespace optimization_guide +namespace page_actions { +class PageActionObserver; +} // namespace page_actions + +namespace signin { +class IdentityManager; +} // namespace signin + namespace syncer { class SyncService; } // namespace syncer @@ -73,6 +81,8 @@ // Returns the CueTarget for the given CueTargetType, or nullptr if there is // none. CueTarget* GetTarget(CueTargetType type); + // Getter function for CUJ of shown cue + const std::string& current_cuj() const { return current_cuj_; } private: // Initiates a model execution request to MES for the current window state. @@ -90,6 +100,9 @@ // Returns true if the cue should be shown to the user. bool IsAllowedToShowCue(); + // Returns true if the user is subject to age restrictions. + bool IsUserSubjectToAgeRestrictions(); + void ShowCue(CueTargetType cue_type, const CueTarget& target, optimization_guide::proto::ContextualCueingResponse response); @@ -97,6 +110,7 @@ CueActionData data, actions::ActionItem*, actions::ActionInvocationContext); + void OnCueHidden(); // Returns the list of cue surfaces that are currently eligible to show a cue. absl::flat_hash_set<optimization_guide::proto::ContextualCueingSurface> @@ -112,7 +126,13 @@ raw_ptr<OptimizationGuideLogger> optimization_guide_logger_; raw_ptr<syncer::SyncService> sync_service_; raw_ptr<TemplateURLService> template_url_service_; + raw_ptr<signin::IdentityManager> identity_manager_; absl::flat_hash_map<CueTargetType, std::unique_ptr<CueTarget>> cue_targets_; + std::string current_cuj_; + +#if !BUILDFLAG(IS_ANDROID) + std::unique_ptr<page_actions::PageActionObserver> page_action_observer_; +#endif base::WeakPtrFactory<ContextualCueingController> weak_ptr_factory_{this}; };
diff --git a/chrome/browser/contextual_cueing/contextual_cueing_controller_browsertest.cc b/chrome/browser/contextual_cueing/contextual_cueing_controller_browsertest.cc index fc14553..43cbc8f7 100644 --- a/chrome/browser/contextual_cueing/contextual_cueing_controller_browsertest.cc +++ b/chrome/browser/contextual_cueing/contextual_cueing_controller_browsertest.cc
@@ -45,6 +45,7 @@ #include "components/optimization_guide/core/optimization_guide_prefs.h" #include "components/optimization_guide/proto/features/contextual_cueing.pb.h" #include "components/prefs/pref_service.h" +#include "components/signin/public/identity_manager/account_capabilities_test_mutator.h" #include "components/sync/test/test_sync_service.h" #include "content/public/browser/navigation_entry.h" #include "content/public/test/browser_test.h" @@ -72,10 +73,11 @@ std::u16string GetMessageText() const override { return u"Test InfoBar"; } }; -class ContextualCueingControllerBrowserTest : public SigninBrowserTestBase { +class ContextualCueingControllerBrowserTestBase : public SigninBrowserTestBase { public: - ContextualCueingControllerBrowserTest() { - scoped_feature_list_.InitAndEnableFeature(kContextualCueingV2); + void SetUp() override { + InitializeFeatureList(); + SigninBrowserTestBase::SetUp(); } void SetUpOnMainThread() override { @@ -172,8 +174,11 @@ ->page_action_controller(); } + virtual void InitializeFeatureList() = 0; + protected: raw_ptr<TestCueTarget> cue_target_ = nullptr; + base::test::ScopedFeatureList scoped_feature_list_; private: syncer::TestSyncService* GetTestSyncService() { @@ -188,7 +193,6 @@ context, base::BindRepeating(&CreateTestSyncService)); } - base::test::ScopedFeatureList scoped_feature_list_; ui::UserDataFactory::ScopedOverride user_ed_override_; }; @@ -206,6 +210,15 @@ return response; } +class ContextualCueingControllerBrowserTest + : public ContextualCueingControllerBrowserTestBase { + public: + void InitializeFeatureList() override { + scoped_feature_list_.InitWithFeatures( + {kContextualCueingV2}, {kContextualCueingV2EnforceAgeRestriction}); + } +}; + IN_PROC_BROWSER_TEST_F(ContextualCueingControllerBrowserTest, NoLongerActiveTabAfterCategoryClassification) { base::HistogramTester histogram_tester; @@ -803,5 +816,100 @@ 1); } +IN_PROC_BROWSER_TEST_F(ContextualCueingControllerBrowserTest, + RecordsCueInteractionWithCUJHistogram) { + // 1. Navigate to a valid eligible URL + ASSERT_TRUE(ui_test_utils::NavigateToURLWithDisposition( + browser(), GURL("https://www.activetab.com/abc"), + WindowOpenDisposition::NEW_FOREGROUND_TAB, + ui_test_utils::BROWSER_TEST_WAIT_FOR_LOAD_STOP)); + + base::HistogramTester histogram_tester; + + // 2. Mock the server response and inject a fake CUJ string + auto response = MakeCompleteResponse(); + response.set_suggested_cuj("test_cuj_string"); + SeedExecutionResult(std::move(response)); + + // 3. Trigger the cue execution flow + SimulateFilterPassed(); + + // 4. Wait for the flow to successfully finish + optimization_guide::RetryForHistogramUntilCountReached( + &histogram_tester, "ContextualCueing.V2.Decision", 1); + + // 5. Confirm flow was completed successfully + histogram_tester.ExpectUniqueSample("ContextualCueing.V2.Decision", + ContextualCueingDecision::kSuccess, 1); + + // 6. Simulate user clicking the cue + auto* action = + actions::ActionManager::Get().FindAction(kActionAnchoredContextualCue); + ASSERT_TRUE(action); + action->InvokeAction(); + + // 7. Verify that the interaction was logged with the hashed CUJ! + histogram_tester.ExpectUniqueSample( + "ContextualCueing.V2.CueInteraction.Clicked", + base::HashMetricName("test_cuj_string"), 1); +} + +class ContextualCueingControllerBrowserTestWithAgeRestriction + : public ContextualCueingControllerBrowserTest { + public: + void InitializeFeatureList() override { + scoped_feature_list_.InitWithFeatures( + {kContextualCueingV2, kContextualCueingV2EnforceAgeRestriction}, + /*disabled_features=*/{}); + } + + void SetUserRestriction(bool is_restricted) { + auto account_info = identity_test_env()->MakePrimaryAccountAvailable( + "user@gmail.com", signin::ConsentLevel::kSignin); + AccountCapabilitiesTestMutator mutator(&account_info.capabilities); + mutator.set_can_use_model_execution_features(!is_restricted); + identity_test_env()->UpdateAccountInfoForAccount(account_info); + } +}; + +IN_PROC_BROWSER_TEST_F(ContextualCueingControllerBrowserTestWithAgeRestriction, + AgeRestrictionEnforced) { + SetUserRestriction(/*is_restricted=*/true); + + ASSERT_TRUE(ui_test_utils::NavigateToURLWithDisposition( + browser(), GURL("https://www.activetab.com/abc"), + WindowOpenDisposition::NEW_FOREGROUND_TAB, + ui_test_utils::BROWSER_TEST_WAIT_FOR_LOAD_STOP)); + + base::HistogramTester histogram_tester; + SeedExecutionResult(MakeCompleteResponse()); + SimulateFilterPassed(); + + optimization_guide::RetryForHistogramUntilCountReached( + &histogram_tester, "ContextualCueing.V2.Decision", 1); + histogram_tester.ExpectUniqueSample( + "ContextualCueing.V2.Decision", + ContextualCueingDecision::kAgeRestrictionEnforced, 1); +} + +IN_PROC_BROWSER_TEST_F(ContextualCueingControllerBrowserTestWithAgeRestriction, + AgeRestrictionPasses) { + SetUserRestriction(/*is_restricted=*/false); + + ASSERT_TRUE(ui_test_utils::NavigateToURLWithDisposition( + browser(), GURL("https://www.activetab.com/abc"), + WindowOpenDisposition::NEW_FOREGROUND_TAB, + ui_test_utils::BROWSER_TEST_WAIT_FOR_LOAD_STOP)); + + base::HistogramTester histogram_tester; + SeedExecutionResult(MakeCompleteResponse()); + SimulateFilterPassed(); + + optimization_guide::RetryForHistogramUntilCountReached( + &histogram_tester, "ContextualCueing.V2.Decision", 1); + histogram_tester.ExpectUniqueSample("ContextualCueing.V2.Decision", + ContextualCueingDecision::kSuccess, 1); +} + } // namespace } // namespace contextual_cueing
diff --git a/chrome/browser/contextual_cueing/contextual_cueing_enums.h b/chrome/browser/contextual_cueing/contextual_cueing_enums.h index 48e2ecf..2a9b47d1 100644 --- a/chrome/browser/contextual_cueing/contextual_cueing_enums.h +++ b/chrome/browser/contextual_cueing/contextual_cueing_enums.h
@@ -68,7 +68,9 @@ kUserOptedOut = 25, // The cue couldn't be shown because it is disabled by enterprise policy. kDisabledByEnterprisePolicy = 26, - kMaxValue = kDisabledByEnterprisePolicy, + // The cue couldn't be shown because the user is subject to age restrictions. + kAgeRestrictionEnforced = 27, + kMaxValue = kAgeRestrictionEnforced, }; // LINT.ThenChange(//tools/metrics/histograms/metadata/contextual_cueing/enums.xml:ContextualCueingDecision)
diff --git a/chrome/browser/contextual_cueing/contextual_cueing_menu_model.cc b/chrome/browser/contextual_cueing/contextual_cueing_menu_model.cc index 8f97fd5..5ae8de9 100644 --- a/chrome/browser/contextual_cueing/contextual_cueing_menu_model.cc +++ b/chrome/browser/contextual_cueing/contextual_cueing_menu_model.cc
@@ -68,19 +68,22 @@ switch (command_id) { case kContextualCueingDismissCommand: RecordContextualCueingInteraction( - ContextualCueingInteraction::kCueDismissed); + ContextualCueingInteraction::kCueDismissed, + controller_->current_cuj()); contextual_cueing_service_->OnCueDismissed(cue_type_); break; case kContextualCueingEditPromptCommand: RecordContextualCueingInteraction( - ContextualCueingInteraction::kCueEditPrompt); + ContextualCueingInteraction::kCueEditPrompt, + controller_->current_cuj()); if (CueTarget* target = controller_->GetTarget(cue_type_)) { target->OnEditPrompt(std::move(data_)); } break; case kContextualCueingOpenSettingsCommand: { RecordContextualCueingInteraction( - ContextualCueingInteraction::kCueSuggestionsSettings); + ContextualCueingInteraction::kCueSuggestionsSettings, + controller_->current_cuj()); chrome::ShowSettingsSubPageForProfile(profile_, chrome::kSuggestionsSubPage); break;
diff --git a/chrome/browser/contextual_cueing/contextual_cueing_metrics.cc b/chrome/browser/contextual_cueing/contextual_cueing_metrics.cc index 283d425b3..d8351e6 100644 --- a/chrome/browser/contextual_cueing/contextual_cueing_metrics.cc +++ b/chrome/browser/contextual_cueing/contextual_cueing_metrics.cc
@@ -5,14 +5,38 @@ #include "chrome/browser/contextual_cueing/contextual_cueing_metrics.h" #include "base/metrics/histogram_functions.h" +#include "base/metrics/metrics_hashes.h" #include "chrome/browser/contextual_cueing/contextual_cueing_enums.h" namespace contextual_cueing { +namespace { + +std::string InteractionTypeToString(ContextualCueingInteraction interaction) { + switch (interaction) { + case ContextualCueingInteraction::kCueClicked: + return "Clicked"; + case ContextualCueingInteraction::kCueDismissed: + return "Dismissed"; + case ContextualCueingInteraction::kCueEditPrompt: + return "EditPrompt"; + case ContextualCueingInteraction::kCueSuggestionsSettings: + return "Settings"; + } +} + +} // namespace + void RecordContextualCueingInteraction( - ContextualCueingInteraction contextual_cueing_interaction) { + ContextualCueingInteraction contextual_cueing_interaction, + const std::string& cuj) { base::UmaHistogramEnumeration("ContextualCueing.V2.CueInteraction", contextual_cueing_interaction); + + std::string histogram_name = + "ContextualCueing.V2.CueInteraction." + + InteractionTypeToString(contextual_cueing_interaction); + base::UmaHistogramSparse(histogram_name, base::HashMetricName(cuj)); } } // namespace contextual_cueing
diff --git a/chrome/browser/contextual_cueing/contextual_cueing_metrics.h b/chrome/browser/contextual_cueing/contextual_cueing_metrics.h index 6511863..faf21fb 100644 --- a/chrome/browser/contextual_cueing/contextual_cueing_metrics.h +++ b/chrome/browser/contextual_cueing/contextual_cueing_metrics.h
@@ -5,12 +5,15 @@ #ifndef CHROME_BROWSER_CONTEXTUAL_CUEING_CONTEXTUAL_CUEING_METRICS_H_ #define CHROME_BROWSER_CONTEXTUAL_CUEING_CONTEXTUAL_CUEING_METRICS_H_ +#include <string> + namespace contextual_cueing { enum class ContextualCueingInteraction; void RecordContextualCueingInteraction( - ContextualCueingInteraction contextual_cueing_interaction); + ContextualCueingInteraction contextual_cueing_interaction, + const std::string& cuj); } // namespace contextual_cueing
diff --git a/chrome/browser/contextual_cueing/features.cc b/chrome/browser/contextual_cueing/features.cc index 0cb3c3ff..06eb351 100644 --- a/chrome/browser/contextual_cueing/features.cc +++ b/chrome/browser/contextual_cueing/features.cc
@@ -7,6 +7,8 @@ namespace contextual_cueing { BASE_FEATURE(kContextualCueingV2, base::FEATURE_DISABLED_BY_DEFAULT); +BASE_FEATURE(kContextualCueingV2EnforceAgeRestriction, + base::FEATURE_DISABLED_BY_DEFAULT); const base::FeatureParam<double> kEduClassifierThreshold( &kContextualCueingV2,
diff --git a/chrome/browser/contextual_cueing/features.h b/chrome/browser/contextual_cueing/features.h index 406bad3..553d20d 100644 --- a/chrome/browser/contextual_cueing/features.h +++ b/chrome/browser/contextual_cueing/features.h
@@ -12,6 +12,8 @@ namespace contextual_cueing { BASE_DECLARE_FEATURE(kContextualCueingV2); +BASE_DECLARE_FEATURE(kContextualCueingV2EnforceAgeRestriction); + extern const base::FeatureParam<double> kEduClassifierThreshold; extern const base::FeatureParam<double> kShoppingClassifierThreshold; extern const base::FeatureParam<int> kMaxNumBackgroundTabs;
diff --git a/chrome/browser/contextual_tasks/BUILD.gn b/chrome/browser/contextual_tasks/BUILD.gn index 76a7d88..385c722 100644 --- a/chrome/browser/contextual_tasks/BUILD.gn +++ b/chrome/browser/contextual_tasks/BUILD.gn
@@ -169,7 +169,11 @@ ] if (is_android) { - sources += [ "android/contextual_tasks_panel_host_android.h" ] + if (is_desktop_android) { + sources += [ "android/contextual_tasks_panel_host_desktop_android.h" ] + } else { + sources += [ "android/contextual_tasks_panel_host_android.h" ] + } } if (!is_android) { @@ -209,6 +213,10 @@ if (is_android) { public_deps += [ "//chrome/browser/context_sharing/tab_bottom_sheet/android" ] + + if (is_desktop_android) { + public_deps += [ "//chrome/browser/ui/side_panel" ] + } } if (!is_android || enable_webui_ntp) { @@ -261,7 +269,11 @@ ] if (is_android) { - sources += [ "android/contextual_tasks_panel_host_android.cc" ] + if (is_desktop_android) { + sources += [ "android/contextual_tasks_panel_host_desktop_android.cc" ] + } else { + sources += [ "android/contextual_tasks_panel_host_android.cc" ] + } } if (!is_android) { @@ -326,6 +338,12 @@ "//chrome/browser/android:tabs_public", "//chrome/browser/context_sharing/tab_bottom_sheet/android", ] + if (is_desktop_android) { + deps += [ + "//chrome/browser/ui/side_panel:side_panel_ui_provider", + "//chrome/browser/ui/side_panel/android", + ] + } } if (!is_android) { @@ -591,6 +609,7 @@ deps = [ ":java_resources", "//base:base_java", + "//base:supplier_java", "//build/android:build_java", "//chrome/browser/android/lifecycle:java", "//chrome/browser/contextual_tasks/fusebox/android:java",
diff --git a/chrome/browser/contextual_tasks/android/contextual_tasks_panel_host_desktop_android.cc b/chrome/browser/contextual_tasks/android/contextual_tasks_panel_host_desktop_android.cc new file mode 100644 index 0000000..d12610fa --- /dev/null +++ b/chrome/browser/contextual_tasks/android/contextual_tasks_panel_host_desktop_android.cc
@@ -0,0 +1,247 @@ +// Copyright 2026 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chrome/browser/contextual_tasks/android/contextual_tasks_panel_host_desktop_android.h" + +#include <utility> + +#include "base/android/jni_android.h" +#include "chrome/browser/context_sharing/tab_bottom_sheet/android/co_browse_views_bridge.h" +#include "chrome/browser/tab_list/tab_list_interface.h" +#include "chrome/browser/ui/browser_window/public/browser_window_interface.h" +#include "chrome/browser/ui/side_panel/android/side_panel_native_view_android.h" +#include "chrome/browser/ui/side_panel/side_panel_entry.h" +#include "chrome/browser/ui/side_panel/side_panel_entry_key.h" +#include "chrome/browser/ui/side_panel/side_panel_enums.h" +#include "chrome/browser/ui/side_panel/side_panel_registry.h" +#include "chrome/browser/ui/side_panel/side_panel_ui.h" +#include "chrome/browser/ui/side_panel/side_panel_ui_provider.h" +#include "components/tabs/public/tab_interface.h" +#include "content/public/browser/web_contents.h" +#include "ui/android/window_android.h" +#include "ui/base/base_window.h" + +using base::android::AttachCurrentThread; + +namespace { +constexpr int kSidePanelMinWidth = 440; +} // namespace + +namespace contextual_tasks { + +// static +std::unique_ptr<ContextualTasksPanelHost> ContextualTasksPanelHost::Create( + BrowserWindowInterface* browser_window) { + return std::make_unique<ContextualTasksPanelHostDesktopAndroid>( + browser_window); +} + +ContextualTasksPanelHostDesktopAndroid::ContextualTasksPanelHostDesktopAndroid( + BrowserWindowInterface* browser_window) + : browser_window_(browser_window) { + MaybeRegisterEntry(); + MaybeCreateBridge(); +} + +ContextualTasksPanelHostDesktopAndroid:: + ~ContextualTasksPanelHostDesktopAndroid() = default; + +void ContextualTasksPanelHostDesktopAndroid::AddObserver( + ContextualTasksPanelHost::Observer* observer) { + observers_.AddObserver(observer); +} + +void ContextualTasksPanelHostDesktopAndroid::RemoveObserver( + ContextualTasksPanelHost::Observer* observer) { + observers_.RemoveObserver(observer); +} + +void ContextualTasksPanelHostDesktopAndroid::Show(AnimationStyle animation) { + MaybeRegisterEntry(); + + auto* side_panel_ui = GetSidePanelUI(); + if (!side_panel_ui) { + return; + } + side_panel_ui->Show( + SidePanelEntry::Key(SidePanelEntry::Id::kContextualTasks), std::nullopt, + /*suppress_animations=*/animation == AnimationStyle::kNoAnimation); +} + +void ContextualTasksPanelHostDesktopAndroid::Close(AnimationStyle animation) { + auto* side_panel_ui = GetSidePanelUI(); + if (!side_panel_ui) { + return; + } + side_panel_ui->Close( + SidePanelEntryHideReason::kSidePanelClosed, + /*suppress_animations=*/animation == AnimationStyle::kNoAnimation); +} + +bool ContextualTasksPanelHostDesktopAndroid::IsPanelInitialized() { + // Bypasses the initialization check to allow SetWebContents to propagate + // correctly during the initial setup flow. + return true; +} + +bool ContextualTasksPanelHostDesktopAndroid::IsPanelOpenForContextualTask() + const { + auto* side_panel_ui = GetSidePanelUI(); + if (!side_panel_ui) { + return false; + } + return side_panel_ui->IsSidePanelEntryShowing( + SidePanelEntry::Key(SidePanelEntry::Id::kContextualTasks)); +} + +bool ContextualTasksPanelHostDesktopAndroid::IsPanelSuppressed() const { + if (suppressed_for_testing_) { + return true; + } + auto* side_panel_ui = GetSidePanelUI(); + if (!side_panel_ui) { + return false; + } + // Contextual Tasks should be suppressed if Glic is currently showing in the + // side panel. See `contextual_tasks_panel_host_desktop.cc` for the + // corresponding logic. + return side_panel_ui->IsSidePanelEntryShowing( + SidePanelEntry::Key(SidePanelEntry::Id::kGlic)); +} + +void ContextualTasksPanelHostDesktopAndroid::SetPanelSuppressedForTesting( + bool suppressed) { + suppressed_for_testing_ = suppressed; +} + +content::WebContents* ContextualTasksPanelHostDesktopAndroid::GetWebContents() { + return web_contents_; +} + +bool ContextualTasksPanelHostDesktopAndroid::MaybeCreateBridge() { + // Reuse the bridge if it exists and the tab we created it with is still + // alive. + if (co_browse_views_bridge_ && tab_ref_) { + return true; + } + + tabs::TabInterface* active_tab = + TabListInterface::From(browser_window_)->GetActiveTab(); + if (!active_tab) { + return false; + } + + tab_ref_ = active_tab->GetWeakPtr(); + co_browse_views_bridge_ = + std::make_unique<context_sharing::CoBrowseViewsBridge>( + *active_tab, + context_sharing::TabBottomSheetClientType::kContextualTasks); + return co_browse_views_bridge_ != nullptr; +} + +void ContextualTasksPanelHostDesktopAndroid::SetWebContents( + content::WebContents* web_contents) { + if (web_contents_ == web_contents) { + return; + } + + if (content::WebContents* prev = std::exchange(web_contents_, web_contents)) { + prev->SetDelegate(nullptr); + } + + if (web_contents_) { + web_contents_->SetDelegate(this); + } + + if (MaybeCreateBridge()) { + co_browse_views_bridge_->SetWebContents(web_contents); + } +} + +void ContextualTasksPanelHostDesktopAndroid::OnEntryHiddenWithReason( + SidePanelEntry* entry, + SidePanelEntryHideReason reason) { + CHECK_EQ(entry->key().id(), SidePanelEntry::Id::kContextualTasks); + is_open_ = false; + NotifySurfaceStateChanged( + ContextualTasksPanelHost::SurfaceState::kClosed, + reason == SidePanelEntryHideReason::kReplaced + ? ContextualTasksPanelHost::StateChangeReason::kSystemAction + : ContextualTasksPanelHost::StateChangeReason::kUserAction); +} + +void ContextualTasksPanelHostDesktopAndroid::OnEntryShown( + SidePanelEntry* entry) { + CHECK_EQ(entry->key().id(), SidePanelEntry::Id::kContextualTasks); + is_open_ = true; + NotifySurfaceStateChanged( + ContextualTasksPanelHost::SurfaceState::kVisible, + ContextualTasksPanelHost::StateChangeReason::kUserAction); +} + +void ContextualTasksPanelHostDesktopAndroid::MaybeRegisterEntry() { + auto* registry = SidePanelRegistry::From(browser_window_); + if (!registry) { + return; + } + + if (registry->GetEntryForKey( + SidePanelEntry::Key(SidePanelEntry::Id::kContextualTasks))) { + return; + } + + auto entry = std::make_unique<SidePanelEntry>( + SidePanelType::kToolbar, + SidePanelEntry::Key(SidePanelEntry::Id::kContextualTasks), + base::BindRepeating(&ContextualTasksPanelHostDesktopAndroid::CreateView, + base::Unretained(this)), + base::BindRepeating([]() { return kSidePanelMinWidth; })); + entry->set_should_show_header(false); + entry->set_should_show_ephemerally_in_toolbar(false); + side_panel_entry_observation_.Observe(entry.get()); + registry->Register(std::move(entry)); +} + +SidePanelUI* ContextualTasksPanelHostDesktopAndroid::GetSidePanelUI() const { + return SidePanelUIProvider::From(browser_window_); +} + +SidePanelNativeView ContextualTasksPanelHostDesktopAndroid::CreateView( + SidePanelEntryScope& scope) { + if (!co_browse_views_bridge_ || !tab_ref_) { + if (!MaybeCreateBridge()) { + return nullptr; + } + co_browse_views_bridge_->CreateCoBrowseViews(web_contents_); + } + + auto view = co_browse_views_bridge_->GetView(); + if (!view) { + return nullptr; + } + return std::make_unique<SidePanelNativeViewAndroid>( + base::android::ScopedJavaGlobalRef<jobject>( + base::android::AttachCurrentThread(), view)); +} + +void ContextualTasksPanelHostDesktopAndroid::NotifySurfaceStateChanged( + ContextualTasksPanelHost::SurfaceState state, + ContextualTasksPanelHost::StateChangeReason reason) { + observers_.Notify(&ContextualTasksPanelHost::Observer::OnSurfaceStateChanged, + state, reason); +} + +content::WebContents* ContextualTasksPanelHostDesktopAndroid::OpenURLFromTab( + content::WebContents* source, + const content::OpenURLParams& params, + base::OnceCallback<void(content::NavigationHandle&)> + navigation_handle_callback) { + if (browser_window_) { + return browser_window_->OpenURL(params, + std::move(navigation_handle_callback)); + } + return nullptr; +} + +} // namespace contextual_tasks
diff --git a/chrome/browser/contextual_tasks/android/contextual_tasks_panel_host_desktop_android.h b/chrome/browser/contextual_tasks/android/contextual_tasks_panel_host_desktop_android.h new file mode 100644 index 0000000..3f955c57 --- /dev/null +++ b/chrome/browser/contextual_tasks/android/contextual_tasks_panel_host_desktop_android.h
@@ -0,0 +1,104 @@ +// Copyright 2026 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CHROME_BROWSER_CONTEXTUAL_TASKS_ANDROID_CONTEXTUAL_TASKS_PANEL_HOST_DESKTOP_ANDROID_H_ +#define CHROME_BROWSER_CONTEXTUAL_TASKS_ANDROID_CONTEXTUAL_TASKS_PANEL_HOST_DESKTOP_ANDROID_H_ + +#include "base/android/scoped_java_ref.h" +#include "base/memory/raw_ptr.h" +#include "base/memory/weak_ptr.h" +#include "base/observer_list.h" +#include "base/scoped_observation.h" +#include "chrome/browser/context_sharing/tab_bottom_sheet/android/co_browse_views_bridge.h" +#include "chrome/browser/contextual_tasks/contextual_tasks_panel_host.h" +#include "chrome/browser/ui/side_panel/side_panel_entry_observer.h" +#include "chrome/browser/ui/side_panel/side_panel_native_view.h" +#include "content/public/browser/web_contents_delegate.h" + +class BrowserWindowInterface; +class SidePanelEntry; +class SidePanelEntryScope; +class SidePanelUI; + +namespace content { +class WebContents; +} + +namespace contextual_tasks { + +// Host class for the Contextual Tasks side panel on Android Desktop. +// This class manages the lifecycle of the side panel entry, creates the +// CoBrowse view, and handles communication between the side panel and the +// browser window. +class ContextualTasksPanelHostDesktopAndroid + : public ContextualTasksPanelHost, + public SidePanelEntryObserver, + public content::WebContentsDelegate { + public: + explicit ContextualTasksPanelHostDesktopAndroid( + BrowserWindowInterface* browser_window); + + ~ContextualTasksPanelHostDesktopAndroid() override; + + // ContextualTasksPanelHost implementation: + void AddObserver(ContextualTasksPanelHost::Observer* observer) override; + void RemoveObserver(ContextualTasksPanelHost::Observer* observer) override; + void Show(AnimationStyle animation) override; + void Close(AnimationStyle animation) override; + bool IsPanelInitialized() override; + bool IsPanelOpenForContextualTask() const override; + bool IsPanelSuppressed() const override; + void SetPanelSuppressedForTesting(bool suppressed) override; + content::WebContents* GetWebContents() override; + void SetWebContents(content::WebContents* web_contents) override; + + // SidePanelEntryObserver implementation: + void OnEntryHiddenWithReason(SidePanelEntry* entry, + SidePanelEntryHideReason reason) override; + void OnEntryShown(SidePanelEntry* entry) override; + + // content::WebContentsDelegate implementation: + content::WebContents* OpenURLFromTab( + content::WebContents* source, + const content::OpenURLParams& params, + base::OnceCallback<void(content::NavigationHandle&)> + navigation_handle_callback) override; + + private: + void MaybeRegisterEntry(); + SidePanelUI* GetSidePanelUI() const; + SidePanelNativeView CreateView(SidePanelEntryScope& scope); + + // Ensures that the CoBrowseViewsBridge is created lazily. + // + // It will attempt to get the active tab from the browser window to initialize + // the bridge. Returns true if the bridge exists. + bool MaybeCreateBridge(); + + void NotifySurfaceStateChanged( + ContextualTasksPanelHost::SurfaceState state, + ContextualTasksPanelHost::StateChangeReason reason); + + const raw_ptr<BrowserWindowInterface> browser_window_; + base::ObserverList<ContextualTasksPanelHost::Observer> observers_; + + // We use ScopedObservation to safely detach as an observer when the host + // is torn down. + base::ScopedObservation<SidePanelEntry, SidePanelEntryObserver> + side_panel_entry_observation_{this}; + + raw_ptr<content::WebContents> web_contents_ = nullptr; + base::WeakPtr<tabs::TabInterface> tab_ref_; + std::unique_ptr<context_sharing::CoBrowseViewsBridge> co_browse_views_bridge_; + + bool suppressed_for_testing_ = false; + bool is_open_ = false; + + base::WeakPtrFactory<ContextualTasksPanelHostDesktopAndroid> weak_factory_{ + this}; +}; + +} // namespace contextual_tasks + +#endif // CHROME_BROWSER_CONTEXTUAL_TASKS_ANDROID_CONTEXTUAL_TASKS_PANEL_HOST_DESKTOP_ANDROID_H_
diff --git a/chrome/browser/contextual_tasks/android/java/src/org/chromium/chrome/browser/contextual_tasks/ContextualTasksBridge.java b/chrome/browser/contextual_tasks/android/java/src/org/chromium/chrome/browser/contextual_tasks/ContextualTasksBridge.java index 63d88b1..2656746 100644 --- a/chrome/browser/contextual_tasks/android/java/src/org/chromium/chrome/browser/contextual_tasks/ContextualTasksBridge.java +++ b/chrome/browser/contextual_tasks/android/java/src/org/chromium/chrome/browser/contextual_tasks/ContextualTasksBridge.java
@@ -13,6 +13,10 @@ import org.jni_zero.NativeMethods; import org.chromium.base.ContextUtils; +import org.chromium.base.UnownedUserDataKey; +import org.chromium.base.supplier.MonotonicObservableSupplier; +import org.chromium.base.supplier.ObservableSuppliers; +import org.chromium.base.supplier.SettableMonotonicObservableSupplier; import org.chromium.build.annotations.NullMarked; import org.chromium.build.annotations.Nullable; import org.chromium.chrome.browser.contextual_tasks.fusebox.ContextualTasksFuseboxManager; @@ -26,6 +30,7 @@ import org.chromium.chrome.browser.ui.messages.snackbar.SnackbarManagerProvider; import org.chromium.content_public.browser.WebContents; import org.chromium.ui.base.ActivityWindowAndroid; +import org.chromium.ui.base.WindowAndroid; import org.chromium.url.GURL; /** @@ -35,6 +40,10 @@ @JNINamespace("contextual_tasks") @NullMarked public class ContextualTasksBridge implements ChromeAndroidTaskFeature { + private static final UnownedUserDataKey< + SettableMonotonicObservableSupplier<ContextualTasksBridge>> + SUPPLIER_KEY = new UnownedUserDataKey<>(); + private final Profile mProfile; private final ActivityWindowAndroid mWindowAndroid; private long mNativeContextualTasksBridge; @@ -54,11 +63,31 @@ mWindowAndroid = windowAndroid; } + /** + * Returns the {@link MonotonicObservableSupplier} for {@link ContextualTasksBridge} from the + * given {@link WindowAndroid}. + */ + public static MonotonicObservableSupplier<ContextualTasksBridge> getSupplier( + WindowAndroid windowAndroid) { + SettableMonotonicObservableSupplier<ContextualTasksBridge> supplier = + SUPPLIER_KEY.retrieveDataFromHost(windowAndroid.getUnownedUserDataHost()); + if (supplier == null) { + supplier = ObservableSuppliers.createMonotonic(); + SUPPLIER_KEY.attachToHost(windowAndroid.getUnownedUserDataHost(), supplier); + } + return supplier; + } + @Override public void onAddedToTask(long nativeBrowserWindowPtr) { if (nativeBrowserWindowPtr == 0) return; mNativeContextualTasksBridge = ContextualTasksBridgeJni.get().init(this, nativeBrowserWindowPtr, mProfile); + + SettableMonotonicObservableSupplier<ContextualTasksBridge> supplier = + (SettableMonotonicObservableSupplier<ContextualTasksBridge>) + getSupplier(mWindowAndroid); + supplier.set(this); } @Override
diff --git a/chrome/browser/contextual_tasks/android/java/src/org/chromium/chrome/browser/contextual_tasks/ContextualTasksBridgeUnitTest.java b/chrome/browser/contextual_tasks/android/java/src/org/chromium/chrome/browser/contextual_tasks/ContextualTasksBridgeUnitTest.java index 1e174a2..b1cdb57 100644 --- a/chrome/browser/contextual_tasks/android/java/src/org/chromium/chrome/browser/contextual_tasks/ContextualTasksBridgeUnitTest.java +++ b/chrome/browser/contextual_tasks/android/java/src/org/chromium/chrome/browser/contextual_tasks/ContextualTasksBridgeUnitTest.java
@@ -65,11 +65,12 @@ ContextualTasksBridgeJni.setInstanceForTesting(mMockJni); when(mMockJni.init(any(), eq(TEST_NATIVE_BROWSER_WINDOW_INTERFACE_PTR), eq(mProfile))) .thenReturn(TEST_NATIVE_BRIDGE_PTR); + when(mWindowAndroid.getUnownedUserDataHost()).thenReturn(mUserDataHost); + when(mWindowAndroid.getActivity()).thenReturn(new WeakReference<>(mMockActivity)); + mBridge = new ContextualTasksBridge(mProfile, mWindowAndroid); mBridge.onAddedToTask(TEST_NATIVE_BROWSER_WINDOW_INTERFACE_PTR); - when(mWindowAndroid.getUnownedUserDataHost()).thenReturn(mUserDataHost); - when(mWindowAndroid.getActivity()).thenReturn(new WeakReference<>(mMockActivity)); HelpAndFeedbackLauncherFactory.setInstanceForTesting(mMockHelpAndFeedbackLauncher); }
diff --git a/chrome/browser/contextual_tasks/fusebox/android/java/src/org/chromium/chrome/browser/contextual_tasks/fusebox/ContextualTasksFusebox.java b/chrome/browser/contextual_tasks/fusebox/android/java/src/org/chromium/chrome/browser/contextual_tasks/fusebox/ContextualTasksFusebox.java index 7ccf577e..3096196 100644 --- a/chrome/browser/contextual_tasks/fusebox/android/java/src/org/chromium/chrome/browser/contextual_tasks/fusebox/ContextualTasksFusebox.java +++ b/chrome/browser/contextual_tasks/fusebox/android/java/src/org/chromium/chrome/browser/contextual_tasks/fusebox/ContextualTasksFusebox.java
@@ -20,13 +20,13 @@ import org.chromium.chrome.browser.omnibox.OmniboxStub; import org.chromium.chrome.browser.omnibox.UrlFocusChangeListener; import org.chromium.chrome.browser.omnibox.fusebox.ComposeboxQueryControllerBridge; +import org.chromium.chrome.browser.omnibox.fusebox.FuseboxCoordinator; import org.chromium.chrome.browser.omnibox.suggestions.action.OmniboxActionDelegateImpl; import org.chromium.chrome.browser.profiles.Profile; import org.chromium.chrome.browser.tabmodel.TabModelSelector; import org.chromium.chrome.browser.tabmodel.TabModelSelectorSupplier; import org.chromium.chrome.browser.ui.edge_to_edge.NoOpTopInsetProvider; import org.chromium.chrome.browser.ui.messages.snackbar.SnackbarManager; -import org.chromium.components.omnibox.AutocompleteRequestType; import org.chromium.ui.base.WindowAndroid; /** The fusebox (omnibox) component for contextual tasks. */ @@ -145,14 +145,10 @@ new UrlFocusChangeListener() { @Override public void onUrlFocusChange(boolean hasFocus) { - if (hasFocus) { - // If user clicked/tapped, ensure we transition to Expanded - // (AI_MODE). - var session = mDataProvider.getFuseboxSessionState(); - if (session != null) { - session.getAutocompleteInput() - .setRequestType(AutocompleteRequestType.AI_MODE); - } + FuseboxCoordinator coordinator = + mLocationBarCoordinator.getFuseboxCoordinator(); + if (coordinator != null) { + coordinator.onContextualTaskFocusChanged(hasFocus); } } });
diff --git a/chrome/browser/contextual_tasks/fusebox/android/java/src/org/chromium/chrome/browser/contextual_tasks/fusebox/ContextualTasksFuseboxDataProvider.java b/chrome/browser/contextual_tasks/fusebox/android/java/src/org/chromium/chrome/browser/contextual_tasks/fusebox/ContextualTasksFuseboxDataProvider.java index e83192b..65daaf64 100644 --- a/chrome/browser/contextual_tasks/fusebox/android/java/src/org/chromium/chrome/browser/contextual_tasks/fusebox/ContextualTasksFuseboxDataProvider.java +++ b/chrome/browser/contextual_tasks/fusebox/android/java/src/org/chromium/chrome/browser/contextual_tasks/fusebox/ContextualTasksFuseboxDataProvider.java
@@ -25,6 +25,7 @@ import org.chromium.chrome.browser.tab.Tab; import org.chromium.components.browser_ui.styles.ChromeColors; import org.chromium.components.metrics.OmniboxEventProtos.OmniboxEventProto.PageClassification; +import org.chromium.components.omnibox.AutocompleteRequestType; import org.chromium.components.security_state.ConnectionMaliciousContentStatus; import org.chromium.components.security_state.ConnectionSecurityLevel; import org.chromium.content_public.browser.WebContents; @@ -120,6 +121,11 @@ } @Override + public @AutocompleteRequestType int getDefaultRequestType() { + return AutocompleteRequestType.AI_MODE; + } + + @Override public @Nullable FuseboxSessionState getFuseboxSessionState() { return mFuseboxSessionState; }
diff --git a/chrome/browser/contextual_tasks/fusebox/android/java/src/org/chromium/chrome/browser/contextual_tasks/fusebox/ContextualTasksSessionState.java b/chrome/browser/contextual_tasks/fusebox/android/java/src/org/chromium/chrome/browser/contextual_tasks/fusebox/ContextualTasksSessionState.java index 0c5579f..cad6897 100644 --- a/chrome/browser/contextual_tasks/fusebox/android/java/src/org/chromium/chrome/browser/contextual_tasks/fusebox/ContextualTasksSessionState.java +++ b/chrome/browser/contextual_tasks/fusebox/android/java/src/org/chromium/chrome/browser/contextual_tasks/fusebox/ContextualTasksSessionState.java
@@ -43,6 +43,11 @@ } @Override + public boolean isContextualTasksState() { + return true; + } + + @Override public void deactivate() { // No-op: Prevent LocationBarMediator from clearing the session when focus is lost. // Contextual Tasks manages its own session lifecycle in ContextualTasksFuseboxManager.
diff --git a/chrome/browser/contextual_tasks/search_ai_mode_promo_tab_helper.cc b/chrome/browser/contextual_tasks/search_ai_mode_promo_tab_helper.cc index db03919..151d6c5 100644 --- a/chrome/browser/contextual_tasks/search_ai_mode_promo_tab_helper.cc +++ b/chrome/browser/contextual_tasks/search_ai_mode_promo_tab_helper.cc
@@ -22,7 +22,6 @@ #include "chrome/browser/signin/identity_manager_factory.h" #include "chrome/browser/signin/signin_promo_util.h" #include "chrome/browser/ui/browser.h" -#include "chrome/browser/ui/browser_finder.h" #include "chrome/browser/ui/browser_window/public/browser_window_interface.h" #include "chrome/browser/ui/browser_window/public/global_browser_collection.h" #include "chrome/browser/ui/views/frame/browser_view.h"
diff --git a/chrome/browser/device_reauth/DEPS b/chrome/browser/device_reauth/DEPS index edc1c89..869d11c2 100644 --- a/chrome/browser/device_reauth/DEPS +++ b/chrome/browser/device_reauth/DEPS
@@ -13,7 +13,6 @@ "+chrome/browser/profiles/profile_keyed_service_factory.h", "+chrome/browser/profiles/profile_manager.h", "+chrome/browser/ui/browser.h", - "+chrome/browser/ui/browser_finder.h", "+chrome/browser/ui/browser_window.h", "+chrome/browser/ui/browser_window/public/browser_window_interface.h", "+chrome/browser/ui/browser_window/public/global_browser_collection.h",
diff --git a/chrome/browser/devtools/chrome_devtools_manager_delegate.cc b/chrome/browser/devtools/chrome_devtools_manager_delegate.cc index f59cce1..5d248f03 100644 --- a/chrome/browser/devtools/chrome_devtools_manager_delegate.cc +++ b/chrome/browser/devtools/chrome_devtools_manager_delegate.cc
@@ -29,7 +29,6 @@ #include "chrome/browser/profiles/keep_alive/profile_keep_alive_types.h" #include "chrome/browser/profiles/profile.h" #include "chrome/browser/profiles/profile_manager.h" -#include "chrome/browser/ui/browser_finder.h" #include "chrome/browser/ui/browser_window/public/browser_window_interface.h" #include "chrome/browser/ui/browser_window/public/global_browser_collection.h" #include "chrome/browser/ui/navigator/browser_navigator.h"
diff --git a/chrome/browser/devtools/devtools_browsertest.cc b/chrome/browser/devtools/devtools_browsertest.cc index ba8d0dc..8e143721 100644 --- a/chrome/browser/devtools/devtools_browsertest.cc +++ b/chrome/browser/devtools/devtools_browsertest.cc
@@ -1335,8 +1335,10 @@ EXPECT_EQ(extensions_instance->GetProcess(), data_frame_rfh->GetSiteInstance()->GetProcess()); - EXPECT_EQ(web_url.GetHost(), - web_frame_rfh->GetSiteInstance()->GetSiteURL().GetHost()); + EXPECT_EQ(web_url.GetHost(), web_frame_rfh->GetSiteInstance() + ->GetSecurityPrincipal() + .GetDeprecatedSiteURL() + .GetHost()); EXPECT_NE(devtools_instance, web_frame_rfh->GetSiteInstance()); EXPECT_NE(extensions_instance, web_frame_rfh->GetSiteInstance()); @@ -1354,8 +1356,10 @@ web_frame_rfh = ChildFrameAt(panel_frame_rfh, 2); EXPECT_EQ(about_blank_url, web_frame_rfh->GetLastCommittedURL()); - EXPECT_EQ(web_url.GetHost(), - web_frame_rfh->GetSiteInstance()->GetSiteURL().GetHost()); + EXPECT_EQ(web_url.GetHost(), web_frame_rfh->GetSiteInstance() + ->GetSecurityPrincipal() + .GetDeprecatedSiteURL() + .GetHost()); EXPECT_NE(devtools_instance, web_frame_rfh->GetSiteInstance()); EXPECT_NE(extensions_instance, web_frame_rfh->GetSiteInstance()); @@ -1452,8 +1456,10 @@ devtools_extension_devtools_page_rfh->GetSiteInstance()); EXPECT_EQ(extensions_instance, devtools_sidebar_pane_extension_rfh->GetSiteInstance()); - EXPECT_EQ(web_url.GetHost(), - http_iframe_rfh->GetSiteInstance()->GetSiteURL().GetHost()); + EXPECT_EQ(web_url.GetHost(), http_iframe_rfh->GetSiteInstance() + ->GetSecurityPrincipal() + .GetDeprecatedSiteURL() + .GetHost()); EXPECT_NE(devtools_instance, http_iframe_rfh->GetSiteInstance()); EXPECT_NE(extensions_instance, http_iframe_rfh->GetSiteInstance()); } @@ -1527,8 +1533,10 @@ EXPECT_TRUE(devtools_instance->GetSecurityPrincipal().SchemeIs( content::kChromeDevToolsScheme)); EXPECT_NE(devtools_instance, extensions_instance); - EXPECT_EQ(web_url.GetHost(), - http_iframe_rfh->GetSiteInstance()->GetSiteURL().GetHost()); + EXPECT_EQ(web_url.GetHost(), http_iframe_rfh->GetSiteInstance() + ->GetSecurityPrincipal() + .GetDeprecatedSiteURL() + .GetHost()); EXPECT_NE(devtools_instance, http_iframe_rfh->GetSiteInstance()); EXPECT_NE(extensions_instance, http_iframe_rfh->GetSiteInstance()); } @@ -1600,7 +1608,9 @@ EXPECT_EQ(extensions_instance, devtools_extension_panel_rfh->GetSiteInstance()); EXPECT_EQ(non_dt_extension_test_url.DeprecatedGetOriginAsURL(), - non_devtools_extension_rfh->GetSiteInstance()->GetSiteURL()); + non_devtools_extension_rfh->GetSiteInstance() + ->GetSecurityPrincipal() + .GetDeprecatedSiteURL()); EXPECT_NE(devtools_instance, non_devtools_extension_rfh->GetSiteInstance()); EXPECT_NE(extensions_instance, non_devtools_extension_rfh->GetSiteInstance()); }
diff --git a/chrome/browser/devtools/devtools_connection_dialog.cc b/chrome/browser/devtools/devtools_connection_dialog.cc index a30a6c1..e97a7fd 100644 --- a/chrome/browser/devtools/devtools_connection_dialog.cc +++ b/chrome/browser/devtools/devtools_connection_dialog.cc
@@ -8,7 +8,6 @@ #include "base/functional/callback_helpers.h" #include "build/build_config.h" #include "chrome/browser/ui/browser.h" -#include "chrome/browser/ui/browser_finder.h" #include "chrome/browser/ui/browser_window.h" #include "chrome/browser/ui/dialogs/browser_dialogs.h" #include "chrome/browser/ui/navigator/browser_navigator.h"
diff --git a/chrome/browser/devtools/devtools_remote_server_infobar_delegate.cc b/chrome/browser/devtools/devtools_remote_server_infobar_delegate.cc index 2fbdee5..7801cf1 100644 --- a/chrome/browser/devtools/devtools_remote_server_infobar_delegate.cc +++ b/chrome/browser/devtools/devtools_remote_server_infobar_delegate.cc
@@ -16,7 +16,6 @@ #include "base/time/time.h" #include "chrome/browser/devtools/global_confirm_info_bar.h" #include "chrome/browser/ui/browser.h" -#include "chrome/browser/ui/browser_finder.h" #include "chrome/browser/ui/navigator/browser_navigator.h" #include "chrome/browser/ui/navigator/browser_navigator_params.h" #include "chrome/grit/generated_resources.h"
diff --git a/chrome/browser/devtools/protocol/target_handler.cc b/chrome/browser/devtools/protocol/target_handler.cc index 919e4a8..e6f2e02 100644 --- a/chrome/browser/devtools/protocol/target_handler.cc +++ b/chrome/browser/devtools/protocol/target_handler.cc
@@ -12,7 +12,6 @@ #include "chrome/browser/devtools/devtools_browser_context_manager.h" #include "chrome/browser/profiles/profile_manager.h" #include "chrome/browser/ui/browser.h" -#include "chrome/browser/ui/browser_finder.h" #include "chrome/browser/ui/browser_window.h" #include "chrome/browser/ui/browser_window/public/browser_window_features.h" #include "chrome/browser/ui/browser_window/public/browser_window_interface_iterator.h"
diff --git a/chrome/browser/download/android/BUILD.gn b/chrome/browser/download/android/BUILD.gn index bc3349b..de50cc4 100644 --- a/chrome/browser/download/android/BUILD.gn +++ b/chrome/browser/download/android/BUILD.gn
@@ -83,6 +83,7 @@ "//chrome/browser/settings:java", "//chrome/browser/tab:java", "//chrome/browser/ui/android/pdf:java", + "//chrome/browser/ui/android/pdf:pdf_utils_java", "//chrome/browser/ui/messages/android:java", "//chrome/browser/util:java", "//components/browser_ui/modaldialog/android:java",
diff --git a/chrome/browser/download/android/java/src/org/chromium/chrome/browser/download/settings/DownloadDirectoryAdapter.java b/chrome/browser/download/android/java/src/org/chromium/chrome/browser/download/settings/DownloadDirectoryAdapter.java index fb3883e..8800b9f 100644 --- a/chrome/browser/download/android/java/src/org/chromium/chrome/browser/download/settings/DownloadDirectoryAdapter.java +++ b/chrome/browser/download/android/java/src/org/chromium/chrome/browser/download/settings/DownloadDirectoryAdapter.java
@@ -109,7 +109,7 @@ DirectoryOption directoryOption = (DirectoryOption) getItem(position); if (directoryOption == null) return view; - TextView titleText = (TextView) view.findViewById(R.id.title); + TextView titleText = view.findViewById(R.id.title); titleText.setText(directoryOption.name); // ModalDialogView may do a measure pass on the view hierarchy to limit the layout inside @@ -134,8 +134,8 @@ DirectoryOption directoryOption = (DirectoryOption) getItem(position); if (directoryOption == null) return view; - TextView titleText = (TextView) view.findViewById(R.id.title); - TextView summaryText = (TextView) view.findViewById(R.id.description); + TextView titleText = view.findViewById(R.id.title); + TextView summaryText = view.findViewById(R.id.description); boolean enabled = isEnabled(position); titleText.setText(directoryOption.name);
diff --git a/chrome/browser/download/android/java/src/org/chromium/chrome/browser/download/settings/DownloadLocationPreferenceAdapter.java b/chrome/browser/download/android/java/src/org/chromium/chrome/browser/download/settings/DownloadLocationPreferenceAdapter.java index c1af8e3..c56f7e3 100644 --- a/chrome/browser/download/android/java/src/org/chromium/chrome/browser/download/settings/DownloadLocationPreferenceAdapter.java +++ b/chrome/browser/download/android/java/src/org/chromium/chrome/browser/download/settings/DownloadLocationPreferenceAdapter.java
@@ -55,10 +55,10 @@ DirectoryOption directoryOption = (DirectoryOption) getItem(position); if (directoryOption == null) return view; - TextView titleText = (TextView) view.findViewById(R.id.title); + TextView titleText = view.findViewById(R.id.title); titleText.setText(directoryOption.name); - TextView summaryText = (TextView) view.findViewById(R.id.description); + TextView summaryText = view.findViewById(R.id.description); if (isEnabled(position)) { String summary = StringUtils.getAvailableBytesForUi(
diff --git a/chrome/browser/download/download_commands.cc b/chrome/browser/download/download_commands.cc index 88661fa..650e7053 100644 --- a/chrome/browser/download/download_commands.cc +++ b/chrome/browser/download/download_commands.cc
@@ -29,7 +29,6 @@ #if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) || \ BUILDFLAG(IS_MAC) #include "chrome/browser/ui/browser.h" -#include "chrome/browser/ui/browser_finder.h" #include "chrome/browser/ui/scoped_tabbed_browser_displayer.h" #endif
diff --git a/chrome/browser/enterprise/BUILD.gn b/chrome/browser/enterprise/BUILD.gn index 9eaca7ec0..c073433 100644 --- a/chrome/browser/enterprise/BUILD.gn +++ b/chrome/browser/enterprise/BUILD.gn
@@ -62,6 +62,7 @@ "//components/enterprise/data_controls/core/browser", "//components/enterprise/data_protection", "//components/enterprise/encryption/cache", + "//components/enterprise/isolated_mode", "//components/enterprise/watermarking", "//components/keyed_service/content", "//components/keyed_service/core",
diff --git a/chrome/browser/enterprise/data_protection/data_protection_navigation_controller.cc b/chrome/browser/enterprise/data_protection/data_protection_navigation_controller.cc index 9b5ad1a..68d91ffc 100644 --- a/chrome/browser/enterprise/data_protection/data_protection_navigation_controller.cc +++ b/chrome/browser/enterprise/data_protection/data_protection_navigation_controller.cc
@@ -13,7 +13,6 @@ #include "chrome/browser/enterprise/data_protection/data_protection_navigation_observer.h" #include "chrome/browser/enterprise/watermark/settings.h" #include "chrome/browser/profiles/profile.h" -#include "chrome/browser/ui/browser_finder.h" #include "chrome/browser/ui/browser_window/public/browser_window_interface.h" #include "chrome/browser/ui/tabs/public/tab_features.h" #include "components/enterprise/connectors/core/reporting_constants.h"
diff --git a/chrome/browser/enterprise/data_protection/data_protection_ui_controller.cc b/chrome/browser/enterprise/data_protection/data_protection_ui_controller.cc index df08039..e76b91d 100644 --- a/chrome/browser/enterprise/data_protection/data_protection_ui_controller.cc +++ b/chrome/browser/enterprise/data_protection/data_protection_ui_controller.cc
@@ -12,7 +12,6 @@ #include "chrome/browser/enterprise/data_protection/data_protection_navigation_observer.h" #include "chrome/browser/enterprise/watermark/settings.h" #include "chrome/browser/profiles/profile.h" -#include "chrome/browser/ui/browser_finder.h" #include "chrome/browser/ui/browser_window/public/browser_window_interface.h" #include "chrome/browser/ui/tabs/public/tab_features.h" #include "chrome/browser/ui/views/frame/browser_view.h"
diff --git a/chrome/browser/enterprise/idle/idle_service.cc b/chrome/browser/enterprise/idle/idle_service.cc index 3bef898..45addfd 100644 --- a/chrome/browser/enterprise/idle/idle_service.cc +++ b/chrome/browser/enterprise/idle/idle_service.cc
@@ -21,7 +21,6 @@ #if !BUILDFLAG(IS_ANDROID) #include "chrome/browser/ui/browser.h" -#include "chrome/browser/ui/browser_finder.h" #include "chrome/browser/ui/browser_window/public/browser_collection_observer.h" #include "chrome/browser/ui/browser_window/public/browser_window_interface_iterator.h" #include "chrome/browser/ui/browser_window/public/profile_browser_collection.h"
diff --git a/chrome/browser/enterprise/reporting/security_reporting_browsertest.cc b/chrome/browser/enterprise/reporting/security_reporting_browsertest.cc index 46a4562e..45bbd32 100644 --- a/chrome/browser/enterprise/reporting/security_reporting_browsertest.cc +++ b/chrome/browser/enterprise/reporting/security_reporting_browsertest.cc
@@ -95,9 +95,16 @@ class SecurityReportingBrowserTest : public MixinBasedPlatformBrowserTest, - public testing::WithParamInterface<testing::tuple<bool, bool>> { + public testing::WithParamInterface<testing::tuple<bool, bool, bool>> { protected: SecurityReportingBrowserTest() { + if (are_policies_enabled()) { + scoped_feature_list_.InitAndEnableFeature( + enterprise_signals::features::kPolicyDataCollectionEnabled); + } else { + scoped_feature_list_.InitAndDisableFeature( + enterprise_signals::features::kPolicyDataCollectionEnabled); + } management_mixin_ = ManagementContextMixin::Create( &mixin_host_, this, { @@ -203,6 +210,9 @@ bool expect_signals_override_value = profile_type != em::ChromeProfileReportRequest::PROFILE_REPORT; + bool expect_base_profile_report = + profile_type != + em::ChromeProfileReportRequest::PROFILE_SECURITY_SIGNALS; EXPECT_EQ(profile_report_request.has_browser_device_identifier(), expect_signals_override_value); @@ -230,26 +240,29 @@ // `profile_signals_report` is a signals report only sub-proto. EXPECT_EQ(chrome_user_profile_info.has_profile_signals_report(), expect_signals_override_value); + EXPECT_EQ(chrome_user_profile_info.profile_id().empty(), + !expect_signals_override_value); - if (!expect_signals_override_value) { - return; + if (expect_signals_override_value) { + VerifyProfileSignalsReport( + chrome_user_profile_info.profile_signals_report(), GetProfile()); + + EXPECT_EQ(chrome_user_profile_info.profile_id(), + enterprise::ProfileIdServiceFactory::GetForProfile(GetProfile()) + ->GetProfileId() + .value()); } - VerifyProfileSignalsReport( - chrome_user_profile_info.profile_signals_report(), GetProfile()); - - ASSERT_FALSE(chrome_user_profile_info.profile_id().empty()); - - EXPECT_EQ(chrome_user_profile_info.profile_id(), - enterprise::ProfileIdServiceFactory::GetForProfile(GetProfile()) - ->GetProfileId() - .value()); - - EXPECT_GT(chrome_user_profile_info.chrome_policies_size(), 0); + if (expect_base_profile_report || are_policies_enabled()) { + EXPECT_GT(chrome_user_profile_info.chrome_policies_size(), 0); + } else { + EXPECT_EQ(chrome_user_profile_info.chrome_policies_size(), 0); + } } bool is_device_managed() { return testing::get<0>(GetParam()); } bool is_affiliated() { return testing::get<1>(GetParam()); } + bool are_policies_enabled() { return testing::get<2>(GetParam()); } bool can_collect_pii_signals() { return is_device_managed() && is_affiliated(); @@ -276,6 +289,7 @@ } private: + base::test::ScopedFeatureList scoped_feature_list_; base::HistogramTester histogram_tester_; std::unique_ptr<ManagementContextMixin> management_mixin_; }; @@ -348,13 +362,15 @@ ManagedDeviceCase, SecurityReportingBrowserTest, testing::Combine(/*is_device_managed=*/testing::Values(true), - /*is_affiliated=*/testing::Bool())); + /*is_affiliated=*/testing::Bool(), + /*are_policies_enabled=*/testing::Bool())); INSTANTIATE_TEST_SUITE_P( UnmanagedDeviceCase, SecurityReportingBrowserTest, testing::Combine(/*is_device_managed=*/testing::Values(false), - /*is_affiliated=*/testing::Values(false))); + /*is_affiliated=*/testing::Values(false), + /*are_policies_enabled=*/testing::Bool())); // Test that confirms the correct form of reports are being triggered. // Collection contexts such as management state don't affect the expectations so @@ -451,6 +467,16 @@ VerifyRequest(test_future.Get(), em::ChromeProfileReportRequest::PROFILE_SECURITY_SIGNALS); + if (!are_policies_enabled()) { + histogram_tester().ExpectBucketCount( + "Enterprise.SecurityReport.User.Trigger", SecurityReportTrigger::kTimer, + 1); + histogram_tester().ExpectBucketCount( + "Enterprise.SecurityReport.User.Trigger", + SecurityReportTrigger::kPolicyChange, 0); + return; + } + // This ensures the Network Service response has time to hop threads // and trigger the "OnReportUploaded" callback. { @@ -486,6 +512,7 @@ All, SecurityReportTriggerBrowserTest, testing::Combine(/*is_device_managed*/ ::testing::Values(false), - /*is_affiliated*/ ::testing::Values(false))); + /*is_affiliated*/ ::testing::Values(false), + /*are_policies_enabled=*/testing::Bool())); } // namespace enterprise_reporting
diff --git a/chrome/browser/enterprise/signin/oidc_authentication_signin_interceptor.cc b/chrome/browser/enterprise/signin/oidc_authentication_signin_interceptor.cc index 339ab5c..cad3ce9c 100644 --- a/chrome/browser/enterprise/signin/oidc_authentication_signin_interceptor.cc +++ b/chrome/browser/enterprise/signin/oidc_authentication_signin_interceptor.cc
@@ -34,7 +34,6 @@ #include "chrome/browser/themes/theme_service.h" #include "chrome/browser/themes/theme_service_factory.h" #include "chrome/browser/ui/browser.h" -#include "chrome/browser/ui/browser_finder.h" #include "chrome/browser/ui/navigator/browser_navigator.h" #include "chrome/browser/ui/navigator/browser_navigator_params.h" #include "chrome/browser/ui/profiles/profile_colors_util.h"
diff --git a/chrome/browser/enterprise/signin/oidc_authentication_signin_interceptor_browsertest.cc b/chrome/browser/enterprise/signin/oidc_authentication_signin_interceptor_browsertest.cc index e07190e..735fb4b2 100644 --- a/chrome/browser/enterprise/signin/oidc_authentication_signin_interceptor_browsertest.cc +++ b/chrome/browser/enterprise/signin/oidc_authentication_signin_interceptor_browsertest.cc
@@ -42,7 +42,6 @@ #include "chrome/browser/signin/identity_manager_factory.h" #include "chrome/browser/signin/identity_test_environment_profile_adaptor.h" #include "chrome/browser/signin/web_signin_interceptor.h" -#include "chrome/browser/ui/browser_finder.h" #include "chrome/browser/ui/browser_tabstrip.h" #include "chrome/browser/ui/signin/dice_web_signin_interceptor_delegate.h" #include "chrome/browser/ui/tabs/tab_strip_model.h"
diff --git a/chrome/browser/enterprise/signin/profile_token_web_signin_interceptor.cc b/chrome/browser/enterprise/signin/profile_token_web_signin_interceptor.cc index 39251b858..5a1b26c 100644 --- a/chrome/browser/enterprise/signin/profile_token_web_signin_interceptor.cc +++ b/chrome/browser/enterprise/signin/profile_token_web_signin_interceptor.cc
@@ -20,7 +20,6 @@ #include "chrome/browser/signin/identity_manager_factory.h" #include "chrome/browser/themes/theme_service.h" #include "chrome/browser/themes/theme_service_factory.h" -#include "chrome/browser/ui/browser_finder.h" #include "chrome/browser/ui/navigator/browser_navigator.h" #include "chrome/browser/ui/navigator/browser_navigator_params.h" #include "chrome/browser/ui/profiles/profile_colors_util.h"
diff --git a/chrome/browser/extensions/BUILD.gn b/chrome/browser/extensions/BUILD.gn index 4d6a25df..8a236dd 100644 --- a/chrome/browser/extensions/BUILD.gn +++ b/chrome/browser/extensions/BUILD.gn
@@ -538,7 +538,9 @@ "//chrome/browser/ssl", "//chrome/browser/tab_group_sync:factories", "//chrome/browser/tab_list", + "//chrome/browser/ui:accelerator_utils_headers", "//chrome/browser/ui:browser_element_identifiers", + "//chrome/browser/ui:simple_message_box_headers", "//chrome/browser/ui:ui_features", "//chrome/browser/ui/autofill", "//chrome/browser/ui/browser_window", @@ -667,6 +669,12 @@ "//chrome/android:chrome_jni_headers", "//chrome/browser/ui/android/tab_model", ] + + if (enable_desktop_android_extensions) { + # pack_extension_job.cc and developer_private_functions.cc include headers + # from android extension bridges under IS_ANDROID. + deps += [ "//chrome/browser/ui/android/extensions:public" ] + } } else { sources += [ "api/chrome_extensions_api_client_non_android.cc", @@ -2124,4 +2132,10 @@ deps += [ "//chrome/browser/extensions/api/mdns" ] } } + + if (enable_desktop_android_extensions) { + # developer_private_api_unittest.cc includes extension_util_bridge.h under + # IS_ANDROID. + deps += [ "//chrome/browser/ui/android/extensions:public" ] + } }
diff --git a/chrome/browser/extensions/api/chrome_extensions_api_client.cc b/chrome/browser/extensions/api/chrome_extensions_api_client.cc index 7aa8718..368d0d7dc 100644 --- a/chrome/browser/extensions/api/chrome_extensions_api_client.cc +++ b/chrome/browser/extensions/api/chrome_extensions_api_client.cc
@@ -155,8 +155,14 @@ // But we do still need to protect some sensitive sub-frame navigation // requests. // Exclude main frame navigation requests. + // TODO(crbug.com/379869738: Remove GetUnsafeValue once there is a better way + // to identify prefetch requests from the browser. Changing this to the + // correct code of `is_null()` breaks functionality as the magic value 0 is + // actually used for prefetches, even though it's usually used by the browser + // process. When uses are correctly ported to content::ChildProcessId we + // should be able to fix this. See also WebRequestPermissions::HideRequest. bool is_browser_request = - request.render_process_id == -1 && + request.global_id.child_id.GetUnsafeValue() == -1 && request.web_request_type != WebRequestResourceType::MAIN_FRAME; // Hide requests made by the Devtools frontend. @@ -183,8 +189,9 @@ ? InstantServiceFactory::GetForProfile(static_cast<Profile*>(context)) : nullptr; if (instant_service) { - is_sensitive_request |= - instant_service->IsInstantProcess(request.render_process_id); + // TODO(crbug.com/379869738): Remove GetUnsafeValue. + is_sensitive_request |= instant_service->IsInstantProcess( + request.global_id.child_id.GetUnsafeValue()); } #endif // !BUILDFLAG(IS_ANDROID)
diff --git a/chrome/browser/extensions/api/chrome_extensions_api_client_unittest.cc b/chrome/browser/extensions/api/chrome_extensions_api_client_unittest.cc index ce8db98..cd260ac 100644 --- a/chrome/browser/extensions/api/chrome_extensions_api_client_unittest.cc +++ b/chrome/browser/extensions/api/chrome_extensions_api_client_unittest.cc
@@ -49,7 +49,7 @@ request_params.url = GURL("https://example.com/script.js"); request_params.initiator = url::Origin::Create(chrome::ChromeUINewTabURLAsGURL()); - request_params.render_process_id = -1; + request_params.global_id = content::GlobalRenderFrameHostId(); request_params.web_request_type = web_request_type; return request_params; }; @@ -68,7 +68,8 @@ // Similar requests made by the renderer should be visible to extensions. WebRequestInfoInitParams params = create_params(WebRequestResourceType::SCRIPT); - params.render_process_id = 2; + params.global_id = content::GlobalRenderFrameHostId( + content::ChildProcessId(2), IPC::mojom::kRoutingIdNone); EXPECT_FALSE(client.ShouldHideBrowserNetworkRequest( nullptr /* context */, WebRequestInfo(std::move(params)))); }
diff --git a/chrome/browser/extensions/api/debugger/debugger_api.cc b/chrome/browser/extensions/api/debugger/debugger_api.cc index 3e79f27..f69e9e3 100644 --- a/chrome/browser/extensions/api/debugger/debugger_api.cc +++ b/chrome/browser/extensions/api/debugger/debugger_api.cc
@@ -37,6 +37,7 @@ #include "content/public/browser/devtools_agent_host.h" #include "content/public/browser/navigation_entry.h" #include "content/public/browser/render_process_host.h" +#include "content/public/browser/security_principal.h" #include "content/public/browser/web_contents.h" #include "content/public/common/url_constants.h" #include "extensions/browser/event_router.h" @@ -260,8 +261,9 @@ if (chrome_pdf::features::IsOopifPdfEnabled() && (IsPdfExtensionOrigin( render_frame_host->GetLastCommittedOrigin()) || - IsPdfExtensionUrl( - render_frame_host->GetSiteInstance()->GetSiteURL()))) { + IsPdfExtensionUrl(render_frame_host->GetSiteInstance() + ->GetSecurityPrincipal() + .GetDeprecatedSiteURL()))) { return content::RenderFrameHost::FrameIterationAction::kContinue; } #endif // BUILDFLAG(ENABLE_PDF) @@ -280,8 +282,10 @@ render_frame_host->GetLastCommittedURL(), &page_url, error) || !ExtensionMayAttachToURLOrInnerURL( extension, extension_profile, - render_frame_host->GetSiteInstance()->GetSiteURL(), &page_url, - error)) { + render_frame_host->GetSiteInstance() + ->GetSecurityPrincipal() + .GetDeprecatedSiteURL(), + &page_url, error)) { result = false; return content::RenderFrameHost::FrameIterationAction::kStop; }
diff --git a/chrome/browser/extensions/api/declarative_net_request/action_tracker_unittest.cc b/chrome/browser/extensions/api/declarative_net_request/action_tracker_unittest.cc index 36a0e619..2640bb6 100644 --- a/chrome/browser/extensions/api/declarative_net_request/action_tracker_unittest.cc +++ b/chrome/browser/extensions/api/declarative_net_request/action_tracker_unittest.cc
@@ -90,11 +90,11 @@ std::string_view url, WebRequestResourceType web_request_type, int tab_id) { - const int kRendererId = 1; + const content::ChildProcessId kRendererId(1); WebRequestInfoInitParams info; info.url = GURL(url); info.web_request_type = web_request_type; - info.render_process_id = kRendererId; + info.global_id.child_id = kRendererId; info.frame_data.tab_id = tab_id; if (web_request_type == WebRequestResourceType::MAIN_FRAME) {
diff --git a/chrome/browser/extensions/api/declarative_net_request/ruleset_manager_unittest.cc b/chrome/browser/extensions/api/declarative_net_request/ruleset_manager_unittest.cc index be69a9f..3cb38df2 100644 --- a/chrome/browser/extensions/api/declarative_net_request/ruleset_manager_unittest.cc +++ b/chrome/browser/extensions/api/declarative_net_request/ruleset_manager_unittest.cc
@@ -143,11 +143,11 @@ std::string_view url, std::optional<url::Origin> initiator = std::nullopt, WebRequestResourceType web_request_type = WebRequestResourceType::OTHER) { - const int kRendererId = 1; + const content::ChildProcessId kRendererId(1); WebRequestInfoInitParams info; info.url = GURL(url); info.method = net::HttpRequestHeaders::kGetMethod; - info.render_process_id = kRendererId; + info.global_id.child_id = kRendererId; info.initiator = std::move(initiator); info.web_request_type = web_request_type; return info; @@ -158,11 +158,11 @@ WebRequestInfoInitParams GetRequestParamsForURLWithHeaders( std::string_view url, const std::vector<std::string>& request_headers) { - const int kRendererId = 1; + const content::ChildProcessId kRendererId(1); WebRequestInfoInitParams info; info.url = GURL(url); info.method = net::HttpRequestHeaders::kGetMethod; - info.render_process_id = kRendererId; + info.global_id.child_id = kRendererId; net::HttpRequestHeaders extra_request_headers; for (const auto& header : request_headers) {
diff --git a/chrome/browser/extensions/api/declarative_webrequest/webrequest_action_unittest.cc b/chrome/browser/extensions/api/declarative_webrequest/webrequest_action_unittest.cc index d4acda9..78c976b 100644 --- a/chrome/browser/extensions/api/declarative_webrequest/webrequest_action_unittest.cc +++ b/chrome/browser/extensions/api/declarative_webrequest/webrequest_action_unittest.cc
@@ -116,14 +116,14 @@ const std::string& extension_id, const WebRequestActionSet* action_set, RequestStage stage) { - const int kRendererId = 2; + const content::ChildProcessId kRendererId(2); EventResponseDeltas deltas; scoped_refptr<net::HttpResponseHeaders> headers( new net::HttpResponseHeaders("")); WebRequestInfoInitParams params; params.url = GURL(url_string); WebRequestInfoInitParams request_params(std::move(params)); - request_params.render_process_id = kRendererId; + request_params.global_id.child_id = kRendererId; WebRequestInfo request_info(std::move(request_params)); WebRequestData request_data(&request_info, stage, headers.get()); std::set<std::string> ignored_tags;
diff --git a/chrome/browser/extensions/api/document_scan/BUILD.gn b/chrome/browser/extensions/api/document_scan/BUILD.gn index 510cb5e0..beed98c8 100644 --- a/chrome/browser/extensions/api/document_scan/BUILD.gn +++ b/chrome/browser/extensions/api/document_scan/BUILD.gn
@@ -31,18 +31,15 @@ public_deps = [ "//base", "//chrome/common/extensions/api", - "//chromeos/crosapi/mojom", "//content/public/browser", "//extensions/browser", "//extensions/common", - "//mojo/public/cpp/bindings:bindings_base", "//ui/gfx", ] deps = [ "//build:chromeos_buildflags", "//chrome/browser:browser_public_dependencies", - "//chrome/browser/ash/crosapi", "//chrome/browser/ash/scanning", "//chrome/browser/extensions", "//chrome/browser/profiles:profile",
diff --git a/chrome/browser/extensions/api/document_scan/document_scan_api.cc b/chrome/browser/extensions/api/document_scan/document_scan_api.cc index 7ba5c3d..faa6bc3 100644 --- a/chrome/browser/extensions/api/document_scan/document_scan_api.cc +++ b/chrome/browser/extensions/api/document_scan/document_scan_api.cc
@@ -10,7 +10,6 @@ #include <vector> #include "base/functional/bind.h" -#include "chrome/browser/ash/crosapi/document_scan_ash.h" #include "chrome/browser/extensions/api/document_scan/document_scan_api_handler.h" #include "chrome/browser/extensions/chrome_extension_function_details.h"
diff --git a/chrome/browser/extensions/api/document_scan/document_scan_type_converters.cc b/chrome/browser/extensions/api/document_scan/document_scan_type_converters.cc index 49b7ec7..48c9605 100644 --- a/chrome/browser/extensions/api/document_scan/document_scan_type_converters.cc +++ b/chrome/browser/extensions/api/document_scan/document_scan_type_converters.cc
@@ -15,10 +15,218 @@ namespace extensions::api::document_scan { namespace { -ScannerOption ConvertLorgnetteScannerOption( - const lorgnette::ScannerOption& input); + +OptionType ConvertLorgnetteOptionType(const lorgnette::OptionType& input) { + switch (input) { + case lorgnette::TYPE_UNKNOWN: + return OptionType::kUnknown; + case lorgnette::TYPE_BOOL: + return OptionType::kBool; + case lorgnette::TYPE_INT: + return OptionType::kInt; + case lorgnette::TYPE_FIXED: + return OptionType::kFixed; + case lorgnette::TYPE_STRING: + return OptionType::kString; + case lorgnette::TYPE_BUTTON: + return OptionType::kButton; + case lorgnette::TYPE_GROUP: + return OptionType::kGroup; + case lorgnette::OptionType_INT_MIN_SENTINEL_DO_NOT_USE_: + case lorgnette::OptionType_INT_MAX_SENTINEL_DO_NOT_USE_: + break; + } + NOTREACHED(); } +OptionUnit ConvertLorgnetteOptionUnit(const lorgnette::OptionUnit& input) { + switch (input) { + case lorgnette::UNIT_NONE: + return OptionUnit::kUnitless; + case lorgnette::UNIT_PIXEL: + return OptionUnit::kPixel; + case lorgnette::UNIT_BIT: + return OptionUnit::kBit; + case lorgnette::UNIT_MM: + return OptionUnit::kMm; + case lorgnette::UNIT_DPI: + return OptionUnit::kDpi; + case lorgnette::UNIT_PERCENT: + return OptionUnit::kPercent; + case lorgnette::UNIT_MICROSECOND: + return OptionUnit::kMicrosecond; + case lorgnette::OptionUnit_INT_MIN_SENTINEL_DO_NOT_USE_: + case lorgnette::OptionUnit_INT_MAX_SENTINEL_DO_NOT_USE_: + break; + } + NOTREACHED(); +} + +ConstraintType ConvertLorgnetteOptionConstraintType( + const lorgnette::OptionConstraint_ConstraintType& input) { + switch (input) { + case lorgnette::OptionConstraint::CONSTRAINT_NONE: + return ConstraintType::kNone; + case lorgnette::OptionConstraint::CONSTRAINT_INT_RANGE: + return ConstraintType::kIntRange; + case lorgnette::OptionConstraint::CONSTRAINT_FIXED_RANGE: + return ConstraintType::kFixedRange; + case lorgnette::OptionConstraint::CONSTRAINT_INT_LIST: + return ConstraintType::kIntList; + case lorgnette::OptionConstraint::CONSTRAINT_FIXED_LIST: + return ConstraintType::kFixedList; + case lorgnette::OptionConstraint::CONSTRAINT_STRING_LIST: + return ConstraintType::kStringList; + case lorgnette:: + OptionConstraint_ConstraintType_OptionConstraint_ConstraintType_INT_MIN_SENTINEL_DO_NOT_USE_: + case lorgnette:: + OptionConstraint_ConstraintType_OptionConstraint_ConstraintType_INT_MAX_SENTINEL_DO_NOT_USE_: + break; + } + NOTREACHED(); +} + +OptionConstraint ConvertLorgnetteOptionConstraint( + const lorgnette::OptionConstraint& input) { + document_scan::OptionConstraint output; + output.type = ConvertLorgnetteOptionConstraintType(input.constraint_type()); + switch (output.type) { + case ConstraintType::kNone: + break; + case ConstraintType::kIntRange: { + if (!input.has_int_range()) { + LOG(WARNING) << "OptionConstraint has type INT_RANGE but does not " + "contain a valid int_range"; + } + auto& input_range = input.int_range(); + output.min.emplace(); + output.min->as_integer = input_range.min(); + output.max.emplace(); + output.max->as_integer = input_range.max(); + output.quant.emplace(); + output.quant->as_integer = input_range.quant(); + break; + } + case ConstraintType::kFixedRange: { + if (!input.has_fixed_range()) { + LOG(WARNING) << "OptionConstraint has type FIXED_RANGE but does not " + "contain a valid fixed_range"; + } + auto& input_range = input.fixed_range(); + output.min.emplace(); + output.min->as_number = input_range.min(); + output.max.emplace(); + output.max->as_number = input_range.max(); + output.quant.emplace(); + output.quant->as_number = input_range.quant(); + break; + } + case ConstraintType::kIntList: + if (input.valid_int().empty()) { + LOG(WARNING) << "OptionConstraint has type INT_LIST but does not " + "contain a valid valid_int"; + } + output.list.emplace(); + output.list->as_integers.emplace(input.valid_int().begin(), + input.valid_int().end()); + break; + case ConstraintType::kFixedList: + if (input.valid_fixed().empty()) { + LOG(WARNING) << "OptionConstraint has type FIXED_LIST but does not " + "contain a valid valid_fixed"; + } + output.list.emplace(); + output.list->as_numbers.emplace(input.valid_fixed().begin(), + input.valid_fixed().end()); + break; + case ConstraintType::kStringList: + if (input.valid_string().empty()) { + LOG(WARNING) << "OptionConstraint has type STRING_LIST but does not " + "contain a valid valid_string"; + } + output.list.emplace(); + output.list->as_strings.emplace(input.valid_string().begin(), + input.valid_string().end()); + break; + } + return output; +} + +std::optional<ScannerOption::Value> GetLorgnetteOptionValue( + const lorgnette::ScannerOption& option) { + std::optional<ScannerOption::Value> result; + switch (ConvertLorgnetteOptionType(option.option_type())) { + case OptionType::kNone: + case OptionType::kUnknown: + case OptionType::kButton: + case OptionType::kGroup: + break; + case OptionType::kBool: + if (option.has_bool_value()) { + result.emplace(); + result->as_boolean.emplace(option.bool_value()); + } + break; + case OptionType::kInt: + if (option.has_int_value()) { + result.emplace(); + if (option.int_value().value_size() == 1) { + result->as_integer.emplace(option.int_value().value(0)); + } else { + result->as_integers.emplace(option.int_value().value().begin(), + option.int_value().value().end()); + } + } + break; + case OptionType::kFixed: + if (option.has_fixed_value()) { + result.emplace(); + if (option.fixed_value().value_size() == 1) { + result->as_number.emplace(option.fixed_value().value(0)); + } else { + result->as_numbers.emplace(option.fixed_value().value().begin(), + option.fixed_value().value().end()); + } + } + break; + case OptionType::kString: + if (option.has_string_value()) { + result.emplace(); + result->as_string.emplace(option.string_value()); + } + break; + } + return result; +} + +ScannerOption ConvertLorgnetteScannerOption( + const lorgnette::ScannerOption& input) { + ScannerOption output; + output.name = input.name(); + output.title = input.title(); + output.description = input.description(); + output.type = ConvertLorgnetteOptionType(input.option_type()); + output.unit = ConvertLorgnetteOptionUnit(input.unit()); + output.value = GetLorgnetteOptionValue(input); + output.constraint = + input.has_constraint() + ? std::optional(ConvertLorgnetteOptionConstraint(input.constraint())) + : std::nullopt; + output.is_detectable = input.detectable(); + output.configurability = + input.sw_settable() ? Configurability::kSoftwareConfigurable + : input.hw_settable() ? Configurability::kHardwareConfigurable + : Configurability::kNotConfigurable; + output.is_auto_settable = input.auto_settable(); + output.is_emulated = input.emulated(); + output.is_active = input.active(); + output.is_advanced = input.advanced(); + output.is_internal = false; + return output; +} + +} // namespace + OperationResult ConvertLorgnetteOperationResult( lorgnette::OperationResult input) { switch (input) { @@ -253,287 +461,6 @@ return option; } -} // namespace extensions::api::document_scan - -namespace mojo { - -namespace document_scan = extensions::api::document_scan; -namespace mojom = crosapi::mojom; - -document_scan::OperationResult -TypeConverter<document_scan::OperationResult, mojom::ScannerOperationResult>:: - Convert(mojom::ScannerOperationResult input) { - switch (input) { - case mojom::ScannerOperationResult::kUnknown: - return document_scan::OperationResult::kUnknown; - case mojom::ScannerOperationResult::kSuccess: - return document_scan::OperationResult::kSuccess; - case mojom::ScannerOperationResult::kUnsupported: - return document_scan::OperationResult::kUnsupported; - case mojom::ScannerOperationResult::kCancelled: - return document_scan::OperationResult::kCancelled; - case mojom::ScannerOperationResult::kDeviceBusy: - return document_scan::OperationResult::kDeviceBusy; - case mojom::ScannerOperationResult::kInvalid: - return document_scan::OperationResult::kInvalid; - case mojom::ScannerOperationResult::kWrongType: - return document_scan::OperationResult::kWrongType; - case mojom::ScannerOperationResult::kEndOfData: - return document_scan::OperationResult::kEof; - case mojom::ScannerOperationResult::kAdfJammed: - return document_scan::OperationResult::kAdfJammed; - case mojom::ScannerOperationResult::kAdfEmpty: - return document_scan::OperationResult::kAdfEmpty; - case mojom::ScannerOperationResult::kCoverOpen: - return document_scan::OperationResult::kCoverOpen; - case mojom::ScannerOperationResult::kIoError: - return document_scan::OperationResult::kIoError; - case mojom::ScannerOperationResult::kAccessDenied: - return document_scan::OperationResult::kAccessDenied; - case mojom::ScannerOperationResult::kNoMemory: - return document_scan::OperationResult::kNoMemory; - case mojom::ScannerOperationResult::kDeviceUnreachable: - return document_scan::OperationResult::kUnreachable; - case mojom::ScannerOperationResult::kDeviceMissing: - return document_scan::OperationResult::kMissing; - case mojom::ScannerOperationResult::kInternalError: - return document_scan::OperationResult::kInternalError; - } -} - -template <> -struct TypeConverter<document_scan::ConnectionType, - mojom::ScannerInfo_ConnectionType> { - static document_scan::ConnectionType Convert( - mojom::ScannerInfo_ConnectionType input) { - switch (input) { - case mojom::ScannerInfo_ConnectionType::kUnspecified: - return document_scan::ConnectionType::kUnspecified; - case mojom::ScannerInfo_ConnectionType::kUsb: - return document_scan::ConnectionType::kUsb; - case mojom::ScannerInfo_ConnectionType::kNetwork: - return document_scan::ConnectionType::kNetwork; - } - } -}; - -} // namespace mojo - -namespace extensions::api::document_scan { - -namespace { - -OptionType ConvertLorgnetteOptionType(const lorgnette::OptionType& input) { - switch (input) { - case lorgnette::TYPE_UNKNOWN: - return OptionType::kUnknown; - case lorgnette::TYPE_BOOL: - return OptionType::kBool; - case lorgnette::TYPE_INT: - return OptionType::kInt; - case lorgnette::TYPE_FIXED: - return OptionType::kFixed; - case lorgnette::TYPE_STRING: - return OptionType::kString; - case lorgnette::TYPE_BUTTON: - return OptionType::kButton; - case lorgnette::TYPE_GROUP: - return OptionType::kGroup; - case lorgnette::OptionType_INT_MIN_SENTINEL_DO_NOT_USE_: - case lorgnette::OptionType_INT_MAX_SENTINEL_DO_NOT_USE_: - break; - } - NOTREACHED(); -} - -OptionUnit ConvertLorgnetteOptionUnit(const lorgnette::OptionUnit& input) { - switch (input) { - case lorgnette::UNIT_NONE: - return OptionUnit::kUnitless; - case lorgnette::UNIT_PIXEL: - return OptionUnit::kPixel; - case lorgnette::UNIT_BIT: - return OptionUnit::kBit; - case lorgnette::UNIT_MM: - return OptionUnit::kMm; - case lorgnette::UNIT_DPI: - return OptionUnit::kDpi; - case lorgnette::UNIT_PERCENT: - return OptionUnit::kPercent; - case lorgnette::UNIT_MICROSECOND: - return OptionUnit::kMicrosecond; - case lorgnette::OptionUnit_INT_MIN_SENTINEL_DO_NOT_USE_: - case lorgnette::OptionUnit_INT_MAX_SENTINEL_DO_NOT_USE_: - break; - } - NOTREACHED(); -} - -ConstraintType ConvertLorgnetteOptionConstraintType( - const lorgnette::OptionConstraint_ConstraintType& input) { - switch (input) { - case lorgnette::OptionConstraint::CONSTRAINT_NONE: - return ConstraintType::kNone; - case lorgnette::OptionConstraint::CONSTRAINT_INT_RANGE: - return ConstraintType::kIntRange; - case lorgnette::OptionConstraint::CONSTRAINT_FIXED_RANGE: - return ConstraintType::kFixedRange; - case lorgnette::OptionConstraint::CONSTRAINT_INT_LIST: - return ConstraintType::kIntList; - case lorgnette::OptionConstraint::CONSTRAINT_FIXED_LIST: - return ConstraintType::kFixedList; - case lorgnette::OptionConstraint::CONSTRAINT_STRING_LIST: - return ConstraintType::kStringList; - case lorgnette:: - OptionConstraint_ConstraintType_OptionConstraint_ConstraintType_INT_MIN_SENTINEL_DO_NOT_USE_: - case lorgnette:: - OptionConstraint_ConstraintType_OptionConstraint_ConstraintType_INT_MAX_SENTINEL_DO_NOT_USE_: - break; - } - NOTREACHED(); -} - -OptionConstraint ConvertLorgnetteOptionConstraint( - const lorgnette::OptionConstraint& input) { - document_scan::OptionConstraint output; - output.type = ConvertLorgnetteOptionConstraintType(input.constraint_type()); - switch (output.type) { - case ConstraintType::kNone: - break; - case ConstraintType::kIntRange: { - if (!input.has_int_range()) { - LOG(WARNING) << "OptionConstraint has type INT_RANGE but does not " - "contain a valid int_range"; - } - auto& input_range = input.int_range(); - output.min.emplace(); - output.min->as_integer = input_range.min(); - output.max.emplace(); - output.max->as_integer = input_range.max(); - output.quant.emplace(); - output.quant->as_integer = input_range.quant(); - break; - } - case ConstraintType::kFixedRange: { - if (!input.has_fixed_range()) { - LOG(WARNING) << "OptionConstraint has type FIXED_RANGE but does not " - "contain a valid fixed_range"; - } - auto& input_range = input.fixed_range(); - output.min.emplace(); - output.min->as_number = input_range.min(); - output.max.emplace(); - output.max->as_number = input_range.max(); - output.quant.emplace(); - output.quant->as_number = input_range.quant(); - break; - } - case ConstraintType::kIntList: - if (input.valid_int().empty()) { - LOG(WARNING) << "OptionConstraint has type INT_LIST but does not " - "contain a valid valid_int"; - } - output.list.emplace(); - output.list->as_integers.emplace(input.valid_int().begin(), - input.valid_int().end()); - break; - case ConstraintType::kFixedList: - if (input.valid_fixed().empty()) { - LOG(WARNING) << "OptionConstraint has type FIXED_LIST but does not " - "contain a valid valid_fixed"; - } - output.list.emplace(); - output.list->as_numbers.emplace(input.valid_fixed().begin(), - input.valid_fixed().end()); - break; - case ConstraintType::kStringList: - if (input.valid_string().empty()) { - LOG(WARNING) << "OptionConstraint has type STRING_LIST but does not " - "contain a valid valid_string"; - } - output.list.emplace(); - output.list->as_strings.emplace(input.valid_string().begin(), - input.valid_string().end()); - break; - } - return output; -} - -std::optional<ScannerOption::Value> GetLorgnetteOptionValue( - const lorgnette::ScannerOption& option) { - std::optional<ScannerOption::Value> result; - switch (ConvertLorgnetteOptionType(option.option_type())) { - case OptionType::kNone: - case OptionType::kUnknown: - case OptionType::kButton: - case OptionType::kGroup: - break; - case OptionType::kBool: - if (option.has_bool_value()) { - result.emplace(); - result->as_boolean.emplace(option.bool_value()); - } - break; - case OptionType::kInt: - if (option.has_int_value()) { - result.emplace(); - if (option.int_value().value_size() == 1) { - result->as_integer.emplace(option.int_value().value(0)); - } else { - result->as_integers.emplace(option.int_value().value().begin(), - option.int_value().value().end()); - } - } - break; - case OptionType::kFixed: - if (option.has_fixed_value()) { - result.emplace(); - if (option.fixed_value().value_size() == 1) { - result->as_number.emplace(option.fixed_value().value(0)); - } else { - result->as_numbers.emplace(option.fixed_value().value().begin(), - option.fixed_value().value().end()); - } - } - break; - case OptionType::kString: - if (option.has_string_value()) { - result.emplace(); - result->as_string.emplace(option.string_value()); - } - break; - } - return result; -} - -ScannerOption ConvertLorgnetteScannerOption( - const lorgnette::ScannerOption& input) { - ScannerOption output; - output.name = input.name(); - output.title = input.title(); - output.description = input.description(); - output.type = ConvertLorgnetteOptionType(input.option_type()); - output.unit = ConvertLorgnetteOptionUnit(input.unit()); - output.value = GetLorgnetteOptionValue(input); - output.constraint = - input.has_constraint() - ? std::optional(ConvertLorgnetteOptionConstraint(input.constraint())) - : std::nullopt; - output.is_detectable = input.detectable(); - output.configurability = - input.sw_settable() ? Configurability::kSoftwareConfigurable - : input.hw_settable() ? Configurability::kHardwareConfigurable - : Configurability::kNotConfigurable; - output.is_auto_settable = input.auto_settable(); - output.is_emulated = input.emulated(); - output.is_active = input.active(); - output.is_advanced = input.advanced(); - output.is_internal = false; - return output; -} - -} // namespace - OpenScannerResponse ConvertLorgnetteOpenScannerResponse( const lorgnette::OpenScannerResponse& input) { OpenScannerResponse output; @@ -587,61 +514,3 @@ } } // namespace extensions::api::document_scan - -namespace mojo { - -crosapi::mojom::ScannerEnumFilterPtr -TypeConverter<crosapi::mojom::ScannerEnumFilterPtr, - extensions::api::document_scan::DeviceFilter>:: - Convert(const extensions::api::document_scan::DeviceFilter& input) { - auto output = crosapi::mojom::ScannerEnumFilter::New(); - output->local = input.local.value_or(false); - output->secure = input.secure.value_or(false); - return output; -} - -extensions::api::document_scan::GetScannerListResponse -TypeConverter<extensions::api::document_scan::GetScannerListResponse, - crosapi::mojom::GetScannerListResponsePtr>:: - Convert(const crosapi::mojom::GetScannerListResponsePtr& input) { - document_scan::GetScannerListResponse output; - output.result = ConvertTo<document_scan::OperationResult>(input->result); - for (const mojom::ScannerInfoPtr& scanner_in : input->scanners) { - document_scan::ScannerInfo& scanner_out = output.scanners.emplace_back(); - scanner_out.scanner_id = scanner_in->id; - scanner_out.name = scanner_in->display_name; - scanner_out.manufacturer = scanner_in->manufacturer; - scanner_out.model = scanner_in->model; - scanner_out.device_uuid = scanner_in->device_uuid; - scanner_out.connection_type = - ConvertTo<document_scan::ConnectionType>(scanner_in->connection_type); - scanner_out.secure = scanner_in->secure; - scanner_out.image_formats = scanner_in->image_formats; - scanner_out.protocol_type = scanner_in->protocol_type.value_or(""); - } - return output; -} - -extensions::api::document_scan::GetOptionGroupsResponse -TypeConverter<extensions::api::document_scan::GetOptionGroupsResponse, - crosapi::mojom::GetOptionGroupsResponsePtr>:: - Convert(const crosapi::mojom::GetOptionGroupsResponsePtr& input) { - document_scan::GetOptionGroupsResponse output; - output.scanner_handle = input->scanner_handle; - output.result = ConvertTo<document_scan::OperationResult>(input->result); - if (!input->groups.has_value()) { - return output; - } - - output.groups.emplace(); - output.groups->reserve(input->groups.value().size()); - for (const mojom::OptionGroupPtr& group_in : input->groups.value()) { - document_scan::OptionGroup& group_out = output.groups->emplace_back(); - group_out.title = group_in->title; - group_out.members = std::vector<std::string>(group_in->members.begin(), - group_in->members.end()); - } - return output; -} - -} // namespace mojo
diff --git a/chrome/browser/extensions/api/document_scan/document_scan_type_converters.h b/chrome/browser/extensions/api/document_scan/document_scan_type_converters.h index 7d352359..86a496f 100644 --- a/chrome/browser/extensions/api/document_scan/document_scan_type_converters.h +++ b/chrome/browser/extensions/api/document_scan/document_scan_type_converters.h
@@ -7,10 +7,8 @@ #include <optional> -#include "chrome/browser/ash/crosapi/document_scan_ash_type_converters.h" #include "chrome/common/extensions/api/document_scan.h" -#include "chromeos/crosapi/mojom/document_scan.mojom.h" -#include "mojo/public/cpp/bindings/type_converter.h" +#include "chromeos/ash/components/dbus/lorgnette/lorgnette_service.pb.h" namespace lorgnette { class CancelScanResponse; @@ -81,36 +79,4 @@ } // namespace extensions::api::document_scan -namespace mojo { - -template <> -struct TypeConverter<extensions::api::document_scan::OperationResult, - crosapi::mojom::ScannerOperationResult> { - static extensions::api::document_scan::OperationResult Convert( - crosapi::mojom::ScannerOperationResult input); -}; - -template <> -struct TypeConverter<crosapi::mojom::ScannerEnumFilterPtr, - extensions::api::document_scan::DeviceFilter> { - static crosapi::mojom::ScannerEnumFilterPtr Convert( - const extensions::api::document_scan::DeviceFilter& input); -}; - -template <> -struct TypeConverter<extensions::api::document_scan::GetScannerListResponse, - crosapi::mojom::GetScannerListResponsePtr> { - static extensions::api::document_scan::GetScannerListResponse Convert( - const crosapi::mojom::GetScannerListResponsePtr& input); -}; - -template <> -struct TypeConverter<extensions::api::document_scan::GetOptionGroupsResponse, - crosapi::mojom::GetOptionGroupsResponsePtr> { - static extensions::api::document_scan::GetOptionGroupsResponse Convert( - const crosapi::mojom::GetOptionGroupsResponsePtr& input); -}; - -} // namespace mojo - #endif // CHROME_BROWSER_EXTENSIONS_API_DOCUMENT_SCAN_DOCUMENT_SCAN_TYPE_CONVERTERS_H_
diff --git a/chrome/browser/extensions/api/document_scan/document_scan_type_converters_unittest.cc b/chrome/browser/extensions/api/document_scan/document_scan_type_converters_unittest.cc index 824da02..8edccaed 100644 --- a/chrome/browser/extensions/api/document_scan/document_scan_type_converters_unittest.cc +++ b/chrome/browser/extensions/api/document_scan/document_scan_type_converters_unittest.cc
@@ -7,16 +7,13 @@ #include <algorithm> #include <optional> -#include "chrome/browser/ash/crosapi/document_scan_ash_type_converters.h" #include "chrome/browser/extensions/api/document_scan/document_scan_test_utils.h" #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" -namespace mojo { namespace { namespace document_scan = extensions::api::document_scan; -namespace mojom = crosapi::mojom; using ::testing::ElementsAre; using ::testing::ElementsAreArray; @@ -30,60 +27,6 @@ return it != results.end() ? std::make_optional(it->result) : std::nullopt; } -TEST(DocumentScanTypeConvertersTest, OperationResult) { - EXPECT_EQ(ConvertTo<document_scan::OperationResult>( - mojom::ScannerOperationResult::kUnknown), - document_scan::OperationResult::kUnknown); - EXPECT_EQ(ConvertTo<document_scan::OperationResult>( - mojom::ScannerOperationResult::kSuccess), - document_scan::OperationResult::kSuccess); - EXPECT_EQ(ConvertTo<document_scan::OperationResult>( - mojom::ScannerOperationResult::kUnsupported), - document_scan::OperationResult::kUnsupported); - EXPECT_EQ(ConvertTo<document_scan::OperationResult>( - mojom::ScannerOperationResult::kCancelled), - document_scan::OperationResult::kCancelled); - EXPECT_EQ(ConvertTo<document_scan::OperationResult>( - mojom::ScannerOperationResult::kDeviceBusy), - document_scan::OperationResult::kDeviceBusy); - EXPECT_EQ(ConvertTo<document_scan::OperationResult>( - mojom::ScannerOperationResult::kInvalid), - document_scan::OperationResult::kInvalid); - EXPECT_EQ(ConvertTo<document_scan::OperationResult>( - mojom::ScannerOperationResult::kWrongType), - document_scan::OperationResult::kWrongType); - EXPECT_EQ(ConvertTo<document_scan::OperationResult>( - mojom::ScannerOperationResult::kEndOfData), - document_scan::OperationResult::kEof); - EXPECT_EQ(ConvertTo<document_scan::OperationResult>( - mojom::ScannerOperationResult::kAdfJammed), - document_scan::OperationResult::kAdfJammed); - EXPECT_EQ(ConvertTo<document_scan::OperationResult>( - mojom::ScannerOperationResult::kAdfEmpty), - document_scan::OperationResult::kAdfEmpty); - EXPECT_EQ(ConvertTo<document_scan::OperationResult>( - mojom::ScannerOperationResult::kCoverOpen), - document_scan::OperationResult::kCoverOpen); - EXPECT_EQ(ConvertTo<document_scan::OperationResult>( - mojom::ScannerOperationResult::kIoError), - document_scan::OperationResult::kIoError); - EXPECT_EQ(ConvertTo<document_scan::OperationResult>( - mojom::ScannerOperationResult::kAccessDenied), - document_scan::OperationResult::kAccessDenied); - EXPECT_EQ(ConvertTo<document_scan::OperationResult>( - mojom::ScannerOperationResult::kNoMemory), - document_scan::OperationResult::kNoMemory); - EXPECT_EQ(ConvertTo<document_scan::OperationResult>( - mojom::ScannerOperationResult::kDeviceUnreachable), - document_scan::OperationResult::kUnreachable); - EXPECT_EQ(ConvertTo<document_scan::OperationResult>( - mojom::ScannerOperationResult::kDeviceMissing), - document_scan::OperationResult::kMissing); - EXPECT_EQ(ConvertTo<document_scan::OperationResult>( - mojom::ScannerOperationResult::kInternalError), - document_scan::OperationResult::kInternalError); -} - TEST(DocumentScanTypeConvertersTest, OptionType) { EXPECT_EQ(document_scan::ConvertLorgnetteOptionTypeForTesting( lorgnette::TYPE_UNKNOWN), @@ -445,98 +388,7 @@ EXPECT_FALSE(output.is_internal); } -TEST(DocumentScanTypeConvertersTest, DeviceFilter_Empty) { - document_scan::DeviceFilter input; - auto output = mojom::ScannerEnumFilter::From(input); - EXPECT_FALSE(output->local); - EXPECT_FALSE(output->secure); -} -TEST(DocumentScanTypeConvertersTest, DeviceFilter_Local) { - document_scan::DeviceFilter input; - input.local = true; - auto output = mojom::ScannerEnumFilter::From(input); - EXPECT_TRUE(output->local); - EXPECT_FALSE(output->secure); -} - -TEST(DocumentScanTypeConvertersTest, DeviceFilter_Secure) { - document_scan::DeviceFilter input; - input.secure = true; - auto output = mojom::ScannerEnumFilter::From(input); - EXPECT_FALSE(output->local); - EXPECT_TRUE(output->secure); -} - -TEST(DocumentScanTypeConvertersTest, GetScannerListResponse_Empty) { - auto input = mojom::GetScannerListResponse::New(); - auto output = input.To<document_scan::GetScannerListResponse>(); - EXPECT_EQ(output.result, document_scan::OperationResult::kUnknown); - EXPECT_EQ(output.scanners.size(), 0U); -} - -TEST(DocumentScanTypeConvertersTest, GetScannerListResponse_Usb) { - auto input = mojom::GetScannerListResponse::New(); - input->result = mojom::ScannerOperationResult::kSuccess; - auto scanner_in = mojom::ScannerInfo::New(); - scanner_in->id = "12345"; - scanner_in->display_name = "12345 (USB)"; - scanner_in->manufacturer = "GoogleTest"; - scanner_in->model = "USB Scanner"; - scanner_in->device_uuid = "56789"; - scanner_in->connection_type = mojom::ScannerInfo_ConnectionType::kUsb; - scanner_in->secure = true; - scanner_in->image_formats = {"image/png", "image/jpeg"}; - // scanner_in->protocol_type is unset. - input->scanners.emplace_back(std::move(scanner_in)); - - auto output = input.To<document_scan::GetScannerListResponse>(); - EXPECT_EQ(output.result, document_scan::OperationResult::kSuccess); - ASSERT_EQ(output.scanners.size(), 1U); - const document_scan::ScannerInfo& scanner_out = output.scanners[0]; - EXPECT_EQ(scanner_out.scanner_id, "12345"); - EXPECT_EQ(scanner_out.name, "12345 (USB)"); - EXPECT_EQ(scanner_out.manufacturer, "GoogleTest"); - EXPECT_EQ(scanner_out.model, "USB Scanner"); - EXPECT_EQ(scanner_out.device_uuid, "56789"); - EXPECT_EQ(scanner_out.connection_type, document_scan::ConnectionType::kUsb); - EXPECT_EQ(scanner_out.secure, true); - EXPECT_THAT(scanner_out.image_formats, - UnorderedElementsAre("image/png", "image/jpeg")); - EXPECT_EQ(scanner_out.protocol_type, ""); -} - -TEST(DocumentScanTypeConvertersTest, GetScannerListResponse_Network) { - auto input = mojom::GetScannerListResponse::New(); - input->result = mojom::ScannerOperationResult::kNoMemory; - auto scanner_in = mojom::ScannerInfo::New(); - scanner_in->id = "12345"; - scanner_in->display_name = "12345"; - scanner_in->manufacturer = "GoogleTest"; - scanner_in->model = "Network Scanner"; - scanner_in->device_uuid = "56789"; - scanner_in->connection_type = mojom::ScannerInfo_ConnectionType::kNetwork; - scanner_in->secure = true; - scanner_in->image_formats = {"image/png", "image/jpeg"}; - scanner_in->protocol_type = "protocol_type"; - input->scanners.emplace_back(std::move(scanner_in)); - - auto output = input.To<document_scan::GetScannerListResponse>(); - EXPECT_EQ(output.result, document_scan::OperationResult::kNoMemory); - ASSERT_EQ(output.scanners.size(), 1U); - const document_scan::ScannerInfo& scanner_out = output.scanners[0]; - EXPECT_EQ(scanner_out.scanner_id, "12345"); - EXPECT_EQ(scanner_out.name, "12345"); - EXPECT_EQ(scanner_out.manufacturer, "GoogleTest"); - EXPECT_EQ(scanner_out.model, "Network Scanner"); - EXPECT_EQ(scanner_out.device_uuid, "56789"); - EXPECT_EQ(scanner_out.connection_type, - document_scan::ConnectionType::kNetwork); - EXPECT_EQ(scanner_out.secure, true); - EXPECT_THAT(scanner_out.image_formats, - UnorderedElementsAre("image/png", "image/jpeg")); - EXPECT_EQ(scanner_out.protocol_type, "protocol_type"); -} TEST(DocumentScanTypeConvertersTest, OpenScannerResponse_Empty) { lorgnette::OpenScannerResponse input; @@ -924,4 +776,3 @@ } } // namespace -} // namespace mojo
diff --git a/chrome/browser/extensions/api/document_scan/fake_document_scan_ash.cc b/chrome/browser/extensions/api/document_scan/fake_document_scan_ash.cc deleted file mode 100644 index 30f89eb7..0000000 --- a/chrome/browser/extensions/api/document_scan/fake_document_scan_ash.cc +++ /dev/null
@@ -1,37 +0,0 @@ -// Copyright 2023 The Chromium Authors -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "chrome/browser/extensions/api/document_scan/fake_document_scan_ash.h" - -#include <utility> - -#include "base/check.h" -#include "base/notimplemented.h" -#include "base/strings/stringprintf.h" -#include "chrome/browser/extensions/api/document_scan/document_scan_test_utils.h" - -namespace extensions { - -FakeDocumentScanAsh::FakeDocumentScanAsh() = default; -FakeDocumentScanAsh::~FakeDocumentScanAsh() = default; - -FakeDocumentScanAsh::OpenScannerState::OpenScannerState() = default; -FakeDocumentScanAsh::OpenScannerState::~OpenScannerState() = default; - -FakeDocumentScanAsh::OpenScannerState::OpenScannerState( - const std::string& client_id, - const std::string& connection_string) - : client_id(client_id), connection_string(connection_string) {} - -void FakeDocumentScanAsh::CloseScanner(const std::string& scanner_handle) { - open_scanners_.erase(scanner_handle); -} - -void FakeDocumentScanAsh::SetOpenScannerResponse( - const std::string& connection_string, - crosapi::mojom::OpenScannerResponsePtr response) { - open_responses_[connection_string] = std::move(response); -} - -} // namespace extensions
diff --git a/chrome/browser/extensions/api/document_scan/fake_document_scan_ash.h b/chrome/browser/extensions/api/document_scan/fake_document_scan_ash.h deleted file mode 100644 index 077386e..0000000 --- a/chrome/browser/extensions/api/document_scan/fake_document_scan_ash.h +++ /dev/null
@@ -1,53 +0,0 @@ -// Copyright 2023 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_EXTENSIONS_API_DOCUMENT_SCAN_FAKE_DOCUMENT_SCAN_ASH_H_ -#define CHROME_BROWSER_EXTENSIONS_API_DOCUMENT_SCAN_FAKE_DOCUMENT_SCAN_ASH_H_ - -#include <map> -#include <optional> -#include <string> -#include <vector> - -#include "chromeos/crosapi/mojom/document_scan.mojom.h" - -namespace extensions { - -// Fake implementation of DocumentScan that doesn't send D-Bus calls to -// lorgnette. -class FakeDocumentScanAsh : public crosapi::mojom::DocumentScan { - public: - FakeDocumentScanAsh(); - FakeDocumentScanAsh(const FakeDocumentScanAsh&) = delete; - FakeDocumentScanAsh(const FakeDocumentScanAsh&&) = delete; - ~FakeDocumentScanAsh() override; - - void CloseScanner(const std::string& scanner_handle); - - void SetOpenScannerResponse(const std::string& connection_string, - crosapi::mojom::OpenScannerResponsePtr response); - - private: - struct OpenScannerState { - OpenScannerState(); - OpenScannerState(const std::string& client_id, - const std::string& connection_string); - ~OpenScannerState(); - - std::string client_id; - std::string connection_string; - }; - - // Map from connection strings to the OpenScannerResponsePtr that should be - // returned. - std::map<std::string, crosapi::mojom::OpenScannerResponsePtr> open_responses_; - - // Map from scanner handles to the original client and scanner used to create - // the handle. - std::map<std::string, OpenScannerState> open_scanners_; -}; - -} // namespace extensions - -#endif // CHROME_BROWSER_EXTENSIONS_API_DOCUMENT_SCAN_FAKE_DOCUMENT_SCAN_ASH_H_
diff --git a/chrome/browser/extensions/api/extension_action/browser_action_apitest.cc b/chrome/browser/extensions/api/extension_action/browser_action_apitest.cc index 4a66bd7..e193f80 100644 --- a/chrome/browser/extensions/api/extension_action/browser_action_apitest.cc +++ b/chrome/browser/extensions/api/extension_action/browser_action_apitest.cc
@@ -23,7 +23,6 @@ #include "chrome/browser/profiles/profile.h" #include "chrome/browser/ui/browser.h" #include "chrome/browser/ui/browser_commands.h" -#include "chrome/browser/ui/browser_finder.h" #include "chrome/browser/ui/browser_window.h" #include "chrome/browser/ui/navigator/browser_navigator_params.h" #include "chrome/browser/ui/toolbar/toolbar_action_view_model.h"
diff --git a/chrome/browser/extensions/api/glic_private/glic_private_api.cc b/chrome/browser/extensions/api/glic_private/glic_private_api.cc index e362b3b..7991949 100644 --- a/chrome/browser/extensions/api/glic_private/glic_private_api.cc +++ b/chrome/browser/extensions/api/glic_private/glic_private_api.cc
@@ -434,46 +434,45 @@ tabs::TabInterface* tab_interface = nullptr; - if (in_new_tab) { - // Navigate to a new tab. - NavigateParams navigate_params(profile, chrome::ChromeUINewTabURLAsGURL(), - ui::PAGE_TRANSITION_LINK); - bool open_in_foreground = - extensions_features::kGlicOpenNewTabInForegroundParam.Get(); - navigate_params.disposition = - open_in_foreground ? WindowOpenDisposition::NEW_FOREGROUND_TAB - : WindowOpenDisposition::NEW_BACKGROUND_TAB; - base::WeakPtr<content::NavigationHandle> navigation_handle = - Navigate(&navigate_params); - if (navigation_handle) { - tab_interface = tabs::TabInterface::MaybeGetFromContents( - navigation_handle->GetWebContents()); - } - } else { - content::RenderFrameHost* rfh = GetRfhForDocumentId(document_id); - if (!rfh) { - Respond(GetPromptResponseValueAndLog( - api::glic_private::ErrorCode::kLocalInvalidDocumentId)); - return; - } - - if (!IsAccountConsistent(IdentityManagerFactory::GetForProfile(profile), - *rfh)) { - Respond(GetPromptResponseValueAndLog( - api::glic_private::ErrorCode::kLocalAccountMismatch)); - return; - } - tab_interface = tabs::TabInterface::MaybeGetFromContents( - content::WebContents::FromRenderFrameHost(rfh)); + content::RenderFrameHost* rfh = GetRfhForDocumentId(document_id); + if (!rfh) { + Respond(GetPromptResponseValueAndLog( + api::glic_private::ErrorCode::kLocalInvalidDocumentId)); + return; } + if (!IsAccountConsistent(IdentityManagerFactory::GetForProfile(profile), + *rfh)) { + Respond(GetPromptResponseValueAndLog( + api::glic_private::ErrorCode::kLocalAccountMismatch)); + return; + } + + tab_interface = tabs::TabInterface::MaybeGetFromContents( + content::WebContents::FromRenderFrameHost(rfh)); if (!tab_interface) { Respond(GetPromptResponseValueAndLog( extensions::api::glic_private::ErrorCode::kLocalNoActiveTab)); return; } - options.target.surface = tab_interface; + if (in_new_tab) { + extensions_features::GlicOpenNewTabDisposition disposition = + extensions_features::kGlicOpenNewTabDispositionParam.Get(); + bool open_in_foreground = true; + if (disposition == + extensions_features::GlicOpenNewTabDisposition::kBackground) { + open_in_foreground = false; + } else if (disposition == extensions_features::GlicOpenNewTabDisposition:: + kForegroundIfNotConsented) { + open_in_foreground = + !CreateProfileState(profile).is_enabled_and_consented; + } + options.target.surface = glic::NewTab( + tab_interface->GetBrowserWindowInterface(), open_in_foreground); + } else { + options.target.surface = tab_interface; + } glic::GlicKeyedService* glic_service = glic::GlicKeyedServiceFactory::GetGlicKeyedService(profile,
diff --git a/chrome/browser/extensions/api/glic_private/glic_private_apitest.cc b/chrome/browser/extensions/api/glic_private/glic_private_apitest.cc index 78e566a2..e925351 100644 --- a/chrome/browser/extensions/api/glic_private/glic_private_apitest.cc +++ b/chrome/browser/extensions/api/glic_private/glic_private_apitest.cc
@@ -195,7 +195,7 @@ GlicPrivateApiNewTabInBackgroundTest() { scoped_feature_list_.InitAndEnableFeatureWithParameters( extensions_features::kApiGlicAccessFromGoogleWebpage, - {{"glic_open_new_tab_in_foreground", "false"}}); + {{"glic_open_new_tab_disposition", "background"}}); } private:
diff --git a/chrome/browser/extensions/api/notifications/extension_notification_handler.cc b/chrome/browser/extensions/api/notifications/extension_notification_handler.cc index fcbbed5..37cc9ddb 100644 --- a/chrome/browser/extensions/api/notifications/extension_notification_handler.cc +++ b/chrome/browser/extensions/api/notifications/extension_notification_handler.cc
@@ -13,9 +13,6 @@ #include "chrome/browser/notifications/notifier_state_tracker_factory.h" #include "chrome/browser/profiles/profile.h" #include "chrome/common/extensions/api/notifications.h" -#include "extensions/browser/app_window/app_window.h" -#include "extensions/browser/app_window/app_window_registry.h" -#include "extensions/browser/app_window/native_app_window.h" #include "extensions/buildflags/buildflags.h" #include "extensions/common/constants.h" #include "extensions/common/extension_id.h"
diff --git a/chrome/browser/extensions/api/tab_capture/tab_capture_api.cc b/chrome/browser/extensions/api/tab_capture/tab_capture_api.cc index 434c78d..77b95c4 100644 --- a/chrome/browser/extensions/api/tab_capture/tab_capture_api.cc +++ b/chrome/browser/extensions/api/tab_capture/tab_capture_api.cc
@@ -21,7 +21,6 @@ #include "chrome/browser/media/webrtc/capture_policy_utils.h" #include "chrome/browser/profiles/profile.h" #include "chrome/browser/tab_list/tab_list_interface.h" -#include "chrome/browser/ui/browser_finder.h" #include "chrome/browser/ui/browser_window/public/browser_window_features.h" #include "chrome/browser/ui/browser_window/public/browser_window_interface.h" #include "chrome/browser/ui/browser_window/public/browser_window_interface_iterator.h"
diff --git a/chrome/browser/extensions/api/tab_groups/tab_groups_api.cc b/chrome/browser/extensions/api/tab_groups/tab_groups_api.cc index 0a01f2a..89f64de2 100644 --- a/chrome/browser/extensions/api/tab_groups/tab_groups_api.cc +++ b/chrome/browser/extensions/api/tab_groups/tab_groups_api.cc
@@ -49,6 +49,7 @@ "Cannot move the group to an index that is in the middle of another group."; constexpr char kCannotMoveGroupIntoMiddleOfPinnedTabsError[] = "Cannot move the group to an index that is in the middle of pinned tabs."; +constexpr char kFailedToMoveGroupError[] = "Failed to move group."; // Returns true if a group could be moved into the |target_index| of the given // |tab_strip|. Sets the |error| string otherwise. @@ -334,6 +335,7 @@ if (cross_window) { // Cross window group moves are asynchronous on Android. OnTabGroupCreated() // will be called later when the group is created in the new window. + AddRef(); // Balanced in OnTabGroupCreated(). return RespondLater(); } #endif // BUILDFLAG(IS_ANDROID) @@ -504,8 +506,11 @@ // Pausing Saved Tab Groups is handled in TabListBridge on Win/Mac/Linux and // in MultiInstanceManagerApi31 on Android. - source_tab_list->MoveTabGroupToWindow(group, target_browser->GetSessionID(), - new_index); + if (!source_tab_list->MoveTabGroupToWindow( + group, target_browser->GetSessionID(), new_index)) { + *error = kFailedToMoveGroupError; + return false; + } return true; } @@ -517,6 +522,8 @@ auto group_object = ExtensionTabUtil::CreateTabGroupObject(group_id); CHECK(group_object); Respond(ArgumentList(api::tab_groups::Get::Results::Create(*group_object))); + + Release(); // Balanced in MoveGroup(). } #endif // BUILDFLAG(IS_ANDROID)
diff --git a/chrome/browser/extensions/api/tab_groups/tab_groups_api_apitest.cc b/chrome/browser/extensions/api/tab_groups/tab_groups_api_apitest.cc index 452d009c..c02792d7 100644 --- a/chrome/browser/extensions/api/tab_groups/tab_groups_api_apitest.cc +++ b/chrome/browser/extensions/api/tab_groups/tab_groups_api_apitest.cc
@@ -119,6 +119,13 @@ ASSERT_TRUE(RunExtensionTest("tab_groups/basics")) << message_; } +// Tests moving a tab group from one window to another using the JavaScript +// API. Regression test for https://crbug.com/509581460, which was a problem +// with ref-counting that only showed up in real JS tests. +IN_PROC_BROWSER_TEST_F(TabGroupsApiTest, MoveToWindow) { + ASSERT_TRUE(RunExtensionTest("tab_groups/move_to_window")) << message_; +} + // Tests that events are restricted to their respective browser contexts, // especially between on-the-record and off-the-record browsers. IN_PROC_BROWSER_TEST_F(TabGroupsApiTest, TestTabGroupEventsAcrossProfiles) {
diff --git a/chrome/browser/extensions/api/tabs/tabs_api.cc b/chrome/browser/extensions/api/tabs/tabs_api.cc index d5680fd..0801bd6 100644 --- a/chrome/browser/extensions/api/tabs/tabs_api.cc +++ b/chrome/browser/extensions/api/tabs/tabs_api.cc
@@ -4,6 +4,8 @@ #include "chrome/browser/extensions/api/tabs/tabs_api.h" +#include "base/metrics/histogram_functions.h" +#include "base/metrics/histogram_macros.h" #include "base/strings/pattern.h" #include "base/strings/string_number_conversions.h" #include "base/strings/utf_string_conversions.h" @@ -47,6 +49,7 @@ #include "components/zoom/zoom_controller.h" #include "content/public/browser/navigation_controller.h" #include "content/public/browser/navigation_entry.h" +#include "extensions/browser/extension_user_activation_service.h" #include "extensions/browser/extension_zoom_request_client.h" #include "extensions/browser/extensions_browser_client.h" #include "extensions/buildflags/buildflags.h" @@ -524,6 +527,138 @@ return split_tabs; } +// These values are persisted to logs. Entries should not be renumbered and +// numeric values should never be reused. +enum class UpdateActionType { + // Updates that are not redirects for the default search engine page. + kOtherUpdates = 0, + // Updates that are redirects for the default search engine page which are + // not a result of a user gesture. + kDSERedirectsWithoutUserGesture = 1, + // Updates that are redirects for the default search engine page after a user + // gesture has occurred. + kDSERedirectsWithUserGesture = 2, + // Updates that are redirects after the user has landed on the search engine + // results page (SERP) for a while. + kDSERedirectsAfterLandingOnSERP = 3, + // The maximum value of the UpdateActionType enum. + kMaxValue = kDSERedirectsAfterLandingOnSERP, +}; + +// These values are persisted to logs. Entries should not be renumbered and +// numeric values should never be reused. +enum class RemoveActionType { + // Removals that are not for the default search engine page. + kOtherRemovals = 0, + // Removals of the default search engine page which are not a result of a user + // gesture. + kDSERemovalsWithoutUserGesture = 1, + // Removals of the default search engine page after a user gesture has + // occurred. + kDSERemovalsWithUserGesture = 2, + // Removals of the default search engine page after the user has landed on the + // search engine results page (SERP) for a while. + kDSERemovalsAfterLandingOnSERP = 3, + // The maximum value of the RemoveActionType enum. + kMaxValue = kDSERemovalsAfterLandingOnSERP, +}; + +bool HasUserActivation(const ExtensionId& extension_id, + content::BrowserContext& browser_context, + content::RenderFrameHost* calling_render_frame_host, + content::WebContents& tab_web_contents, + bool extension_function_user_gesture) { + return extension_function_user_gesture || + ExtensionUserActivationService::Get(&browser_context) + ->HasTransientActivation(extension_id) || + (calling_render_frame_host && + calling_render_frame_host->HasTransientUserActivation()) || + (tab_web_contents.GetPrimaryMainFrame() && + tab_web_contents.GetPrimaryMainFrame()->HasTransientUserActivation()); +} + +void CheckDSERedirect(const ExtensionId& extension_id, + content::BrowserContext& browser_context, + content::RenderFrameHost* calling_render_frame_host, + content::WebContents& tab_web_contents, + const GURL& destination_url, + bool extension_function_user_gesture) { + auto is_dse_redirect = [&browser_context, + &destination_url](const GURL& source_url) { + return ExtensionsBrowserClient::Get()->IsDefaultSearchEngineRedirect( + &browser_context, source_url, destination_url); + }; + + // If there is a pending entry, proceed to checking user gestures since the + // user may have not yet landed on the DSE page. + content::NavigationEntry* entry = + tab_web_contents.GetController().GetPendingEntry(); + if (!entry) { + entry = tab_web_contents.GetController().GetLastCommittedEntry(); + // Assume no redirect if user has landed on the DSE page for a while. + if (entry && base::Time::Now() - entry->GetTimestamp() > base::Seconds(5)) { + base::UmaHistogramEnumeration( + "Extensions.Tabs.UpdateAction", + is_dse_redirect(entry->GetURL()) + ? UpdateActionType::kDSERedirectsAfterLandingOnSERP + : UpdateActionType::kOtherUpdates); + return; + } + } + if (!entry || !is_dse_redirect(entry->GetURL())) { + base::UmaHistogramEnumeration("Extensions.Tabs.UpdateAction", + UpdateActionType::kOtherUpdates); + return; + } + bool has_user_activation = HasUserActivation( + extension_id, browser_context, calling_render_frame_host, + tab_web_contents, extension_function_user_gesture); + base::UmaHistogramEnumeration( + "Extensions.Tabs.UpdateAction", + has_user_activation ? UpdateActionType::kDSERedirectsWithUserGesture + : UpdateActionType::kDSERedirectsWithoutUserGesture); +} + +void CheckDSERemoval(const ExtensionId& extension_id, + content::BrowserContext& browser_context, + content::RenderFrameHost* calling_render_frame_host, + content::WebContents& tab_web_contents, + bool extension_function_user_gesture) { + auto is_dse = [&browser_context](const GURL& source_url) { + return ExtensionsBrowserClient::Get()->IsDefaultSearchEngineRedirect( + &browser_context, source_url, GURL()); + }; + + // If there is a pending entry, proceed to checking user gestures since the + // user may have not yet landed on the DSE page. + content::NavigationEntry* entry = + tab_web_contents.GetController().GetPendingEntry(); + if (!entry) { + entry = tab_web_contents.GetController().GetLastCommittedEntry(); + // Assume no removal if user has landed on the DSE page for a while. + if (entry && base::Time::Now() - entry->GetTimestamp() > base::Seconds(5)) { + base::UmaHistogramEnumeration( + "Extensions.Tabs.RemoveAction", + is_dse(entry->GetURL()) + ? RemoveActionType::kDSERemovalsAfterLandingOnSERP + : RemoveActionType::kOtherRemovals); + return; + } + } + if (!entry || !is_dse(entry->GetURL())) { + base::UmaHistogramEnumeration("Extensions.Tabs.RemoveAction", + RemoveActionType::kOtherRemovals); + return; + } + bool has_user_activation = HasUserActivation( + extension_id, browser_context, calling_render_frame_host, + tab_web_contents, extension_function_user_gesture); + base::UmaHistogramEnumeration( + "Extensions.Tabs.RemoveAction", + has_user_activation ? RemoveActionType::kDSERemovalsWithUserGesture + : RemoveActionType::kDSERemovalsWithoutUserGesture); +} + } // namespace namespace tabs_internal { @@ -2657,6 +2792,8 @@ return false; } + CheckDSERedirect(extension()->id(), *browser_context(), render_frame_host(), + *web_contents, *url, user_gesture()); content::NavigationController::LoadURLParams load_params(*url); // Treat extension-initiated navigations as renderer-initiated so that the URL @@ -2989,6 +3126,9 @@ return false; } + CheckDSERemoval(extension()->id(), *browser_context(), render_frame_host(), + *contents, user_gesture()); + // Don't let the extension remove a tab if the user is dragging tabs around. if (!ExtensionTabUtil::IsTabStripEditable(*window->profile())) { *error = ExtensionTabUtil::kTabStripNotEditableError;
diff --git a/chrome/browser/extensions/api/tabs/tabs_apitest.cc b/chrome/browser/extensions/api/tabs/tabs_apitest.cc index 030f5d53..1b697928 100644 --- a/chrome/browser/extensions/api/tabs/tabs_apitest.cc +++ b/chrome/browser/extensions/api/tabs/tabs_apitest.cc
@@ -5,6 +5,7 @@ #include "base/strings/stringprintf.h" #include "base/strings/to_string.h" #include "base/test/bind.h" +#include "base/test/metrics/histogram_tester.h" #include "build/build_config.h" #include "build/chromeos_buildflags.h" #include "chrome/browser/extensions/api/tabs/tabs_api.h" @@ -12,18 +13,24 @@ #include "chrome/browser/extensions/extension_tab_util.h" #include "chrome/browser/prefs/incognito_mode_prefs.h" #include "chrome/browser/profiles/profile.h" +#include "chrome/browser/search_engines/template_url_service_factory.h" #include "chrome/browser/tab_list/tab_list_interface.h" #include "chrome/common/chrome_switches.h" #include "chrome/common/pref_names.h" #include "chrome/common/url_constants.h" #include "components/policy/core/common/policy_pref_names.h" #include "components/prefs/pref_service.h" +#include "components/search_engines/template_url.h" +#include "components/search_engines/template_url_data.h" +#include "components/search_engines/template_url_service.h" +#include "content/public/browser/navigation_entry.h" #include "content/public/browser/render_widget_host_view.h" #include "content/public/common/content_features.h" #include "content/public/test/back_forward_cache_util.h" #include "content/public/test/browser_test.h" #include "content/public/test/browser_test_utils.h" #include "content/public/test/prerender_test_util.h" +#include "extensions/browser/event_router.h" #include "extensions/test/extension_test_message_listener.h" #include "extensions/test/result_catcher.h" #include "extensions/test/test_extension_dir.h" @@ -76,8 +83,8 @@ ExtensionApiTabTest::SetUpCommandLine(command_line); // Override the default which InProcessBrowserTest adds if it doesn't see a // homepage. - command_line->AppendSwitchASCII( - switches::kHomePage, chrome::kChromeUINewTabURL); + command_line->AppendSwitchASCII(switches::kHomePage, + chrome::kChromeUINewTabURL); } }; @@ -658,7 +665,8 @@ ASSERT_TRUE(RunExtensionTest("tabs/prerendering_into_new_tab")) << message_; } -// TODO(https://crbug.com/449095632): Port to desktop android. +// TODO(https://crbug.com/449095632): Port to desktop android. Currently fails +// because the chrome.tabGroups.onRemoved notification is not received. #if BUILDFLAG(ENABLE_EXTENSIONS) // Tests the tabs.onUpdated events dispatched when moving a tab group from one @@ -759,3 +767,314 @@ } #endif // BUILDFLAG(ENABLE_EXTENSIONS) + +class ExtensionApiTabDSERedirectTest : public ExtensionApiTabTest { + public: + ExtensionApiTabDSERedirectTest() = default; + ~ExtensionApiTabDSERedirectTest() override = default; + + void SetUpOnMainThread() override { + ExtensionApiTabTest::SetUpOnMainThread(); + + GURL search_url = embedded_test_server()->GetURL("/search"); + + TemplateURLService* template_url_service = + TemplateURLServiceFactory::GetForProfile(profile()); + TemplateURLData data; + data.SetShortName(u"Test"); + data.SetKeyword(u"test"); + data.SetURL(search_url.spec() + "?q={searchTerms}"); + TemplateURL* template_url = + template_url_service->Add(std::make_unique<TemplateURL>(data)); + template_url_service->SetUserSelectedDefaultSearchProvider(template_url); + } + + void SetupDSEPage() { + content::WebContents* web_contents = + GetTabListInterface()->GetActiveTab()->GetContents(); + std::ignore = content::NavigateToURL( + web_contents, embedded_test_server()->GetURL("/search?q=foo")); + + web_contents->GetController().GetLastCommittedEntry()->SetTimestamp( + base::Time::Now()); + } +}; + +IN_PROC_BROWSER_TEST_F(ExtensionApiTabDSERedirectTest, + DSERedirectTabsUpdateAction) { + SetupDSEPage(); + + base::HistogramTester histogram_tester; + + static constexpr char kManifest[] = + R"({ + "name": "UpdateAction Extension", + "version": "0.1", + "manifest_version": 3, + "permissions": ["tabs"], + "background": { "service_worker" : "background.js" } + })"; + static constexpr char kBackground[] = + R"( + chrome.tabs.query({active: true, currentWindow: true}, (tabs) => { + chrome.tabs.update(tabs[0].id, {url: 'http://example.com'}, () => { + chrome.test.succeed(); + }); + }); + )"; + + extensions::TestExtensionDir test_dir; + test_dir.WriteManifest(kManifest); + test_dir.WriteFile(FILE_PATH_LITERAL("background.js"), kBackground); + + extensions::ResultCatcher result_catcher; + ASSERT_TRUE(LoadExtension(test_dir.UnpackedPath())); + ASSERT_TRUE(result_catcher.GetNextResult()); + + histogram_tester.ExpectBucketCount("Extensions.Tabs.UpdateAction", + 1 /* kDSERemovalsWithoutUserGesture */, 1); +} + +IN_PROC_BROWSER_TEST_F(ExtensionApiTabDSERedirectTest, + DSERedirectTabsUpdateAction_UserGesture) { + SetupDSEPage(); + + base::HistogramTester histogram_tester; + + static constexpr char kManifest[] = + R"({ + "name": "UpdateAction Extension", + "version": "0.1", + "manifest_version": 3, + "permissions": ["tabs"], + "background": { "service_worker" : "background.js" } + })"; + static constexpr char kBackground[] = + R"( + chrome.tabs.query({active: true, currentWindow: true}, (tabs) => { + chrome.test.runWithUserGesture(() => { + chrome.tabs.update(tabs[0].id, {url: 'http://example.com'}, () => { + chrome.test.succeed(); + }); + }); + }); + )"; + + extensions::TestExtensionDir test_dir; + test_dir.WriteManifest(kManifest); + test_dir.WriteFile(FILE_PATH_LITERAL("background.js"), kBackground); + + extensions::ResultCatcher result_catcher; + ASSERT_TRUE(LoadExtension(test_dir.UnpackedPath())); + ASSERT_TRUE(result_catcher.GetNextResult()); + + histogram_tester.ExpectBucketCount("Extensions.Tabs.UpdateAction", + 2 /* kDSERedirectsWithUserGesture */, 1); +} + +IN_PROC_BROWSER_TEST_F(ExtensionApiTabDSERedirectTest, + DSERedirectTabsUpdateAction_UserGesture_Async) { + SetupDSEPage(); + + base::HistogramTester histogram_tester; + + static constexpr char kManifest[] = + R"({ + "name": "UpdateAction Extension", + "version": "0.1", + "manifest_version": 3, + "permissions": ["tabs"], + "background": { "service_worker" : "background.js" } + })"; + static constexpr char kBackground[] = + R"( + chrome.tabs.query({active: true, currentWindow: true}, async (tabs) => { + chrome.test.runWithUserGesture(() => { + chrome.tabs.update(tabs[0].id, {}, () => { + chrome.test.succeed(); + }); + }); + await new Promise(resolve => setTimeout(resolve, 0)); + chrome.tabs.update(tabs[0].id, {url: 'http://example.com'}, () => { + chrome.test.succeed(); + }); + }); + )"; + + extensions::TestExtensionDir test_dir; + test_dir.WriteManifest(kManifest); + test_dir.WriteFile(FILE_PATH_LITERAL("background.js"), kBackground); + + extensions::ResultCatcher result_catcher; + ASSERT_TRUE(LoadExtension(test_dir.UnpackedPath())); + ASSERT_TRUE(result_catcher.GetNextResult()); + + histogram_tester.ExpectBucketCount("Extensions.Tabs.UpdateAction", + 2 /* kDSERedirectsWithUserGesture */, 1); +} + +IN_PROC_BROWSER_TEST_F(ExtensionApiTabDSERedirectTest, + DSERedirectTabsUpdateAction_UserGesture_EventRouter) { + SetupDSEPage(); + + base::HistogramTester histogram_tester; + + static constexpr char kManifest[] = + R"({ + "name": "UpdateAction Extension", + "version": "0.1", + "manifest_version": 3, + "permissions": ["tabs"], + "background": { "service_worker" : "background.js" } + })"; + static constexpr char kBackground[] = + R"( + chrome.test.onMessage.addListener((msg) => { + chrome.tabs.query({active: true, currentWindow: true}, (tabs) => { + chrome.tabs.update(tabs[0].id, {url: 'http://example.com'}, () => { + chrome.test.succeed(); + }); + }); + }); + )"; + + extensions::TestExtensionDir test_dir; + test_dir.WriteManifest(kManifest); + test_dir.WriteFile(FILE_PATH_LITERAL("background.js"), kBackground); + + extensions::ResultCatcher result_catcher; + const extensions::Extension* extension = + LoadExtension(test_dir.UnpackedPath()); + ASSERT_TRUE(extension); + + auto event = std::make_unique<extensions::Event>( + extensions::events::FOR_TEST, "test.onMessage", base::ListValue(), + profile()); + event->user_gesture = extensions::EventRouter::UserGestureState::kEnabled; + // Dispatch the event which will ultimately trigger RouteDispatchEvent + // with `params->is_user_gesture` = true. + extensions::EventRouter::Get(profile())->DispatchEventToExtension( + extension->id(), std::move(event)); + + ASSERT_TRUE(result_catcher.GetNextResult()); + + histogram_tester.ExpectBucketCount("Extensions.Tabs.UpdateAction", + 2 /* kDSERedirectsWithUserGesture */, 1); +} + +IN_PROC_BROWSER_TEST_F(ExtensionApiTabDSERedirectTest, DSETabsRemoveAction) { + tabs::TabInterface* new_tab = + GetTabListInterface()->OpenTab(GURL("about:blank"), -1, true); + content::WebContents* web_contents = new_tab->GetContents(); + std::ignore = content::NavigateToURL( + web_contents, embedded_test_server()->GetURL("/search?q=foo")); + + web_contents->GetController().GetLastCommittedEntry()->SetTimestamp( + base::Time::Now()); + + base::HistogramTester histogram_tester; + + static constexpr char kManifest[] = + R"({ + "name": "RemoveAction Extension", + "version": "0.1", + "manifest_version": 3, + "permissions": ["tabs"], + "background": { "service_worker" : "background.js" } + })"; + static constexpr char kBackground[] = + R"( + chrome.tabs.query({active: true, currentWindow: true}, (tabs) => { + chrome.tabs.remove(tabs[0].id, () => { + chrome.test.succeed(); + }); + }); + )"; + + extensions::TestExtensionDir test_dir; + test_dir.WriteManifest(kManifest); + test_dir.WriteFile(FILE_PATH_LITERAL("background.js"), kBackground); + + extensions::ResultCatcher result_catcher; + ASSERT_TRUE(LoadExtension(test_dir.UnpackedPath())); + ASSERT_TRUE(result_catcher.GetNextResult()); + + histogram_tester.ExpectBucketCount("Extensions.Tabs.RemoveAction", + 1 /* kDSERemovalsWithoutUserGesture */, 1); +} + +IN_PROC_BROWSER_TEST_F(ExtensionApiTabDSERedirectTest, NonDSETabsRemoveAction) { + std::ignore = GetTabListInterface()->OpenTab(GURL("about:blank"), -1, true); + + base::HistogramTester histogram_tester; + + static constexpr char kManifest[] = + R"({ + "name": "RemoveAction Extension", + "version": "0.1", + "manifest_version": 3, + "permissions": ["tabs"], + "background": { "service_worker" : "background.js" } + })"; + static constexpr char kBackground[] = + R"( + chrome.tabs.query({active: true, currentWindow: true}, (tabs) => { + chrome.tabs.remove(tabs[0].id, () => { + chrome.test.succeed(); + }); + }); + )"; + + extensions::TestExtensionDir test_dir; + test_dir.WriteManifest(kManifest); + test_dir.WriteFile(FILE_PATH_LITERAL("background.js"), kBackground); + + extensions::ResultCatcher result_catcher; + ASSERT_TRUE(LoadExtension(test_dir.UnpackedPath())); + ASSERT_TRUE(result_catcher.GetNextResult()); + + histogram_tester.ExpectBucketCount("Extensions.Tabs.RemoveAction", + 0 /* kOtherRemovals */, 1); +} + +IN_PROC_BROWSER_TEST_F(ExtensionApiTabDSERedirectTest, + DSETabsRemoveActionAfterLandingOnSERP) { + tabs::TabInterface* new_tab = + GetTabListInterface()->OpenTab(GURL("about:blank"), -1, true); + content::WebContents* web_contents = new_tab->GetContents(); + std::ignore = content::NavigateToURL( + web_contents, embedded_test_server()->GetURL("/search?q=foo")); + + web_contents->GetController().GetLastCommittedEntry()->SetTimestamp( + base::Time::Now() - base::Seconds(6)); + + base::HistogramTester histogram_tester; + + static constexpr char kManifest[] = + R"({ + "name": "RemoveAction Extension", + "version": "0.1", + "manifest_version": 3, + "permissions": ["tabs"], + "background": { "service_worker" : "background.js" } + })"; + static constexpr char kBackground[] = + R"( + chrome.tabs.query({active: true, currentWindow: true}, (tabs) => { + chrome.tabs.remove(tabs[0].id, () => { + chrome.test.succeed(); + }); + }); + )"; + + extensions::TestExtensionDir test_dir; + test_dir.WriteManifest(kManifest); + test_dir.WriteFile(FILE_PATH_LITERAL("background.js"), kBackground); + + extensions::ResultCatcher result_catcher; + ASSERT_TRUE(LoadExtension(test_dir.UnpackedPath())); + ASSERT_TRUE(result_catcher.GetNextResult()); + + histogram_tester.ExpectBucketCount("Extensions.Tabs.RemoveAction", + 3 /* kDSERemovalsAfterLandingOnSERP */, 1); +}
diff --git a/chrome/browser/extensions/api/tabs/tabs_test.cc b/chrome/browser/extensions/api/tabs/tabs_test.cc index 9122817..b67ec4b 100644 --- a/chrome/browser/extensions/api/tabs/tabs_test.cc +++ b/chrome/browser/extensions/api/tabs/tabs_test.cc
@@ -2090,39 +2090,46 @@ EXPECT_TRUE(tab_list->GetTab(index)->GetContents()->WasDiscarded()); } -#if BUILDFLAG(ENABLE_EXTENSIONS) - IN_PROC_BROWSER_TEST_F(ExtensionTabsTest, TestGroupDetachedAndReInserted) { // Create the `TabsEventRouter`, which is required to get a tab update event. TabsWindowsAPI::Get(profile())->InitTabsEventRouter(); - chrome::AddTabAt(browser(), GURL(), -1, true); - chrome::AddTabAt(browser(), GURL(), -1, true); - chrome::AddTabAt(browser(), GURL(), -1, true); + GURL about_blank("about:blank"); + ASSERT_TRUE(NavigateToURLInNewTab(about_blank)); + ASSERT_TRUE(NavigateToURLInNewTab(about_blank)); + ASSERT_TRUE(NavigateToURLInNewTab(about_blank)); - tab_groups::TabGroupId group = - browser()->tab_strip_model()->AddToNewGroup({0, 1}); + TabListInterface* tab_list = + TabListInterface::From(browser_window_interface()); + std::optional<tab_groups::TabGroupId> group = tab_list->CreateTabGroup({ + tab_list->GetTab(0)->GetHandle(), + tab_list->GetTab(1)->GetHandle(), + }); + ASSERT_TRUE(group); + + BrowserWindowInterface* second_browser = + CreateBrowserWindowWithType(BrowserWindowInterface::TYPE_NORMAL); + ASSERT_TRUE(second_browser); + TabListInterface* destination_tab_list = + TabListInterface::From(second_browser); + ASSERT_TRUE(destination_tab_list); + destination_tab_list->OpenTab(about_blank, -1); TestEventRouterObserver event_observer(EventRouter::Get(profile())); - std::unique_ptr<DetachedTabCollection> detached_group = - browser()->tab_strip_model()->DetachTabGroupForInsertion(group); + tab_list->MoveTabGroupToWindow(*group, second_browser->GetSessionID(), 0); - event_observer.WaitForEventWithName(api::tabs::OnUpdated::kEventName); - EXPECT_TRUE( - event_observer.events().contains(api::tabs::OnUpdated::kEventName)); - - event_observer.ClearEvents(); - - browser()->tab_strip_model()->InsertDetachedTabGroupAt( - std::move(detached_group), 1); - - // Group added as well as the tab's group changed event should be sent. + // Verify a tabs.onUpdated event was sent. In practice, more than one is + // dispatched (multiple tabs and they are added / removed from a group). + // The exact events are tested more thoroughly in a similar test in the + // API test `ExtensionApiTabTest.MovingAGroupToANewWindow`. event_observer.WaitForEventWithName(api::tabs::OnUpdated::kEventName); EXPECT_TRUE( event_observer.events().contains(api::tabs::OnUpdated::kEventName)); } +#if BUILDFLAG(ENABLE_EXTENSIONS) + IN_PROC_BROWSER_TEST_F(ExtensionTabsTest, Freezing) { // Create a background tab. ui_test_utils::NavigateToURLWithDisposition(
diff --git a/chrome/browser/extensions/api/web_request/web_request_permissions_unittest.cc b/chrome/browser/extensions/api/web_request/web_request_permissions_unittest.cc index 11f34a5..d4d9079e 100644 --- a/chrome/browser/extensions/api/web_request/web_request_permissions_unittest.cc +++ b/chrome/browser/extensions/api/web_request/web_request_permissions_unittest.cc
@@ -90,10 +90,10 @@ TEST_F(ExtensionWebRequestHelpersTestWithThreadsTest, BlocklistUpdateUrlsHidden) { auto create_request_params = [](const std::string& url) { - const int kRendererProcessId = 2; + const content::ChildProcessId kRendererProcessId(2); WebRequestInfoInitParams request; request.url = GURL(url); - request.render_process_id = kRendererProcessId; + request.global_id.child_id = kRendererProcessId; return request; }; @@ -120,7 +120,7 @@ WebRequestInfoInitParams info_params; info_params.url = example_com; info_params.initiator = initiator; - info_params.render_process_id = -1; + info_params.global_id.child_id = content::ChildProcessId(); info_params.web_request_type = web_request_type; info_params.is_navigation_request = is_navigation_request; return info_params;
diff --git a/chrome/browser/extensions/browsertest_util.cc b/chrome/browser/extensions/browsertest_util.cc index 5446aaa..2c7a443 100644 --- a/chrome/browser/extensions/browsertest_util.cc +++ b/chrome/browser/extensions/browsertest_util.cc
@@ -20,7 +20,6 @@ #include "chrome/browser/extensions/window_controller_list.h" #include "chrome/browser/profiles/profile.h" #include "chrome/browser/ui/browser.h" -#include "chrome/browser/ui/browser_finder.h" #include "chrome/browser/ui/tabs/tab_strip_model.h" #include "chrome/browser/web_applications/web_app_helpers.h" #include "chrome/test/base/ui_test_utils.h"
diff --git a/chrome/browser/extensions/chrome_content_browser_client_extensions_part.cc b/chrome/browser/extensions/chrome_content_browser_client_extensions_part.cc index 6bbd8ea..736c27a 100644 --- a/chrome/browser/extensions/chrome_content_browser_client_extensions_part.cc +++ b/chrome/browser/extensions/chrome_content_browser_client_extensions_part.cc
@@ -533,8 +533,9 @@ ExtensionRegistry::Get( outermost_main_frame->GetSiteInstance()->GetBrowserContext()) ->enabled_extensions() - .GetExtensionOrAppByURL( - outermost_main_frame->GetSiteInstance()->GetSiteURL()); + .GetExtensionOrAppByURL(outermost_main_frame->GetSiteInstance() + ->GetSecurityPrincipal() + .GetDeprecatedSiteURL()); return !extension || !extension->is_extension(); } @@ -758,8 +759,8 @@ // for isolated origins or cross-site iframes). For that case, don't look up // the hosted app's Extension from the site URL using GetExtensionOrAppByURL, // since it isn't treated as a hosted app. - const Extension* extension = - GetEnabledExtensionFromSiteURL(context, site_instance->GetSiteURL()); + const Extension* extension = GetEnabledExtensionFromSiteURL( + context, site_instance->GetSecurityPrincipal().GetDeprecatedSiteURL()); if (!extension) { return; } @@ -839,7 +840,7 @@ #endif // BUILDFLAG(ENABLE_GUEST_VIEW) const Extension* extension = registry->enabled_extensions().GetByID( - main_frame_site.GetSiteURL().GetHost()); + main_frame_site.GetSecurityPrincipal().GetDeprecatedSiteURL().GetHost()); extension_webkit_preferences::SetPreferences(extension, web_prefs); return true; }
diff --git a/chrome/browser/extensions/chrome_extensions_browser_client_non_android.cc b/chrome/browser/extensions/chrome_extensions_browser_client_non_android.cc index cabc0eb..cfa3208 100644 --- a/chrome/browser/extensions/chrome_extensions_browser_client_non_android.cc +++ b/chrome/browser/extensions/chrome_extensions_browser_client_non_android.cc
@@ -109,7 +109,7 @@ webapps::kIsolatedAppScheme)) { base::expected<web_app::IsolatedWebAppUrlInfo, std::string> url_info = web_app::IsolatedWebAppUrlInfo::Create( - owner_site_instance->GetSiteURL()); + owner_site_instance->GetSecurityPrincipal().GetDeprecatedSiteURL()); DCHECK(url_info.has_value()) << url_info.error(); auto* profile = Profile::FromBrowserContext(browser_context);
diff --git a/chrome/browser/extensions/extension_action_runner.cc b/chrome/browser/extensions/extension_action_runner.cc index 52da02b..a18b16c 100644 --- a/chrome/browser/extensions/extension_action_runner.cc +++ b/chrome/browser/extensions/extension_action_runner.cc
@@ -50,7 +50,6 @@ #if BUILDFLAG(ENABLE_EXTENSIONS) #include "chrome/browser/extensions/api/side_panel/side_panel_service.h" -#include "chrome/browser/ui/browser_finder.h" #include "chrome/browser/ui/browser_window.h" #include "chrome/browser/ui/extensions/extensions_dialogs.h" #endif
diff --git a/chrome/browser/extensions/extension_browsertest.cc b/chrome/browser/extensions/extension_browsertest.cc index c465470..4882d24 100644 --- a/chrome/browser/extensions/extension_browsertest.cc +++ b/chrome/browser/extensions/extension_browsertest.cc
@@ -55,7 +55,6 @@ #if BUILDFLAG(ENABLE_EXTENSIONS) #include "chrome/browser/ui/browser.h" -#include "chrome/browser/ui/browser_finder.h" #include "chrome/browser/ui/tabs/tab_strip_model.h" #include "chrome/test/base/ui_test_utils.h" #endif
diff --git a/chrome/browser/extensions/extension_install_ui_browsertest.cc b/chrome/browser/extensions/extension_install_ui_browsertest.cc index e0efb25..5836fac 100644 --- a/chrome/browser/extensions/extension_install_ui_browsertest.cc +++ b/chrome/browser/extensions/extension_install_ui_browsertest.cc
@@ -14,7 +14,6 @@ #include "chrome/browser/themes/theme_service_factory.h" #include "chrome/browser/ui/browser.h" #include "chrome/browser/ui/browser_commands.h" -#include "chrome/browser/ui/browser_finder.h" #include "chrome/common/url_constants.h" #include "components/crx_file/id_util.h" #include "components/infobars/content/content_infobar_manager.h"
diff --git a/chrome/browser/extensions/extension_tab_util.cc b/chrome/browser/extensions/extension_tab_util.cc index b87b0c9..6238051 100644 --- a/chrome/browser/extensions/extension_tab_util.cc +++ b/chrome/browser/extensions/extension_tab_util.cc
@@ -72,7 +72,6 @@ #else #include "chrome/browser/resource_coordinator/tab_lifecycle_unit_external.h" #include "chrome/browser/ui/browser.h" // nogncheck -#include "chrome/browser/ui/browser_finder.h" // nogncheck #include "chrome/browser/ui/navigator/browser_navigator_params.h" // nogncheck #include "chrome/browser/ui/recently_audible_helper.h" // nogncheck #include "chrome/browser/ui/tabs/tab_enums.h" // nogncheck
diff --git a/chrome/browser/extensions/extension_user_activation_service_browsertest.cc b/chrome/browser/extensions/extension_user_activation_service_browsertest.cc new file mode 100644 index 0000000..cf2607fc --- /dev/null +++ b/chrome/browser/extensions/extension_user_activation_service_browsertest.cc
@@ -0,0 +1,104 @@ +// Copyright 2026 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "extensions/browser/extension_user_activation_service.h" + +#include "chrome/browser/extensions/extension_browsertest.h" +#include "chrome/browser/profiles/profile.h" +#include "content/public/test/browser_test.h" +#include "content/public/test/browser_test_utils.h" +#include "extensions/browser/event_router.h" +#include "extensions/common/extension_id.h" +#include "extensions/test/extension_test_message_listener.h" +#include "extensions/test/test_extension_dir.h" + +namespace extensions { + +class ExtensionUserActivationServiceBrowserTest : public ExtensionBrowserTest { + public: + void SetUpOnMainThread() override { + ExtensionBrowserTest::SetUpOnMainThread(); + + // Load a simple extension that listens to messages. + test_dir_.WriteManifest(R"({ + "name": "Test", + "version": "1.0", + "manifest_version": 3, + "permissions": ["storage"] + })"); + test_dir_.WriteFile( + FILE_PATH_LITERAL("page.html"), + R"(<html><body><script src="page.js"></script></body></html>)"); + test_dir_.WriteFile(FILE_PATH_LITERAL("page.js"), R"( + chrome.storage.onChanged.addListener(() => {}); + chrome.test.sendMessage('ready'); + )"); + + ExtensionTestMessageListener listener("ready"); + extension_ = LoadExtension(test_dir_.UnpackedPath()); + ASSERT_TRUE(extension_); + + GURL page_url = extension_->GetResourceURL("page.html"); + ASSERT_TRUE(NavigateToURL(GetActiveWebContents(), page_url)); + + EXPECT_TRUE(listener.WaitUntilSatisfied()); + } + + protected: + const Extension* extension() const { return extension_.get(); } + + private: + TestExtensionDir test_dir_; + scoped_refptr<const Extension> extension_; +}; + +// Tests that when an extension calls an API method while a user gesture is +// active, the service records it properly. +IN_PROC_BROWSER_TEST_F(ExtensionUserActivationServiceBrowserTest, + ApiFunctionCall) { + ExtensionUserActivationService* service = + ExtensionUserActivationService::Get(profile()); + ASSERT_TRUE(service); + + content::WebContents* web_contents = GetActiveWebContents(); + + // An API function call without a user gesture. + EXPECT_FALSE(service->HasTransientActivation(extension()->id())); + EXPECT_TRUE(content::ExecJs(web_contents, + "chrome.storage.local.set({foo: 'bar'});", + content::EXECUTE_SCRIPT_NO_USER_GESTURE)); + EXPECT_FALSE(service->HasTransientActivation(extension()->id())); + + // An API function call with a user gesture. + EXPECT_TRUE( + content::ExecJs(web_contents, "chrome.storage.local.set({foo: 'bar'});")); + EXPECT_TRUE(service->HasTransientActivation(extension()->id())); +} + +// Tests that when an extension event is dispatched with a user gesture, the +// service records it properly. +IN_PROC_BROWSER_TEST_F(ExtensionUserActivationServiceBrowserTest, + EventDispatch) { + ExtensionUserActivationService* service = + ExtensionUserActivationService::Get(profile()); + ASSERT_TRUE(service); + + EventRouter* event_router = EventRouter::Get(profile()); + + // Event dispatch without a user gesture. + auto event1 = std::make_unique<Event>(events::FOR_TEST, "storage.onChanged", + base::ListValue(), profile()); + event1->user_gesture = EventRouter::UserGestureState::kNotEnabled; + event_router->DispatchEventToExtension(extension()->id(), std::move(event1)); + EXPECT_FALSE(service->HasTransientActivation(extension()->id())); + + // Event dispatch with a user gesture. + auto event2 = std::make_unique<Event>(events::FOR_TEST, "storage.onChanged", + base::ListValue(), profile()); + event2->user_gesture = EventRouter::UserGestureState::kEnabled; + event_router->DispatchEventToExtension(extension()->id(), std::move(event2)); + EXPECT_TRUE(service->HasTransientActivation(extension()->id())); +} + +} // namespace extensions
diff --git a/chrome/browser/extensions/file_handlers/web_file_handlers_permission_handler.cc b/chrome/browser/extensions/file_handlers/web_file_handlers_permission_handler.cc index 18505f8..42dabcacd 100644 --- a/chrome/browser/extensions/file_handlers/web_file_handlers_permission_handler.cc +++ b/chrome/browser/extensions/file_handlers/web_file_handlers_permission_handler.cc
@@ -18,7 +18,6 @@ #include "base/strings/string_util.h" #include "base/strings/utf_string_conversions.h" #include "chrome/browser/profiles/profile.h" -#include "chrome/browser/ui/browser_finder.h" #include "chrome/browser/ui/extensions/extensions_dialogs.h" #include "chrome/browser/web_applications/web_app_utils.h" #include "chrome/grit/generated_resources.h"
diff --git a/chrome/browser/extensions/navigation_extension_enabler_browsertest.cc b/chrome/browser/extensions/navigation_extension_enabler_browsertest.cc index 40c0121..d511dcbc 100644 --- a/chrome/browser/extensions/navigation_extension_enabler_browsertest.cc +++ b/chrome/browser/extensions/navigation_extension_enabler_browsertest.cc
@@ -13,6 +13,7 @@ #include "chrome/browser/extensions/chrome_test_extension_loader.h" #include "chrome/browser/extensions/extension_browsertest.h" #include "chrome/browser/profiles/profile.h" +#include "content/public/browser/security_principal.h" #include "content/public/browser/site_isolation_policy.h" #include "content/public/common/content_features.h" #include "content/public/common/isolated_world_ids.h" @@ -245,10 +246,14 @@ #if !BUILDFLAG(IS_MAC) && !BUILDFLAG(IS_WIN) EXPECT_NE(subframe->GetSiteInstance(), extension_site_instance); if (content::SiteIsolationPolicy::IsErrorPageIsolationEnabled(false)) { - EXPECT_EQ(subframe->GetSiteInstance()->GetSiteURL(), + EXPECT_EQ(subframe->GetSiteInstance() + ->GetSecurityPrincipal() + .GetDeprecatedSiteURL(), GURL(content::kUnreachableWebDataURL)); } else { - EXPECT_EQ(subframe->GetSiteInstance()->GetSiteURL(), + EXPECT_EQ(subframe->GetSiteInstance() + ->GetSecurityPrincipal() + .GetDeprecatedSiteURL(), GURL(kExtensionInvalidRequestURL)); // The disabled extension process should be locked. EXPECT_TRUE(subframe->GetProcess()->IsProcessLockedToSiteForTesting());
diff --git a/chrome/browser/extensions/open_tab_helper.cc b/chrome/browser/extensions/open_tab_helper.cc index 86d1268..d21fe15 100644 --- a/chrome/browser/extensions/open_tab_helper.cc +++ b/chrome/browser/extensions/open_tab_helper.cc
@@ -28,7 +28,6 @@ #if !BUILDFLAG(IS_ANDROID) #include "chrome/browser/ui/browser.h" -#include "chrome/browser/ui/browser_finder.h" #include "chrome/browser/ui/tabs/tab_enums.h" #endif
diff --git a/chrome/browser/extensions/process_manager_browsertest.cc b/chrome/browser/extensions/process_manager_browsertest.cc index a34a3b7..6ab06c3 100644 --- a/chrome/browser/extensions/process_manager_browsertest.cc +++ b/chrome/browser/extensions/process_manager_browsertest.cc
@@ -44,6 +44,7 @@ #include "content/public/browser/navigation_entry.h" #include "content/public/browser/render_frame_host.h" #include "content/public/browser/render_process_host.h" +#include "content/public/browser/security_principal.h" #include "content/public/browser/web_contents.h" #include "content/public/common/content_features.h" #include "content/public/test/browser_test.h" @@ -758,8 +759,10 @@ ExtensionHost* extension_host = pm->GetBackgroundHostForExtension(extension->id()); - EXPECT_EQ(extension->url(), - extension_host->host_contents()->GetSiteInstance()->GetSiteURL()); + EXPECT_EQ(extension->url(), extension_host->host_contents() + ->GetSiteInstance() + ->GetSecurityPrincipal() + .GetDeprecatedSiteURL()); processes.insert(extension_host->render_process_host()->GetDeprecatedID()); }
diff --git a/chrome/browser/extensions/process_map_browsertest.cc b/chrome/browser/extensions/process_map_browsertest.cc index dc4b749..d7ab426 100644 --- a/chrome/browser/extensions/process_map_browsertest.cc +++ b/chrome/browser/extensions/process_map_browsertest.cc
@@ -13,7 +13,6 @@ #include "base/strings/stringprintf.h" #include "chrome/browser/extensions/extension_browsertest.h" #include "chrome/browser/profiles/profile.h" -#include "chrome/browser/ui/browser.h" #include "content/public/browser/render_frame_host.h" #include "content/public/browser/render_process_host.h" #include "content/public/browser/security_principal.h" @@ -24,10 +23,9 @@ #include "content/public/test/browser_test.h" #include "content/public/test/browser_test_utils.h" #include "content/public/test/test_navigation_observer.h" -#include "extensions/browser/app_window/app_window.h" -#include "extensions/browser/app_window/app_window_registry.h" #include "extensions/browser/browsertest_util.h" #include "extensions/browser/guest_view/web_view/web_view_guest.h" +#include "extensions/buildflags/buildflags.h" #include "extensions/common/constants.h" #include "extensions/common/mojom/context_type.mojom.h" #include "extensions/test/extension_test_message_listener.h" @@ -35,6 +33,13 @@ #include "net/dns/mock_host_resolver.h" #include "testing/gtest/include/gtest/gtest.h" +#if BUILDFLAG(ENABLE_PLATFORM_APPS) +#include "extensions/browser/app_window/app_window.h" +#include "extensions/browser/app_window/app_window_registry.h" +#endif + +static_assert(BUILDFLAG(ENABLE_EXTENSIONS_CORE)); + namespace extensions { class ProcessMapBrowserTest : public ExtensionBrowserTest { @@ -371,6 +376,7 @@ return extension; } +#if BUILDFLAG(ENABLE_PLATFORM_APPS) content::WebContents* GetAppWindowContents() { AppWindowRegistry* registry = AppWindowRegistry::Get(profile()); if (registry->app_windows().size() != 1) { @@ -381,6 +387,7 @@ return (*registry->app_windows().begin())->web_contents(); } +#endif // BUILDFLAG(ENABLE_PLATFORM_APPS) content::WebContents* GetWebViewFromEmbedder(content::WebContents* embedder) { std::vector<content::WebContents*> inner_web_contents = @@ -408,8 +415,10 @@ // Opens a new tab to a Web UI page. void OpenWebUi() { + // Use chrome://version because it is Web UI on all platforms, including + // Android (which has some native UI chrome:// pages like settings). ASSERT_TRUE( - NavigateToURL(GetActiveWebContents(), GURL("chrome://settings"))); + NavigateToURL(GetActiveWebContents(), GURL("chrome://version"))); } // Opens a new tab to a page in the given `extension`. @@ -1408,6 +1417,8 @@ } } +// These tests use platform app windows. +#if BUILDFLAG(ENABLE_PLATFORM_APPS) IN_PROC_BROWSER_TEST_F(ProcessMapBrowserTest, IsPrivilegedExtensionProcess_WebViews) { const Extension* extension = AddExtensionWithWebViewAndOpen(); @@ -1462,6 +1473,7 @@ {mojom::ContextType::kWebPage, mojom::ContextType::kUntrustedWebUi}, "webview process without extension passed"); } +#endif // BUILDFLAG(ENABLE_PLATFORM_APPS) IN_PROC_BROWSER_TEST_F(ProcessMapBrowserTest, IsPrivilegedExtensionProcess_UserScripts) {
diff --git a/chrome/browser/extensions/user_host_restrictions_browsertest.cc b/chrome/browser/extensions/user_host_restrictions_browsertest.cc index 9d0f9e2..50b055d 100644 --- a/chrome/browser/extensions/user_host_restrictions_browsertest.cc +++ b/chrome/browser/extensions/user_host_restrictions_browsertest.cc
@@ -7,13 +7,13 @@ #include "base/test/scoped_feature_list.h" #include "chrome/browser/extensions/extension_apitest.h" #include "chrome/browser/profiles/profile.h" -#include "chrome/browser/ui/browser.h" #include "components/sessions/content/session_tab_helper.h" #include "content/public/test/browser_test.h" #include "extensions/browser/background_script_executor.h" #include "extensions/browser/permissions/scripting_permissions_modifier.h" #include "extensions/browser/permissions_manager.h" #include "extensions/browser/script_executor.h" +#include "extensions/buildflags/buildflags.h" #include "extensions/common/constants.h" #include "extensions/common/extension_features.h" #include "extensions/common/mojom/api_permission_id.mojom.h" @@ -24,6 +24,8 @@ #include "net/dns/mock_host_resolver.h" #include "testing/gtest/include/gtest/gtest.h" +static_assert(BUILDFLAG(ENABLE_EXTENSIONS_CORE)); + namespace extensions { // A parameterized test suite exercising user host restrictions. The param @@ -40,7 +42,7 @@ ~UserHostRestrictionsBrowserTest() override = default; void SetUpOnMainThread() override { - ExtensionBrowserTest::SetUpOnMainThread(); + ExtensionApiTest::SetUpOnMainThread(); host_resolver()->AddRule("*", "127.0.0.1"); }
diff --git a/chrome/browser/extensions/user_script_listener_browsertest.cc b/chrome/browser/extensions/user_script_listener_browsertest.cc index 66a49a7..b417a6a 100644 --- a/chrome/browser/extensions/user_script_listener_browsertest.cc +++ b/chrome/browser/extensions/user_script_listener_browsertest.cc
@@ -2,16 +2,20 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "chrome/browser/extensions/extension_browsertest.h" #include "chrome/browser/extensions/user_script_listener.h" -#include "chrome/browser/ui/browser.h" + +#include "chrome/browser/extensions/extension_browsertest.h" #include "chrome/test/base/testing_profile.h" -#include "chrome/test/base/ui_test_utils.h" +#include "content/public/browser/navigation_controller.h" #include "content/public/browser/navigation_handle.h" +#include "content/public/browser/web_contents.h" #include "content/public/test/browser_test.h" #include "content/public/test/browser_test_utils.h" #include "content/public/test/test_navigation_observer.h" #include "extensions/browser/extensions_browser_client.h" +#include "extensions/buildflags/buildflags.h" + +static_assert(BUILDFLAG(ENABLE_EXTENSIONS_CORE)); namespace extensions { @@ -31,9 +35,9 @@ content::TestNavigationObserver nav_observer(web_contents, 1); content::DidStartNavigationObserver start_observer(web_contents); - ui_test_utils::NavigateToURLWithDisposition( - browser(), embedded_test_server()->GetURL("/echo"), - WindowOpenDisposition::CURRENT_TAB, ui_test_utils::BROWSER_TEST_NO_WAIT); + GURL url = embedded_test_server()->GetURL("/echo"); + web_contents->GetController().LoadURL( + url, content::Referrer(), ui::PAGE_TRANSITION_LINK, std::string()); start_observer.Wait(); ASSERT_TRUE(start_observer.navigation_handle());
diff --git a/chrome/browser/external_protocol/DEPS b/chrome/browser/external_protocol/DEPS index bedb0d2..b178c40c 100644 --- a/chrome/browser/external_protocol/DEPS +++ b/chrome/browser/external_protocol/DEPS
@@ -9,7 +9,6 @@ "+chrome/browser/sharing/click_to_call/click_to_call_utils.h", "+chrome/browser/shell_integration.h", "+chrome/browser/ui/browser.h", - "+chrome/browser/ui/browser_finder.h", "+chrome/browser/ui/browser_window/public/browser_window_interface.h", "+chrome/browser/ui/browser_window/public/global_browser_collection.h", "+chrome/browser/ui/tabs/tab_strip_model.h",
diff --git a/chrome/browser/facilitated_payments/ui/android/internal/java/res/layout/pix_account_linking_prompt_b.xml b/chrome/browser/facilitated_payments/ui/android/internal/java/res/layout/pix_account_linking_prompt_b.xml index c186695..b65b8b7f 100644 --- a/chrome/browser/facilitated_payments/ui/android/internal/java/res/layout/pix_account_linking_prompt_b.xml +++ b/chrome/browser/facilitated_payments/ui/android/internal/java/res/layout/pix_account_linking_prompt_b.xml
@@ -64,6 +64,17 @@ android:textAppearance="@style/TextAppearance.TextLarge.Primary" android:focusable="true" /> + <TextView + android:id="@+id/pix_code_detection_settings_link" + android:layout_height="wrap_content" + android:layout_width="match_parent" + android:layout_marginTop="@dimen/pix_account_linking_prompt_settings_link_top_margin" + android:layout_marginHorizontal="@dimen/pix_account_linking_prompt_value_prop_message_margin_horizontal" + android:text="@string/pix_account_linking_prompt_b_settings_link" + android:textAppearance="@style/TextAppearance.TextMedium.Secondary" + android:textColorLink="@macro/default_text_color_link" + android:focusable="true" /> + <org.chromium.ui.widget.ButtonCompat android:id="@+id/accept_button" android:layout_width="match_parent"
diff --git a/chrome/browser/facilitated_payments/ui/android/internal/java/res/values/dimens.xml b/chrome/browser/facilitated_payments/ui/android/internal/java/res/values/dimens.xml index 87ddb51..810d17a6 100644 --- a/chrome/browser/facilitated_payments/ui/android/internal/java/res/values/dimens.xml +++ b/chrome/browser/facilitated_payments/ui/android/internal/java/res/values/dimens.xml
@@ -54,4 +54,5 @@ <dimen name="pix_account_linking_prompt_accept_button_top_margin">20dp</dimen> <dimen name="pix_account_linking_prompt_decline_button_top_margin">8dp</dimen> <dimen name="pix_account_linking_prompt_decline_button_bottom_margin">8dp</dimen> + <dimen name="pix_account_linking_prompt_settings_link_top_margin">12dp</dimen> </resources>
diff --git a/chrome/browser/facilitated_payments/ui/android/internal/java/src/org/chromium/chrome/browser/facilitated_payments/FacilitatedPaymentsPaymentMethodsControllerRobolectricTest.java b/chrome/browser/facilitated_payments/ui/android/internal/java/src/org/chromium/chrome/browser/facilitated_payments/FacilitatedPaymentsPaymentMethodsControllerRobolectricTest.java index 135d502d..8909b5c4 100644 --- a/chrome/browser/facilitated_payments/ui/android/internal/java/src/org/chromium/chrome/browser/facilitated_payments/FacilitatedPaymentsPaymentMethodsControllerRobolectricTest.java +++ b/chrome/browser/facilitated_payments/ui/android/internal/java/src/org/chromium/chrome/browser/facilitated_payments/FacilitatedPaymentsPaymentMethodsControllerRobolectricTest.java
@@ -44,6 +44,8 @@ import static org.chromium.chrome.browser.facilitated_payments.FacilitatedPaymentsPaymentMethodsProperties.PaymentAppProperties.PAYMENT_APP_NAME; import static org.chromium.chrome.browser.facilitated_payments.FacilitatedPaymentsPaymentMethodsProperties.PixAccountLinkingPromptProperties.ACCEPT_BUTTON_CALLBACK; import static org.chromium.chrome.browser.facilitated_payments.FacilitatedPaymentsPaymentMethodsProperties.PixAccountLinkingPromptProperties.DECLINE_BUTTON_CALLBACK; +import static org.chromium.chrome.browser.facilitated_payments.FacilitatedPaymentsPaymentMethodsProperties.PixAccountLinkingPromptProperties.SETTINGS_LINK_CALLBACK; +import static org.chromium.chrome.browser.facilitated_payments.FacilitatedPaymentsPaymentMethodsProperties.PixAccountLinkingPromptProperties.VIDEO_LINK_CALLBACK; import static org.chromium.chrome.browser.facilitated_payments.FacilitatedPaymentsPaymentMethodsProperties.SCREEN; import static org.chromium.chrome.browser.facilitated_payments.FacilitatedPaymentsPaymentMethodsProperties.SCREEN_VIEW_MODEL; import static org.chromium.chrome.browser.facilitated_payments.FacilitatedPaymentsPaymentMethodsProperties.SURVIVES_NAVIGATION; @@ -1443,9 +1445,14 @@ mFacilitatedPaymentsPaymentMethodsModel .get(SCREEN_VIEW_MODEL) .getAllProperties(); - assertThat(propertyKeys, hasSize(2)); + assertThat(propertyKeys, hasSize(4)); assertThat( - propertyKeys, containsInAnyOrder(ACCEPT_BUTTON_CALLBACK, DECLINE_BUTTON_CALLBACK)); + propertyKeys, + containsInAnyOrder( + ACCEPT_BUTTON_CALLBACK, + DECLINE_BUTTON_CALLBACK, + SETTINGS_LINK_CALLBACK, + VIDEO_LINK_CALLBACK)); assertThat(mFacilitatedPaymentsPaymentMethodsModel.get(SURVIVES_NAVIGATION), is(true)); }
diff --git a/chrome/browser/facilitated_payments/ui/android/internal/java/src/org/chromium/chrome/browser/facilitated_payments/FacilitatedPaymentsPaymentMethodsMediator.java b/chrome/browser/facilitated_payments/ui/android/internal/java/src/org/chromium/chrome/browser/facilitated_payments/FacilitatedPaymentsPaymentMethodsMediator.java index b0262aa..01827d27 100644 --- a/chrome/browser/facilitated_payments/ui/android/internal/java/src/org/chromium/chrome/browser/facilitated_payments/FacilitatedPaymentsPaymentMethodsMediator.java +++ b/chrome/browser/facilitated_payments/ui/android/internal/java/src/org/chromium/chrome/browser/facilitated_payments/FacilitatedPaymentsPaymentMethodsMediator.java
@@ -35,6 +35,8 @@ import static org.chromium.chrome.browser.facilitated_payments.FacilitatedPaymentsPaymentMethodsProperties.PaymentAppProperties.PAYMENT_APP_NAME; import static org.chromium.chrome.browser.facilitated_payments.FacilitatedPaymentsPaymentMethodsProperties.PixAccountLinkingPromptProperties.ACCEPT_BUTTON_CALLBACK; import static org.chromium.chrome.browser.facilitated_payments.FacilitatedPaymentsPaymentMethodsProperties.PixAccountLinkingPromptProperties.DECLINE_BUTTON_CALLBACK; +import static org.chromium.chrome.browser.facilitated_payments.FacilitatedPaymentsPaymentMethodsProperties.PixAccountLinkingPromptProperties.SETTINGS_LINK_CALLBACK; +import static org.chromium.chrome.browser.facilitated_payments.FacilitatedPaymentsPaymentMethodsProperties.PixAccountLinkingPromptProperties.VIDEO_LINK_CALLBACK; import static org.chromium.chrome.browser.facilitated_payments.FacilitatedPaymentsPaymentMethodsProperties.SCREEN; import static org.chromium.chrome.browser.facilitated_payments.FacilitatedPaymentsPaymentMethodsProperties.SCREEN_VIEW_MODEL; import static org.chromium.chrome.browser.facilitated_payments.FacilitatedPaymentsPaymentMethodsProperties.SURVIVES_NAVIGATION; @@ -52,10 +54,13 @@ import static org.chromium.components.browser_ui.settings.SettingsNavigation.SettingsFragment.PAYMENT_METHODS; import android.content.Context; +import android.content.Intent; import android.content.pm.PackageManager; import android.content.pm.ResolveInfo; import android.content.res.Configuration; import android.graphics.Bitmap; +import android.net.Uri; +import android.text.TextUtils; import androidx.annotation.VisibleForTesting; @@ -253,6 +258,20 @@ .set(ACCEPT_BUTTON_CALLBACK, v -> mDelegate.onPixAccountLinkingPromptAccepted()); mModel.get(SCREEN_VIEW_MODEL) .set(DECLINE_BUTTON_CALLBACK, v -> mDelegate.onPixAccountLinkingPromptDeclined()); + mModel.get(SCREEN_VIEW_MODEL) + .set(SETTINGS_LINK_CALLBACK, v -> startSettings(FINANCIAL_ACCOUNTS)); + mModel.get(SCREEN_VIEW_MODEL) + .set( + VIDEO_LINK_CALLBACK, + v -> { + String videoUrl = + ChromeFeatureList.getFieldTrialParamByFeature( + ChromeFeatureList.ENABLE_PIX_ACCOUNT_LINKING_NATIVE, + "video_url_on_prompt"); + if (!TextUtils.isEmpty(videoUrl)) { + openUrl(videoUrl); + } + }); // Prevent the bottom sheet from closing during page navigations. mModel.set(SURVIVES_NAVIGATION, true); mModel.set(VISIBLE_STATE, SHOWN); @@ -451,6 +470,12 @@ .startSettings(mContext, settingsFragment); } + private void openUrl(String url) { + Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(url)); + intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + mContext.startActivity(intent); + } + @VisibleForTesting PropertyModel createBankAccountModel(Context context, BankAccount bankAccount) { return new PropertyModel.Builder(BankAccountProperties.NON_TRANSFORMING_KEYS)
diff --git a/chrome/browser/facilitated_payments/ui/android/internal/java/src/org/chromium/chrome/browser/facilitated_payments/FacilitatedPaymentsPaymentMethodsProperties.java b/chrome/browser/facilitated_payments/ui/android/internal/java/src/org/chromium/chrome/browser/facilitated_payments/FacilitatedPaymentsPaymentMethodsProperties.java index b74c037..653ce083 100644 --- a/chrome/browser/facilitated_payments/ui/android/internal/java/src/org/chromium/chrome/browser/facilitated_payments/FacilitatedPaymentsPaymentMethodsProperties.java +++ b/chrome/browser/facilitated_payments/ui/android/internal/java/src/org/chromium/chrome/browser/facilitated_payments/FacilitatedPaymentsPaymentMethodsProperties.java
@@ -257,9 +257,18 @@ new WritableObjectPropertyKey<>("accept_button_callback"); static final WritableObjectPropertyKey<OnClickListener> DECLINE_BUTTON_CALLBACK = new WritableObjectPropertyKey<>("decline_button_callback"); + static final WritableObjectPropertyKey<OnClickListener> SETTINGS_LINK_CALLBACK = + new WritableObjectPropertyKey<>("settings_link_callback"); + static final WritableObjectPropertyKey<OnClickListener> VIDEO_LINK_CALLBACK = + new WritableObjectPropertyKey<>("video_link_callback"); /** All the properties of Pix account linking prompt. */ - static final PropertyKey[] ALL_KEYS = {ACCEPT_BUTTON_CALLBACK, DECLINE_BUTTON_CALLBACK}; + static final PropertyKey[] ALL_KEYS = { + ACCEPT_BUTTON_CALLBACK, + DECLINE_BUTTON_CALLBACK, + SETTINGS_LINK_CALLBACK, + VIDEO_LINK_CALLBACK + }; } private FacilitatedPaymentsPaymentMethodsProperties() {}
diff --git a/chrome/browser/facilitated_payments/ui/android/internal/java/src/org/chromium/chrome/browser/facilitated_payments/FacilitatedPaymentsPaymentMethodsViewTest.java b/chrome/browser/facilitated_payments/ui/android/internal/java/src/org/chromium/chrome/browser/facilitated_payments/FacilitatedPaymentsPaymentMethodsViewTest.java index 8652510..d3d51dc6 100644 --- a/chrome/browser/facilitated_payments/ui/android/internal/java/src/org/chromium/chrome/browser/facilitated_payments/FacilitatedPaymentsPaymentMethodsViewTest.java +++ b/chrome/browser/facilitated_payments/ui/android/internal/java/src/org/chromium/chrome/browser/facilitated_payments/FacilitatedPaymentsPaymentMethodsViewTest.java
@@ -22,6 +22,8 @@ import static org.chromium.chrome.browser.facilitated_payments.FacilitatedPaymentsPaymentMethodsProperties.ItemType.CONTINUE_BUTTON; import static org.chromium.chrome.browser.facilitated_payments.FacilitatedPaymentsPaymentMethodsProperties.ItemType.EWALLET; import static org.chromium.chrome.browser.facilitated_payments.FacilitatedPaymentsPaymentMethodsProperties.ItemType.PAYMENT_APP; +import static org.chromium.chrome.browser.facilitated_payments.FacilitatedPaymentsPaymentMethodsProperties.PixAccountLinkingPromptProperties.SETTINGS_LINK_CALLBACK; +import static org.chromium.chrome.browser.facilitated_payments.FacilitatedPaymentsPaymentMethodsProperties.PixAccountLinkingPromptProperties.VIDEO_LINK_CALLBACK; import static org.chromium.chrome.browser.facilitated_payments.FacilitatedPaymentsPaymentMethodsProperties.SCREEN; import static org.chromium.chrome.browser.facilitated_payments.FacilitatedPaymentsPaymentMethodsProperties.SCREEN_VIEW_MODEL; import static org.chromium.chrome.browser.facilitated_payments.FacilitatedPaymentsPaymentMethodsProperties.SURVIVES_NAVIGATION; @@ -932,9 +934,13 @@ @MediumTest @EnableFeatures({"EnablePixAccountLinkingNative:prompt_variant/VariationB"}) public void testPixAccountLinkingPromptContents_VariantB() { + boolean[] videoLinkClicked = new boolean[1]; runOnUiThreadBlocking( () -> { mModel.set(SCREEN, PIX_ACCOUNT_LINKING_PROMPT); + mModel.get(SCREEN_VIEW_MODEL).set(SETTINGS_LINK_CALLBACK, v -> {}); + mModel.get(SCREEN_VIEW_MODEL) + .set(VIDEO_LINK_CALLBACK, v -> videoLinkClicked[0] = true); mModel.set(VISIBLE_STATE, SHOWN); }); BottomSheetTestSupport.waitForOpen(mBottomSheetController); @@ -947,17 +953,32 @@ TextView valuePropMessage1 = mView.getContentView().findViewById(R.id.value_prop_message_1); assertThat( - valuePropMessage1.getText(), + valuePropMessage1.getText().toString(), is("Pay without copying and pasting Pix code. See how it works")); assertNotNull(valuePropMessage1.getCompoundDrawablesRelative()[0]); TextView valuePropMessage2 = mView.getContentView().findViewById(R.id.value_prop_message_2); assertThat(valuePropMessage2.getText(), is("Google encryption protects your info")); assertNotNull(valuePropMessage2.getCompoundDrawablesRelative()[0]); + TextView settingsLink = + mView.getContentView().findViewById(R.id.pix_code_detection_settings_link); + assertThat( + settingsLink.getText().toString(), + is("To turn off Pix code detection, go to Chrome settings")); + ButtonCompat acceptButton = mView.getContentView().findViewById(R.id.accept_button); assertThat(acceptButton.getText(), is("Link your account")); ButtonCompat declineButton = mView.getContentView().findViewById(R.id.decline_button); assertThat(declineButton.getText(), is("Not now")); + + android.text.Spanned spannedText = (android.text.Spanned) valuePropMessage1.getText(); + android.text.style.ClickableSpan[] spans = + spannedText.getSpans( + 0, spannedText.length(), android.text.style.ClickableSpan.class); + assertThat(spans.length, is(1)); + + runOnUiThreadBlocking(() -> spans[0].onClick(valuePropMessage1)); + assertThat(videoLinkClicked[0], is(true)); } @Test
diff --git a/chrome/browser/facilitated_payments/ui/android/internal/java/src/org/chromium/chrome/browser/facilitated_payments/PixAccountLinkingPrompt.java b/chrome/browser/facilitated_payments/ui/android/internal/java/src/org/chromium/chrome/browser/facilitated_payments/PixAccountLinkingPrompt.java index c64218fd..3e1e390 100644 --- a/chrome/browser/facilitated_payments/ui/android/internal/java/src/org/chromium/chrome/browser/facilitated_payments/PixAccountLinkingPrompt.java +++ b/chrome/browser/facilitated_payments/ui/android/internal/java/src/org/chromium/chrome/browser/facilitated_payments/PixAccountLinkingPrompt.java
@@ -7,17 +7,23 @@ import static org.chromium.chrome.browser.facilitated_payments.FacilitatedPaymentsPaymentMethodsProperties.PixAccountLinkingPromptProperties.ACCEPT_BUTTON_CALLBACK; import static org.chromium.chrome.browser.facilitated_payments.FacilitatedPaymentsPaymentMethodsProperties.PixAccountLinkingPromptProperties.ALL_KEYS; import static org.chromium.chrome.browser.facilitated_payments.FacilitatedPaymentsPaymentMethodsProperties.PixAccountLinkingPromptProperties.DECLINE_BUTTON_CALLBACK; +import static org.chromium.chrome.browser.facilitated_payments.FacilitatedPaymentsPaymentMethodsProperties.PixAccountLinkingPromptProperties.SETTINGS_LINK_CALLBACK; +import static org.chromium.chrome.browser.facilitated_payments.FacilitatedPaymentsPaymentMethodsProperties.PixAccountLinkingPromptProperties.VIDEO_LINK_CALLBACK; +import android.text.method.LinkMovementMethod; import android.view.LayoutInflater; import android.view.View; import android.widget.FrameLayout; import android.widget.LinearLayout; +import android.widget.TextView; import org.chromium.build.annotations.NullMarked; import org.chromium.chrome.browser.flags.ChromeFeatureList; import org.chromium.ui.modelutil.PropertyKey; import org.chromium.ui.modelutil.PropertyModel; import org.chromium.ui.modelutil.PropertyModelChangeProcessor; +import org.chromium.ui.text.ChromeClickableSpan; +import org.chromium.ui.text.SpanApplier; import org.chromium.ui.widget.ButtonCompat; /** This class is used to show the PIX account linking prompt. */ @@ -69,6 +75,56 @@ } else if (propertyKey == DECLINE_BUTTON_CALLBACK) { ButtonCompat declineButton = view.findViewById(R.id.decline_button); declineButton.setOnClickListener(model.get(DECLINE_BUTTON_CALLBACK)); + } else if (propertyKey == SETTINGS_LINK_CALLBACK) { + TextView settingsLink = view.findViewById(R.id.pix_code_detection_settings_link); + if (settingsLink != null) { + settingsLink.setText( + SpanApplier.applySpans( + settingsLink + .getContext() + .getString( + R.string + .pix_account_linking_prompt_b_settings_link), + new SpanApplier.SpanInfo( + "<link1>", + "</link1>", + new ChromeClickableSpan( + settingsLink.getContext(), + v -> model.get(SETTINGS_LINK_CALLBACK).onClick(v)) { + @Override + public void updateDrawState( + android.text.TextPaint textPaint) { + super.updateDrawState(textPaint); + textPaint.setUnderlineText(false); + } + }))); + settingsLink.setMovementMethod(LinkMovementMethod.getInstance()); + } + } else if (propertyKey == VIDEO_LINK_CALLBACK) { + TextView valueProp1 = view.findViewById(R.id.value_prop_message_1); + if (valueProp1 != null) { + valueProp1.setText( + SpanApplier.applySpans( + valueProp1 + .getContext() + .getString( + R.string + .pix_account_linking_prompt_b_value_prop_message_1), + new SpanApplier.SpanInfo( + "<link1>", + "</link1>", + new ChromeClickableSpan( + valueProp1.getContext(), + v -> model.get(VIDEO_LINK_CALLBACK).onClick(v)) { + @Override + public void updateDrawState( + android.text.TextPaint textPaint) { + super.updateDrawState(textPaint); + textPaint.setUnderlineText(false); + } + }))); + valueProp1.setMovementMethod(LinkMovementMethod.getInstance()); + } } else { assert false : "Unhandled update to property: " + propertyKey; }
diff --git a/chrome/browser/feed/android/BUILD.gn b/chrome/browser/feed/android/BUILD.gn index c0ffd47..a3e49009 100644 --- a/chrome/browser/feed/android/BUILD.gn +++ b/chrome/browser/feed/android/BUILD.gn
@@ -217,7 +217,6 @@ robolectric_library("junit") { sources = [ "java/src/org/chromium/chrome/browser/feed/FakeLinearLayoutManager.java", - "java/src/org/chromium/chrome/browser/feed/FeedFeaturesTest.java", "java/src/org/chromium/chrome/browser/feed/FeedListContentManagerTest.java", "java/src/org/chromium/chrome/browser/feed/FeedLoggingParametersTest.java", "java/src/org/chromium/chrome/browser/feed/FeedPlaceholderLayoutTest.java",
diff --git a/chrome/browser/feed/android/java/src/org/chromium/chrome/browser/feed/FeedFeatures.java b/chrome/browser/feed/android/java/src/org/chromium/chrome/browser/feed/FeedFeatures.java index f9bf51a6..e6436b82 100644 --- a/chrome/browser/feed/android/java/src/org/chromium/chrome/browser/feed/FeedFeatures.java +++ b/chrome/browser/feed/android/java/src/org/chromium/chrome/browser/feed/FeedFeatures.java
@@ -9,7 +9,6 @@ import org.chromium.base.ResettersForTesting; import org.chromium.build.annotations.NullMarked; import org.chromium.build.annotations.Nullable; -import org.chromium.chrome.browser.feed.componentinterfaces.SurfaceCoordinator.StreamTabId; import org.chromium.chrome.browser.flags.ChromeFeatureList; import org.chromium.chrome.browser.preferences.Pref; import org.chromium.chrome.browser.profiles.Profile; @@ -58,21 +57,4 @@ sFakePrefServiceForTest = fakePref; ResettersForTesting.register(() -> sFakePrefServiceForTest = null); } - - /** - * Returns the feed tab ID to restore depending on the configured logic controlling the - * "stickiness" of the selected feed tab. - */ - public static @StreamTabId int getFeedTabIdToRestore(Profile profile) { - // Default behavior (reset_for_every_new_ntp). - setLastSeenFeedTabId(profile, StreamTabId.FOR_YOU); - return StreamTabId.FOR_YOU; - } - - public static void setLastSeenFeedTabId(Profile profile, @StreamTabId int tabId) { - // Note: the "first check" flag is updated here to make sure that if setLastSeenFeedTabId is - // called before getFeedTabIdToRestore, the value set here is taken into account in by the - // latter at least for some of the restore logic atlernatives. - getPrefService(profile).setInteger(Pref.LAST_SEEN_FEED_TYPE, tabId); - } }
diff --git a/chrome/browser/feed/android/java/src/org/chromium/chrome/browser/feed/FeedFeaturesTest.java b/chrome/browser/feed/android/java/src/org/chromium/chrome/browser/feed/FeedFeaturesTest.java deleted file mode 100644 index 40a9645..0000000 --- a/chrome/browser/feed/android/java/src/org/chromium/chrome/browser/feed/FeedFeaturesTest.java +++ /dev/null
@@ -1,66 +0,0 @@ -// Copyright 2021 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.feed; - -import static org.junit.Assert.assertEquals; -import static org.mockito.ArgumentMatchers.anyInt; -import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.Mockito.doAnswer; -import static org.mockito.Mockito.when; - -import org.junit.Before; -import org.junit.Rule; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.Mock; -import org.mockito.invocation.InvocationOnMock; -import org.mockito.junit.MockitoJUnit; -import org.mockito.junit.MockitoRule; -import org.mockito.quality.Strictness; - -import org.chromium.base.test.BaseRobolectricTestRunner; -import org.chromium.chrome.browser.feed.componentinterfaces.SurfaceCoordinator.StreamTabId; -import org.chromium.chrome.browser.preferences.Pref; -import org.chromium.chrome.browser.profiles.Profile; -import org.chromium.components.prefs.PrefService; - -/** Unit tests for {@link FeedFeatures}. */ -@RunWith(BaseRobolectricTestRunner.class) -public class FeedFeaturesTest { - @Rule public MockitoRule rule = MockitoJUnit.rule().strictness(Strictness.WARN); - - @Mock private Profile mProfile; - @Mock private PrefService mPrefService; - - private @StreamTabId int mPrefStoredTab; - - @Before - public void setUp() { - // Defaults the stored pref to return the Following feed. - mPrefStoredTab = StreamTabId.FOLLOWING; - // mPrefService = mock(PrefService.class); - FeedFeatures.setFakePrefsForTest(mPrefService); - when(mPrefService.getInteger(Pref.LAST_SEEN_FEED_TYPE)) - .thenAnswer((InvocationOnMock i) -> mPrefStoredTab); - doAnswer( - (InvocationOnMock invocation) -> { - Object[] args = invocation.getArguments(); - mPrefStoredTab = (Integer) args[1]; - return null; - }) - .when(mPrefService) - .setInteger(eq(Pref.LAST_SEEN_FEED_TYPE), anyInt()); - } - - @Test - public void testAlwaysResetByDefault() { - assertEquals(StreamTabId.FOR_YOU, FeedFeatures.getFeedTabIdToRestore(mProfile)); - assertEquals(StreamTabId.FOR_YOU, mPrefStoredTab); - // Simulates a Following tab selection. - FeedFeatures.setLastSeenFeedTabId(mProfile, StreamTabId.FOLLOWING); - assertEquals(StreamTabId.FOR_YOU, FeedFeatures.getFeedTabIdToRestore(mProfile)); - assertEquals(StreamTabId.FOR_YOU, mPrefStoredTab); - } -}
diff --git a/chrome/browser/feed/android/java/src/org/chromium/chrome/browser/feed/FeedStream.java b/chrome/browser/feed/android/java/src/org/chromium/chrome/browser/feed/FeedStream.java index 7610537..43eab92 100644 --- a/chrome/browser/feed/android/java/src/org/chromium/chrome/browser/feed/FeedStream.java +++ b/chrome/browser/feed/android/java/src/org/chromium/chrome/browser/feed/FeedStream.java
@@ -119,7 +119,7 @@ disposition = BrowserUiUtils.getDispositionFromMetaState(mLastMetaState); } mLastMetaState = 0; - openSuggestionUrl(url, disposition, /* inGroup= */ false, options); + openSuggestionUrl(url, disposition, /* inGroup= */ false); break; case OpenMode.NEW_TAB: mBridge.reportOpenAction( @@ -127,19 +127,13 @@ getSliceIdFromView(options.actionSourceView()), OpenActionType.NEW_TAB); openSuggestionUrl( - url, - WindowOpenDisposition.NEW_BACKGROUND_TAB, - /* inGroup= */ false, - options); + url, WindowOpenDisposition.NEW_BACKGROUND_TAB, /* inGroup= */ false); break; case OpenMode.INCOGNITO_TAB: mBridge.reportOtherUserAction( FeedUserActionType.TAPPED_OPEN_IN_NEW_INCOGNITO_TAB); openSuggestionUrl( - url, - WindowOpenDisposition.OFF_THE_RECORD, - /* inGroup= */ false, - options); + url, WindowOpenDisposition.OFF_THE_RECORD, /* inGroup= */ false); break; case OpenMode.DOWNLOAD_LINK: mBridge.reportOtherUserAction(FeedUserActionType.TAPPED_DOWNLOAD); @@ -155,10 +149,7 @@ getSliceIdFromView(options.actionSourceView()), OpenActionType.NEW_TAB_IN_GROUP); openSuggestionUrl( - url, - WindowOpenDisposition.NEW_BACKGROUND_TAB, - /* inGroup= */ true, - options); + url, WindowOpenDisposition.NEW_BACKGROUND_TAB, /* inGroup= */ true); break; } @@ -230,12 +221,7 @@ mBridge.updateUserProfileOnLinkClick(new GURL(url), entityArray); } - // TODO(crbug.com/407797637): Remove the unused parameter openOptions. - private void openSuggestionUrl( - String url, - int disposition, - boolean inGroup, - @SuppressWarnings("unused") OpenUrlOptions openOptions) { + private void openSuggestionUrl(String url, int disposition, boolean inGroup) { int pageId = sPageId.incrementAndGet(); if (disposition != WindowOpenDisposition.NEW_BACKGROUND_TAB && mReliabilityLogger != null) {
diff --git a/chrome/browser/feed/android/java/src/org/chromium/chrome/browser/feed/FeedStreamTest.java b/chrome/browser/feed/android/java/src/org/chromium/chrome/browser/feed/FeedStreamTest.java index c6010daf..3f2b919f 100644 --- a/chrome/browser/feed/android/java/src/org/chromium/chrome/browser/feed/FeedStreamTest.java +++ b/chrome/browser/feed/android/java/src/org/chromium/chrome/browser/feed/FeedStreamTest.java
@@ -509,70 +509,6 @@ @Test @SmallTest - public void testOpenUrlWithWebFeedRecommendation() { - bindToView(); - FeedStream.FeedSurfaceActionsHandler handler = - (FeedStream.FeedSurfaceActionsHandler) - mContentManager.getContextValues(0).get(SurfaceActionsHandler.KEY); - handler.openUrl( - OpenMode.SAME_TAB, - TEST_URL, - new OpenUrlOptions() { - @Override - public boolean shouldShowWebFeedAccelerator() { - return true; - } - - @Override - public String webFeedName() { - return "someWebFeedName"; - } - }); - RobolectricUtil.runAllBackgroundAndUi(); - verify(mActionDelegate) - .openSuggestionUrl( - eq(WindowOpenDisposition.CURRENT_TAB), - mLoadUrlParamsCaptor.capture(), - eq(false), - anyInt(), - eq(handler), - anyInt()); - } - - @Test - @SmallTest - public void testOpenUrlNotShouldShowWebFeedAccelerator() { - bindToView(); - FeedStream.FeedSurfaceActionsHandler handler = - (FeedStream.FeedSurfaceActionsHandler) - mContentManager.getContextValues(0).get(SurfaceActionsHandler.KEY); - handler.openUrl( - OpenMode.SAME_TAB, - TEST_URL, - new OpenUrlOptions() { - @Override - public boolean shouldShowWebFeedAccelerator() { - return false; - } - - @Override - public String webFeedName() { - return "someWebFeedName"; - } - }); - RobolectricUtil.runAllBackgroundAndUi(); - verify(mActionDelegate) - .openSuggestionUrl( - eq(WindowOpenDisposition.CURRENT_TAB), - mLoadUrlParamsCaptor.capture(), - eq(false), - anyInt(), - eq(handler), - anyInt()); - } - - @Test - @SmallTest public void testLogLaunchFinishedOnOpenSuggestionUrl() { bindToView(); FeedStream.FeedSurfaceActionsHandler handler =
diff --git a/chrome/browser/feed/android/java/src/org/chromium/chrome/browser/feed/Stream.java b/chrome/browser/feed/android/java/src/org/chromium/chrome/browser/feed/Stream.java index 72c14aa6..d12f609 100644 --- a/chrome/browser/feed/android/java/src/org/chromium/chrome/browser/feed/Stream.java +++ b/chrome/browser/feed/android/java/src/org/chromium/chrome/browser/feed/Stream.java
@@ -33,9 +33,6 @@ /** Request the immediate refresh of the contents of the active stream. */ default void refreshStream() {} - - /** Disable the follow button, used in case of an error scenario. */ - default void disableFollowButton() {} } /** Called when the Stream is no longer needed. */
diff --git a/chrome/browser/feed/android/java/src/org/chromium/chrome/browser/feed/componentinterfaces/SurfaceCoordinator.java b/chrome/browser/feed/android/java/src/org/chromium/chrome/browser/feed/componentinterfaces/SurfaceCoordinator.java index 85e30bf5..25c74f8 100644 --- a/chrome/browser/feed/android/java/src/org/chromium/chrome/browser/feed/componentinterfaces/SurfaceCoordinator.java +++ b/chrome/browser/feed/android/java/src/org/chromium/chrome/browser/feed/componentinterfaces/SurfaceCoordinator.java
@@ -32,7 +32,7 @@ void onActivityResumed(); /** Enumeration of the possible selection options of feed tabs. */ - @IntDef({StreamTabId.DEFAULT, StreamTabId.FOR_YOU, StreamTabId.FOLLOWING}) + @IntDef({StreamTabId.DEFAULT, StreamTabId.FOR_YOU}) @interface StreamTabId { /** * Used for NTP restore operations, when it may be desirable to recover the previous tab @@ -42,9 +42,6 @@ /** Selects the For you feed tab. */ int FOR_YOU = 0; - - /** Selects the Following feed tab. */ - int FOLLOWING = 1; }; void restoreInstanceState(@Nullable String state);
diff --git a/chrome/browser/feed/android/refresh_task_scheduler_impl.cc b/chrome/browser/feed/android/refresh_task_scheduler_impl.cc index 69e6b2f..883e063e2d2 100644 --- a/chrome/browser/feed/android/refresh_task_scheduler_impl.cc +++ b/chrome/browser/feed/android/refresh_task_scheduler_impl.cc
@@ -20,8 +20,6 @@ switch (id) { case RefreshTaskId::kRefreshForYouFeed: return background_task::TaskIds::FEEDV2_REFRESH_JOB_ID; - case RefreshTaskId::kRefreshWebFeed: - return background_task::TaskIds::WEBFEEDS_REFRESH_JOB_ID; } } } // namespace @@ -71,8 +69,6 @@ switch (task_id) { case RefreshTaskId::kRefreshForYouFeed: return for_you_task_complete_callback_; - case RefreshTaskId::kRefreshWebFeed: - return web_feeds_task_complete_callback_; } }
diff --git a/chrome/browser/feedback/show_feedback_page_browsertest.cc b/chrome/browser/feedback/show_feedback_page_browsertest.cc index 425d84b7..5046662 100644 --- a/chrome/browser/feedback/show_feedback_page_browsertest.cc +++ b/chrome/browser/feedback/show_feedback_page_browsertest.cc
@@ -16,7 +16,6 @@ #include "chrome/browser/feedback/feedback_dialog_utils.h" #include "chrome/browser/profiles/profile.h" #include "chrome/browser/ui/browser.h" -#include "chrome/browser/ui/browser_finder.h" #include "chrome/browser/ui/browser_window/public/global_browser_collection.h" #include "chrome/browser/ui/webui/feedback/feedback_dialog.h" #include "chrome/common/pref_names.h"
diff --git a/chrome/browser/feedback/system_logs/log_sources/chrome_internal_log_source_browsertest.cc b/chrome/browser/feedback/system_logs/log_sources/chrome_internal_log_source_browsertest.cc index c8b06135..c6b06f0e 100644 --- a/chrome/browser/feedback/system_logs/log_sources/chrome_internal_log_source_browsertest.cc +++ b/chrome/browser/feedback/system_logs/log_sources/chrome_internal_log_source_browsertest.cc
@@ -201,21 +201,5 @@ } #endif // BUILDFLAG(IS_CHROMEOS) -#if BUILDFLAG(IS_MAC) && BUILDFLAG(ENABLE_UPDATER) -IN_PROC_BROWSER_TEST_F(ChromeInternalLogSourceTest, UpdaterDataPresent) { - base::ScopedClosureRunner service_override = - updater::OverrideService(updater::UpdateService::Result::kSuccess, {}); - base::RunLoop loop; - updater::CheckForUpdate( - base::BindRepeating([](const updater::UpdateService::UpdateState&) { - }).Then(loop.QuitWhenIdleClosure())); - loop.Run(); - - std::unique_ptr<SystemLogsResponse> response = GetChromeInternalLogs(); - EXPECT_EQ(response->at("update_error_code"), "0/0"); - EXPECT_EQ(response->at("update_hresult"), "0"); -} -#endif // BUILDFLAG(IS_MAC) && BUILDFLAG(ENABLE_UPDATER) - } // namespace } // namespace system_logs
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 05a3746..6c589d2c 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
@@ -44,7 +44,6 @@ #include "chrome/browser/permissions/permission_decision_auto_blocker_factory.h" #include "chrome/browser/profiles/profile.h" #include "chrome/browser/profiles/profile_manager.h" -#include "chrome/browser/ui/browser_finder.h" #include "chrome/browser/ui/file_system_access/file_system_access_dialogs.h" #include "chrome/browser/ui/file_system_access/file_system_access_restricted_directory_dialog.h" #include "chrome/common/chrome_paths.h" @@ -70,6 +69,7 @@ #include "third_party/blink/public/common/features_generated.h" #include "third_party/blink/public/mojom/file_system_access/file_system_access_manager.mojom.h" #include "ui/base/l10n/l10n_util.h" +#include "ui/display/types/display_constants.h" #include "url/gurl.h" #include "url/origin.h" @@ -987,9 +987,13 @@ } // Drop fullscreen mode so that the user sees the URL bar. - base::ScopedClosureRunner fullscreen_block = - web_contents->ForSecurityDropFullscreen( - /*display_id=*/display::kInvalidDisplayId); + auto blocker = web_contents->ForSecurityDropFullscreen( + /*display_id=*/display::kInvalidDisplayId); + if (!blocker) { + RunCallbackAndRecordPermissionRequestOutcome( + std::move(callback), PermissionRequestOutcome::kRequestAborted); + return; + } if (context_->IsEligibleToUpgradePermissionRequestToRestorePrompt( origin_, path_info_.path, handle_type_, user_action_, type_)) { @@ -1001,7 +1005,7 @@ origin_, request_data_list}, base::BindOnce(&PermissionGrantImpl::OnRestorePermissionRequestResult, this, std::move(callback)), - std::move(fullscreen_block)); + std::move(*blocker)); return; } @@ -1019,7 +1023,7 @@ {file_request_data}}, base::BindOnce(&PermissionGrantImpl::OnPermissionRequestResult, this, std::move(callback)), - std::move(fullscreen_block)); + std::move(*blocker)); } const url::Origin& origin() const {
diff --git a/chrome/browser/flag-metadata.json b/chrome/browser/flag-metadata.json index d8a3dd3..f60975b3 100644 --- a/chrome/browser/flag-metadata.json +++ b/chrome/browser/flag-metadata.json
@@ -38,11 +38,6 @@ "expiry_milestone": 110 }, { - "name": "ack-copy-output-request-early-for-view-transition", - "owners": [ "hitawala@chromium.org", "vasilyt@chromium.org", "jonross@chromium.org" ], - "expiry_milestone": 165 - }, - { "name": "ai-hub-new-badge", "owners": [ "joemerramos@google.com", @@ -2247,7 +2242,7 @@ "toyoshim@chromium.org", "//content/browser/preloading/prerender/OWNERS" ], - "expiry_milestone": 150 + "expiry_milestone": 160 }, { "name": "default-site-instance-groups", @@ -3022,6 +3017,11 @@ "expiry_milestone": 160 }, { + "name": "enable-cast-streaming-offer-hardware-first", + "owners": [ "jophba@chromium.org", "openscreen-eng@chromium.org" ], + "expiry_milestone": 160 + }, + { "name": "enable-cast-streaming-vp8", "owners": [ "jophba@chromium.org", "openscreen-eng@chromium.org" ], "expiry_milestone": 160 @@ -6364,6 +6364,11 @@ "expiry_milestone": 160 }, { + "name": "long-screenshots-lenient-memory-check", + "owners": ["jhimawan@google.com", "jhimawan@chromium.org"], + "expiry_milestone": 150 + }, + { "name": "mac-address-randomization", "owners": [ "chadduffin@chromium.org", "cros-device-enablement@google.com"], "expiry_milestone": 135 @@ -9720,16 +9725,6 @@ "expiry_milestone": -1 }, { - "name": "web-feed-deprecation", - "owners": [ "//chrome/android/feed/OWNERS", "jianli@chromium.org" ], - "expiry_milestone": 145 - }, - { - "name": "web-feed-onboarding", - "owners": ["//chrome/android/feed/OWNERS", "feed@chromium.org" ], - "expiry_milestone": 140 - }, - { "name": "web-hid-in-web-view", "owners": [ "giovax@google.com", "pwa-commercial@google.com", "pwa-team@google.com" ], "expiry_milestone": 180
diff --git a/chrome/browser/flag_descriptions.h b/chrome/browser/flag_descriptions.h index 43ef5d2..51cbb2b1 100644 --- a/chrome/browser/flag_descriptions.h +++ b/chrome/browser/flag_descriptions.h
@@ -2394,13 +2394,6 @@ "icons. See https://github.com/WICG/file-handling/blob/main/explainer.md " "for more information."; -inline constexpr char kAckCopyOutputRequestEarlyForViewTransitionName[] = - "Ack CopyOutputRequest early for View Transition"; -inline constexpr char kAckCopyOutputRequestEarlyForViewTransitionDescription[] = - "If enabled, send acks for CopyOutputRequest completion immediately to " - "unblock navigation for ViewTransitions while CopyOutputRequests are in " - "progress. This is a fast-path for ViewTransitions."; - inline constexpr char kFillOnAccountSelectName[] = "Fill passwords on account selection"; inline constexpr char kFillOnAccountSelectDescription[] = @@ -2506,6 +2499,13 @@ "supported by the platform (regardless of recommendation). If disabled, " "hardware VP9 encoding will never be used."; +inline constexpr char kCastStreamingOfferHardwareFirstName[] = + "Enables Cast Streaming to offer only hardware accelerated codecs first"; +inline constexpr char kCastStreamingOfferHardwareFirstDescription[] = + "When enabled, the Cast Streaming session negotiates only the hardware " + "accelerated codecs first, and only offers software encoding if the " + "receiver rejects the initial offer."; + inline constexpr char kCastStreamingMediaVideoEncoderName[] = "Toggles using the media::VideoEncoder implementation for Cast Streaming"; inline constexpr char kCastStreamingMediaVideoEncoderDescription[] = @@ -5752,6 +5752,12 @@ inline constexpr char kLogoViewRefactorDescription[] = "Enables the Logo View Refactor feature."; +inline constexpr char kLongScreenshotsLenientMemoryCheckName[] = + "Long Screenshots Lenient Memory Check"; +inline constexpr char kLongScreenshotsLenientMemoryCheckDescription[] = + "Use more lenient memory checks (CRITICAL instead of MODERATE) for long " + "screenshots."; + inline constexpr char kMaliciousApkDownloadCheckName[] = "Malicious APK download check"; inline constexpr char kMaliciousApkDownloadCheckDescription[] = @@ -6114,14 +6120,6 @@ inline constexpr char kUseAngleGLES[] = "OpenGL ES"; inline constexpr char kUseAngleVulkan[] = "Vulkan"; -inline constexpr char kWebFeedDeprecationName[] = "Web feed deprecation"; -inline constexpr char kWebFeedDeprecationDescription[] = - "Deprecate the web feed."; - -inline constexpr char kWebFeedOnboardingName[] = "Web Feed Onboarding"; -inline constexpr char kWebFeedOnboardingDescription[] = - "Helps the user understand how to use the web feed."; - inline constexpr char kXplatSyncedSetupName[] = "Cross-platform synced setup"; inline constexpr char kXplatSyncedSetupDescription[] = "Enables the Cross-platform synced setup feature.";
diff --git a/chrome/browser/flags/android/chrome_feature_list.cc b/chrome/browser/flags/android/chrome_feature_list.cc index 1b8134c..78c38fc 100644 --- a/chrome/browser/flags/android/chrome_feature_list.cc +++ b/chrome/browser/flags/android/chrome_feature_list.cc
@@ -207,7 +207,6 @@ &feed::kFeedRecyclerBinderUnmountOnDetach, &feed::kFeedSignedOutViewDemotion, &feed::kInterestFeedV2, - &feed::kWebFeedOnboarding, &feed::kXsurfaceMetricsReporting, &finds::features::kChromeFinds, &history::kOrganicRepeatableQueries, @@ -385,6 +384,7 @@ &kLockTopControlsOnLargeTablets, &kLockTopControlsOnLargeTabletsV2, &kLogoViewRefactor, + &kLongScreenshotsLenientMemoryCheck, &kMayLaunchUrlUsesSeparateStoragePartition, &kMediaIndicatorsAndroid, &kMostVisitedTilesCustomization, @@ -739,7 +739,8 @@ BASE_FEATURE(kLockBackPressHandlerAtStart, base::FEATURE_ENABLED_BY_DEFAULT); BASE_FEATURE(kLockTopControlsOnLargeTablets, base::FEATURE_DISABLED_BY_DEFAULT); BASE_FEATURE(kLockTopControlsOnLargeTabletsV2, base::FEATURE_DISABLED_BY_DEFAULT); -BASE_FEATURE(kLogoViewRefactor, base::FEATURE_DISABLED_BY_DEFAULT); +BASE_FEATURE(kLogoViewRefactor, base::FEATURE_ENABLED_BY_DEFAULT); +BASE_FEATURE(kLongScreenshotsLenientMemoryCheck, base::FEATURE_DISABLED_BY_DEFAULT); // Enables an experimental feature which forces mayLaunchUrl to use a different // storage partition. This may reduce performance. This should not be enabled by // default.
diff --git a/chrome/browser/flags/android/chrome_feature_list.h b/chrome/browser/flags/android/chrome_feature_list.h index 8223468b..1461a18 100644 --- a/chrome/browser/flags/android/chrome_feature_list.h +++ b/chrome/browser/flags/android/chrome_feature_list.h
@@ -213,6 +213,7 @@ BASE_DECLARE_FEATURE(kLockTopControlsOnLargeTablets); BASE_DECLARE_FEATURE(kLockTopControlsOnLargeTabletsV2); BASE_DECLARE_FEATURE(kLogoViewRefactor); +BASE_DECLARE_FEATURE(kLongScreenshotsLenientMemoryCheck); BASE_DECLARE_FEATURE(kMayLaunchUrlUsesSeparateStoragePartition); BASE_DECLARE_FEATURE(kMediaIndicatorsAndroid); BASE_DECLARE_FEATURE(kMostVisitedTilesCustomization);
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 d872168..9ec5a7dc 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
@@ -512,6 +512,8 @@ public static final String LOCK_TOP_CONTROLS_ON_LARGE_TABLETS_V2 = "LockTopControlsOnLargeTabletsV2"; public static final String LOGO_VIEW_REFACTOR = "LogoViewRefactor"; + public static final String LONG_SCREENSHOTS_LENIENT_MEMORY_CHECK = + "LongScreenshotsLenientMemoryCheck"; public static final String LOOKALIKE_NAVIGATION_URL_SUGGESTIONS_UI = "LookalikeUrlNavigationSuggestionsUI"; public static final String LOW_END_MEMORY_EXPERIMENT = BaseFeatures.LOW_END_MEMORY_EXPERIMENT; @@ -720,7 +722,6 @@ public static final String WEB_APK_INSTALL_FAILURE_NOTIFICATION = "WebApkInstallFailureNotification"; public static final String WEB_APK_MIN_SHELL_APK_VERSION = "WebApkMinShellVersion"; - public static final String WEB_FEED_ONBOARDING = "WebFeedOnboarding"; public static final String WEB_OTP_CROSS_DEVICE_SIMPLE_STRING = "WebOtpCrossDeviceSimpleString"; public static final String XPLAT_SYNCED_SETUP = "XplatSyncedSetup"; public static final String XSURFACE_METRICS_REPORTING = "XsurfaceMetricsReporting"; @@ -1053,8 +1054,7 @@ /* defaultValue= */ true, /* defaultValueInTests= */ true); public static final CachedFlag sLogoViewRefactor = - newCachedFlag( - LOGO_VIEW_REFACTOR, /* defaultValue= */ false, /* defaultValueInTests= */ true); + newCachedFlag(LOGO_VIEW_REFACTOR, /* defaultValue= */ true); public static final CachedFlag sMaliciousApkDownloadCheck = newCachedFlag( MALICIOUS_APK_DOWNLOAD_CHECK,
diff --git a/chrome/browser/glic/BUILD.gn b/chrome/browser/glic/BUILD.gn index 3c49283..d9deba8 100644 --- a/chrome/browser/glic/BUILD.gn +++ b/chrome/browser/glic/BUILD.gn
@@ -63,9 +63,8 @@ "public/glic_passkeys.h", "public/glic_side_panel_coordinator.cc", "public/glic_side_panel_coordinator.h", - "service/glic_instance_helper.cc", "service/glic_instance_helper.h", - "service/metrics/glic_instance_helper_metrics.h", + "service/glic_ui_types.h", "service/metrics/metrics_types.cc", "service/metrics/metrics_types.h", ] @@ -194,6 +193,33 @@ sources = [ "glic_enums.h" ] } +source_set("glic_metrics") { + sources = [ + "service/glic_state_tracker.cc", + "service/glic_state_tracker.h", + "service/metrics/glic_instance_coordinator_metrics.cc", + "service/metrics/glic_instance_coordinator_metrics.h", + "service/metrics/glic_instance_helper_metrics.cc", + "service/metrics/glic_instance_helper_metrics.h", + "service/metrics/glic_instance_metrics.cc", + "service/metrics/glic_instance_metrics.h", + "service/metrics/glic_metrics_session_manager.cc", + "service/metrics/glic_metrics_session_manager.h", + ] + deps = [ + ":enum", + ":glic", + ":mojo_bindings", + "//base", + "//chrome/browser/glic/public:features", + "//components/metrics", + "//components/prefs", + "//components/skills/public", + "//content/public/browser", + "//services/metrics/public/cpp:ukm_builders", + ] +} + # TODO(b/430371855): This is the better target for resources only needed # by the Glic implementation. Prefer adding to this target any new sources or # dependencies that are only needed internally. @@ -277,24 +303,15 @@ "public/glic_keyed_service_factory.cc", "service/glic_instance_coordinator_impl.cc", "service/glic_instance_coordinator_impl.h", + "service/glic_instance_helper.cc", "service/glic_instance_impl.cc", "service/glic_instance_impl.h", "service/glic_invoke_handler.cc", "service/glic_invoke_handler.h", "service/glic_invoke_task.cc", "service/glic_invoke_task.h", - "service/glic_state_tracker.cc", - "service/glic_state_tracker.h", "service/glic_ui_embedder.h", "service/glic_ui_types.cc", - "service/glic_ui_types.h", - "service/metrics/glic_instance_coordinator_metrics.cc", - "service/metrics/glic_instance_coordinator_metrics.h", - "service/metrics/glic_instance_helper_metrics.cc", - "service/metrics/glic_instance_metrics.cc", - "service/metrics/glic_instance_metrics.h", - "service/metrics/glic_metrics_session_manager.cc", - "service/metrics/glic_metrics_session_manager.h", "suggestions/caching_zero_state_suggestions_manager.cc", "suggestions/caching_zero_state_suggestions_manager.h", "widget/browser_conditions.cc", @@ -303,9 +320,13 @@ "widget/conversions.h", ] - public_deps = [ "//chrome/browser:browser_public_dependencies" ] + public_deps = [ + "//chrome/browser:browser_public_dependencies", + "//chrome/browser/actor", + ] deps = [ ":glic", + ":glic_metrics", "//chrome/browser:browser_process", "//chrome/browser:global_features", "//chrome/browser/actor", @@ -395,8 +416,6 @@ "host/context/glic_screenshot_capturer.h", "widget/glic_floating_ui.cc", "widget/glic_floating_ui.h", - "widget/glic_inactive_floating_ui.cc", - "widget/glic_inactive_floating_ui.h", "widget/glic_inactive_side_panel_ui.cc", "widget/glic_inactive_side_panel_ui.h", "widget/glic_side_panel_coordinator_impl.cc", @@ -444,8 +463,6 @@ "glic_settings_util_android.cc", "widget/glic_floating_ui_android.cc", "widget/glic_floating_ui_android.h", - "widget/glic_inactive_floating_ui_android.cc", - "widget/glic_inactive_floating_ui_android.h", "widget/glic_inactive_side_panel_ui_android.cc", "widget/glic_inactive_side_panel_ui_android.h", "widget/glic_side_panel_coordinator_android.cc", @@ -558,6 +575,7 @@ ] deps = [ ":glic", + ":glic_metrics", ":impl", "//base/test:test_support", "//chrome/browser/background/glic", @@ -641,6 +659,7 @@ deps = [ ":glic", + ":glic_metrics", ":impl", "//chrome/app:command_ids", "//chrome/browser:browser_features", @@ -687,7 +706,6 @@ "host/glic_api_browsertest.cc", "host/glic_permission_enforcement_browsertest.cc", "host/guest_util_browsertest.cc", - "host/new_glic_api_alt_browsertest.cc", "public/glic_enabling_browsertest.cc", "widget/application_hotkey_delegate_browsertest.cc", "widget/glic_side_panel_coordinator_browsertest.cc", @@ -769,6 +787,7 @@ testonly = true defines = [ "HAS_OUT_OF_PROC_TEST_RUNNER" ] sources = [ + "glic_selection_observer_interactive_uitest.cc", "glic_settings_util_interactive_uitest.cc", "glic_user_status_interactive_uitest.cc", "host/glic_actor_attempt_form_filling_interactive_ui_test.cc", @@ -810,6 +829,7 @@ deps = [ ":glic", + ":glic_metrics", ":impl", "//chrome/browser:global_features", "//chrome/browser/actor",
diff --git a/chrome/browser/glic/actor/BUILD.gn b/chrome/browser/glic/actor/BUILD.gn index 8e10ed87..0f8a535 100644 --- a/chrome/browser/glic/actor/BUILD.gn +++ b/chrome/browser/glic/actor/BUILD.gn
@@ -16,6 +16,7 @@ "//chrome/browser/actor", "//chrome/browser/actor/ui", "//chrome/browser/glic", + "//chrome/browser/glic:glic_metrics", "//chrome/browser/glic:mojo_bindings", "//chrome/browser/signin", "//chrome/common:actor_webui_mojom",
diff --git a/chrome/browser/glic/actor/glic_actor_task_manager.cc b/chrome/browser/glic/actor/glic_actor_task_manager.cc index 6752a9da..c459994 100644 --- a/chrome/browser/glic/actor/glic_actor_task_manager.cc +++ b/chrome/browser/glic/actor/glic_actor_task_manager.cc
@@ -28,6 +28,7 @@ #include "chrome/browser/glic/host/glic_mojom_traits.h" #include "chrome/browser/glic/public/glic_keyed_service.h" #include "chrome/browser/glic/public/glic_keyed_service_factory.h" +#include "chrome/browser/glic/service/metrics/glic_instance_metrics.h" #include "chrome/browser/profiles/profile.h" #include "chrome/browser/ui/browser_window/public/browser_window_interface_iterator.h" #include "chrome/common/actor.mojom-shared.h" @@ -51,6 +52,8 @@ BASE_FEATURE(kGlicReloadAfterPerformActionsCrash, base::FEATURE_ENABLED_BY_DEFAULT); BASE_FEATURE(kGlicRetryFailedObservations, base::FEATURE_ENABLED_BY_DEFAULT); +BASE_FEATURE(kGlicRequireConversationIdForActorTask, + base::FEATURE_DISABLED_BY_DEFAULT); // Observations can sometimes fail due to timeouts or issues stemming from // high-load scenarios. When we retry, give it a few seconds to increase the @@ -84,57 +87,78 @@ } // namespace +GlicActorClientSession::GlicActorClientSession(GlicActorTaskManager* manager) + : manager_(*manager) { + // Unretained is safe because the subscription cancels the callback when + // this is destroyed. + can_act_on_web_changed_subscription_ = + actor_policy_checker().AddActOnWebCapabilityChangedCallback( + base::BindRepeating(&GlicActorClientSession::CanActOnWebChanged, + base::Unretained(this))); +} + +GlicActorClientSession::~GlicActorClientSession() { + CancelTask(); +} + GlicActorTaskManager::GlicActorTaskManager( Profile* profile, actor::ActorKeyedService* actor_keyed_service, - GlicActorPolicyChecker& actor_policy_checker) + GlicActorPolicyChecker& actor_policy_checker, + GlicInstanceMetrics* instance_metrics, + Delegate* delegate) : profile_(profile), actor_keyed_service_(actor_keyed_service), - actor_policy_checker_(actor_policy_checker) { + actor_policy_checker_(actor_policy_checker), + instance_metrics_(instance_metrics), + delegate_(delegate) { CHECK(profile_); CHECK(actor_keyed_service_); - - // Unretained is safe because the subscription cancels the callback when this - // is destroyed. - can_act_on_web_changed_subscription_ = - actor_policy_checker.AddActOnWebCapabilityChangedCallback( - base::BindRepeating(&GlicActorTaskManager::CanActOnWebChanged, - base::Unretained(this))); + CHECK(base::FeatureList::IsEnabled(features::kGlicActor)); } GlicActorTaskManager::~GlicActorTaskManager() = default; -void GlicActorTaskManager::CreateTask( - base::WeakPtr<actor::ActorTaskDelegate> delegate, - std::optional<std::string> conversation_id, +void GlicActorClientSession::CreateTask( actor::webui::mojom::TaskOptionsPtr options, - mojom::WebClientHandler::CreateTaskCallback callback) { - if (!base::FeatureList::IsEnabled(features::kGlicActor)) { - std::move(callback).Run( - base::unexpected(mojom::CreateTaskErrorReason::kTaskSystemUnavailable)); - return; - } - + glic::mojom::WebClientHandler::CreateTaskCallback callback) { + instance_metrics().OnCreateTask(); if (!current_task_id_.is_null()) { std::move(callback).Run( base::unexpected(mojom::CreateTaskErrorReason::kExistingActiveTask)); return; } + // Conversation ID must be available since a turn is required to create a task + // and an ID becomes available at first turn. If you hit this in a test you + // probably need to call RegisterConversation on your GlicInstance. + // TODO(b/494212836) - The front end currently doesn't guarantee that + // RegisterConversation is called first. Allow creating a task without a + // conversationId until that's fixed (the conversationId in ActorTask isn't + // yet used). + const std::optional<std::string> conversation_id = + manager_->delegate_->conversation_id(); + if (!conversation_id.has_value() && + base::FeatureList::IsEnabled(kGlicRequireConversationIdForActorTask)) { + std::move(callback).Run(base::unexpected( + mojom::CreateTaskErrorReason::kConversationNotRegistered)); + return; + } + const GlicActorPolicyChecker::CannotActReason reason_to_log = - actor_policy_checker_->CanActOnWeb() + actor_policy_checker().CanActOnWeb() ? GlicActorPolicyChecker::CannotActReason::kNone - : actor_policy_checker_->CannotActOnWebReason(); + : actor_policy_checker().CannotActOnWebReason(); base::UmaHistogramEnumeration("Actor.Task.CreateFailedReason", reason_to_log); - if (!actor_policy_checker_->CanActOnWeb()) { + if (!actor_policy_checker().CanActOnWeb()) { // TODO(bokan): This was moved here to preserve behavior; the failure case // was only counting policy blocks which are a Glic-only concept. However, // the UMA histogram is in Actor which implies it records all sources of // actor tasks. This histogram should probably be migrated to be Glic // namespaced. actor::RecordActorTaskCreated(/*success=*/false); - actor_keyed_service_->GetJournal().Log( + actor_keyed_service().GetJournal().Log( GURL(), actor::TaskId(), "GlicActorTaskManager::CreateTask", actor::JournalDetailsBuilder() .AddError("Actuation capability disabled") @@ -159,23 +183,24 @@ options->duration = actor::webui::mojom::TaskDuration::kDefault; } - current_task_id_ = actor_keyed_service_->CreateTaskWithOptions( + current_task_id_ = actor_keyed_service().CreateTaskWithOptions( actor::TaskSourceInfo(actor::TaskSourceInfo::Client::kGlic, conversation_id), - &actor_policy_checker_.get(), std::move(options), std::move(delegate)); + &actor_policy_checker(), std::move(options), + manager_->delegate_->GetActorTaskDelegate()); CHECK(!current_task_id_.is_null()); - actuating_changed_callbacks_.Notify(true); + manager_->SetActuating(true); actor_task_state_changed_subscription_ = - actor_keyed_service_->AddTaskStateChangedCallback(base::BindRepeating( - &GlicActorTaskManager::NotifyActorTaskStateChanged, + actor_keyed_service().AddTaskStateChangedCallback(base::BindRepeating( + &GlicActorClientSession::NotifyActorTaskStateChanged, base::Unretained(this))); std::move(callback).Run(current_task_id_.value()); } -void GlicActorTaskManager::PerformActionsFinished( +void GlicActorClientSession::PerformActionsFinished( mojom::WebClientHandler::PerformActionsCallback callback, actor::TaskId task_id, base::TimeTicks start_time, @@ -189,13 +214,13 @@ std::optional<size_t> index_of_failed_action; actor::ExtractErrorResult(action_results, &result_code, index_of_failed_action); - actor_keyed_service_->GetJournal().Log( + actor_keyed_service().GetJournal().Log( GURL::EmptyGURL(), task_id, "PerformActionsFinished", actor::JournalDetailsBuilder() .Add("result_code", base::ToString(result_code)) .Build()); - actor::ActorTask* task = actor_keyed_service_->GetTask(task_id); + actor::ActorTask* task = actor_keyed_service().GetTask(task_id); // TODO(b/470985724): Reply at the time the task is stopped/canceled instead // of here. if (!task) { @@ -221,7 +246,7 @@ actor::ExtractErrorResult(action_results, &controller_result_code, controller_index_of_failed_action); auto journal_entry = - actor_keyed_service_->GetJournal().CreatePendingAsyncEntry( + actor_keyed_service().GetJournal().CreatePendingAsyncEntry( GURL(), task_id, MakeBrowserTrackUUID(task_id), "TabObservationController", actor::JournalDetailsBuilder() @@ -234,12 +259,12 @@ // owned by this class and the controller guarantees that it will not run // the callback after its own destruction. auto done_callback = - base::BindOnce(&GlicActorTaskManager::OnPerformActionsComplete, + base::BindOnce(&GlicActorClientSession::OnPerformActionsComplete, base::Unretained(this), std::move(callback), start_time, action_results, std::move(journal_entry)); auto controller = std::make_unique<actor::TabObservationController>( - profile_, task_id, start_time, skip_async_observation_information, + &profile(), task_id, start_time, skip_async_observation_information, action_results, std::move(done_callback)); controller->set_screenshot_collection_options( @@ -262,7 +287,7 @@ // but ensure we respond with kRendererCrashed since the reload/crash is // state-destructive. auto retry_perform_actions_finished = base::BindOnce( - &GlicActorTaskManager::PerformActionsFinished, + &GlicActorClientSession::PerformActionsFinished, weak_ptr_factory_.GetWeakPtr(), std::move(callback), task_id, start_time, skip_async_observation_information, std::move(screenshot_collection_options), std::move(action_results)); @@ -273,13 +298,13 @@ } actor::BuildActionsResultWithObservations( - *profile_, start_time, std::move(action_results), *task, + profile(), start_time, std::move(action_results), *task, skip_async_observation_information, screenshot_collection_options, - base::BindOnce(&GlicActorTaskManager::DidFinishBuildObservation, + base::BindOnce(&GlicActorClientSession::DidFinishBuildObservation, weak_ptr_factory_.GetWeakPtr(), std::move(callback))); } -void GlicActorTaskManager::OnPerformActionsComplete( +void GlicActorClientSession::OnPerformActionsComplete( mojom::WebClientHandler::PerformActionsCallback callback, base::TimeTicks start_time, std::vector<actor::ActionResultWithLatencyInfo> action_results, @@ -359,7 +384,7 @@ std::move(callback).Run(mojo_base::ProtoWrapper(response)); } -void GlicActorTaskManager::DidFinishBuildObservation( +void GlicActorClientSession::DidFinishBuildObservation( mojom::WebClientHandler::PerformActionsCallback callback, base::TimeTicks start_time, std::vector<actor::ActionResultWithLatencyInfo> action_results, @@ -386,7 +411,7 @@ if (tab_observation.result() != TabObservation::TAB_OBSERVATION_OK) { attempted_observation_retry_ = true; - actor_keyed_service_->GetJournal().Log( + actor_keyed_service().GetJournal().Log( GURL::EmptyGURL(), task_id, "Retrying failed observation", actor::JournalDetailsBuilder() .Add("tab_id", tab_observation.id()) @@ -394,7 +419,7 @@ .Build()); auto retry_perform_actions_finished = base::BindOnce( - &GlicActorTaskManager::PerformActionsFinished, + &GlicActorClientSession::PerformActionsFinished, weak_ptr_factory_.GetWeakPtr(), std::move(callback), task_id, start_time, skip_async_observation_information, std::move(screenshot_collection_options), @@ -414,9 +439,9 @@ std::move(callback).Run(mojo_base::ProtoWrapper(*result)); } -void GlicActorTaskManager::ReloadCrashedTab(tabs::TabInterface& crashed_tab, - actor::TaskId task_id, - base::OnceClosure callback) { +void GlicActorClientSession::ReloadCrashedTab(tabs::TabInterface& crashed_tab, + actor::TaskId task_id, + base::OnceClosure callback) { // TODO(b/464019189): This code only deals with the first crashed tab per // Task. If they are multiple tabs that crashed we might want to figure out // how to deal with that. @@ -427,31 +452,32 @@ } CHECK(contents->IsCrashed()); - actor_keyed_service_->GetJournal().Log( + actor_keyed_service().GetJournal().Log( contents->GetLastCommittedURL(), task_id, "GlicActorTaskManager::ReloadCrashedTab", /*details=*/{}); reload_observer_ = std::make_unique<actor::ObservationDelayController>( - task_id, actor_keyed_service_->GetJournal()); + task_id, actor_keyed_service().GetJournal()); // TODO(b/471205189): Should `check_for_repost` be true here since a user // isn't in control? contents->GetController().Reload(content::ReloadType::NORMAL, true); reload_observer_->Wait( crashed_tab, - base::BindOnce(&GlicActorTaskManager::ReloadObserverDone, + base::BindOnce(&GlicActorClientSession::ReloadObserverDone, base::Unretained(this), crashed_tab.GetHandle(), std::move(callback))); } -void GlicActorTaskManager::PerformActions( +void GlicActorClientSession::PerformActions( const std::vector<uint8_t>& actions_proto, mojom::WebClientHandler::PerformActionsCallback callback) { + instance_metrics().OnPerformActions(); base::TimeTicks start_time = base::TimeTicks::Now(); // TODO(bokan): Refactor the actor code in this class into an actor-specific // wrapper for proto-to-actor conversion. optimization_guide::proto::Actions actions; if (!actions.ParseFromArray(actions_proto.data(), actions_proto.size())) { // TODO(bokan): include the base64 proto in the error - actor_keyed_service_->GetJournal().Log( + actor_keyed_service().GetJournal().Log( GURL(), actor::TaskId(), "GlicPerformActions", actor::JournalDetailsBuilder().AddError("Invalid Proto").Build()); std::move(callback).Run( @@ -459,14 +485,14 @@ return; } - actor_keyed_service_->GetJournal().Log( + actor_keyed_service().GetJournal().Log( GURL(), actor::TaskId(actions.task_id()), "GlicPerformActions", actor::JournalDetailsBuilder() .Add("proto", actor::ToBase64(actions)) .Build()); if (!actions.has_task_id()) { - actor_keyed_service_->GetJournal().Log( + actor_keyed_service().GetJournal().Log( GURL(), actor::TaskId(actions.task_id()), "GlicPerformActions", actor::JournalDetailsBuilder().AddError("Missing Task Id").Build()); std::move(callback).Run( @@ -475,8 +501,8 @@ } actor::TaskId task_id(actions.task_id()); - if (!actor_keyed_service_->GetTask(task_id)) { - actor_keyed_service_->GetJournal().Log(GURL::EmptyGURL(), task_id, + if (!actor_keyed_service().GetTask(task_id)) { + actor_keyed_service().GetJournal().Log(GURL::EmptyGURL(), task_id, "Act Failed", actor::JournalDetailsBuilder() .AddError("No such task") @@ -492,7 +518,7 @@ actor::BuildToolRequestResult requests = actor::BuildToolRequest(actions); if (!requests.has_value()) { - actor_keyed_service_->GetJournal().Log( + actor_keyed_service().GetJournal().Log( GURL::EmptyGURL(), task_id, "Act Failed", actor::JournalDetailsBuilder() .AddError("Failed to convert proto::Actions to ToolRequest") @@ -510,18 +536,18 @@ actions.skip_async_observation_collection(); attempted_observation_retry_ = false; - actor_keyed_service_->PerformActions( + actor_keyed_service().PerformActions( task_id, std::move(requests.value()), actor::ActorTaskMetadata(actions), - base::BindOnce(&GlicActorTaskManager::PerformActionsFinished, + base::BindOnce(&GlicActorClientSession::PerformActionsFinished, GetWeakPtr(), std::move(callback), task_id, start_time, skip_async_observation_information, actor::GetScreenshotCollectionOptions(actions))); } -void GlicActorTaskManager::CancelActions( +void GlicActorClientSession::CancelActions( actor::TaskId task_id, mojom::WebClientHandler::CancelActionsCallback callback) { - actor::ActorTask* task = actor_keyed_service_->GetTask(task_id); + actor::ActorTask* task = actor_keyed_service().GetTask(task_id); if (!task) { std::move(callback).Run(mojom::CancelActionsResult::kTaskNotFound); return; @@ -535,9 +561,10 @@ : mojom::CancelActionsResult::kFailed)); } -void GlicActorTaskManager::StopActorTask( +void GlicActorClientSession::StopActorTask( actor::TaskId task_id, mojom::ActorTaskStopReason stop_reason) { + instance_metrics().OnStopActorTask(); actor::ActorTask::StoppedReason reason; switch (stop_reason) { case glic::mojom::ActorTaskStopReason::kTaskComplete: @@ -575,13 +602,14 @@ #endif } -void GlicActorTaskManager::PauseActorTask( +void GlicActorClientSession::PauseActorTask( actor::TaskId task_id, mojom::ActorTaskPauseReason pause_reason, tabs::TabInterface::Handle tab_handle) { - actor::ActorTask* task = actor_keyed_service_->GetTask(task_id); + instance_metrics().OnPauseActorTask(); + actor::ActorTask* task = actor_keyed_service().GetTask(task_id); if (!task || task->IsCompleted() || task->IsUnderUserControl()) { - actor_keyed_service_->GetJournal().Log( + actor_keyed_service().GetJournal().Log( GURL::EmptyGURL(), task_id, "Failed to pause task", actor::JournalDetailsBuilder() .AddError(task ? "Task is not running" : "No such task") @@ -601,14 +629,15 @@ task->Pause(from_actor); } -void GlicActorTaskManager::ResumeActorTask( +void GlicActorClientSession::ResumeActorTask( actor::TaskId task_id, const mojom::GetTabContextOptions& context_options, glic::mojom::WebClientHandler::ResumeActorTaskCallback callback) { - actor::ActorTask* task = actor_keyed_service_->GetTask(task_id); + instance_metrics().OnResumeActorTask(); + actor::ActorTask* task = actor_keyed_service().GetTask(task_id); if (!task || !task->IsUnderUserControl()) { std::string error_message = task ? "Task is not paused" : "No such task"; - actor_keyed_service_->GetJournal().Log(GURL::EmptyGURL(), task_id, + actor_keyed_service().GetJournal().Log(GURL::EmptyGURL(), task_id, "Failed to resume task", actor::JournalDetailsBuilder() .AddError(error_message) @@ -643,7 +672,7 @@ } if (!tab_of_resumed_task) { std::string error_message = "No tab for observation"; - actor_keyed_service_->GetJournal().Log(GURL::EmptyGURL(), task_id, + actor_keyed_service().GetJournal().Log(GURL::EmptyGURL(), task_id, "Failed to resume task", actor::JournalDetailsBuilder() .AddError(error_message) @@ -708,26 +737,31 @@ std::move(callback), CreateTabData(tab_of_resumed_task), resume_response_code); - actor_keyed_service_->RequestTabObservation( + actor_keyed_service().RequestTabObservation( *tab_of_resumed_task, task_id, context_options.screenshot_collection_options, std::move(observation_callback)); } -bool GlicActorTaskManager::IsActuating() const { +bool GlicActorClientSession::IsActuating() const { return !!current_task_id_; } +bool GlicActorTaskManager::IsActuating() const { + return session_ && session_->IsActuating(); +} + base::CallbackListSubscription GlicActorTaskManager::AddActuatingChangedCallback( base::RepeatingCallback<void(bool)> callback) { return actuating_changed_callbacks_.Add(std::move(callback)); } -void GlicActorTaskManager::InterruptActorTask(actor::TaskId task_id) { - actor::ActorTask* task = actor_keyed_service_->GetTask(task_id); +void GlicActorClientSession::InterruptActorTask(actor::TaskId task_id) { + instance_metrics().InterruptActorTask(); + actor::ActorTask* task = actor_keyed_service().GetTask(task_id); if (!task) { - actor_keyed_service_->GetJournal().Log(GURL::EmptyGURL(), task_id, + actor_keyed_service().GetJournal().Log(GURL::EmptyGURL(), task_id, "Failed to interrupt task", actor::JournalDetailsBuilder() .AddError("No such task") @@ -738,10 +772,11 @@ task->Interrupt(); } -void GlicActorTaskManager::UninterruptActorTask(actor::TaskId task_id) { - actor::ActorTask* task = actor_keyed_service_->GetTask(task_id); +void GlicActorClientSession::UninterruptActorTask(actor::TaskId task_id) { + instance_metrics().UninterruptActorTask(); + actor::ActorTask* task = actor_keyed_service().GetTask(task_id); if (!task) { - actor_keyed_service_->GetJournal().Log(GURL::EmptyGURL(), task_id, + actor_keyed_service().GetJournal().Log(GURL::EmptyGURL(), task_id, "Failed to uninterrupt task", actor::JournalDetailsBuilder() .AddError("No such task") @@ -756,7 +791,7 @@ task->Uninterrupt(next_state); } -void GlicActorTaskManager::CreateActorTab( +void GlicActorClientSession::CreateActorTab( actor::TaskId task_id, bool open_in_background, const std::optional<int32_t>& initiator_tab_id, @@ -770,20 +805,20 @@ ? SessionID::FromSerializedValue(*initiator_window_id) : SessionID::InvalidValue(); - actor_keyed_service_->CreateActorTab( + actor_keyed_service().CreateActorTab( task_id, open_in_background, initiator_tab_handle, initiator_window_session_id, - base::BindOnce(&GlicActorTaskManager::CreateActorTabFinished, + base::BindOnce(&GlicActorClientSession::CreateActorTabFinished, GetWeakPtr(), std::move(callback))); } -void GlicActorTaskManager::CreateActorTabFinished( +void GlicActorClientSession::CreateActorTabFinished( glic::mojom::WebClientHandler::CreateActorTabCallback callback, tabs::TabInterface* new_tab) { std::move(callback).Run(CreateTabData(new_tab)); } -void GlicActorTaskManager::ReloadObserverDone( +void GlicActorClientSession::ReloadObserverDone( tabs::TabHandle tab_handle, base::OnceClosure callback, actor::ObservationDelayController::Result result) { @@ -793,10 +828,10 @@ if (tab) { size_t last_navigation_count = reload_observer_->NavigationCount(); reload_observer_ = std::make_unique<actor::ObservationDelayController>( - current_task_id_, actor_keyed_service_->GetJournal()); + current_task_id_, actor_keyed_service().GetJournal()); reload_observer_->SetNavigationCount(last_navigation_count + 1); reload_observer_->Wait( - *tab, base::BindOnce(&GlicActorTaskManager::ReloadObserverDone, + *tab, base::BindOnce(&GlicActorClientSession::ReloadObserverDone, base::Unretained(this), tab_handle, std::move(callback))); return; @@ -806,21 +841,29 @@ std::move(callback).Run(); } -void GlicActorTaskManager::CancelTask() { +void GlicActorClientSession::CancelTask() { if (current_task_id_) { StopTaskImpl(current_task_id_, actor::ActorTask::StoppedReason::kStoppedByUser); } } -void GlicActorTaskManager::CanActOnWebChanged(bool can_act_on_web) { +void GlicActorTaskManager::CancelTask() { + if (!session_) { + return; + } + session_->CancelTask(); +} + +void GlicActorClientSession::CanActOnWebChanged(bool can_act_on_web) { if (!can_act_on_web && current_task_id_) { StopTaskImpl(current_task_id_, actor::ActorTask::StoppedReason::kChromeFailure); } } -void GlicActorTaskManager::NotifyActorTaskStateChanged(actor::ActorTask& task) { +void GlicActorClientSession::NotifyActorTaskStateChanged( + actor::ActorTask& task) { CHECK(!task.id().is_null()); if (current_task_id_ != task.id()) { return; @@ -831,17 +874,16 @@ attempted_reload_after_crash_ = false; reload_observer_.reset(); actor_task_state_changed_subscription_.reset(); - - actuating_changed_callbacks_.Notify(false); + manager_->SetActuating(false); } } -void GlicActorTaskManager::StopTaskImpl( +void GlicActorClientSession::StopTaskImpl( actor::TaskId task_id, actor::ActorTask::StoppedReason reason) { - actor::ActorTask* task = actor_keyed_service_->GetTask(task_id); + actor::ActorTask* task = actor_keyed_service().GetTask(task_id); if (!task || task->IsCompleted()) { - actor_keyed_service_->GetJournal().Log( + actor_keyed_service().GetJournal().Log( GURL::EmptyGURL(), task_id, "Failed to stop task", actor::JournalDetailsBuilder() .AddError(task ? "Task already stopped" : "No such task") @@ -850,11 +892,53 @@ return; } - actor_keyed_service_->StopTask(task->id(), reason); + actor_keyed_service().StopTask(task->id(), reason); +} + +actor::ActorKeyedService& GlicActorClientSession::actor_keyed_service() const { + return *manager_->actor_keyed_service_; +} + +GlicActorPolicyChecker& GlicActorClientSession::actor_policy_checker() const { + return *manager_->actor_policy_checker_; +} + +GlicInstanceMetrics& GlicActorClientSession::instance_metrics() const { + return *manager_->instance_metrics_; +} + +Profile& GlicActorClientSession::profile() const { + return *manager_->profile_; } base::WeakPtr<GlicActorTaskManager> GlicActorTaskManager::GetWeakPtr() { return weak_ptr_factory_.GetWeakPtr(); } +GlicActorClientSession* GlicActorTaskManager::GetClientSessionForTesting() { + return session_.get(); +} + +base::WeakPtr<GlicActorClientSession> GlicActorClientSession::GetWeakPtr() { + return weak_ptr_factory_.GetWeakPtr(); +} + +GlicActorClientSession* GlicActorTaskManager::BindSession() { + session_ = std::make_unique<GlicActorClientSession>(this); + return session_.get(); +} + +void GlicActorTaskManager::UnbindSession() { + session_.reset(); + SetActuating(false); +} + +void GlicActorTaskManager::SetActuating(bool actuating) { + if (actuating_ == actuating) { + return; + } + actuating_ = actuating; + actuating_changed_callbacks_.Notify(actuating_); +} + } // namespace glic
diff --git a/chrome/browser/glic/actor/glic_actor_task_manager.h b/chrome/browser/glic/actor/glic_actor_task_manager.h index 90521cbd..147ec08b 100644 --- a/chrome/browser/glic/actor/glic_actor_task_manager.h +++ b/chrome/browser/glic/actor/glic_actor_task_manager.h
@@ -35,21 +35,66 @@ } // namespace actor namespace glic { +class GlicActorClientSession; +class GlicInstanceMetrics; // Manages actor-related tasks for GlicKeyedService. class GlicActorTaskManager { public: + class Delegate { + public: + virtual std::optional<std::string> conversation_id() const = 0; + virtual base::WeakPtr<actor::ActorTaskDelegate> GetActorTaskDelegate() = 0; + }; explicit GlicActorTaskManager(Profile* profile, actor::ActorKeyedService* actor_keyed_service, - GlicActorPolicyChecker& actor_policy_checker); + GlicActorPolicyChecker& actor_policy_checker, + GlicInstanceMetrics* instance_metrics, + Delegate* delegate); GlicActorTaskManager(const GlicActorTaskManager&) = delete; GlicActorTaskManager& operator=(const GlicActorTaskManager&) = delete; ~GlicActorTaskManager(); - void CreateTask(base::WeakPtr<actor::ActorTaskDelegate> delegate, - std::optional<std::string> conversation_id, - actor::webui::mojom::TaskOptionsPtr options, - mojom::WebClientHandler::CreateTaskCallback callback); + void MaybeShowDeactivationToastUi(); + + void CancelTask(); + bool IsActuating() const; + + // Adds a callback that is run when the actuating state changes. + base::CallbackListSubscription AddActuatingChangedCallback( + base::RepeatingCallback<void(bool)> callback); + + GlicActorClientSession* BindSession(); + void UnbindSession(); + + base::WeakPtr<GlicActorTaskManager> GetWeakPtr(); + + GlicActorClientSession* GetClientSessionForTesting(); + + private: + void SetActuating(bool actuating); + friend class GlicActorClientSession; + + raw_ptr<Profile> profile_; + raw_ptr<actor::ActorKeyedService> actor_keyed_service_; + const raw_ref<GlicActorPolicyChecker> actor_policy_checker_; + raw_ptr<GlicInstanceMetrics> instance_metrics_; + bool actuating_ = false; + base::RepeatingCallbackList<void(bool)> actuating_changed_callbacks_; + raw_ptr<Delegate> delegate_; + std::unique_ptr<GlicActorClientSession> session_; + base::WeakPtrFactory<GlicActorTaskManager> weak_ptr_factory_{this}; +}; + +class GlicActorClientSession { + public: + explicit GlicActorClientSession(GlicActorTaskManager* manager); + ~GlicActorClientSession(); + + bool IsActuating() const; + void CanActOnWebChanged(bool can_act_on_web); + void CreateTask(actor::webui::mojom::TaskOptionsPtr options, + glic::mojom::WebClientHandler::CreateTaskCallback callback); void PerformActions(const std::vector<uint8_t>& actions_proto, mojom::WebClientHandler::PerformActionsCallback callback); void CancelActions(actor::TaskId task_id, @@ -71,17 +116,9 @@ const std::optional<int32_t>& initiator_tab_id, const std::optional<int32_t>& initiator_window_id, glic::mojom::WebClientHandler::CreateActorTabCallback callback); - void MaybeShowDeactivationToastUi(); - void CancelTask(); - void CanActOnWebChanged(bool can_act_on_web); - bool IsActuating() const; - // Adds a callback that is run when the actuating state changes. - base::CallbackListSubscription AddActuatingChangedCallback( - base::RepeatingCallback<void(bool)> callback); - - base::WeakPtr<GlicActorTaskManager> GetWeakPtr(); + base::WeakPtr<GlicActorClientSession> GetWeakPtr(); private: void PerformActionsFinished( @@ -125,10 +162,15 @@ void NotifyActorTaskStateChanged(actor::ActorTask& task); void StopTaskImpl(actor::TaskId task_id, actor::ActorTask::StoppedReason reason); + actor::ActorKeyedService& actor_keyed_service() const; + GlicActorPolicyChecker& actor_policy_checker() const; + GlicInstanceMetrics& instance_metrics() const; + Profile& profile() const; - raw_ptr<Profile> profile_; - raw_ptr<actor::ActorKeyedService> actor_keyed_service_; - actor::TaskId current_task_id_; + std::unique_ptr<actor::ObservationDelayController> reload_observer_; + std::vector<std::unique_ptr<actor::TabObservationController>> + observation_controllers_; + // Only attempt to reload a crashed tab once *per task*. Crashes should be // rare so if we're getting repeated crashes it's likely being triggered by // actor code; retrying repeatedly will only trigger more crashes. After the @@ -137,21 +179,12 @@ // code. bool attempted_reload_after_crash_ = false; bool attempted_observation_retry_ = false; - std::unique_ptr<actor::ObservationDelayController> reload_observer_; - - std::vector<std::unique_ptr<actor::TabObservationController>> - observation_controllers_; - - const raw_ref<GlicActorPolicyChecker> actor_policy_checker_; - + actor::TaskId current_task_id_; std::optional<base::CallbackListSubscription> actor_task_state_changed_subscription_; - base::CallbackListSubscription can_act_on_web_changed_subscription_; - - base::RepeatingCallbackList<void(bool)> actuating_changed_callbacks_; - - base::WeakPtrFactory<GlicActorTaskManager> weak_ptr_factory_{this}; + raw_ref<GlicActorTaskManager> manager_; + base::WeakPtrFactory<GlicActorClientSession> weak_ptr_factory_{this}; }; } // namespace glic
diff --git a/chrome/browser/glic/android/BUILD.gn b/chrome/browser/glic/android/BUILD.gn index af6a467..762fa6f 100644 --- a/chrome/browser/glic/android/BUILD.gn +++ b/chrome/browser/glic/android/BUILD.gn
@@ -61,6 +61,7 @@ "java/src/org/chromium/chrome/browser/glic/GlicNavigationUtils.java", "java/src/org/chromium/chrome/browser/glic/GlicPromoCoordinator.java", "java/src/org/chromium/chrome/browser/glic/GlicSettings.java", + "java/src/org/chromium/chrome/browser/glic/GlicTaskMenuCoordinator.java", "java/src/org/chromium/chrome/browser/glic/GlicToolbarButtonController.java", "java/src/org/chromium/chrome/browser/glic/GlicUtils.java", ] @@ -125,8 +126,8 @@ "glic_keyed_service_android.cc", "glic_keyed_service_android.h", "glic_keyed_service_factory_android.cc", - "glic_settings_navigation_android.cc", - "glic_settings_navigation_android.h", + "glic_navigation_utils_android.cc", + "glic_navigation_utils_android.h", ] public_deps = [ ":jni_headers" ] deps = [ @@ -153,6 +154,7 @@ "java/src/org/chromium/chrome/browser/glic/GlicHelperUnitTest.java", "java/src/org/chromium/chrome/browser/glic/GlicPromoCoordinatorUnitTest.java", "java/src/org/chromium/chrome/browser/glic/GlicSettingsUnitTest.java", + "java/src/org/chromium/chrome/browser/glic/GlicTaskMenuCoordinatorUnitTest.java", "java/src/org/chromium/chrome/browser/glic/GlicToolbarButtonControllerTest.java", ] deps = [
diff --git a/chrome/browser/glic/android/glic_navigation_utils_android.cc b/chrome/browser/glic/android/glic_navigation_utils_android.cc new file mode 100644 index 0000000..879d76b --- /dev/null +++ b/chrome/browser/glic/android/glic_navigation_utils_android.cc
@@ -0,0 +1,27 @@ +// Copyright 2026 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chrome/browser/glic/android/glic_navigation_utils_android.h" + +#include "base/android/jni_android.h" +#include "chrome/browser/profiles/profile.h" +#include "content/public/browser/web_contents.h" + +// Must come after all headers that specialize FromJniType() / ToJniType(). +#include "chrome/browser/glic/android/jni_headers/GlicNavigationUtils_jni.h" + +namespace glic { + +void ShowGlicSettings() { + Java_GlicNavigationUtils_showGlicSettings( + base::android::AttachCurrentThread()); +} + +void ShowSignIn(Profile* profile, content::WebContents* web_contents) { + Java_GlicNavigationUtils_showSignIn( + base::android::AttachCurrentThread(), profile->GetJavaObject(), + web_contents ? web_contents->GetJavaWebContents() : nullptr); +} + +} // namespace glic
diff --git a/chrome/browser/glic/android/glic_navigation_utils_android.h b/chrome/browser/glic/android/glic_navigation_utils_android.h new file mode 100644 index 0000000..617b8f6c --- /dev/null +++ b/chrome/browser/glic/android/glic_navigation_utils_android.h
@@ -0,0 +1,25 @@ +// Copyright 2026 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CHROME_BROWSER_GLIC_ANDROID_GLIC_NAVIGATION_UTILS_ANDROID_H_ +#define CHROME_BROWSER_GLIC_ANDROID_GLIC_NAVIGATION_UTILS_ANDROID_H_ + +class Profile; + +namespace content { +class WebContents; +} + +namespace glic { + +// Opens the GLIC settings page on Android. +void ShowGlicSettings(); + +// Opens the GLIC signin activity on Android. `web_contents` is used to find the +// activity to display the sign-in sheet. +void ShowSignIn(Profile* profile, content::WebContents* web_contents); + +} // namespace glic + +#endif // CHROME_BROWSER_GLIC_ANDROID_GLIC_NAVIGATION_UTILS_ANDROID_H_
diff --git a/chrome/browser/glic/android/glic_settings_navigation_android.cc b/chrome/browser/glic/android/glic_settings_navigation_android.cc deleted file mode 100644 index c05dfc1..0000000 --- a/chrome/browser/glic/android/glic_settings_navigation_android.cc +++ /dev/null
@@ -1,19 +0,0 @@ -// Copyright 2026 The Chromium Authors -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "chrome/browser/glic/android/glic_settings_navigation_android.h" - -#include "base/android/jni_android.h" - -// Must come after all headers that specialize FromJniType() / ToJniType(). -#include "chrome/browser/glic/android/jni_headers/GlicNavigationUtils_jni.h" - -namespace glic { - -void ShowGlicSettings() { - Java_GlicNavigationUtils_showGlicSettings( - base::android::AttachCurrentThread()); -} - -} // namespace glic
diff --git a/chrome/browser/glic/android/glic_settings_navigation_android.h b/chrome/browser/glic/android/glic_settings_navigation_android.h deleted file mode 100644 index f1372bb..0000000 --- a/chrome/browser/glic/android/glic_settings_navigation_android.h +++ /dev/null
@@ -1,15 +0,0 @@ -// Copyright 2026 The Chromium Authors -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef CHROME_BROWSER_GLIC_ANDROID_GLIC_SETTINGS_NAVIGATION_ANDROID_H_ -#define CHROME_BROWSER_GLIC_ANDROID_GLIC_SETTINGS_NAVIGATION_ANDROID_H_ - -namespace glic { - -// Opens the GLIC settings page on Android. -void ShowGlicSettings(); - -} // namespace glic - -#endif // CHROME_BROWSER_GLIC_ANDROID_GLIC_SETTINGS_NAVIGATION_ANDROID_H_
diff --git a/chrome/browser/glic/android/java/src/org/chromium/chrome/browser/glic/GlicTaskMenuCoordinator.java b/chrome/browser/glic/android/java/src/org/chromium/chrome/browser/glic/GlicTaskMenuCoordinator.java new file mode 100644 index 0000000..118f26f2 --- /dev/null +++ b/chrome/browser/glic/android/java/src/org/chromium/chrome/browser/glic/GlicTaskMenuCoordinator.java
@@ -0,0 +1,214 @@ +// Copyright 2026 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +package org.chromium.chrome.browser.glic; + +import android.content.Context; +import android.graphics.Color; +import android.graphics.drawable.ColorDrawable; +import android.view.View; + +import androidx.annotation.VisibleForTesting; + +import org.chromium.build.annotations.NullMarked; +import org.chromium.build.annotations.Nullable; +import org.chromium.chrome.browser.actor.ActorTask; +import org.chromium.chrome.browser.tab.TabSelectionType; +import org.chromium.chrome.browser.tabmodel.TabModelSelector; +import org.chromium.chrome.browser.tabmodel.TabModelUtils; +import org.chromium.components.browser_ui.widget.BrowserUiListMenuUtils; +import org.chromium.components.browser_ui.widget.ListItemBuilder; +import org.chromium.ui.listmenu.BasicListMenu; +import org.chromium.ui.listmenu.ListMenu; +import org.chromium.ui.listmenu.ListMenuItemProperties; +import org.chromium.ui.modelutil.MVCListAdapter.ModelList; +import org.chromium.ui.modelutil.PropertyModel; +import org.chromium.ui.widget.AnchoredPopupWindow; +import org.chromium.ui.widget.RectProvider; +import org.chromium.ui.widget.ViewRectProvider; + +import java.util.List; +import java.util.Set; +import java.util.function.Supplier; + +/** + * Coordinates the shared task list drop-down list menu for Glic buttons. Takes a collection of + * supplied active background tasks and presents them inside an anchored window. + */ +@NullMarked +public class GlicTaskMenuCoordinator { + private final Context mContext; + + private final Supplier<@Nullable TabModelSelector> mTabModelSelectorSupplier; + private final GlicToolbarButtonController.GlicButtonDelegate mToggleGlicCallback; + private @Nullable AnchoredPopupWindow mMenuWindow; + + /** + * Constructs the task menu coordinator. + * + * @param context The Android context. + * @param tabModelSelectorSupplier Supplier for the active TabModelSelector. + * @param toggleGlicCallback Callback to activate or open the Glic UI sheet panel. + */ + public GlicTaskMenuCoordinator( + Context context, + Supplier<@Nullable TabModelSelector> tabModelSelectorSupplier, + GlicToolbarButtonController.GlicButtonDelegate toggleGlicCallback) { + mContext = context; + + mTabModelSelectorSupplier = tabModelSelectorSupplier; + mToggleGlicCallback = toggleGlicCallback; + } + + /** + * Displays the task menu anchored directly against an Android View layout element. + * + * @param anchorView The View to anchor the pop-up list overlay. + * @param tasks The collection of active actor tasks to list. + */ + public void show(View anchorView, List<ActorTask> tasks) { + showInternal(new ViewRectProvider(anchorView), anchorView.getRootView(), tasks); + } + + /** + * Displays the task menu anchored to coordinate boundaries supplied by a RectProvider. + * + * @param rectProvider Coordinates defining the geometric anchor frame. + * @param rootView The root view hierarchy stack to inject the popup layer. + * @param tasks The collection of active actor tasks to list. + */ + public void show(RectProvider rectProvider, View rootView, List<ActorTask> tasks) { + showInternal(rectProvider, rootView, tasks); + } + + /** Safely dismisses and tears down the floating task popup window overlay if visible. */ + public void dismiss() { + if (mMenuWindow != null) { + mMenuWindow.dismiss(); + mMenuWindow = null; + } + } + + /** + * Checks whether the pop-up menu bubble is active and visible on screen. + * + * @return true if the window drop-down overlay is visible. + */ + public boolean isShowing() { + return mMenuWindow != null && mMenuWindow.isShowing(); + } + + private void showInternal(RectProvider rectProvider, View rootView, List<ActorTask> tasks) { + ModelList modelList = buildModelList(tasks); + + ListMenu.Delegate delegate = + new ListMenu.Delegate() { + @Override + public void onItemSelected(PropertyModel model, View view) { + View.OnClickListener listener = + model.get(ListMenuItemProperties.CLICK_LISTENER); + + if (listener != null) { + listener.onClick(view); + } + } + }; + + BasicListMenu listMenu = + BrowserUiListMenuUtils.getBasicListMenu(mContext, modelList, delegate); + View contentView = listMenu.getContentView(); + + // Add gap to the right of the menu so it is not at the right edge of the screen. + int endOffsetPx = + mContext.getResources().getDimensionPixelSize(R.dimen.glic_task_menu_end_offset); + int lateralPadding = contentView.getPaddingLeft() + contentView.getPaddingRight(); + int widthPx = listMenu.getMaxItemWidth() + lateralPadding; + + int maxWidthPx = + mContext.getResources().getDimensionPixelSize(R.dimen.glic_task_menu_max_width); + widthPx = Math.min(widthPx, maxWidthPx); + + mMenuWindow = + new AnchoredPopupWindow.Builder( + mContext, + rootView, + new ColorDrawable(Color.TRANSPARENT), + () -> contentView, + rectProvider) + .setFocusable(true) + .setTouchModal(true) + .setDismissOnTouchInteraction(true) + .setHorizontalOverlapAnchor(true) + .setVerticalOverlapAnchor(false) + .setPreferredHorizontalOrientation( + AnchoredPopupWindow.HorizontalOrientation.LAYOUT_DIRECTION) + .setDesiredContentWidth(widthPx) + .setMaxWidth(maxWidthPx) + .setMargin(endOffsetPx) + .setAnimateFromAnchor(true) + .setAllowNonTouchableSize(true) + .build(); + mMenuWindow.show(); + } + + @VisibleForTesting + ModelList buildModelList(List<ActorTask> tasks) { + ModelList modelList = new ModelList(); + int endIconWidthPx = + mContext.getResources().getDimensionPixelSize(R.dimen.glic_menu_dot_width); + + // TODO(crbug.com/498721993): Listen to the task and update menu item when needed. + for (ActorTask task : tasks) { + ListItemBuilder builder = + new ListItemBuilder() + .withTitle(task.getTitle()) + .withIsIncognito(false) + .withIsTextEllipsizedAtEnd(true) + .withClickListener( + v -> { + switchToActuatingTab(task.getLastActedTabs()); + mToggleGlicCallback.onClick(/* preventClose= */ true); + dismiss(); + }); + + if (GlicButtonStateController.mapTaskStateToButtonState(task.getState()) + == GlicButtonStateController.ButtonState.NEEDS_REVIEW) { + builder.withStartIconRes(R.drawable.ic_hourglass_empty_24dp) + .withEndIconRes(R.drawable.glic_menu_dot) + .withEndIconWidth(endIconWidthPx); + } else { + builder.withStartIconRes(R.drawable.ic_arrow_selector_spark_24dp); + } + + modelList.add(builder.build()); + } + + // Divider + modelList.add(BasicListMenu.buildMenuDivider(false)); + + // Ask Gemini + modelList.add( + new ListItemBuilder() + .withTitleRes(R.string.glic_button_entrypoint_ask_gemini_label) + .withStartIconRes(R.drawable.ic_spark_24dp) + .withIsIncognito(false) + .withClickListener( + v -> { + mToggleGlicCallback.onClick(/* preventClose= */ false); + dismiss(); + }) + .build()); + return modelList; + } + + private void switchToActuatingTab(Set<Integer> tabs) { + if (!tabs.isEmpty()) { + int tabId = tabs.iterator().next(); + TabModelSelector selector = mTabModelSelectorSupplier.get(); + if (selector != null) { + TabModelUtils.selectTabById(selector, tabId, TabSelectionType.FROM_USER); + } + } + } +}
diff --git a/chrome/browser/glic/android/java/src/org/chromium/chrome/browser/glic/GlicTaskMenuCoordinatorUnitTest.java b/chrome/browser/glic/android/java/src/org/chromium/chrome/browser/glic/GlicTaskMenuCoordinatorUnitTest.java new file mode 100644 index 0000000..4c05f8d --- /dev/null +++ b/chrome/browser/glic/android/java/src/org/chromium/chrome/browser/glic/GlicTaskMenuCoordinatorUnitTest.java
@@ -0,0 +1,108 @@ +// Copyright 2026 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +package org.chromium.chrome.browser.glic; + +import static org.junit.Assert.assertEquals; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; + +import android.content.Context; +import android.view.View; + +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.robolectric.RuntimeEnvironment; +import org.robolectric.annotation.Config; + +import org.chromium.base.test.BaseRobolectricTestRunner; +import org.chromium.base.test.util.Batch; +import org.chromium.chrome.browser.actor.ActorTask; +import org.chromium.chrome.browser.tabmodel.TabModelSelector; +import org.chromium.ui.listmenu.ListMenuItemProperties; +import org.chromium.ui.modelutil.MVCListAdapter.ListItem; +import org.chromium.ui.modelutil.MVCListAdapter.ModelList; + +import java.util.Arrays; +import java.util.Collections; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +/** Unit tests for {@link GlicTaskMenuCoordinator}. */ +@RunWith(BaseRobolectricTestRunner.class) +@Config(manifest = Config.NONE) +@Batch(Batch.UNIT_TESTS) +public class GlicTaskMenuCoordinatorUnitTest { + @Rule public final MockitoRule mMockitoRule = MockitoJUnit.rule(); + + @Mock private TabModelSelector mTabModelSelector; + @Mock private GlicToolbarButtonController.GlicButtonDelegate mToggleGlicCallback; + + private Context mContext; + private GlicTaskMenuCoordinator mCoordinator; + + @Before + public void setUp() { + mContext = RuntimeEnvironment.getApplication(); + mCoordinator = + new GlicTaskMenuCoordinator(mContext, () -> mTabModelSelector, mToggleGlicCallback); + } + + @Test + public void testBuildModelList_WithActiveTasks() { + ActorTask task1 = mock(ActorTask.class); + doReturn("Task One").when(task1).getTitle(); + ActorTask task2 = mock(ActorTask.class); + doReturn("Task Two").when(task2).getTitle(); + + List<ActorTask> tasks = Arrays.asList(task1, task2); + ModelList modelList = mCoordinator.buildModelList(tasks); + + // 2 tasks + 1 divider + 1 Ask Gemini = 4 items total + assertEquals(4, modelList.size()); + + ListItem item1 = modelList.get(0); + assertEquals("Task One", item1.model.get(ListMenuItemProperties.TITLE)); + + ListItem item2 = modelList.get(1); + assertEquals("Task Two", item2.model.get(ListMenuItemProperties.TITLE)); + } + + @Test + public void testClickAskGemini_TriggersCallbackWithFalse() { + ModelList modelList = mCoordinator.buildModelList(Collections.emptyList()); + // Index 0 is divider, Index 1 is Ask Gemini + ListItem askGeminiItem = modelList.get(1); + + View.OnClickListener clickListener = + askGeminiItem.model.get(ListMenuItemProperties.CLICK_LISTENER); + clickListener.onClick(null); + + verify(mToggleGlicCallback).onClick(/* preventClose= */ false); + } + + @Test + public void testClickActorTask_TriggersTabSwitchAndCallbackWithTrue() { + ActorTask task = mock(ActorTask.class); + doReturn("Task Title").when(task).getTitle(); + Set<Integer> tabIds = new HashSet<>(Arrays.asList(123)); + doReturn(tabIds).when(task).getLastActedTabs(); + + ModelList modelList = mCoordinator.buildModelList(Arrays.asList(task)); + ListItem taskItem = modelList.get(0); + + View.OnClickListener clickListener = + taskItem.model.get(ListMenuItemProperties.CLICK_LISTENER); + clickListener.onClick(null); + + verify(mToggleGlicCallback).onClick(/* preventClose= */ true); + } +}
diff --git a/chrome/browser/glic/android/java/src/org/chromium/chrome/browser/glic/GlicToolbarButtonController.java b/chrome/browser/glic/android/java/src/org/chromium/chrome/browser/glic/GlicToolbarButtonController.java index f0c6ba709..0078386 100644 --- a/chrome/browser/glic/android/java/src/org/chromium/chrome/browser/glic/GlicToolbarButtonController.java +++ b/chrome/browser/glic/android/java/src/org/chromium/chrome/browser/glic/GlicToolbarButtonController.java
@@ -8,8 +8,6 @@ import android.app.Activity; import android.content.Context; -import android.graphics.Color; -import android.graphics.drawable.ColorDrawable; import android.graphics.drawable.Drawable; import android.view.View; @@ -21,9 +19,7 @@ import org.chromium.chrome.browser.browser_controls.BrowserControlsVisibilityManager; import org.chromium.chrome.browser.glic.GlicButtonStateController.ButtonState; import org.chromium.chrome.browser.tab.Tab; -import org.chromium.chrome.browser.tab.TabSelectionType; import org.chromium.chrome.browser.tabmodel.TabModelSelector; -import org.chromium.chrome.browser.tabmodel.TabModelUtils; import org.chromium.chrome.browser.toolbar.adaptive.AdaptiveToolbarButtonVariant; import org.chromium.chrome.browser.toolbar.adaptive.AdaptiveToolbarFeatures; import org.chromium.chrome.browser.toolbar.optional_button.BaseButtonDataProvider; @@ -31,22 +27,12 @@ import org.chromium.chrome.browser.toolbar.optional_button.ButtonData.ButtonSpec; import org.chromium.chrome.browser.ui.browser_window.ChromeAndroidTask; import org.chromium.chrome.browser.user_education.IphCommandBuilder; -import org.chromium.components.browser_ui.widget.BrowserUiListMenuUtils; -import org.chromium.components.browser_ui.widget.ListItemBuilder; import org.chromium.components.embedder_support.util.UrlUtilities; import org.chromium.components.feature_engagement.EventConstants; import org.chromium.components.feature_engagement.FeatureConstants; import org.chromium.components.feature_engagement.Tracker; -import org.chromium.ui.listmenu.BasicListMenu; -import org.chromium.ui.listmenu.ListMenu; -import org.chromium.ui.listmenu.ListMenuItemProperties; -import org.chromium.ui.modelutil.MVCListAdapter.ModelList; -import org.chromium.ui.modelutil.PropertyModel; -import org.chromium.ui.widget.AnchoredPopupWindow; -import org.chromium.ui.widget.ViewRectProvider; import java.util.List; -import java.util.Set; import java.util.function.Supplier; /** Defines a toolbar button to open the Glic bottom sheet. */ @@ -75,7 +61,7 @@ private final ButtonSpec mDoneSpec; private final GlicButtonStateController mStateController; - private @Nullable AnchoredPopupWindow mMenuWindow; + private @Nullable GlicTaskMenuCoordinator mTaskMenuCoordinator; /** * @param activity The Android activity. @@ -210,135 +196,13 @@ @Override public void destroy() { mStateController.destroy(); + if (mTaskMenuCoordinator != null) { + mTaskMenuCoordinator.dismiss(); + mTaskMenuCoordinator = null; + } super.destroy(); } - private void showTaskMenu(View anchorView, List<ActorTask> tasks) { - ModelList modelList = new ModelList(); - int endIconWidthPx = - anchorView - .getContext() - .getResources() - .getDimensionPixelSize(R.dimen.glic_menu_dot_width); - - // TODO(crbug.com/498721993): Listen to the task and update menu item when needed. - for (ActorTask task : tasks) { - ListItemBuilder builder = - new ListItemBuilder() - .withTitle(task.getTitle()) - .withIsIncognito(false) - .withIsTextEllipsizedAtEnd(true) - .withClickListener( - v -> { - switchToActuatingTab(task.getLastActedTabs()); - mToggleGlicCallback.onClick(true); - dismissMenu(); - }); - - if (GlicButtonStateController.mapTaskStateToButtonState(task.getState()) - == ButtonState.NEEDS_REVIEW) { - builder.withStartIconRes(R.drawable.ic_hourglass_empty_24dp) - .withEndIconRes(R.drawable.glic_menu_dot) - .withEndIconWidth(endIconWidthPx); - } else { - builder.withStartIconRes(R.drawable.ic_arrow_selector_spark_24dp); - } - - modelList.add(builder.build()); - } - - // Divider - modelList.add(BasicListMenu.buildMenuDivider(false)); - - // Item 2: Ask Gemini - modelList.add( - new ListItemBuilder() - .withTitleRes(R.string.glic_button_entrypoint_ask_gemini_label) - .withStartIconRes(R.drawable.ic_spark_24dp) - .withIsIncognito(false) - .withClickListener( - v -> { - mToggleGlicCallback.onClick(false); - dismissMenu(); - }) - .build()); - - ListMenu.Delegate delegate = - new ListMenu.Delegate() { - @Override - public void onItemSelected(PropertyModel model, View view) { - View.OnClickListener listener = - model.get(ListMenuItemProperties.CLICK_LISTENER); - - if (listener != null) { - listener.onClick(view); - } - } - }; - - BasicListMenu listMenu = - BrowserUiListMenuUtils.getBasicListMenu( - anchorView.getContext(), modelList, delegate); - View contentView = listMenu.getContentView(); - - // Add gap to the right of the menu so it is not at the right edge of the screen. - ViewRectProvider anchorRectProvider = new ViewRectProvider(anchorView); - int endOffsetPx = - anchorView - .getContext() - .getResources() - .getDimensionPixelSize(R.dimen.glic_task_menu_end_offset); - - int lateralPadding = contentView.getPaddingLeft() + contentView.getPaddingRight(); - int widthPx = listMenu.getMaxItemWidth() + lateralPadding; - - int maxWidthPx = - anchorView - .getContext() - .getResources() - .getDimensionPixelSize(R.dimen.glic_task_menu_max_width); - widthPx = Math.min(widthPx, maxWidthPx); - - AnchoredPopupWindow.Builder builder = - new AnchoredPopupWindow.Builder( - anchorView.getContext(), - anchorView.getRootView(), - new ColorDrawable(Color.TRANSPARENT), - () -> contentView, - anchorRectProvider) - .setFocusable(true) - .setTouchModal(true) - .setDismissOnTouchInteraction(true) - .setHorizontalOverlapAnchor(true) - .setVerticalOverlapAnchor(false) - .setPreferredHorizontalOrientation( - AnchoredPopupWindow.HorizontalOrientation.LAYOUT_DIRECTION) - .setDesiredContentWidth(widthPx) - .setMaxWidth(maxWidthPx) - .setMargin(endOffsetPx) - .setAnimateFromAnchor(true) - .setAllowNonTouchableSize(true); - mMenuWindow = builder.build(); - mMenuWindow.show(); - } - - private void dismissMenu() { - if (mMenuWindow != null) { - mMenuWindow.dismiss(); - mMenuWindow = null; - } - } - - private void switchToActuatingTab(Set<Integer> tabs) { - if (!tabs.isEmpty()) { - int tabId = tabs.iterator().next(); - TabModelSelector selector = mTabModelSelectorSupplier.get(); - if (selector != null) { - TabModelUtils.selectTabById(selector, tabId, TabSelectionType.FROM_USER); - } - } - } - @Override protected @Nullable IphCommandBuilder getIphCommandBuilder(Tab tab) { return new IphCommandBuilder( @@ -352,8 +216,8 @@ public void onClick(View view) { mStateController.setPersistDoneState(false); - if (mMenuWindow != null && mMenuWindow.isShowing()) { - dismissMenu(); + if (mTaskMenuCoordinator != null && mTaskMenuCoordinator.isShowing()) { + mTaskMenuCoordinator.dismiss(); return; } @@ -365,7 +229,12 @@ && mStateController.getActiveTaskIdOnTab(currentTab.getId()) != null; if (!isOnActingTab && !tasks.isEmpty()) { - showTaskMenu(view, tasks); + if (mTaskMenuCoordinator == null) { + mTaskMenuCoordinator = + new GlicTaskMenuCoordinator( + mActivity, mTabModelSelectorSupplier, mToggleGlicCallback); + } + mTaskMenuCoordinator.show(view, tasks); return; } }
diff --git a/chrome/browser/glic/browser_ui/BUILD.gn b/chrome/browser/glic/browser_ui/BUILD.gn index 4a76981..84e28f6c0 100644 --- a/chrome/browser/glic/browser_ui/BUILD.gn +++ b/chrome/browser/glic/browser_ui/BUILD.gn
@@ -193,6 +193,7 @@ sources = [ "context_sharing_border_view_interactive_uitest.cc", "glic_iph_controller_interactive_uitest.cc", + "glic_nudge_controller_interactive_uitest.cc", "glic_tab_indicator_helper_interactive_uitest.cc", "tab_underline_view_interactive_uitest.cc", ]
diff --git a/chrome/browser/glic/browser_ui/glic_nudge_controller_interactive_uitest.cc b/chrome/browser/glic/browser_ui/glic_nudge_controller_interactive_uitest.cc new file mode 100644 index 0000000..853673e --- /dev/null +++ b/chrome/browser/glic/browser_ui/glic_nudge_controller_interactive_uitest.cc
@@ -0,0 +1,95 @@ +// Copyright 2026 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/test/scoped_feature_list.h" +#include "chrome/browser/contextual_cueing/features.h" +#include "chrome/browser/glic/browser_ui/glic_nudge_controller.h" +#include "chrome/browser/glic/glic_pref_names.h" +#include "chrome/browser/glic/public/glic_enabling.h" +#include "chrome/browser/glic/suggestions/contextual_cueing_features.h" +#include "chrome/browser/glic/test_support/interactive_glic_test.h" +#include "chrome/browser/profiles/profile.h" +#include "chrome/browser/ui/actions/chrome_action_id.h" +#include "chrome/browser/ui/browser.h" +#include "chrome/browser/ui/browser_element_identifiers.h" +#include "chrome/browser/ui/browser_window/public/browser_window_features.h" +#include "chrome/browser/ui/page_action/page_action_controller.h" +#include "chrome/browser/ui/page_action/page_action_observer.h" +#include "chrome/browser/ui/tabs/public/tab_features.h" +#include "chrome/browser/ui/views/interaction/browser_elements_views.h" +#include "chrome/browser/ui/views/tabs/tab_strip_action_container.h" +#include "chrome/test/base/interactive_test_utils.h" +#include "chrome/test/base/ui_test_utils.h" +#include "chrome/test/interaction/interactive_browser_test.h" +#include "components/prefs/pref_service.h" +#include "content/public/test/browser_test.h" + +namespace glic { + +class GlicNudgeControllerInteractiveUiTest : public test::InteractiveGlicTest { + public: + GlicNudgeControllerInteractiveUiTest() { + feature_list_.InitWithFeatures( + /*enabled_features=*/{}, + /*disabled_features=*/{contextual_cueing::kContextualCueingV2}); + } + + void SetUpOnMainThread() override { + InteractiveBrowserTest::SetUpOnMainThread(); + GlicEnabling::SetBypassEnablementChecksForTesting(true); + browser()->profile()->GetPrefs()->SetBoolean(prefs::kGlicPinnedToTabstrip, + true); + + ASSERT_TRUE(tab_strip_action_container()); + } + + void TearDownOnMainThread() override { + GlicEnabling::SetBypassEnablementChecksForTesting(false); + } + + GlicNudgeController* nudge_controller() { + return browser()->browser_window_features()->glic_nudge_controller(); + } + + TabStripActionContainer* tab_strip_action_container() { + return BrowserElementsViews::From(browser()) + ->GetViewAs<TabStripActionContainer>(kTabStripActionContainerElementId); + } + + private: + base::test::ScopedFeatureList feature_list_; +}; + +IN_PROC_BROWSER_TEST_F(GlicNudgeControllerInteractiveUiTest, + ShowsTabStripNudge) { + content::WebContents* web_contents = + browser()->tab_strip_model()->GetActiveWebContents(); + + EXPECT_FALSE(tab_strip_action_container()->GetIsShowingGlicNudge()); + nudge_controller()->UpdateNudgeLabel( + web_contents, "Nudge Label", "Prompt Suggestion", "Anchored Message Text", + std::nullopt, base::DoNothing()); + + EXPECT_TRUE(tab_strip_action_container()->GetIsShowingGlicNudge()); +} + +IN_PROC_BROWSER_TEST_F(GlicNudgeControllerInteractiveUiTest, + HidesTabStripNudge) { + content::WebContents* web_contents = + browser()->tab_strip_model()->GetActiveWebContents(); + + EXPECT_FALSE(tab_strip_action_container()->GetIsShowingGlicNudge()); + nudge_controller()->UpdateNudgeLabel( + web_contents, "Nudge Label", "Prompt Suggestion", "Anchored Message Text", + std::nullopt, base::DoNothing()); + + EXPECT_TRUE(tab_strip_action_container()->GetIsShowingGlicNudge()); + + nudge_controller()->UpdateNudgeLabel( + web_contents, std::string(), std::nullopt, std::string(), + GlicNudgeActivity::kNudgeDismissed, base::DoNothing()); + EXPECT_FALSE(tab_strip_action_container()->GetIsShowingGlicNudge()); +} + +} // namespace glic
diff --git a/chrome/browser/glic/browser_ui/glic_selection_widget.cc b/chrome/browser/glic/browser_ui/glic_selection_widget.cc index 2293857..6d83874 100644 --- a/chrome/browser/glic/browser_ui/glic_selection_widget.cc +++ b/chrome/browser/glic/browser_ui/glic_selection_widget.cc
@@ -5,7 +5,6 @@ #include "chrome/browser/glic/browser_ui/glic_selection_widget.h" #include "base/strings/strcat.h" -#include "chrome/browser/glic/browser_ui/glic_vector_icon_manager.h" #include "chrome/browser/glic/public/features.h" #include "chrome/browser/glic/resources/grit/glic_browser_resources.h" #include "chrome/browser/platform_util.h" @@ -31,7 +30,6 @@ #include "ui/views/animation/ink_drop.h" #include "ui/views/controls/button/image_button.h" #include "ui/views/controls/button/image_button_factory.h" -#include "ui/views/controls/button/label_button.h" #include "ui/views/controls/button/md_text_button.h" #include "ui/views/controls/highlight_path_generator.h" #include "ui/views/layout/box_layout.h" @@ -42,15 +40,12 @@ namespace { constexpr size_t kMaxSelectionLengthForTooltip = 50; -constexpr int kIconSize = 20; +constexpr int kIconSize = 14; -constexpr int kButtonCornerRadius = 14; - +constexpr int kCornerRadius = 12; // The two pills are visually grouped together by having a smaller border radius -// on the sides where they meet. The outer radius is roughly 50% of the expected -// height to create a fully rounded pill shape. -constexpr int kPillCornerRadiusOuter = 16; -constexpr int kPillCornerRadiusInner = 8; +// on the sides where they meet. +constexpr int kCornerRadiusInner = 4; class GlicSelectionContentsView : public views::View { METADATA_HEADER(GlicSelectionContentsView, views::View) @@ -67,10 +62,8 @@ auto border1 = std::make_unique<views::BubbleBorder>( views::BubbleBorder::NONE, views::BubbleBorder::STANDARD_SHADOW); - border1->SetColor(ui::kColorSysBase); - border1->set_rounded_corners( - gfx::RoundedCornersF(kPillCornerRadiusOuter, kPillCornerRadiusInner, - kPillCornerRadiusInner, kPillCornerRadiusOuter)); + border1->SetColor(ui::kColorSysSurface); + border1->set_rounded_corners(gfx::RoundedCornersF(kCornerRadius)); // BubbleBorders add a shadow inset on all sides. We use a negative // spacing here so the visible backgrounds of the pills are closer together @@ -85,15 +78,15 @@ views::BoxLayout::CrossAxisAlignment::kCenter); SetLayoutManager(std::move(layout)); - auto* ask_pill = AddChildView(std::make_unique<views::BoxLayoutView>()); - ask_pill->SetOrientation(views::BoxLayout::Orientation::kHorizontal); - ask_pill->SetInsideBorderInsets(gfx::Insets::VH(4, 4)); - ask_pill->SetBetweenChildSpacing(4); - ask_pill->SetCrossAxisAlignment( + ask_pill_ = AddChildView(std::make_unique<views::BoxLayoutView>()); + ask_pill_->SetOrientation(views::BoxLayout::Orientation::kHorizontal); + ask_pill_->SetInsideBorderInsets(gfx::Insets::VH(4, 4)); + ask_pill_->SetBetweenChildSpacing(4); + ask_pill_->SetCrossAxisAlignment( views::BoxLayout::CrossAxisAlignment::kCenter); - ask_pill->SetBackground( + ask_pill_->SetBackground( std::make_unique<views::BubbleBackground>(border1.get())); - ask_pill->SetBorder(std::move(border1)); + ask_pill_->SetBorder(std::move(border1)); // Ask Gemini Button std::u16string truncated_text; @@ -110,19 +103,18 @@ IDS_GLIC_SELECTION_ASK_ABOUT, base::StrCat({u"\"", truncated_text, u"\""})); auto* ask_gemini_btn = - ask_pill->AddChildView(std::make_unique<views::MdTextButton>( + ask_pill_->AddChildView(std::make_unique<views::MdTextButton>( std::move(on_ask_gemini), l10n_util::GetStringUTF16( IDS_GLIC_BUTTON_ENTRYPOINT_ASK_GEMINI_LABEL))); ask_gemini_btn->SetStyle(ui::ButtonStyle::kText); ask_gemini_btn->SetTooltipText(ask_gemini_tooltip); ask_gemini_btn->SetImageLabelSpacing(4); - ask_gemini_btn->SetEnabledTextColors(ui::kColorSysOnSurface); + ask_gemini_btn->SetEnabledTextColors(ui::kColorSysOnSurfaceVariant); ask_gemini_btn->SetTextColor(views::Button::STATE_DISABLED, ui::kColorLabelForegroundDisabled); - ask_gemini_btn->SetCustomPadding( - views::LayoutProvider::Get()->GetInsetsMetric( - views::INSETS_ICON_BUTTON)); + ask_gemini_btn->SetLabelStyle(views::style::STYLE_BODY_5); + ask_gemini_btn->SetCustomPadding(gfx::Insets::TLBR(0, 2, 0, 6)); gfx::ImageSkia* icon_skia = ui::ResourceBundle::GetSharedInstance().GetImageSkiaNamed( @@ -130,7 +122,21 @@ gfx::ImageSkia resized_icon = gfx::ImageSkiaOperations::CreateResizedImage( *icon_skia, skia::ImageOperations::RESIZE_BEST, gfx::Size(kIconSize, kIconSize)); - auto icon_model = ui::ImageModel::FromImageSkia(resized_icon); + + auto generator = base::BindRepeating( + [](gfx::ImageSkia icon, const ui::ColorProvider* color_provider) { + if (!color_provider) { + return icon; + } + SkColor circle_bg_color = + color_provider->GetColor(ui::kColorSysBaseContainer); + return gfx::ImageSkiaOperations::CreateImageWithCircleBackground( + 10, circle_bg_color, icon); + }, + resized_icon); + + auto icon_model = ui::ImageModel::FromImageGenerator(std::move(generator), + gfx::Size(20, 20)); ask_gemini_btn->SetImageModel(views::Button::STATE_NORMAL, icon_model); ask_gemini_btn->SetImageModel(views::Button::STATE_HOVERED, icon_model); @@ -142,7 +148,7 @@ ask_gemini_btn->SetHasInkDropActionOnClick(true); ask_gemini_btn->SetShowInkDropWhenHotTracked(true); views::InstallRoundRectHighlightPathGenerator(ask_gemini_btn, gfx::Insets(), - kButtonCornerRadius); + kCornerRadius); views::InkDrop::Get(ask_gemini_btn) ->SetBaseColorCallback(base::BindRepeating( [](views::View* host) { @@ -156,18 +162,18 @@ auto copy_tooltip = gfx::LocateAndRemoveAcceleratorChar( l10n_util::GetStringUTF16(IDS_APP_COPY), nullptr, nullptr); auto* copy_btn = - ask_pill->AddChildView(views::ImageButton::CreateIconButton( + ask_pill_->AddChildView(views::ImageButton::CreateIconButton( std::move(on_copy), vector_icons::kContentCopyIcon, copy_tooltip)); copy_btn->SetTooltipText(copy_tooltip); copy_btn->SetImageVerticalAlignment(views::ImageButton::ALIGN_MIDDLE); copy_btn->SetBorder( views::CreateEmptyBorder(views::LayoutProvider::Get()->GetInsetsMetric( - views::INSETS_ICON_BUTTON))); + views::INSETS_VECTOR_IMAGE_BUTTON))); views::SetImageFromVectorIconWithColor( copy_btn, vector_icons::kContentCopyIcon, kIconSize, - views::IconColors(ui::kColorSysOnSurface, + views::IconColors(ui::kColorSysOnSurfaceVariant, ui::kColorLabelForegroundDisabled, - ui::kColorSysOnSurface)); + ui::kColorSysOnSurfaceVariant)); CreateToolbarInkdropCallbacks(copy_btn, kColorToolbarInkDropHover, kColorToolbarInkDropRipple); @@ -175,19 +181,19 @@ auto copy_link_tooltip = l10n_util::GetStringUTF16(IDS_CONTENT_CONTEXT_COPYLINKTOTEXT); copy_link_btn_ = - ask_pill->AddChildView(views::ImageButton::CreateIconButton( + ask_pill_->AddChildView(views::ImageButton::CreateIconButton( std::move(on_copy_link), vector_icons::kLinkIcon, copy_link_tooltip)); copy_link_btn_->SetTooltipText(copy_link_tooltip); copy_link_btn_->SetImageVerticalAlignment(views::ImageButton::ALIGN_MIDDLE); copy_link_btn_->SetBorder( views::CreateEmptyBorder(views::LayoutProvider::Get()->GetInsetsMetric( - views::INSETS_ICON_BUTTON))); + views::INSETS_VECTOR_IMAGE_BUTTON))); views::SetImageFromVectorIconWithColor( copy_link_btn_, vector_icons::kLinkIcon, kIconSize, - views::IconColors(ui::kColorSysOnSurface, + views::IconColors(ui::kColorSysOnSurfaceVariant, ui::kColorLabelForegroundDisabled, - ui::kColorSysOnSurface)); + ui::kColorSysOnSurfaceVariant)); CreateToolbarInkdropCallbacks(copy_link_btn_, kColorToolbarInkDropHover, kColorToolbarInkDropRipple); copy_link_btn_->SetEnabled(false); @@ -200,10 +206,9 @@ views::BoxLayout::CrossAxisAlignment::kCenter); auto border2 = std::make_unique<views::BubbleBorder>( views::BubbleBorder::NONE, views::BubbleBorder::STANDARD_SHADOW); - border2->SetColor(ui::kColorSysBase); - border2->set_rounded_corners( - gfx::RoundedCornersF(kPillCornerRadiusInner, kPillCornerRadiusOuter, - kPillCornerRadiusOuter, kPillCornerRadiusInner)); + border2->SetColor(ui::kColorSysSurface); + border2->set_rounded_corners(gfx::RoundedCornersF( + kCornerRadiusInner, kCornerRadius, kCornerRadius, kCornerRadiusInner)); dismiss_pill_->SetBackground( std::make_unique<views::BubbleBackground>(border2.get())); dismiss_pill_->SetBorder(std::move(border2)); @@ -234,9 +239,9 @@ views::INSETS_VECTOR_IMAGE_BUTTON))); views::SetImageFromVectorIconWithColor( dismiss_btn_, vector_icons::kCloseIcon, kIconSize, - views::IconColors(ui::kColorSysOnSurfaceSubtle, + views::IconColors(ui::kColorSysOnSurfaceVariant, ui::kColorLabelForegroundDisabled, - ui::kColorSysOnSurface)); + ui::kColorSysOnSurfaceVariant)); CreateToolbarInkdropCallbacks(dismiss_btn_, kColorToolbarInkDropHover, kColorToolbarInkDropRipple); } @@ -250,12 +255,30 @@ if (dismiss_pill_) { dismiss_pill_->layer()->SetOpacity(1.0f); } + if (ask_pill_) { + auto* bubble_border = + static_cast<views::BubbleBorder*>(ask_pill_->GetBorder()); + if (bubble_border) { + bubble_border->set_rounded_corners( + gfx::RoundedCornersF(kCornerRadius, kCornerRadiusInner, + kCornerRadiusInner, kCornerRadius)); + ask_pill_->SchedulePaint(); + } + } } void OnMouseExited(const ui::MouseEvent& event) override { if (dismiss_pill_) { dismiss_pill_->layer()->SetOpacity(0.0f); } + if (ask_pill_) { + auto* bubble_border = + static_cast<views::BubbleBorder*>(ask_pill_->GetBorder()); + if (bubble_border) { + bubble_border->set_rounded_corners(gfx::RoundedCornersF(kCornerRadius)); + ask_pill_->SchedulePaint(); + } + } } void SetCopyLinkEnabled(bool enabled) { @@ -270,9 +293,9 @@ is_pinned ? vector_icons::kCaretDownIcon : vector_icons::kCaretUpIcon; views::SetImageFromVectorIconWithColor( pin_btn_, icon, kIconSize, - views::IconColors(ui::kColorSysOnSurfaceSubtle, + views::IconColors(ui::kColorSysOnSurfaceVariant, ui::kColorLabelForegroundDisabled, - ui::kColorSysOnSurface)); + ui::kColorSysOnSurfaceVariant)); pin_btn_->SetTooltipText(l10n_util::GetStringUTF16( is_pinned ? IDS_TAB_SEARCH_BUTTON_CXMENU_UNPIN : IDS_TAB_SEARCH_BUTTON_CXMENU_PIN)); @@ -296,6 +319,7 @@ raw_ptr<views::ImageButton> copy_link_btn_ = nullptr; raw_ptr<views::ImageButton> pin_btn_ = nullptr; raw_ptr<views::ImageButton> dismiss_btn_ = nullptr; + raw_ptr<views::BoxLayoutView> ask_pill_ = nullptr; raw_ptr<views::BoxLayoutView> dismiss_pill_ = nullptr; }; @@ -383,7 +407,7 @@ SetShowCloseButton(false); // Remove default dialog margins so the custom button fills the entire bubble. set_margins(gfx::Insets(0)); - set_corner_radius(16); + set_corner_radius(kCornerRadius); SetBackgroundColor(ui::ColorVariant(SK_ColorTRANSPARENT)); set_shadow(views::BubbleBorder::NO_SHADOW); SetCanActivate(false);
diff --git a/chrome/browser/glic/browser_ui/tab_underline_controller.cc b/chrome/browser/glic/browser_ui/tab_underline_controller.cc index f74c96a..23a9b08 100644 --- a/chrome/browser/glic/browser_ui/tab_underline_controller.cc +++ b/chrome/browser/glic/browser_ui/tab_underline_controller.cc
@@ -56,12 +56,7 @@ base::Unretained(this))); } - if (ShouldUseSignalsForContextualTasks()) { - contextual_tasks::ActiveTaskContextProvider* active_task_context_provider = - contextual_tasks::ActiveTaskContextProvider::From( - browser_window_interface_); - contextual_task_observation_.Observe(active_task_context_provider); - } + MaybeObserveContextualTasks(); if (glic_service_) { // Fetch the latest context access indicator status from service. We can't @@ -461,4 +456,15 @@ return base::FeatureList::IsEnabled(contextual_tasks::kContextualTasks); } +void TabUnderlineController::MaybeObserveContextualTasks() { + if (ShouldUseSignalsForContextualTasks() && + !contextual_task_observation_.IsObserving()) { + if (auto* active_task_context_provider = + contextual_tasks::ActiveTaskContextProvider::From( + browser_window_interface_)) { + contextual_task_observation_.Observe(active_task_context_provider); + } + } +} + } // namespace glic
diff --git a/chrome/browser/glic/browser_ui/tab_underline_controller.h b/chrome/browser/glic/browser_ui/tab_underline_controller.h index 549ec997..201a5ec 100644 --- a/chrome/browser/glic/browser_ui/tab_underline_controller.h +++ b/chrome/browser/glic/browser_ui/tab_underline_controller.h
@@ -132,6 +132,9 @@ // being shared via pinning or active following. void UpdateUnderlineView(UpdateUnderlineReason reason); + // Helper to observe contextual tasks if enabled and not already observing. + void MaybeObserveContextualTasks(); + // Off to On. Throw away everything we have and start the animation from // the beginning. void ShowAndAnimateUnderline(bool triggered_by_glic);
diff --git a/chrome/browser/glic/e2e_test/glic_e2e_test.cc b/chrome/browser/glic/e2e_test/glic_e2e_test.cc index 7b273bf..41e50d26 100644 --- a/chrome/browser/glic/e2e_test/glic_e2e_test.cc +++ b/chrome/browser/glic/e2e_test/glic_e2e_test.cc
@@ -23,6 +23,7 @@ #include "chrome/browser/glic/public/glic_keyed_service.h" #include "chrome/browser/glic/public/glic_keyed_service_factory.h" #include "chrome/browser/glic/public/service/glic_instance_coordinator.h" +#include "chrome/browser/glic/service/glic_instance_impl.h" #include "chrome/browser/glic/suggestions/contextual_cueing_features.h" #include "chrome/browser/glic/test_support/glic_test_util.h" #include "chrome/browser/glic/test_support/interactive_test_util.h" @@ -333,9 +334,13 @@ } void GlicE2ETest::ThrottleGlicNetwork() { - for (auto* guest_contents : - glic::GetAllGlicGuestWebContentsForTesting(browser()->profile())) { - ThrottleWebContentsNetwork(guest_contents); + for (auto* instance : instance_coordinator().GetInstances()) { + auto* instance_impl = static_cast<GlicInstanceImpl*>(instance); + content::WebContents* guest_contents = + instance_impl->host().web_client_contents(); + if (guest_contents) { + ThrottleWebContentsNetwork(guest_contents); + } } }
diff --git a/chrome/browser/glic/fre/glic_fre_controller.cc b/chrome/browser/glic/fre/glic_fre_controller.cc index 7d493f35b..7c3b29a 100644 --- a/chrome/browser/glic/fre/glic_fre_controller.cc +++ b/chrome/browser/glic/fre/glic_fre_controller.cc
@@ -29,7 +29,6 @@ #include "chrome/browser/predictors/loading_predictor.h" #include "chrome/browser/predictors/loading_predictor_factory.h" #include "chrome/browser/shell_integration.h" -#include "chrome/browser/ui/browser_finder.h" #include "chrome/browser/ui/browser_tabstrip.h" #include "chrome/common/channel_info.h" #include "chrome/common/chrome_features.h"
diff --git a/chrome/browser/glic/glic_metrics.h b/chrome/browser/glic/glic_metrics.h index 6d1d3fa3..b20cc97c 100644 --- a/chrome/browser/glic/glic_metrics.h +++ b/chrome/browser/glic/glic_metrics.h
@@ -87,7 +87,13 @@ kFailedTimedOutDidNotCompleteOnboarding = 17, kFailedLostInstance = 18, kFailedSawNavigationDidNotCompleteOnboarding = 19, - kMaxValue = kFailedSawNavigationDidNotCompleteOnboarding, + kFailedUnknown = 20, + kFailedInvalidConversationId = 21, + kFailedInvokeInProgress = 22, + kFailedInvalidConfiguration = 23, + kFailedNoClientFrame = 24, + kFailedNoClipboardMetadata = 25, + kMaxValue = kFailedNoClipboardMetadata, }; // LINT.ThenChange(//tools/metrics/histograms/metadata/glic/enums.xml:ShareImageResult)
diff --git a/chrome/browser/glic/glic_profile_manager.cc b/chrome/browser/glic/glic_profile_manager.cc index 046f213..9f43dd8 100644 --- a/chrome/browser/glic/glic_profile_manager.cc +++ b/chrome/browser/glic/glic_profile_manager.cc
@@ -45,6 +45,7 @@ std::optional<Profile*> g_forced_profile_for_launch_; std::optional<net::NetworkChangeNotifier::ConnectionType> g_forced_connection_type_; + } // namespace namespace glic { @@ -82,7 +83,7 @@ GlicProfileManager::GlicProfileManager() : memory_consumer_registration_( - /*consumer_name=*/"GlicProfileManager", + /*consumer_name=*/kMemoryConsumerName, /*traits=*/std::nullopt, // TODO(crbug.com/489671163): Fill traits. this, base::MemoryConsumerRegistration::CheckUnregister::kDisabled,
diff --git a/chrome/browser/glic/glic_profile_manager.h b/chrome/browser/glic/glic_profile_manager.h index 3dfd7db..a685094 100644 --- a/chrome/browser/glic/glic_profile_manager.h +++ b/chrome/browser/glic/glic_profile_manager.h
@@ -27,6 +27,8 @@ public ProfileObserver, public base::PassiveMemoryConsumer { public: + static constexpr char kMemoryConsumerName[] = "GlicProfileManager"; + GlicProfileManager(); ~GlicProfileManager() override;
diff --git a/chrome/browser/glic/glic_profile_manager_browsertest.cc b/chrome/browser/glic/glic_profile_manager_browsertest.cc index 727ffad..e1a89db5 100644 --- a/chrome/browser/glic/glic_profile_manager_browsertest.cc +++ b/chrome/browser/glic/glic_profile_manager_browsertest.cc
@@ -39,6 +39,7 @@ #include "chrome/test/base/ui_test_utils.h" #include "components/prefs/pref_service.h" #include "content/public/test/browser_test.h" +#include "content/public/test/memory_coordinator_browsertest_util.h" #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" #include "ui/base/ozone_buildflags.h" @@ -430,10 +431,12 @@ GTEST_SKIP() << "This test only applies if prewarming is enabled."; } ResetPrewarming(); - base::RunLoop run_loop; - base::MemoryPressureListener::SimulatePressureNotificationAsync( - base::MEMORY_PRESSURE_LEVEL_CRITICAL, run_loop.QuitClosure()); - run_loop.Run(); + + content::test::ScopedMemoryLimitOverride scoped_memory_limit_override( + GlicProfileManager::kMemoryConsumerName); + scoped_memory_limit_override.SetLimit(0); + scoped_memory_limit_override.NotifyReleaseMemory(); + EXPECT_EQ(WaitForShouldPreload(), GlicPrewarmingChecksResult::kUnderMemoryPressure); }
diff --git a/chrome/browser/glic/glic_selection_observer.cc b/chrome/browser/glic/glic_selection_observer.cc index f9a0294..bdcbc0b 100644 --- a/chrome/browser/glic/glic_selection_observer.cc +++ b/chrome/browser/glic/glic_selection_observer.cc
@@ -27,7 +27,6 @@ #include "chrome/browser/glic/public/glic_keyed_service.h" #include "chrome/browser/glic/public/service/glic_instance_coordinator.h" #include "chrome/browser/profiles/profile.h" -#include "chrome/browser/ui/browser_finder.h" #include "chrome/browser/ui/browser_window/public/browser_window_features.h" #include "chrome/browser/ui/browser_window/public/browser_window_interface.h" #include "chrome/browser/ui/toasts/api/toast_id.h" @@ -129,6 +128,29 @@ std::move(invoke_glic))); } +mojom::AdditionalContextPtr CreateAdditionalContext( + content::WebContents* web_contents, + const std::u16string& selected_text) { + auto context = mojom::AdditionalContext::New(); + context->source = mojom::AdditionalContextSource::kTextSelection; + std::vector<mojom::AdditionalContextPartPtr> parts; + if (!selected_text.empty()) { + auto context_data = mojom::ContextData::New(); + context_data->mime_type = kSelectionMimeType; + std::string utf8_text = base::UTF16ToUTF8(selected_text); + context_data->data = + mojo_base::BigBuffer(base::as_bytes(base::span(utf8_text))); + parts.push_back( + mojom::AdditionalContextPart::NewData(std::move(context_data))); + } + if (auto* tab_interface = + tabs::TabInterface::MaybeGetFromContents(web_contents)) { + context->tab_id = tab_interface->GetHandle().raw_value(); + } + context->parts = std::move(parts); + return context; +} + } // namespace GlicSelectionObserver::GlicSelectionObserver(content::WebContents* web_contents) @@ -441,26 +463,11 @@ Profile* profile = Profile::FromBrowserContext(web_contents->GetBrowserContext()); if (auto* glic_keyed_service = GlicKeyedService::Get(profile)) { - auto context = mojom::AdditionalContext::New(); - context->source = mojom::AdditionalContextSource::kTextSelection; - std::vector<mojom::AdditionalContextPartPtr> parts; - - { - auto context_data = mojom::ContextData::New(); - context_data->mime_type = kSelectionMimeType; - std::string utf8_text = base::UTF16ToUTF8(selected_text); - context_data->data = - mojo_base::BigBuffer(base::as_bytes(base::span(utf8_text))); - parts.push_back( - mojom::AdditionalContextPart::NewData(std::move(context_data))); - } - - context->parts = std::move(parts); - GlicInvokeOptions options(glic::Target(tab_interface), mojom::InvocationSource::kNudge); - options.additional_context = std::move(context); - + options.additional_context = AdditionalTabContext( + CreateAdditionalContext(web_contents.get(), selected_text), + content::GlobalRenderFrameHostId(), PolicyCheck::kNone); glic_keyed_service->Invoke(std::move(options)); } } @@ -493,11 +500,10 @@ if (has_sent_selection_context_ && glic_keyed_service_) { if (glic_keyed_service_->GetInstanceForTab(tab_interface)) { - auto context = mojom::AdditionalContext::New(); - context->source = mojom::AdditionalContextSource::kTextSelection; - context->parts = std::vector<mojom::AdditionalContextPartPtr>(); - glic_keyed_service_->SendAdditionalContext(tab_interface->GetHandle(), - std::move(context)); + // TODO(b/508916357): Use the invoke API. + glic_keyed_service_->SendAdditionalContext( + tab_interface->GetHandle(), + CreateAdditionalContext(web_contents(), u"")); } has_sent_selection_context_ = false; } @@ -521,24 +527,10 @@ views::Widget::ClosedReason::kLostFocus); } - auto context = mojom::AdditionalContext::New(); - context->source = mojom::AdditionalContextSource::kTextSelection; - std::vector<mojom::AdditionalContextPartPtr> parts; - - { - auto context_data = mojom::ContextData::New(); - context_data->mime_type = kSelectionMimeType; - std::string utf8_text = base::UTF16ToUTF8(selected_text); - context_data->data = - mojo_base::BigBuffer(base::as_bytes(base::span(utf8_text))); - parts.push_back( - mojom::AdditionalContextPart::NewData(std::move(context_data))); - } - - context->parts = std::move(parts); - - glic_keyed_service_->SendAdditionalContext(tab_interface->GetHandle(), - std::move(context)); + // TODO(b/508916357): Use the invoke API. + glic_keyed_service_->SendAdditionalContext( + tab_interface->GetHandle(), + CreateAdditionalContext(web_contents(), selected_text)); has_sent_selection_context_ = true; } else { if (!features::kGlicSelectionPromptUpdatesOnly.Get()) {
diff --git a/chrome/browser/glic/glic_selection_observer_interactive_uitest.cc b/chrome/browser/glic/glic_selection_observer_interactive_uitest.cc new file mode 100644 index 0000000..3f0aa0ce --- /dev/null +++ b/chrome/browser/glic/glic_selection_observer_interactive_uitest.cc
@@ -0,0 +1,73 @@ +// Copyright 2026 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chrome/browser/glic/glic_selection_observer.h" +#include "chrome/browser/glic/public/features.h" +#include "chrome/browser/glic/test_support/interactive_glic_test.h" +#include "chrome/browser/ui/browser.h" +#include "chrome/browser/ui/browser_element_identifiers.h" +#include "chrome/browser/ui/browser_window/public/browser_window_interface.h" +#include "chrome/browser/ui/views/frame/contents_web_view.h" +#include "chrome/test/interaction/interactive_browser_test.h" +#include "components/tabs/public/tab_interface.h" +#include "content/public/test/browser_test.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "url/gurl.h" + +namespace glic { +namespace { + +class GlicSelectionObserverInteractiveUiTest + : public test::InteractiveGlicTest { + public: + GlicSelectionObserverInteractiveUiTest() { + scoped_feature_list_.InitWithFeaturesAndParameters( + {{features::kGlic, {}}, + {features::kGlicSelectionPrompt, {{"updates_only", "true"}}}}, + {}); + } + + protected: + static constexpr char kCheckContextJs[] = R"JS( + () => { + let c = document.querySelector('#additionalContextResult'); + return !!c && + c.innerText.includes('Tab ID: ') && + c.innerText.includes('MIME Type: application/x-glic-selection') && + c.innerText.includes('Data: This page'); + } + )JS"; + + auto SelectAll() { + return Do([this] { + content::WebContents* web_contents = + browser()->tab_strip_model()->GetActiveWebContents(); + web_contents->SelectAll(); + }); + } + + private: + base::test::ScopedFeatureList scoped_feature_list_; +}; + +IN_PROC_BROWSER_TEST_F(GlicSelectionObserverInteractiveUiTest, + SelectionUpdatesContext) { + DEFINE_LOCAL_ELEMENT_IDENTIFIER_VALUE(kActiveTab); + + const GURL url = embedded_test_server()->GetURL("/title2.html"); + const DeepQuery kPathToBody{ + "body", + }; + + RunTestSequence( + InstrumentTab(kActiveTab, std::nullopt, browser(), true), + NavigateWebContents(kActiveTab, url), OpenGlic(), + WaitForWebContentsPainted(kActiveTab), + MoveMouseTo(kActiveTab, kPathToBody), ClickMouse(), ClickMouse(), + SelectAll(), + WaitForJsResult(test::kGlicContentsElementId, kCheckContextJs)); +} + +} // namespace +} // namespace glic
diff --git a/chrome/browser/glic/glic_settings_util_android.cc b/chrome/browser/glic/glic_settings_util_android.cc index 758249a3..e5da8893 100644 --- a/chrome/browser/glic/glic_settings_util_android.cc +++ b/chrome/browser/glic/glic_settings_util_android.cc
@@ -5,7 +5,7 @@ #include "chrome/browser/glic/glic_settings_util.h" #include "base/notimplemented.h" -#include "chrome/browser/glic/android/glic_settings_navigation_android.h" +#include "chrome/browser/glic/android/glic_navigation_utils_android.h" namespace glic {
diff --git a/chrome/browser/glic/host/auth_controller.cc b/chrome/browser/glic/host/auth_controller.cc index 4a7f3113..9c91ab4 100644 --- a/chrome/browser/glic/host/auth_controller.cc +++ b/chrome/browser/glic/host/auth_controller.cc
@@ -21,6 +21,10 @@ #include "google_apis/gaia/gaia_auth_util.h" #include "mojo/public/cpp/bindings/callback_helpers.h" +#if BUILDFLAG(IS_ANDROID) +#include "chrome/browser/glic/android/glic_navigation_utils_android.h" +#endif + namespace glic { namespace { @@ -159,12 +163,6 @@ return; } profile_->GetPrefs()->SetBoolean(prefs::kGlicPartitionNeedsCookieSync, true); - if (after_signin_callback_ && - after_signin_callback_expiration_time_ > base::TimeTicks::Now()) { - if (GetTokenState() == TokenState::kOk) { - std::move(after_signin_callback_).Run(); - } - } } void AuthController::OnRefreshTokenUpdatedForAccount( @@ -217,17 +215,15 @@ std::move(callback).Run(sync_success); } -void AuthController::ShowReauthForAccount(base::OnceClosure after_signin) { - after_signin_callback_ = std::move(after_signin); - // TODO(crbug.com/396500584): Check what timeout is appropriate. - after_signin_callback_expiration_time_ = - base::TimeTicks::Now() + base::Minutes(5); +void AuthController::ShowReauthForAccount(content::WebContents* web_contents) { CoreAccountInfo primary_account_info = identity_manager_->GetPrimaryAccountInfo(signin::ConsentLevel::kSignin); -#if !BUILDFLAG(IS_ANDROID) // TODO(b/477997050): Implement for android +#if !BUILDFLAG(IS_ANDROID) signin_ui_util::ShowReauthForAccount( profile_, primary_account_info.email, signin_metrics::AccessPoint::kGlicLaunchButton); +#else + glic::ShowSignIn(profile_, web_contents); #endif }
diff --git a/chrome/browser/glic/host/auth_controller.h b/chrome/browser/glic/host/auth_controller.h index e8ee228..3b88d0d 100644 --- a/chrome/browser/glic/host/auth_controller.h +++ b/chrome/browser/glic/host/auth_controller.h
@@ -14,6 +14,10 @@ class Profile; +namespace content { +class WebContents; +} + namespace glic { class GlicCookieSynchronizer; @@ -56,14 +60,10 @@ // Called when the client reports that it has encountered a transient error. void OnClientTransientError(mojo_base::mojom::AbslStatusCode status_code); - // Show the sign-in page. `after_signin` will be called after the user has - // signed in. It will not be called if the user cancels the sign-in, or the - // sign-in doesn't happen before: - // * ShowReauthForAccount is called again - // * The AuthController is destroyed - // * Too much time has passed (5 minutes). + // Show the sign-in page. `web_contents` is used on Android to find the + // activity to display the sign-in sheet. // TODO(crbug.com/406529330): Track sign-in flow correctly. - void ShowReauthForAccount(base::OnceClosure after_signin); + void ShowReauthForAccount(content::WebContents* web_contents); void SetCookieSynchronizerForTesting( std::unique_ptr<GlicCookieSynchronizer> synchronizer); @@ -102,8 +102,6 @@ bool sync_success); raw_ptr<Profile> profile_; - base::OnceClosure after_signin_callback_; - base::TimeTicks after_signin_callback_expiration_time_; raw_ptr<signin::IdentityManager> identity_manager_; std::unique_ptr<GlicCookieSynchronizer> cookie_synchronizer_; base::ScopedObservation<signin::IdentityManager,
diff --git a/chrome/browser/glic/host/context/glic_focused_browser_manager_impl.cc b/chrome/browser/glic/host/context/glic_focused_browser_manager_impl.cc index f44aaa41..0f29d74 100644 --- a/chrome/browser/glic/host/context/glic_focused_browser_manager_impl.cc +++ b/chrome/browser/glic/host/context/glic_focused_browser_manager_impl.cc
@@ -7,7 +7,6 @@ #include "base/functional/bind.h" #include "chrome/browser/glic/common/future_browser_features.h" #include "chrome/browser/glic/host/context/glic_sharing_utils.h" -#include "chrome/browser/ui/browser_finder.h" #include "chrome/browser/ui/browser_window.h" #include "chrome/browser/ui/browser_window/public/browser_window_interface.h" #include "chrome/browser/ui/browser_window/public/browser_window_interface_iterator.h"
diff --git a/chrome/browser/glic/host/context/glic_focused_tab_manager.cc b/chrome/browser/glic/host/context/glic_focused_tab_manager.cc index 170941c..8e22a20 100644 --- a/chrome/browser/glic/host/context/glic_focused_tab_manager.cc +++ b/chrome/browser/glic/host/context/glic_focused_tab_manager.cc
@@ -12,7 +12,6 @@ #include "chrome/browser/glic/host/context/glic_sharing_utils.h" #include "chrome/browser/glic/host/context/glic_tab_data.h" #include "chrome/browser/ui/browser.h" -#include "chrome/browser/ui/browser_finder.h" #include "chrome/browser/ui/browser_window.h" #include "chrome/browser/ui/browser_window/public/desktop_browser_window_capabilities.h" #include "chrome/browser/ui/tabs/tab_strip_model.h"
diff --git a/chrome/browser/glic/host/context/glic_page_context_fetcher.cc b/chrome/browser/glic/host/context/glic_page_context_fetcher.cc index 6ca8297..c5e4950 100644 --- a/chrome/browser/glic/host/context/glic_page_context_fetcher.cc +++ b/chrome/browser/glic/host/context/glic_page_context_fetcher.cc
@@ -15,6 +15,7 @@ #include "base/time/time.h" #include "base/types/expected.h" #include "build/build_config.h" +#include "chrome/browser/actor/actor_metrics.h" #include "chrome/browser/actor/actor_proto_conversion.h" #include "chrome/browser/actor/actor_tab_data.h" #include "chrome/browser/actor/aggregated_journal.h" @@ -195,7 +196,8 @@ if (tab) { if (auto* actor_tab_data = actor::ActorTabData::From(tab.get())) { actor_tab_data->DidObserveContent( - page_context.annotated_page_content_result->proto); + page_context.annotated_page_content_result->proto, + actor::ApcSource::kGlic); } }
diff --git a/chrome/browser/glic/host/context/glic_screenshot_capturer.cc b/chrome/browser/glic/host/context/glic_screenshot_capturer.cc index 3b7475f..324decac 100644 --- a/chrome/browser/glic/host/context/glic_screenshot_capturer.cc +++ b/chrome/browser/glic/host/context/glic_screenshot_capturer.cc
@@ -13,7 +13,6 @@ #include "base/time/time.h" #include "chrome/browser/media/webrtc/desktop_media_picker_controller.h" #include "chrome/browser/ui/browser.h" -#include "chrome/browser/ui/browser_finder.h" #include "chrome/browser/ui/browser_window.h" #include "chrome/common/chrome_features.h" #include "chrome/grit/generated_resources.h"
diff --git a/chrome/browser/glic/host/context/glic_share_image_handler.cc b/chrome/browser/glic/host/context/glic_share_image_handler.cc index e401cd3..f01b78b 100644 --- a/chrome/browser/glic/host/context/glic_share_image_handler.cc +++ b/chrome/browser/glic/host/context/glic_share_image_handler.cc
@@ -15,6 +15,7 @@ #include "chrome/browser/glic/fre/glic_fre_controller.h" #include "chrome/browser/glic/host/context/glic_page_context_fetcher.h" #include "chrome/browser/glic/host/guest_util.h" +#include "chrome/browser/glic/public/features.h" #include "chrome/browser/glic/public/glic_instance.h" #include "chrome/browser/glic/public/glic_keyed_service.h" #include "chrome/browser/ui/browser_window/public/browser_window_interface.h" @@ -256,6 +257,24 @@ return; } + if (base::FeatureList::IsEnabled(features::kGlicShareImageViaInvoke)) { + GlicInvokeOptions invoke_options(mojom::InvocationSource::kSharedImage); + invoke_options.additional_context = + AdditionalTabContext(std::move(additional_context_), + render_frame_host_id_, PolicyCheck::kClipboard); + invoke_options.target.surface = tab; + invoke_options.target.conversation = NewConversation(); + invoke_options.fre_override = mojom::FreOverride::kTrustFirstClick; + invoke_options.on_error = base::BindOnce( + &GlicShareImageHandler::OnInvokeError, weak_ptr_factory_.GetWeakPtr()); + invoke_options.on_success = base::BindOnce( + &GlicShareImageHandler::ShareComplete, weak_ptr_factory_.GetWeakPtr(), + ShareImageResult::kSentImageToClient); + service_->Invoke(std::move(invoke_options)); + StopObservingNavigation(); + return; + } + content::ClipboardEndpoint source( ui::DataTransferEndpoint( rfh->GetMainFrame()->GetLastCommittedURL(), @@ -516,27 +535,85 @@ return instance; } +void GlicShareImageHandler::OnInvokeError(GlicInvokeError error) { + switch (error) { + case GlicInvokeError::kUnknown: + ShareComplete(ShareImageResult::kFailedUnknown); + break; + case GlicInvokeError::kTimeout: + if (!GlicEnabling::HasConsentedForProfile(service_->profile())) { + ShareComplete( + ShareImageResult::kFailedTimedOutDidNotCompleteOnboarding); + } else { + ShareComplete(ShareImageResult::kFailedTimedOut); + } + break; + case GlicInvokeError::kInvalidConversationId: + ShareComplete(ShareImageResult::kFailedInvalidConversationId); + break; + case GlicInvokeError::kInvalidTab: + ShareComplete(ShareImageResult::kFailedNoTab); + break; + case GlicInvokeError::kTabClosed: + ShareComplete(ShareImageResult::kFailedNoTab); + break; + case GlicInvokeError::kInstanceDestroyed: + ShareComplete(ShareImageResult::kFailedLostInstance); + break; + case GlicInvokeError::kInvokeInProgress: + ShareComplete(ShareImageResult::kFailedInvokeInProgress); + break; + case GlicInvokeError::kInvalidConfiguration: + ShareComplete(ShareImageResult::kFailedInvalidConfiguration); + break; + case GlicInvokeError::kAdditionalContextSawNavigation: + ShareComplete(ShareImageResult::kFailedSawNavigation); + break; + case GlicInvokeError::kAdditionalContextFailedCopyPolicy: + ShareComplete(ShareImageResult::kFailedClipboardCopyPolicy); + break; + case GlicInvokeError::kAdditionalContextFailedPastePolicy: + ShareComplete(ShareImageResult::kFailedClipboardPastePolicy); + break; + case GlicInvokeError::kAdditionalContextNoSourceFrame: + ShareComplete(ShareImageResult::kFailedNoFrame); + break; + case GlicInvokeError::kAdditionalContextNoClientFrame: + ShareComplete(ShareImageResult::kFailedNoClientFrame); + break; + case GlicInvokeError::kAdditionalContextNoClipboardMetadata: + ShareComplete(ShareImageResult::kFailedNoClipboardMetadata); + break; + default: + ShareComplete(ShareImageResult::kFailedUnknown); + break; + } +} + void GlicShareImageHandler::ShareComplete(ShareImageResult result) { if (result == ShareImageResult::kSentImageToClient) { - // Do final checks for readiness before sending the context. - tabs::TabInterface* tab = tab_handle_.Get(); - if (!tab) { - ShareComplete(ShareImageResult::kFailedNoTab); - return; - } - std::optional<bool> optional_is_client_ready = IsClientReady(*tab); - if (!optional_is_client_ready.has_value()) { - // If we get nullopt, it sharing is already completed, so bail. - return; - } - bool is_client_ready = *optional_is_client_ready; - if (!is_client_ready) { - ShareComplete(ShareImageResult::kFailedClientUnreadied); - return; - } + if (!base::FeatureList::IsEnabled(features::kGlicShareImageViaInvoke)) { + // Do final checks for readiness before sending the context. + tabs::TabInterface* tab = tab_handle_.Get(); + if (!tab) { + ShareComplete(ShareImageResult::kFailedNoTab); + return; + } + std::optional<bool> optional_is_client_ready = IsClientReady(*tab); + if (!optional_is_client_ready.has_value()) { + // If we get nullopt, it sharing is already completed, so bail. + return; + } + bool is_client_ready = *optional_is_client_ready; + if (!is_client_ready) { + ShareComplete(ShareImageResult::kFailedClientUnreadied); + return; + } - service_->SendAdditionalContext(tab_handle_, - std::move(additional_context_)); + // If we're using the invoke API, then the context has already been sent. + service_->SendAdditionalContext(tab_handle_, + std::move(additional_context_)); + } } else if (result != ShareImageResult::kFailedClipboardPastePolicy && result != ShareImageResult::kFailedClipboardCopyPolicy) { // Policy checks already show UI when they fail and don't need a toast.
diff --git a/chrome/browser/glic/host/context/glic_share_image_handler.h b/chrome/browser/glic/host/context/glic_share_image_handler.h index 0cafda4..b4e1eec 100644 --- a/chrome/browser/glic/host/context/glic_share_image_handler.h +++ b/chrome/browser/glic/host/context/glic_share_image_handler.h
@@ -14,6 +14,7 @@ #include "chrome/browser/glic/glic_metrics.h" #include "chrome/browser/glic/host/glic.mojom.h" #include "chrome/browser/glic/public/glic_instance.h" +#include "chrome/browser/glic/public/glic_invoke_options.h" #include "chrome/browser/page_content_annotations/multi_source_page_context_fetcher.h" #include "chrome/common/chrome_render_frame.mojom.h" #include "components/lens/lens_metadata.mojom.h" @@ -105,6 +106,10 @@ // instance change. std::optional<GlicInstance*> GetAndVerifyInstance(tabs::TabInterface* tab); + // Called if the invoke API hits a failure. This completes the share process + // and causes metrics to be logged. + void OnInvokeError(GlicInvokeError error); + // Called when the end result of sharing is known. Sends context on success. void ShareComplete(ShareImageResult result);
diff --git a/chrome/browser/glic/host/context/glic_share_image_handler_unittest.cc b/chrome/browser/glic/host/context/glic_share_image_handler_unittest.cc index a40734b..e3986d4 100644 --- a/chrome/browser/glic/host/context/glic_share_image_handler_unittest.cc +++ b/chrome/browser/glic/host/context/glic_share_image_handler_unittest.cc
@@ -152,6 +152,8 @@ handler_->DidFinishNavigation(handle); } + void OnInvokeError(GlicInvokeError error) { handler_->OnInvokeError(error); } + protected: content::BrowserTaskEnvironment task_environment_; content::RenderViewHostTestEnabler enabler_; @@ -247,4 +249,116 @@ 1); } +TEST_F(GlicShareImageHandlerTest, OnInvokeErrorUnknown) { + OnInvokeError(GlicInvokeError::kUnknown); + histogram_tester_.ExpectBucketCount( + "Glic.TabContext.ShareImageResult", + static_cast<int>(ShareImageResult::kFailedUnknown), 1); +} + +TEST_F(GlicShareImageHandlerTest, OnInvokeErrorInvalidConversationId) { + OnInvokeError(GlicInvokeError::kInvalidConversationId); + histogram_tester_.ExpectBucketCount( + "Glic.TabContext.ShareImageResult", + static_cast<int>(ShareImageResult::kFailedInvalidConversationId), 1); +} + +TEST_F(GlicShareImageHandlerTest, OnInvokeErrorInvokeInProgress) { + OnInvokeError(GlicInvokeError::kInvokeInProgress); + histogram_tester_.ExpectBucketCount( + "Glic.TabContext.ShareImageResult", + static_cast<int>(ShareImageResult::kFailedInvokeInProgress), 1); +} + +TEST_F(GlicShareImageHandlerTest, OnInvokeErrorInvalidConfiguration) { + OnInvokeError(GlicInvokeError::kInvalidConfiguration); + histogram_tester_.ExpectBucketCount( + "Glic.TabContext.ShareImageResult", + static_cast<int>(ShareImageResult::kFailedInvalidConfiguration), 1); +} + +TEST_F(GlicShareImageHandlerTest, OnInvokeErrorAdditionalContextNoClientFrame) { + OnInvokeError(GlicInvokeError::kAdditionalContextNoClientFrame); + histogram_tester_.ExpectBucketCount( + "Glic.TabContext.ShareImageResult", + static_cast<int>(ShareImageResult::kFailedNoClientFrame), 1); +} + +TEST_F(GlicShareImageHandlerTest, + OnInvokeErrorAdditionalContextNoClipboardMetadata) { + OnInvokeError(GlicInvokeError::kAdditionalContextNoClipboardMetadata); + histogram_tester_.ExpectBucketCount( + "Glic.TabContext.ShareImageResult", + static_cast<int>(ShareImageResult::kFailedNoClipboardMetadata), 1); +} + +TEST_F(GlicShareImageHandlerTest, OnInvokeErrorTimeoutConsented) { + SetFreCompletion(true); + OnInvokeError(GlicInvokeError::kTimeout); + histogram_tester_.ExpectBucketCount( + "Glic.TabContext.ShareImageResult", + static_cast<int>(ShareImageResult::kFailedTimedOut), 1); +} + +TEST_F(GlicShareImageHandlerTest, OnInvokeErrorTimeoutNotConsented) { + SetFreCompletion(false); + OnInvokeError(GlicInvokeError::kTimeout); + histogram_tester_.ExpectBucketCount( + "Glic.TabContext.ShareImageResult", + static_cast<int>( + ShareImageResult::kFailedTimedOutDidNotCompleteOnboarding), + 1); +} + +TEST_F(GlicShareImageHandlerTest, OnInvokeErrorInvalidTab) { + OnInvokeError(GlicInvokeError::kInvalidTab); + histogram_tester_.ExpectBucketCount( + "Glic.TabContext.ShareImageResult", + static_cast<int>(ShareImageResult::kFailedNoTab), 1); +} + +TEST_F(GlicShareImageHandlerTest, OnInvokeErrorTabClosed) { + OnInvokeError(GlicInvokeError::kTabClosed); + histogram_tester_.ExpectBucketCount( + "Glic.TabContext.ShareImageResult", + static_cast<int>(ShareImageResult::kFailedNoTab), 1); +} + +TEST_F(GlicShareImageHandlerTest, OnInvokeErrorInstanceDestroyed) { + OnInvokeError(GlicInvokeError::kInstanceDestroyed); + histogram_tester_.ExpectBucketCount( + "Glic.TabContext.ShareImageResult", + static_cast<int>(ShareImageResult::kFailedLostInstance), 1); +} + +TEST_F(GlicShareImageHandlerTest, OnInvokeErrorAdditionalContextSawNavigation) { + OnInvokeError(GlicInvokeError::kAdditionalContextSawNavigation); + histogram_tester_.ExpectBucketCount( + "Glic.TabContext.ShareImageResult", + static_cast<int>(ShareImageResult::kFailedSawNavigation), 1); +} + +TEST_F(GlicShareImageHandlerTest, + OnInvokeErrorAdditionalContextFailedCopyPolicy) { + OnInvokeError(GlicInvokeError::kAdditionalContextFailedCopyPolicy); + histogram_tester_.ExpectBucketCount( + "Glic.TabContext.ShareImageResult", + static_cast<int>(ShareImageResult::kFailedClipboardCopyPolicy), 1); +} + +TEST_F(GlicShareImageHandlerTest, + OnInvokeErrorAdditionalContextFailedPastePolicy) { + OnInvokeError(GlicInvokeError::kAdditionalContextFailedPastePolicy); + histogram_tester_.ExpectBucketCount( + "Glic.TabContext.ShareImageResult", + static_cast<int>(ShareImageResult::kFailedClipboardPastePolicy), 1); +} + +TEST_F(GlicShareImageHandlerTest, OnInvokeErrorAdditionalContextNoSourceFrame) { + OnInvokeError(GlicInvokeError::kAdditionalContextNoSourceFrame); + histogram_tester_.ExpectBucketCount( + "Glic.TabContext.ShareImageResult", + static_cast<int>(ShareImageResult::kFailedNoFrame), 1); +} + } // namespace glic
diff --git a/chrome/browser/glic/host/glic.mojom b/chrome/browser/glic/host/glic.mojom index a8ae1f5..1a017f4 100644 --- a/chrome/browser/glic/host/glic.mojom +++ b/chrome/browser/glic/host/glic.mojom
@@ -264,14 +264,6 @@ NewTabMojo new_tab; }; -// The level of in-flight navigation events allowed without canceling the -// invocation. -enum AllowedInflightNavigation { - kNone, - kSameDomain, - kAll, -}; - // @generate glic_api // Configuration to override the default ZSS behavior for the invocation, // only having an impact if ZSS would be shown for the invocation.
diff --git a/chrome/browser/glic/host/glic_actor_interactive_uitest_common.cc b/chrome/browser/glic/host/glic_actor_interactive_uitest_common.cc index ab2c92c..9346fbc 100644 --- a/chrome/browser/glic/host/glic_actor_interactive_uitest_common.cc +++ b/chrome/browser/glic/host/glic_actor_interactive_uitest_common.cc
@@ -18,6 +18,7 @@ #include "base/test/scoped_feature_list.h" #include "build/build_config.h" #include "chrome/browser/actor/actor_keyed_service.h" +#include "chrome/browser/actor/actor_metrics.h" #include "chrome/browser/actor/actor_proto_conversion.h" #include "chrome/browser/actor/actor_tab_data.h" #include "chrome/browser/actor/actor_task.h" @@ -658,7 +659,8 @@ actor::ActorTabData* tab_data = actor::ActorTabData::From(tab_handle_.Get()); if (tab_data) { - tab_data->DidObserveContent(*annotated_page_content_); + tab_data->DidObserveContent(*annotated_page_content_, + actor::ApcSource::kActor); } } run_loop.Quit();
diff --git a/chrome/browser/glic/host/glic_api_browsertest.cc b/chrome/browser/glic/host/glic_api_browsertest.cc index 98172ce..bc4c2d79 100644 --- a/chrome/browser/glic/host/glic_api_browsertest.cc +++ b/chrome/browser/glic/host/glic_api_browsertest.cc
@@ -184,8 +184,6 @@ "GlicApiTestWithMqlsIdGetterEnabled", "GlicApiTestWithMqlsIdGetterDisabled", "GlicApiTestRuntimeFeatureOff", - "GlicApiTestWithWebActuationSettingDisabled", - "GlicApiTestWithWebActuationSettingEnabled", "GlicApiTestWithGeminiActOnWebPolicy", "GlicApiTestWithWebContentsWarming", "GlicApiTestHibernateAllOnMemoryPressure", @@ -359,27 +357,6 @@ base::test::ScopedFeatureList scoped_feature_list_; }; -class GlicApiTestWithWebActuationSettingEnabled : public GlicApiTestWithOneTab { - public: - GlicApiTestWithWebActuationSettingEnabled() { - feature_list_.InitWithFeatures({features::kGlicWebActuationSetting}, {}); - } - - private: - base::test::ScopedFeatureList feature_list_; -}; - -class GlicApiTestWithWebActuationSettingDisabled - : public GlicApiTestWithOneTab { - public: - GlicApiTestWithWebActuationSettingDisabled() { - feature_list_.InitWithFeatures({}, {features::kGlicWebActuationSetting}); - } - - private: - base::test::ScopedFeatureList feature_list_; -}; - class GlicApiTestWithMqlsIdGetterEnabled : public GlicApiTestWithOneTab { public: GlicApiTestWithMqlsIdGetterEnabled() { @@ -725,24 +702,6 @@ AssertAllTestsRegistered(GetTestSuiteNames()); } -IN_PROC_BROWSER_TEST_P(GlicApiTestWithWebActuationSettingDisabled, - testWebActuationSettingIsUndefinedWhenFeatureDisabled) { - ExecuteJsTest(); -} - -IN_PROC_BROWSER_TEST_P(GlicApiTestWithWebActuationSettingEnabled, - testGetWebActuationSetting) { - glic::GlicKeyedService::Get(browser()->profile()) - ->enabling() - .SetUserEnabledActuationOnWeb(false); - ExecuteJsTest(); - - glic::GlicKeyedService::Get(browser()->profile()) - ->enabling() - .SetUserEnabledActuationOnWeb(true); - ContinueJsTest(); -} - // TODO(crbug.com/409042450): This is a flaky on MSAN. #if defined(SLOW_BINARY) #define MAYBE_testReload DISABLED_testReload @@ -3691,14 +3650,6 @@ DefaultTestParamSet(), &WithTestParams::PrintTestVariant); INSTANTIATE_TEST_SUITE_P(, - GlicApiTestWithWebActuationSettingDisabled, - DefaultTestParamSet(), - &WithTestParams::PrintTestVariant); -INSTANTIATE_TEST_SUITE_P(, - GlicApiTestWithWebActuationSettingEnabled, - DefaultTestParamSet(), - &WithTestParams::PrintTestVariant); -INSTANTIATE_TEST_SUITE_P(, GlicApiTestWithGeminiActOnWebPolicy, DefaultTestParamSet(), &WithTestParams::PrintTestVariant);
diff --git a/chrome/browser/glic/host/glic_internals.mojom b/chrome/browser/glic/host/glic_internals.mojom index 4f8d67a..a05d930 100644 --- a/chrome/browser/glic/host/glic_internals.mojom +++ b/chrome/browser/glic/host/glic_internals.mojom
@@ -28,7 +28,6 @@ string? skill_id; string? error_message; mojo_base.mojom.TimeDelta? timeout; - AllowedInflightNavigation allowed_inflight_navigation; bool auto_submit; FreOverride fre_override = kUnspecified; bool wait_for_panel_open;
diff --git a/chrome/browser/glic/host/glic_internals_page_handler.cc b/chrome/browser/glic/host/glic_internals_page_handler.cc index 27d3018..f23b206a 100644 --- a/chrome/browser/glic/host/glic_internals_page_handler.cc +++ b/chrome/browser/glic/host/glic_internals_page_handler.cc
@@ -192,7 +192,9 @@ options.prompts = std::move(mojo_options->prompts); if (mojo_options->additional_context) { - options.additional_context = std::move(mojo_options->additional_context); + options.additional_context = AdditionalTabContext( + std::move(mojo_options->additional_context), + content::GlobalRenderFrameHostId(), PolicyCheck::kClipboard); } if (mojo_options->conversation->is_new_conversation()) { @@ -217,19 +219,6 @@ options.wait_for_panel_open = mojo_options->wait_for_panel_open; options.target.actuation_target = mojo_options->actuation_target; - switch (mojo_options->allowed_inflight_navigation) { - case mojom::AllowedInflightNavigation::kSameDomain: - options.allowed_inflight_navigation = - AllowedInflightNavigation::kSameDomain; - break; - case mojom::AllowedInflightNavigation::kNone: - options.allowed_inflight_navigation = AllowedInflightNavigation::kNone; - break; - case mojom::AllowedInflightNavigation::kAll: - options.allowed_inflight_navigation = AllowedInflightNavigation::kAll; - break; - } - auto split_callback = base::SplitOnceCallback(std::move(callback)); options.on_success = base::BindOnce(
diff --git a/chrome/browser/glic/host/glic_page_handler.cc b/chrome/browser/glic/host/glic_page_handler.cc index 3ee91e3a..0082aba794 100644 --- a/chrome/browser/glic/host/glic_page_handler.cc +++ b/chrome/browser/glic/host/glic_page_handler.cc
@@ -38,6 +38,7 @@ #include "chrome/browser/feedback/feedback_uploader_chrome.h" #include "chrome/browser/feedback/feedback_uploader_factory_chrome.h" #include "chrome/browser/glic/actor/glic_actor_policy_checker.h" +#include "chrome/browser/glic/actor/glic_actor_task_manager.h" #include "chrome/browser/glic/common/future_browser_features.h" #include "chrome/browser/glic/common/glic_navigation.h" #include "chrome/browser/glic/fre/fre_util.h" @@ -708,6 +709,11 @@ web_client_.set_disconnect_handler(base::BindOnce( &GlicWebClientHandler::WebClientDisconnected, base::Unretained(this))); + if (base::FeatureList::IsEnabled(features::kGlicActor)) { + actor_client_session_ = + host().instance_delegate().BindActorClientSession()->GetWeakPtr(); + } + page_metadata_manager_ = std::make_unique<PageMetadataManager>(profile_, web_client_.get()); @@ -1158,46 +1164,48 @@ void CreateTask(actor::webui::mojom::TaskOptionsPtr options, CreateTaskCallback callback) override { - host().instance_delegate().CreateTask(nullptr, std::move(options), - std::move(callback)); + if (!actor_client_session_) { + std::move(callback).Run(base::unexpected( + mojom::CreateTaskErrorReason::kTaskSystemUnavailable)); + return; + } + actor_client_session_->CreateTask(std::move(options), std::move(callback)); } void PerformActions(const std::vector<uint8_t>& actions_proto, PerformActionsCallback callback) override { - if (!base::FeatureList::IsEnabled(features::kGlicActor)) { + if (!actor_client_session_) { receiver_.ReportBadMessage( "PerformActions cannot be called without GlicActor enabled."); return; } - host().instance_delegate().PerformActions(actions_proto, - std::move(callback)); + actor_client_session_->PerformActions(actions_proto, std::move(callback)); } void CancelActions(int32_t task_id, CancelActionsCallback callback) override { - if (!base::FeatureList::IsEnabled(features::kGlicActor)) { + if (!actor_client_session_) { receiver_.ReportBadMessage( "CancelActions cannot be called without GlicActor enabled."); return; } - host().instance_delegate().CancelActions(actor::TaskId(task_id), - std::move(callback)); + actor_client_session_->CancelActions(actor::TaskId(task_id), + std::move(callback)); } void StopActorTask(int32_t task_id, mojom::ActorTaskStopReason stop_reason) override { - if (!base::FeatureList::IsEnabled(features::kGlicActor)) { + if (!actor_client_session_) { receiver_.ReportBadMessage( "StopActorTask cannot be called without GlicActor enabled."); return; } - host().instance_delegate().StopActorTask(actor::TaskId(task_id), - stop_reason); + actor_client_session_->StopActorTask(actor::TaskId(task_id), stop_reason); } void PauseActorTask(int32_t task_id, mojom::ActorTaskPauseReason pause_reason, std::optional<int32_t> tab_id) override { - if (!base::FeatureList::IsEnabled(features::kGlicActor)) { + if (!actor_client_session_) { receiver_.ReportBadMessage( "PauseActorTask cannot be called without GlicActor enabled."); return; @@ -1206,41 +1214,40 @@ if (tab_id.has_value()) { tab_handle = tabs::TabInterface::Handle(*tab_id); } - host().instance_delegate().PauseActorTask(actor::TaskId(task_id), - pause_reason, tab_handle); + actor_client_session_->PauseActorTask(actor::TaskId(task_id), pause_reason, + tab_handle); } void ResumeActorTask(int32_t task_id, glic::mojom::GetTabContextOptionsPtr context_options, ResumeActorTaskCallback callback) override { - if (!base::FeatureList::IsEnabled(features::kGlicActor)) { + if (!actor_client_session_) { receiver_.ReportBadMessage( "ResumeActorTask cannot be called without GlicActor enabled."); return; } - host().instance_delegate().ResumeActorTask( + actor_client_session_->ResumeActorTask( actor::TaskId(task_id), *context_options, std::move(callback)); } void InterruptActorTask(int32_t task_id, std::optional<glic::mojom::ActorTaskInterruptReason> interrupt_reason) override { - if (!base::FeatureList::IsEnabled(features::kGlicActor)) { + if (!actor_client_session_) { receiver_.ReportBadMessage( "InterruptActorTask cannot be called without GlicActor enabled."); return; } - host().instance_delegate().InterruptActorTask(actor::TaskId(task_id), - interrupt_reason); + actor_client_session_->InterruptActorTask(actor::TaskId(task_id)); } void UninterruptActorTask(int32_t task_id) override { - if (!base::FeatureList::IsEnabled(features::kGlicActor)) { + if (!actor_client_session_) { receiver_.ReportBadMessage( "UninterruptActorTask cannot be called without GlicActor enabled."); return; } - host().instance_delegate().UninterruptActorTask(actor::TaskId(task_id)); + actor_client_session_->UninterruptActorTask(actor::TaskId(task_id)); } void CreateSkill(mojom::CreateSkillRequestPtr request, @@ -1329,12 +1336,12 @@ std::optional<int32_t> initiator_tab_id, std::optional<int32_t> initiator_window_id, CreateActorTabCallback callback) override { - if (!base::FeatureList::IsEnabled(features::kGlicActor)) { + if (!actor_client_session_) { receiver_.ReportBadMessage( "StopActorTask cannot be called without GlicActor enabled."); return; } - host().instance_delegate().CreateActorTab( + actor_client_session_->CreateActorTab( actor::TaskId(task_id), open_in_background, initiator_tab_id, initiator_window_id, std::move(callback)); } @@ -2072,6 +2079,7 @@ if (skills_service_) { skills_service_->RemoveObserver(this); } + actor_client_session_ = nullptr; } void WebClientDisconnected() { @@ -2417,6 +2425,7 @@ raw_ptr<skills::SkillsService> skills_service_; base::WeakPtr<actor::AutofillSelectionDialogEventHandler> autofill_selection_event_handler_; + base::WeakPtr<GlicActorClientSession> actor_client_session_; bool floating_panel_can_attach_ = false; }; @@ -2432,6 +2441,7 @@ page_(std::move(page)) { VLOG(1) << "Glic [PageHandler] Constructor"; CHECK(host_); + MarkProcessAsGlic(webui_contents->GetPrimaryMainFrame()->GetProcess()); host_->WebUIPageHandlerAdded(this); host_->AddPanelStateObserver(this); UpdatePageState(host_->GetPanelState(web_client_handler_.get()).kind); @@ -2594,8 +2604,7 @@ } void GlicPageHandler::SignInAndClosePanel() { - GetGlicService()->GetAuthController().ShowReauthForAccount(base::DoNothing()); - host().ClosePanel(this); + GetGlicService()->GetAuthController().ShowReauthForAccount(webui_contents_); } void GlicPageHandler::ResizeWidget(const gfx::Size& size,
diff --git a/chrome/browser/glic/host/glic_ui_interactive_uitest.cc b/chrome/browser/glic/host/glic_ui_interactive_uitest.cc index 79c8a4bd6..8a77d36 100644 --- a/chrome/browser/glic/host/glic_ui_interactive_uitest.cc +++ b/chrome/browser/glic/host/glic_ui_interactive_uitest.cc
@@ -185,6 +185,31 @@ return steps; } + auto WaitForMockElementChecked(const DeepQuery& where, bool checked) { + return Do([this, where, checked]() { + content::RenderFrameHost* frame = FindGlicGuestMainFrame(); + ASSERT_TRUE(frame); + auto result = + content::EvalJs(frame, content::JsReplace(R"js( + (async () => { + return new Promise((resolve) => { + const check = () => { + const el = document.querySelector($1); + if (el && el.checked === $2) { + resolve(true); + } else { + setTimeout(check, 100); + } + }; + check(); + }); + })() + )js", + where[0], checked)); + EXPECT_EQ(true, result); + }); + } + auto CheckEscapeKeyDismisses(const DeepQuery& panel) { return InAnyContext( WaitForShow(test::kGlicHostElementId), CheckElementVisible(panel, true), @@ -318,16 +343,11 @@ // TODO(crbug.com/454087646): Not reliable yet. IN_PROC_BROWSER_TEST_P(GlicUiConnectedUiTest, - DISABLED_CanNotAttachWithMinimizedBrowser) { - RunTestSequence( - OpenGlic(GlicInstrumentMode::kHostAndContents), - CheckMockElementChecked({"#canAttachCheckbox"}, true), - Do([&]() { browser()->GetBrowserView().Minimize(); }), - // TODO(harringtond): Ideally this would wait until not checked, rather - // than check only once. There's no guarantee the web client - // has been updated before this code runs. Currently, this - // test works, though it's a risk for flakiness. - CheckMockElementChecked({"#canAttachCheckbox"}, false)); + CanNotAttachWithMinimizedBrowser) { + RunTestSequence(OpenGlic(GlicInstrumentMode::kHostAndContents), Detach(), + WaitForMockElementChecked({"#canAttachCheckbox"}, true), + Do([&]() { browser()->GetBrowserView().Minimize(); }), + WaitForMockElementChecked({"#canAttachCheckbox"}, false)); } IN_PROC_BROWSER_TEST_P(GlicUiConnectedUiTest,
diff --git a/chrome/browser/glic/host/guest_util.cc b/chrome/browser/glic/host/guest_util.cc index bc129ae..ebcceae 100644 --- a/chrome/browser/glic/host/guest_util.cc +++ b/chrome/browser/glic/host/guest_util.cc
@@ -5,7 +5,10 @@ #include "chrome/browser/glic/host/guest_util.h" #include "base/command_line.h" +#include "base/memory/ptr_util.h" +#include "base/memory/raw_ptr.h" #include "base/metrics/histogram_functions.h" +#include "base/supports_user_data.h" #include "build/build_config.h" #include "chrome/browser/browser_process.h" #include "chrome/browser/glic/glic_pref_names.h" @@ -16,6 +19,8 @@ #include "chrome/common/chrome_features.h" #include "chrome/common/chrome_switches.h" #include "chrome/common/webui_url_constants.h" +#include "components/guest_view/browser/guest_view_base.h" +#include "components/guest_view/browser/guest_view_manager.h" #include "components/guest_view/buildflags/buildflags.h" #include "components/language/core/common/language_util.h" #include "components/prefs/pref_service.h" @@ -24,6 +29,8 @@ #include "content/public/browser/render_frame_host.h" #include "content/public/browser/render_process_host.h" #include "content/public/browser/web_contents.h" +#include "content/public/browser/web_contents_observer.h" +#include "content/public/browser/web_contents_user_data.h" #include "extensions/buildflags/buildflags.h" #include "mojo/public/cpp/bindings/associated_remote.h" #include "net/base/url_util.h" @@ -35,12 +42,6 @@ #include "third_party/skia/include/core/SkRegion.h" #include "ui/gfx/geometry/skia_conversions.h" #include "url/gurl.h" - -// Note: guest_view isn't available on android mobile yet. Once it is, we can -// include these unconditionally. -#if BUILDFLAG(ENABLE_GUEST_VIEW) -#include "components/guest_view/browser/guest_view_base.h" -#endif #if BUILDFLAG(ENABLE_EXTENSIONS_CORE) #include "extensions/browser/guest_view/web_view/web_view_guest.h" #else @@ -53,6 +54,83 @@ namespace { +// Attached to the WebUI WebContents using WebContentsUserData. +// Acts as both a marker for Glic WebUI and a link to its guest WebContents. +class GlicWebUiData : public content::WebContentsUserData<GlicWebUiData>, + public content::WebContentsObserver { + public: + ~GlicWebUiData() override = default; + + // Call this when the guest is attached to establish the link. + void SetGuestContents(content::WebContents* guest_contents) { + Observe(guest_contents); + } + + // Returns the guest WebContents if it is attached and valid, nullptr + // otherwise. + content::WebContents* guest_contents() const { + content::WebContents* guest = web_contents(); + if (!guest) { + return nullptr; + } + auto* guest_view = guest_view::GuestViewBase::FromWebContents(guest); + if (guest_view && !guest_view->attached()) { + return nullptr; + } + return guest; + } + + private: + explicit GlicWebUiData(content::WebContents* webui_contents) + : content::WebContentsUserData<GlicWebUiData>(*webui_contents), + content::WebContentsObserver(nullptr), + webui_contents_(webui_contents) {} + friend class content::WebContentsUserData<GlicWebUiData>; + WEB_CONTENTS_USER_DATA_KEY_DECL(); + + using WebContentsObserver::web_contents; + + raw_ptr<content::WebContents> webui_contents_; +}; + +WEB_CONTENTS_USER_DATA_KEY_IMPL(GlicWebUiData); + +// Attached to RenderProcessHost to identify Glic processes. +class GlicProcessUserData : public base::SupportsUserData::Data { + public: + static constexpr char kKey[] = "glic::GlicProcessUserData"; + + ~GlicProcessUserData() override = default; + + static GlicProcessUserData* FromProcessHost(content::RenderProcessHost* rph) { + return rph ? static_cast<GlicProcessUserData*>(rph->GetUserData(kKey)) + : nullptr; + } + + static void MarkProcess(content::RenderProcessHost* rph) { + if (rph && !FromProcessHost(rph)) { + rph->SetUserData(kKey, base::WrapUnique(new GlicProcessUserData())); + } + } + + private: + GlicProcessUserData() = default; +}; + +// Attached to Guest WebContents to identify it directly. +class GlicGuestMarker : public content::WebContentsUserData<GlicGuestMarker> { + public: + ~GlicGuestMarker() override = default; + + private: + explicit GlicGuestMarker(content::WebContents* web_contents) + : content::WebContentsUserData<GlicGuestMarker>(*web_contents) {} + friend class content::WebContentsUserData<GlicGuestMarker>; + WEB_CONTENTS_USER_DATA_KEY_DECL(); +}; + +WEB_CONTENTS_USER_DATA_KEY_IMPL(GlicGuestMarker); + // LINT.IfChange(WebViewAutoPlayProgress) enum class WebViewAutoPlayProgress { kWebContentsObserverRegistered = 0, @@ -87,19 +165,23 @@ } }; -std::vector<Host*> GetAllHosts(content::BrowserContext* context) { - std::vector<Host*> hosts; - GlicKeyedService* service = - GlicKeyedServiceFactory::GetGlicKeyedService(context, /*create=*/false); - if (service) { - for (auto* instance : service->instance_coordinator().GetInstances()) { - hosts.push_back(&instance->host()); - } +bool IsGlicGuest(content::WebContents* web_contents) { + if (!web_contents || + GlicGuestMarker::FromWebContents(web_contents) == nullptr) { + return false; } - return hosts; + auto* guest_view = guest_view::GuestViewBase::FromWebContents(web_contents); + return guest_view && guest_view->attached(); +} +} // namespace + +void MarkProcessAsGlic(content::RenderProcessHost* rph) { + GlicProcessUserData::MarkProcess(rph); } -} // namespace +void CreateGlicWebUiData(content::WebContents* webui_contents) { + GlicWebUiData::CreateForWebContents(webui_contents); +} GURL GetGuestURL() { auto* command_line = base::CommandLine::ForCurrentProcess(); @@ -180,18 +262,12 @@ bool IsGlicWebUI(const content::WebContents* web_contents) { return web_contents && - web_contents->GetLastCommittedURL() == chrome::kChromeUIGlicURL; + GlicWebUiData::FromWebContents(web_contents) != nullptr; } bool IsProcessHostForGlic(content::RenderProcessHost* process_host) { - for (Host* host : GetAllHosts(process_host->GetBrowserContext())) { - auto* webui_contents = host->webui_contents(); - if (webui_contents && - webui_contents->GetPrimaryMainFrame()->GetProcess() == process_host) { - return true; - } - } - return false; + return process_host && + GlicProcessUserData::FromProcessHost(process_host) != nullptr; } content::WebContents* GetGlicGuestWebContents( @@ -199,14 +275,8 @@ if (!webui_contents) { return nullptr; } - for (Host* host : GetAllHosts(webui_contents->GetBrowserContext())) { - if (host->webui_contents() == webui_contents) { - content::RenderFrameHost* guest_rfh = host->GetGuestMainFrame(); - return guest_rfh ? content::WebContents::FromRenderFrameHost(guest_rfh) - : nullptr; - } - } - return nullptr; + auto* data = GlicWebUiData::FromWebContents(webui_contents); + return data ? data->guest_contents() : nullptr; } bool OnGuestAdded(content::WebContents* guest_contents) { @@ -236,22 +306,21 @@ guest_contents->SetSupportsDraggableRegions(true); #endif // !BUILDFLAG(IS_ANDROID) - for (Host* host : GetAllHosts(top->GetBrowserContext())) { - auto* webui_contents = host->webui_contents(); - if (!webui_contents || top != webui_contents) { - continue; - } + if (auto* data = GlicWebUiData::FromWebContents(top)) { + data->SetGuestContents(guest_contents); + GlicGuestMarker::CreateForWebContents(guest_contents); + GlicProcessUserData::MarkProcess( + guest_contents->GetPrimaryMainFrame()->GetProcess()); #if !BUILDFLAG(IS_ANDROID) // TODO(harringtond): This looks wrong, either fix or document this. blink::web_pref::WebPreferences prefs(top->GetOrCreateWebPreferences()); prefs.default_font_size = - host->webui_contents()->GetOrCreateWebPreferences().default_font_size; + top->GetOrCreateWebPreferences().default_font_size; top->SetWebPreferences(prefs); #else // TODO(b/470059315): What do we do for Android? #endif - break; } guest_contents->SetUserData( @@ -266,31 +335,12 @@ return true; } -std::vector<content::WebContents*> GetAllGlicGuestWebContentsForTesting( - content::BrowserContext* browser_context) { - std::vector<content::WebContents*> guest_contents; - for (Host* host : GetAllHosts(browser_context)) { - auto* guest = host->web_client_contents(); - if (guest) { - guest_contents.push_back(guest); - } - } - return guest_contents; -} - bool IsMediaRequestFromGlic(content::BrowserContext* browser_context, const std::string& request_id) { - for (Host* host : GetAllHosts(browser_context)) { - auto* guest = host->web_client_contents(); - if (!guest) { - continue; - } - if (content::MediaSession::GetRequestIdFromWebContents(guest).ToString() == - request_id) { - return true; - } - } - return false; + content::WebContents* web_contents = + content::MediaSession::GetWebContentsFromRequestId(request_id); + return web_contents && web_contents->GetBrowserContext() == browser_context && + IsGlicGuest(web_contents); } } // namespace glic
diff --git a/chrome/browser/glic/host/guest_util.h b/chrome/browser/glic/host/guest_util.h index fcc606f..2faf78f3 100644 --- a/chrome/browser/glic/host/guest_util.h +++ b/chrome/browser/glic/host/guest_util.h
@@ -14,7 +14,7 @@ class RenderFrameHost; class RenderProcessHost; class WebContents; -} +} // namespace content namespace glic { @@ -56,10 +56,11 @@ bool IsMediaRequestFromGlic(content::BrowserContext* browser_context, const std::string& request_id); -// Returns all Glic guest WebContents for the given browser context. -std::vector<content::WebContents*> GetAllGlicGuestWebContentsForTesting( - content::BrowserContext* browser_context); +// Identifies Glic processes. +void MarkProcessAsGlic(content::RenderProcessHost* rph); +// Instantiates Glic WebUI metadata on a WebContents. +void CreateGlicWebUiData(content::WebContents* webui_contents); } // namespace glic #endif // CHROME_BROWSER_GLIC_HOST_GUEST_UTIL_H_
diff --git a/chrome/browser/glic/host/guest_util_browsertest.cc b/chrome/browser/glic/host/guest_util_browsertest.cc index bbf5aeeb..24671ae4 100644 --- a/chrome/browser/glic/host/guest_util_browsertest.cc +++ b/chrome/browser/glic/host/guest_util_browsertest.cc
@@ -8,6 +8,7 @@ #include "base/test/scoped_feature_list.h" #include "chrome/browser/extensions/tab_helper.h" #include "chrome/browser/glic/glic_pref_names.h" +#include "chrome/browser/glic/test_support/glic_browser_test.h" #include "chrome/browser/glic/test_support/glic_test_environment.h" #include "chrome/browser/profiles/profile_manager.h" #include "chrome/browser/signin/identity_manager_factory.h" @@ -109,32 +110,11 @@ ->CreateGuestViewManagerDelegate())); } -class GuestUtilBrowserTest : public InProcessBrowserTest { - public: - GuestUtilBrowserTest() = default; - GuestUtilBrowserTest(const GuestUtilBrowserTest&) = delete; - GuestUtilBrowserTest& operator=(const GuestUtilBrowserTest&) = delete; - - ~GuestUtilBrowserTest() override = default; - - void SetUpOnMainThread() override { - InProcessBrowserTest::SetUpOnMainThread(); - } - - void TearDownOnMainThread() override { - InProcessBrowserTest::TearDownOnMainThread(); - } - - void SetUpCommandLine(base::CommandLine* command_line) override { - // Load blank page in glic guest view - command_line->AppendSwitchASCII(::switches::kGlicGuestURL, "about:blank"); - } - +class GuestUtilBrowserTest : public GlicBrowserTest { protected: guest_view::TestGuestViewManagerFactory& factory() { return factory_; } private: - glic::GlicTestEnvironment glic_test_environment_; guest_view::TestGuestViewManagerFactory factory_; }; @@ -159,7 +139,7 @@ IN_PROC_BROWSER_TEST_F(GuestUtilBrowserTest, OnGuestAdded_Glic) { EXPECT_EQ(0ULL, GetGuestViewManager(factory()).GetCurrentGuestCount()); - OpenWebUiWithGuestView(GURL{chrome::kChromeUIGlicURL}); + ASSERT_OK(OpenGlicForActiveTab()); auto* guest_view = GetGuestViewManager(factory()).WaitForSingleGuestViewCreated();
diff --git a/chrome/browser/glic/host/host.h b/chrome/browser/glic/host/host.h index 7c63f4f..919b51e9 100644 --- a/chrome/browser/glic/host/host.h +++ b/chrome/browser/glic/host/host.h
@@ -40,6 +40,7 @@ class WebUIContentsContainer; class GlicInstanceMetrics; class GlicInstanceMetricsBackwardsCompatibility; +class GlicActorClientSession; class GlicSkillsManager; @@ -99,36 +100,7 @@ const std::optional<int32_t>& window_id, glic::mojom::WebClientHandler::CreateTabCallback callback) = 0; // TODO(mcnee): `delegate` appears unused. - virtual void CreateTask( - base::WeakPtr<actor::ActorTaskDelegate> delegate, - actor::webui::mojom::TaskOptionsPtr options, - mojom::WebClientHandler::CreateTaskCallback callback) = 0; - virtual void PerformActions( - const std::vector<uint8_t>& actions_proto, - mojom::WebClientHandler::PerformActionsCallback callback) = 0; - virtual void CancelActions( - actor::TaskId task_id, - mojom::WebClientHandler::CancelActionsCallback callback) = 0; - virtual void StopActorTask(actor::TaskId task_id, - mojom::ActorTaskStopReason stop_reason) = 0; - virtual void PauseActorTask(actor::TaskId task_id, - mojom::ActorTaskPauseReason pause_reason, - tabs::TabInterface::Handle tab_handle) = 0; - virtual void ResumeActorTask( - actor::TaskId task_id, - const mojom::GetTabContextOptions& context_options, - glic::mojom::WebClientHandler::ResumeActorTaskCallback callback) = 0; - virtual void InterruptActorTask( - actor::TaskId task_id, - std::optional<mojom::ActorTaskInterruptReason> interrupt_reason) = 0; - virtual void UninterruptActorTask(actor::TaskId task_id) = 0; - - virtual void CreateActorTab( - actor::TaskId task_id, - bool open_in_background, - const std::optional<int32_t>& initiator_tab_id, - const std::optional<int32_t>& initiator_window_id, - glic::mojom::WebClientHandler::CreateActorTabCallback callback) = 0; + virtual GlicActorClientSession* BindActorClientSession() = 0; virtual void FetchZeroStateSuggestions( bool is_first_run,
diff --git a/chrome/browser/glic/host/new_glic_api_alt_browsertest.cc b/chrome/browser/glic/host/new_glic_api_alt_browsertest.cc deleted file mode 100644 index 65dffc57..0000000 --- a/chrome/browser/glic/host/new_glic_api_alt_browsertest.cc +++ /dev/null
@@ -1,84 +0,0 @@ -// Copyright 2026 The Chromium Authors -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "base/test/scoped_feature_list.h" -#include "chrome/browser/glic/host/glic_features.mojom-features.h" -#include "chrome/browser/glic/test_support/glic_browser_interactive_migration_test.h" -#include "chrome/browser/glic/test_support/new_glic_api_test.h" -#include "chrome/browser/tab_list/tab_list_interface.h" -#include "chrome/common/chrome_features.h" -#include "content/public/test/browser_test.h" -#include "content/public/test/browser_test_utils.h" - -namespace glic { -namespace { - -struct TestParams { - bool enable_scroll_to_pdf = false; - bool trust_first_onboarding_arm1 = false; - bool trust_first_onboarding_arm2 = false; - bool auto_open_pdf = false; -}; - -class WithTestParams : public testing::WithParamInterface<TestParams> { - public: - WithTestParams() {} - - static std::string PrintTestVariant( - const ::testing::TestParamInfo<TestParams>& info) { - return "Default"; - } -}; - -using GlicApiBrowserAltTest = - GlicApiBrowserTestMixin<GlicBrowserInteractiveMigrationTest>; - -class NewGlicApiAltTest : public GlicApiBrowserAltTest, public WithTestParams { - public: - NewGlicApiAltTest() : GlicApiBrowserAltTest("./new_glic_api_browsertest.js") { - features_.InitWithFeaturesAndParameters( - {{features::kGlic, {}}, {mojom::features::kGlicMultiTab, {}}}, {}); - } - - void SetUpOnMainThread() override { - GlicApiBrowserAltTest::SetUpOnMainThread(); - - ASSERT_TRUE(content::NavigateToURL( - GetTabListInterface()->GetActiveTab()->GetContents(), - GetTestUrl("page.html"))); - } - - private: - base::test::ScopedFeatureList features_; -}; - -// !!!!!!!!!!!! WARNING !!!!!!!!!!!!! -// TODO(b/508621027): DO NOT ADD MORE TESTS HERE! -// This test is here only to compare flake rates with new_glic_api_browsertest. -// !!!!!!!!!!!! WARNING !!!!!!!!!!!!! - -IN_PROC_BROWSER_TEST_P(NewGlicApiAltTest, testDoNothing) { - ASSERT_EQ(GetTabListInterface()->GetTabCount(), 1); - ASSERT_EQ(GetTabListInterface()->GetTab(0)->GetContents()->GetURL(), - GetTestUrl("page.html")); - ASSERT_OK(OpenGlicForActiveTab()); - ExecuteJsTest(); -} - -// !!!!!!!!!!!! WARNING !!!!!!!!!!!!! -// TODO(b/508621027): DO NOT ADD MORE TESTS HERE! -// This test is here only to compare flake rates with new_glic_api_browsertest. -// !!!!!!!!!!!! WARNING !!!!!!!!!!!!! - -auto DefaultTestParamSet() { - return testing::Values(TestParams{}); -} - -INSTANTIATE_TEST_SUITE_P(, - NewGlicApiAltTest, - DefaultTestParamSet(), - &WithTestParams::PrintTestVariant); - -} // namespace -} // namespace glic
diff --git a/chrome/browser/glic/host/new_glic_api_browsertest.cc b/chrome/browser/glic/host/new_glic_api_browsertest.cc index 1fdf877..f432e96 100644 --- a/chrome/browser/glic/host/new_glic_api_browsertest.cc +++ b/chrome/browser/glic/host/new_glic_api_browsertest.cc
@@ -133,6 +133,8 @@ "NewGlicApiMultiProfileTest", "NewGlicApiTestWithDefaultTabContextDisabled", "NewGlicApiTestWithDefaultTabContextEnabled", + "NewGlicApiTestWithWebActuationSettingDisabled", + "NewGlicApiTestWithWebActuationSettingEnabled", #if !BUILDFLAG(IS_ANDROID) "NewGlicApiTestWithSkills", #endif @@ -353,6 +355,42 @@ ExecuteJsTest(); } +class NewGlicApiTestWithWebActuationSettingEnabled : public NewGlicApiTest { + public: + NewGlicApiTestWithWebActuationSettingEnabled() { + feature_list_.InitWithFeatures({features::kGlicWebActuationSetting}, {}); + } + + private: + base::test::ScopedFeatureList feature_list_; +}; + +IN_PROC_BROWSER_TEST_P(NewGlicApiTestWithWebActuationSettingEnabled, + testGetWebActuationSetting) { + service()->enabling().SetUserEnabledActuationOnWeb(false); + ASSERT_OK(OpenGlicForActiveTab()); + ExecuteJsTest(); + + service()->enabling().SetUserEnabledActuationOnWeb(true); + ContinueJsTest(); +} + +class NewGlicApiTestWithWebActuationSettingDisabled : public NewGlicApiTest { + public: + NewGlicApiTestWithWebActuationSettingDisabled() { + feature_list_.InitWithFeatures({}, {features::kGlicWebActuationSetting}); + } + + private: + base::test::ScopedFeatureList feature_list_; +}; + +IN_PROC_BROWSER_TEST_P(NewGlicApiTestWithWebActuationSettingDisabled, + testWebActuationSettingIsUndefinedWhenFeatureDisabled) { + ASSERT_OK(OpenGlicForActiveTab()); + ExecuteJsTest(); +} + IN_PROC_BROWSER_TEST_P(NewGlicApiTestWithWebContentsWarming, testWebClientReadyOnPreload) { auto container = @@ -1237,6 +1275,16 @@ DefaultTestParamSet(), &WithTestParams::PrintTestVariant); +INSTANTIATE_TEST_SUITE_P(, + NewGlicApiTestWithWebActuationSettingDisabled, + DefaultTestParamSet(), + &WithTestParams::PrintTestVariant); + +INSTANTIATE_TEST_SUITE_P(, + NewGlicApiTestWithWebActuationSettingEnabled, + DefaultTestParamSet(), + &WithTestParams::PrintTestVariant); + // Skills are not supported yet on Android. #if !BUILDFLAG(IS_ANDROID) INSTANTIATE_TEST_SUITE_P(, @@ -1256,6 +1304,10 @@ NewGlicApiTestWithDefaultTabContextDisabled); GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST( NewGlicApiTestWithDefaultTabContextEnabled); +GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST( + NewGlicApiTestWithWebActuationSettingDisabled); +GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST( + NewGlicApiTestWithWebActuationSettingEnabled); #if !BUILDFLAG(IS_ANDROID) GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(NewGlicApiTestWithSkills); #endif
diff --git a/chrome/browser/glic/host/webui_contents_container.cc b/chrome/browser/glic/host/webui_contents_container.cc index 121f2e1d..bb4dd8c0 100644 --- a/chrome/browser/glic/host/webui_contents_container.cc +++ b/chrome/browser/glic/host/webui_contents_container.cc
@@ -11,6 +11,7 @@ #include "base/trace_event/trace_event.h" #include "chrome/browser/glic/glic_profile_manager.h" #include "chrome/browser/glic/host/glic_ui.h" +#include "chrome/browser/glic/host/guest_util.h" #include "chrome/browser/glic/host/host.h" #include "chrome/browser/glic/public/glic_keyed_service.h" #include "chrome/browser/glic/public/glic_keyed_service_factory.h" @@ -64,6 +65,7 @@ "WebUIContentsContainerImpl::WebUIContentsContainerImpl", perfetto::Flow::FromPointer(this)); CHECK(web_contents_); + CreateGlicWebUiData(web_contents_.get()); Observe(web_contents_.get()); web_contents_->SetPageBaseBackgroundColor( web_contents_->GetColorProvider().GetColor(kColorGlicBackground));
diff --git a/chrome/browser/glic/public/glic_invoke_options.cc b/chrome/browser/glic/public/glic_invoke_options.cc index ba539f6d..9b72bb5 100644 --- a/chrome/browser/glic/public/glic_invoke_options.cc +++ b/chrome/browser/glic/public/glic_invoke_options.cc
@@ -23,6 +23,28 @@ ZssConfig::ZssConfig(const ZssConfig&) = default; ZssConfig& ZssConfig::operator=(const ZssConfig&) = default; +AdditionalTabContext::AdditionalTabContext( + glic::mojom::AdditionalContextPtr context, + content::GlobalRenderFrameHostId source_rfh_id, + PolicyCheck policy_check) + : context(std::move(context)), + source_rfh_id(source_rfh_id), + policy_check(policy_check) {} +AdditionalTabContext::~AdditionalTabContext() = default; +AdditionalTabContext::AdditionalTabContext(const AdditionalTabContext& other) + : context(mojo::Clone(other.context)), + source_rfh_id(other.source_rfh_id), + policy_check(other.policy_check) {} +AdditionalTabContext& AdditionalTabContext::operator=( + const AdditionalTabContext& other) { + if (this != &other) { + context = mojo::Clone(other.context); + source_rfh_id = other.source_rfh_id; + policy_check = other.policy_check; + } + return *this; +} + Target::Target() = default; Target::Target(Target&&) = default; Target& Target::operator=(Target&&) = default; @@ -56,15 +78,12 @@ GlicInvokeOptions::GlicInvokeOptions( glic::mojom::InvocationSource invocation_source) - : invocation_source(invocation_source), - actuation_timeout(base::Minutes(10)) {} + : invocation_source(invocation_source) {} GlicInvokeOptions::GlicInvokeOptions( Target target, glic::mojom::InvocationSource invocation_source) - : invocation_source(invocation_source), - target(std::move(target)), - actuation_timeout(base::Minutes(10)) {} + : invocation_source(invocation_source), target(std::move(target)) {} GlicInvokeOptions::~GlicInvokeOptions() = default;
diff --git a/chrome/browser/glic/public/glic_invoke_options.h b/chrome/browser/glic/public/glic_invoke_options.h index 620c25c..5699955af 100644 --- a/chrome/browser/glic/public/glic_invoke_options.h +++ b/chrome/browser/glic/public/glic_invoke_options.h
@@ -17,6 +17,7 @@ #include "chrome/browser/glic/host/glic.mojom.h" #include "chrome/browser/glic/public/context/glic_sharing_manager.h" #include "components/tabs/public/tab_interface.h" +#include "content/public/browser/global_routing_id.h" class BrowserWindowInterface; @@ -104,12 +105,29 @@ std::optional<std::string> additional_content; }; -// The level of in-flight navigation events allowed without canceling the -// invocation. -enum class AllowedInflightNavigation { +// Specifies the type of policy check to perform. +enum class PolicyCheck { kNone, - kSameDomain, - kAll, + // Performs clipboard copy and past policy checks. + kClipboard, +}; + +// Provides additional context and information about its source. +struct AdditionalTabContext { + AdditionalTabContext(glic::mojom::AdditionalContextPtr context, + content::GlobalRenderFrameHostId source_rfh_id, + PolicyCheck policy_check); + ~AdditionalTabContext(); + AdditionalTabContext(const AdditionalTabContext&); + AdditionalTabContext& operator=(const AdditionalTabContext&); + + glic::mojom::AdditionalContextPtr context; + + // The RenderFrameHost ID of the source of the invocation, if applicable. + content::GlobalRenderFrameHostId source_rfh_id; + + // Specifies the policy check to perform, if any. + PolicyCheck policy_check = PolicyCheck::kClipboard; }; // Possible errors that can occur during a Glic invocation. @@ -129,6 +147,19 @@ kInvokeInProgress, // The provided invocation configuration is invalid. kInvalidConfiguration, + // Observed a navigation before the policy checks completed. + kAdditionalContextSawNavigation, + // The clipboard copy policy check failed for the given additional context. + kAdditionalContextFailedCopyPolicy, + // The clipboard paste policy check failed for the given additional context. + kAdditionalContextFailedPastePolicy, + // Could not find the source frame instance for the policy checks. + kAdditionalContextNoSourceFrame, + // Could not find the web client frame instance for the policy checks. + kAdditionalContextNoClientFrame, + // Could not create clipboard metadata for policy checks. This is likely due + // to the context type not yet being supported. + kAdditionalContextNoClipboardMetadata, }; // Details for invoking Glic with tabs shared. See @@ -170,7 +201,7 @@ // included with the invocation. // Warning: not fully implemented. // TODO(b/504627812): finish implementing. - glic::mojom::AdditionalContextPtr additional_context; + std::optional<AdditionalTabContext> additional_context; // Tabs to pin as part of invocation. TabSharingOptions tab_sharing; @@ -203,13 +234,6 @@ // The amount of time to wait before canceling the invocation. std::optional<base::TimeDelta> timeout; - // The amount of time to wait for actuation to complete after it starts. - std::optional<base::TimeDelta> actuation_timeout; - - // The level of navigation events allowed without canceling the invocation. - AllowedInflightNavigation allowed_inflight_navigation = - AllowedInflightNavigation::kAll; - // Whether to wait until the side panel has fully opened and the web // contents have stabilized before sending the invoke payload to the client. // Defaults to false. If the panel was already open when the invoke was
diff --git a/chrome/browser/glic/public/glic_keyed_service.cc b/chrome/browser/glic/public/glic_keyed_service.cc index cce59b0..e66e9b43 100644 --- a/chrome/browser/glic/public/glic_keyed_service.cc +++ b/chrome/browser/glic/public/glic_keyed_service.cc
@@ -59,7 +59,6 @@ #include "chrome/browser/profiles/profile_attributes_storage.h" #include "chrome/browser/profiles/profile_manager.h" #include "chrome/browser/tab_list/tab_list_interface.h" -#include "chrome/browser/ui/browser_finder.h" #include "chrome/browser/ui/browser_window/public/browser_window_interface.h" #include "chrome/browser/ui/browser_window/public/browser_window_interface_iterator.h" #include "chrome/browser/ui/navigator/browser_navigator.h"
diff --git a/chrome/browser/glic/service/glic_instance_coordinator_browsertest.cc b/chrome/browser/glic/service/glic_instance_coordinator_browsertest.cc index 0e52f9f..51c0bdb 100644 --- a/chrome/browser/glic/service/glic_instance_coordinator_browsertest.cc +++ b/chrome/browser/glic/service/glic_instance_coordinator_browsertest.cc
@@ -979,12 +979,7 @@ })); // Create a task to make it "actuating". - base::test::TestFuture< - base::expected<int32_t, glic::mojom::CreateTaskErrorReason>> - create_task_future; - instance->CreateTask(nullptr, actor::webui::mojom::TaskOptions::New(), - create_task_future.GetCallback()); - ASSERT_TRUE(create_task_future.Get().has_value()); + ASSERT_OK(CreateActorTask(instance)); EXPECT_TRUE(instance->IsActuating()); // Reload the instance. @@ -994,12 +989,7 @@ EXPECT_TRUE(base::test::RunUntil([&]() { return !instance->IsActuating(); })); // verify that task can be created again. - base::test::TestFuture< - base::expected<int32_t, glic::mojom::CreateTaskErrorReason>> - create_task_future2; - instance->CreateTask(nullptr, actor::webui::mojom::TaskOptions::New(), - create_task_future2.GetCallback()); - ASSERT_TRUE(create_task_future2.Get().has_value()); + ASSERT_OK(CreateActorTask(instance)); EXPECT_TRUE(instance->IsActuating()); } @@ -1682,12 +1672,7 @@ })); // Create a task to make it "actuating". - base::test::TestFuture< - base::expected<int32_t, glic::mojom::CreateTaskErrorReason>> - create_task_future; - instance1->CreateTask(nullptr, actor::webui::mojom::TaskOptions::New(), - create_task_future.GetCallback()); - ASSERT_TRUE(create_task_future.Get().has_value()); + ASSERT_OK(CreateActorTask(instance1)); EXPECT_TRUE(instance1->IsActuating()); // Close the side panel on tab 1 to prevent new tab daisy chaining. @@ -1852,13 +1837,7 @@ handler->Invoke(); // Create a task AFTER Invoke. - base::test::TestFuture< - base::expected<int32_t, glic::mojom::CreateTaskErrorReason>> - create_task_future; - instance->CreateTask(nullptr, actor::webui::mojom::TaskOptions::New(), - create_task_future.GetCallback()); - ASSERT_TRUE(create_task_future.Get().has_value()); - actor::TaskId task_id(create_task_future.Get().value()); + ASSERT_OK_AND_ASSIGN(actor::TaskId task_id, CreateActorTask(instance)); // Wait for IsActuating to be true. EXPECT_TRUE(actuating_true_future.Wait()); @@ -1867,8 +1846,8 @@ EXPECT_FALSE(success_future.IsReady()); // Stop the task and verify callback IS called. - instance->StopActorTask(task_id, - glic::mojom::ActorTaskStopReason::kTaskComplete); + instance->GetActorTaskManager()->GetClientSessionForTesting()->StopActorTask( + task_id, glic::mojom::ActorTaskStopReason::kTaskComplete); EXPECT_TRUE(success_future.Wait());
diff --git a/chrome/browser/glic/service/glic_instance_coordinator_impl.cc b/chrome/browser/glic/service/glic_instance_coordinator_impl.cc index cc26d63..f8b75377 100644 --- a/chrome/browser/glic/service/glic_instance_coordinator_impl.cc +++ b/chrome/browser/glic/service/glic_instance_coordinator_impl.cc
@@ -35,7 +35,6 @@ #include "chrome/browser/profiles/profile_manager.h" #include "chrome/browser/tab_list/tab_list_interface.h" #include "chrome/browser/ui/browser_commands.h" -#include "chrome/browser/ui/browser_finder.h" #include "chrome/browser/ui/browser_window/public/browser_window_interface_iterator.h" #include "chrome/common/chrome_features.h" #include "components/prefs/pref_service.h" @@ -712,6 +711,13 @@ mutable_options.focus_on_show = source_instance.HasFocus(); mutable_options.reinitialize_if_already_active = true; + // TODO(b/510405771): Remove animation suppression once bottom sheet hide is + // cancelable. + if (auto* side_panel_options = std::get_if<SidePanelShowOptions>( + &mutable_options.embedder_options)) { + side_panel_options->suppress_opening_animation = true; + } + GlicInstanceImpl* target_instance = nullptr; if (!info->conversation_id.empty()) { target_instance = GetInstanceImplForConversationId(info->conversation_id);
diff --git a/chrome/browser/glic/service/glic_instance_helper.cc b/chrome/browser/glic/service/glic_instance_helper.cc index 22aca73..8ea2ad63 100644 --- a/chrome/browser/glic/service/glic_instance_helper.cc +++ b/chrome/browser/glic/service/glic_instance_helper.cc
@@ -4,6 +4,9 @@ #include "chrome/browser/glic/service/glic_instance_helper.h" +#include "chrome/browser/glic/service/metrics/glic_instance_helper_metrics.h" +#include "components/tabs/public/tab_interface.h" + namespace glic { DEFINE_USER_DATA(GlicInstanceHelper); @@ -17,6 +20,7 @@ GlicInstanceHelper::GlicInstanceHelper(tabs::TabInterface* tab) : tab_(tab), + metrics_(std::make_unique<GlicInstanceHelperMetrics>()), scoped_unowned_user_data_(tab->GetUnownedUserDataHost(), *this) { #if BUILDFLAG(IS_ANDROID) InitJavaObject(); @@ -38,7 +42,7 @@ void GlicInstanceHelper::SetBoundInstance(Instance* instance) { bound_instance_ = instance; if (bound_instance_) { - metrics_.OnBoundToInstance(bound_instance_->id()); + metrics_->OnBoundToInstance(bound_instance_->id()); } #if BUILDFLAG(IS_ANDROID) NotifyJavaInstanceTitleChanged(); @@ -68,7 +72,7 @@ void GlicInstanceHelper::OnPinnedByInstance(Instance* instance) { CHECK(instance); pinned_instances_.insert(instance); - metrics_.OnPinnedByInstance(instance->id()); + metrics_->OnPinnedByInstance(instance->id()); } void GlicInstanceHelper::OnUnpinnedByInstance(Instance* instance) { @@ -83,11 +87,11 @@ } void GlicInstanceHelper::SetIsDaisyChained(DaisyChainSource source) { - metrics_.SetIsDaisyChained(source); + metrics_->SetIsDaisyChained(source); } void GlicInstanceHelper::OnDaisyChainAction(DaisyChainFirstAction action) { - metrics_.OnDaisyChainAction(action); + metrics_->OnDaisyChainAction(action); } base::CallbackListSubscription GlicInstanceHelper::SubscribeToDestruction(
diff --git a/chrome/browser/glic/service/glic_instance_helper.h b/chrome/browser/glic/service/glic_instance_helper.h index ccae4d1..5f8b8ac 100644 --- a/chrome/browser/glic/service/glic_instance_helper.h +++ b/chrome/browser/glic/service/glic_instance_helper.h
@@ -5,14 +5,13 @@ #ifndef CHROME_BROWSER_GLIC_SERVICE_GLIC_INSTANCE_HELPER_H_ #define CHROME_BROWSER_GLIC_SERVICE_GLIC_INSTANCE_HELPER_H_ +#include <memory> #include <optional> #include "base/callback_list.h" #include "build/build_config.h" #include "chrome/browser/glic/public/glic_instance.h" -#include "chrome/browser/glic/service/metrics/glic_instance_helper_metrics.h" #include "chrome/browser/glic/service/metrics/metrics_types.h" -#include "components/tabs/public/tab_interface.h" #include "ui/base/unowned_user_data/scoped_unowned_user_data.h" #if BUILDFLAG(IS_ANDROID) @@ -21,6 +20,9 @@ namespace glic { +class GlicInstanceHelperMetrics; +enum class DaisyChainFirstAction; + // Attaches a InstanceId to a TabInterface. An instance of this class is // created by and owned by TabFeatures. class GlicInstanceHelper { @@ -65,8 +67,8 @@ private: raw_ptr<Instance> bound_instance_ = nullptr; base::flat_set<raw_ptr<Instance>> pinned_instances_; - GlicInstanceHelperMetrics metrics_; raw_ptr<tabs::TabInterface> tab_; + std::unique_ptr<GlicInstanceHelperMetrics> metrics_; ui::ScopedUnownedUserData<GlicInstanceHelper> scoped_unowned_user_data_; base::RepeatingCallbackList<void(tabs::TabInterface*)> on_destroy_callback_list_;
diff --git a/chrome/browser/glic/service/glic_instance_helper_unittest.cc b/chrome/browser/glic/service/glic_instance_helper_unittest.cc index 0abaa38..60c6e278 100644 --- a/chrome/browser/glic/service/glic_instance_helper_unittest.cc +++ b/chrome/browser/glic/service/glic_instance_helper_unittest.cc
@@ -7,6 +7,7 @@ #include "base/test/metrics/histogram_tester.h" #include "base/test/task_environment.h" #include "chrome/browser/glic/public/glic_instance.h" +#include "chrome/browser/glic/service/metrics/glic_instance_helper_metrics.h" #include "components/tabs/public/mock_tab_interface.h" #include "testing/gtest/include/gtest/gtest.h" #include "ui/base/unowned_user_data/unowned_user_data_host.h"
diff --git a/chrome/browser/glic/service/glic_instance_impl.cc b/chrome/browser/glic/service/glic_instance_impl.cc index f234d20..9f616c8c 100644 --- a/chrome/browser/glic/service/glic_instance_impl.cc +++ b/chrome/browser/glic/service/glic_instance_impl.cc
@@ -77,7 +77,6 @@ #include "chrome/browser/glic/host/context/glic_empty_pinned_tab_manager.h" #include "chrome/browser/glic/widget/conversions.h" #include "chrome/browser/glic/widget/glic_floating_ui_android.h" -#include "chrome/browser/glic/widget/glic_inactive_floating_ui_android.h" #include "chrome/browser/glic/widget/glic_inactive_side_panel_ui_android.h" #include "chrome/browser/glic/widget/glic_side_panel_ui_android.h" #else @@ -105,9 +104,6 @@ BASE_FEATURE(kGlicUnpinOnUnbindIfUnused, base::FEATURE_ENABLED_BY_DEFAULT); -BASE_FEATURE(kGlicRequireConversationIdForActorTask, - base::FEATURE_DISABLED_BY_DEFAULT); - BASE_FEATURE(kSuppressFocusOnReady, base::FEATURE_ENABLED_BY_DEFAULT); constexpr size_t kMaxRecentConversationsForPanel = 3; @@ -298,7 +294,8 @@ if (auto* actor_keyed_service = actor::ActorKeyedServiceFactory::GetActorKeyedService(profile_)) { actor_task_manager_ = std::make_unique<GlicActorTaskManager>( - profile, actor_keyed_service, service_->actor_policy_checker()); + profile_, actor_keyed_service, service_->actor_policy_checker(), + &instance_metrics_, this); } browser_collection_observation_.Observe( @@ -685,100 +682,11 @@ /*success=*/true, created_tab, source_tab); } -void GlicInstanceImpl::CreateTask( - base::WeakPtr<actor::ActorTaskDelegate> delegate, - actor::webui::mojom::TaskOptionsPtr options, - mojom::WebClientHandler::CreateTaskCallback callback) { - if (!actor_task_manager_) { - std::move(callback).Run( - base::unexpected(mojom::CreateTaskErrorReason::kTaskSystemUnavailable)); - return; +GlicActorClientSession* GlicInstanceImpl::BindActorClientSession() { + if (actor_task_manager_) { + return actor_task_manager_->BindSession(); } - - // Conversation ID must be available since a turn is required to create a task - // and an ID becomes available at first turn. If you hit this in a test you - // probably need to call RegisterConversation on your GlicInstance. - // TODO(b/494212836) - The front end currently doesn't guarantee that - // RegisterConversation is called first. Allow creating a task without a - // conversationId until that's fixed (the conversationId in ActorTask isn't - // yet used). - std::optional<std::string> id = conversation_id(); - if (!id.has_value() && - base::FeatureList::IsEnabled(kGlicRequireConversationIdForActorTask)) { - std::move(callback).Run(base::unexpected( - mojom::CreateTaskErrorReason::kConversationNotRegistered)); - return; - } - - instance_metrics_.OnCreateTask(); - actor_task_manager_->CreateTask(weak_ptr_factory_.GetWeakPtr(), id, - std::move(options), std::move(callback)); -} - -void GlicInstanceImpl::PerformActions( - const std::vector<uint8_t>& actions_proto, - mojom::WebClientHandler::PerformActionsCallback callback) { - CHECK(actor_task_manager_); - instance_metrics_.OnPerformActions(); - actor_task_manager_->PerformActions(actions_proto, std::move(callback)); -} - -void GlicInstanceImpl::CancelActions( - actor::TaskId task_id, - mojom::WebClientHandler::CancelActionsCallback callback) { - CHECK(actor_task_manager_); - actor_task_manager_->CancelActions(task_id, std::move(callback)); -} - -void GlicInstanceImpl::StopActorTask(actor::TaskId task_id, - mojom::ActorTaskStopReason stop_reason) { - CHECK(actor_task_manager_); - instance_metrics_.OnStopActorTask(); - actor_task_manager_->StopActorTask(task_id, stop_reason); -} - -void GlicInstanceImpl::PauseActorTask(actor::TaskId task_id, - mojom::ActorTaskPauseReason pause_reason, - tabs::TabInterface::Handle tab_handle) { - CHECK(actor_task_manager_); - instance_metrics_.OnPauseActorTask(); - actor_task_manager_->PauseActorTask(task_id, pause_reason, tab_handle); -} - -void GlicInstanceImpl::ResumeActorTask( - actor::TaskId task_id, - const mojom::GetTabContextOptions& context_options, - glic::mojom::WebClientHandler::ResumeActorTaskCallback callback) { - CHECK(actor_task_manager_); - instance_metrics_.OnResumeActorTask(); - actor_task_manager_->ResumeActorTask(task_id, context_options, - std::move(callback)); -} - -void GlicInstanceImpl::InterruptActorTask( - actor::TaskId task_id, - std::optional<mojom::ActorTaskInterruptReason> interrupt_reason) { - CHECK(actor_task_manager_); - instance_metrics_.InterruptActorTask(); - actor_task_manager_->InterruptActorTask(task_id); -} - -void GlicInstanceImpl::UninterruptActorTask(actor::TaskId task_id) { - CHECK(actor_task_manager_); - instance_metrics_.UninterruptActorTask(); - actor_task_manager_->UninterruptActorTask(task_id); -} - -void GlicInstanceImpl::CreateActorTab( - actor::TaskId task_id, - bool open_in_background, - const std::optional<int32_t>& initiator_tab_id, - const std::optional<int32_t>& initiator_window_id, - glic::mojom::WebClientHandler::CreateActorTabCallback callback) { - CHECK(actor_task_manager_); - actor_task_manager_->CreateActorTab(task_id, open_in_background, - initiator_tab_id, initiator_window_id, - std::move(callback)); + return nullptr; } void GlicInstanceImpl::GetZeroStateSuggestionsAndSubscribe( @@ -852,7 +760,7 @@ } if (auto* entry = GetEmbedderEntry(key)) { - CloseInternal(key, *entry, CloseOptions{}); + CloseInternal(key, *entry, {.suppress_animations = true}); } // Deactivate if this was the active embedder. This ensures predictable state @@ -970,6 +878,11 @@ return std::nullopt; } +base::WeakPtr<actor::ActorTaskDelegate> +GlicInstanceImpl::GetActorTaskDelegate() { + return weak_ptr_factory_.GetWeakPtr(); +} + std::string GlicInstanceImpl::conversation_title() const { return conversation_info_->conversation_title; }
diff --git a/chrome/browser/glic/service/glic_instance_impl.h b/chrome/browser/glic/service/glic_instance_impl.h index 9d576d4..d9c0033 100644 --- a/chrome/browser/glic/service/glic_instance_impl.h +++ b/chrome/browser/glic/service/glic_instance_impl.h
@@ -71,7 +71,8 @@ public Host::Observer, public GlicSharingManagerProvider, public GlicUiEmbedder::Delegate, - public actor::ActorTaskDelegate { + public actor::ActorTaskDelegate, + public GlicActorTaskManager::Delegate { public: class InstanceCoordinatorDelegate { public: @@ -184,6 +185,7 @@ const InstanceId& id() const override; void SetIdForRestoration(InstanceId id); std::optional<std::string> conversation_id() const override; + base::WeakPtr<actor::ActorTaskDelegate> GetActorTaskDelegate() override; std::string conversation_title() const override; base::CallbackListSubscription RegisterStateChange( StateChangeCallback callback) override; @@ -203,35 +205,7 @@ bool open_in_background, const std::optional<int32_t>& window_id, glic::mojom::WebClientHandler::CreateTabCallback callback) override; - void CreateTask( - base::WeakPtr<actor::ActorTaskDelegate> delegate, - actor::webui::mojom::TaskOptionsPtr options, - mojom::WebClientHandler::CreateTaskCallback callback) override; - void PerformActions( - const std::vector<uint8_t>& actions_proto, - mojom::WebClientHandler::PerformActionsCallback callback) override; - void CancelActions( - actor::TaskId task_id, - mojom::WebClientHandler::CancelActionsCallback callback) override; - void StopActorTask(actor::TaskId task_id, - mojom::ActorTaskStopReason stop_reason) override; - void PauseActorTask(actor::TaskId task_id, - mojom::ActorTaskPauseReason pause_reason, - tabs::TabInterface::Handle tab_handle) override; - void ResumeActorTask( - actor::TaskId task_id, - const mojom::GetTabContextOptions& context_options, - glic::mojom::WebClientHandler::ResumeActorTaskCallback callback) override; - void InterruptActorTask( - actor::TaskId task_id, - std::optional<mojom::ActorTaskInterruptReason> interrupt_reason) override; - void UninterruptActorTask(actor::TaskId task_id) override; - void CreateActorTab( - actor::TaskId task_id, - bool open_in_background, - const std::optional<int32_t>& initiator_tab_id, - const std::optional<int32_t>& initiator_window_id, - glic::mojom::WebClientHandler::CreateActorTabCallback callback) override; + GlicActorClientSession* BindActorClientSession() override; void FetchZeroStateSuggestions( bool is_first_run, std::optional<std::vector<std::string>> supported_tools,
diff --git a/chrome/browser/glic/service/glic_invoke_handler.cc b/chrome/browser/glic/service/glic_invoke_handler.cc index f0cff8df..8c1b060 100644 --- a/chrome/browser/glic/service/glic_invoke_handler.cc +++ b/chrome/browser/glic/service/glic_invoke_handler.cc
@@ -21,7 +21,6 @@ #include "chrome/browser/profiles/profile.h" #include "chrome/browser/tab_list/tab_list_interface.h" #include "chrome/browser/ui/browser_commands.h" -#include "chrome/browser/ui/browser_finder.h" #include "chrome/browser/ui/browser_window/public/browser_window_interface_iterator.h" #include "chrome/browser/ui/browser_window/public/create_browser_window.h" #include "chrome/common/chrome_features.h" @@ -167,6 +166,7 @@ weak_ptr_factory_.GetWeakPtr())); } } + if (!options_.tab_sharing.tabs_to_pin.empty()) { CHECK(options_.tab_sharing.pin_trigger != GlicPinTrigger::kUnknown); instance_->sharing_manager().PinTabs(options_.tab_sharing.tabs_to_pin, @@ -180,6 +180,14 @@ std::make_unique<WaitForNavigationTask>(tab_->GetContents())); } + if (options_.additional_context.has_value() && + options_.additional_context->policy_check == PolicyCheck::kClipboard) { + tasks.push_back(std::make_unique<CopyPolicyTask>( + &*instance_, options_, + base::BindOnce(&GlicInvokeHandler::OnError, + weak_ptr_factory_.GetWeakPtr()))); + } + auto show_options = ShowOptions::ForSidePanel( *tab_, GlicPinTrigger::kInstanceCreation, options_.invocation_source); @@ -214,6 +222,14 @@ tasks.push_back(std::make_unique<StabilizationTask>(tab_->GetContents())); } + if (options_.additional_context.has_value() && + options_.additional_context->policy_check == PolicyCheck::kClipboard) { + tasks.push_back(std::make_unique<PastePolicyCheckTask>( + tab_->GetContents(), &*instance_, options_, + base::BindOnce(&GlicInvokeHandler::OnError, + weak_ptr_factory_.GetWeakPtr()))); + } + tasks.push_back(std::make_unique<WaitForFreCompletionTask>( instance_->profile(), options_.fre_override)); @@ -316,8 +332,8 @@ mojo_options->prompts = options_.prompts; } - if (options_.additional_context) { - mojo_options->context = std::move(options_.additional_context); + if (options_.additional_context && options_.additional_context->context) { + mojo_options->context = std::move(options_.additional_context->context); } mojo_options->auto_submit = auto_submit_passkey_.has_value();
diff --git a/chrome/browser/glic/service/glic_invoke_task.cc b/chrome/browser/glic/service/glic_invoke_task.cc index dd684d9..cc076e66 100644 --- a/chrome/browser/glic/service/glic_invoke_task.cc +++ b/chrome/browser/glic/service/glic_invoke_task.cc
@@ -6,7 +6,11 @@ #include "base/barrier_closure.h" #include "base/functional/bind.h" +#include "base/functional/callback_helpers.h" +#include "base/strings/escape.h" +#include "base/strings/utf_string_conversions.h" #include "base/task/sequenced_task_runner.h" +#include "chrome/browser/enterprise/data_protection/data_protection_clipboard_utils.h" #include "chrome/browser/glic/public/glic_enabling.h" #include "chrome/browser/glic/public/glic_keyed_service.h" #include "chrome/browser/glic/service/glic_instance_impl.h" @@ -17,6 +21,48 @@ namespace glic { +namespace { + +// Based on URLToImageMarkup from clipboard_utilities.cc. +std::u16string GetImageMarkup(const GURL& src_url, + content::RenderFrameHost* rfh) { + if (!src_url.is_valid()) { + return u""; + } + std::u16string alt = u""; + auto* contents = content::WebContents::FromRenderFrameHost(rfh); + if (contents) { + std::u16string title = base::EscapeForHTML(contents->GetTitle()); + if (!title.empty()) { + alt = base::StrCat({u" alt=\"", title, u"\""}); + } + } + std::u16string spec = base::EscapeForHTML(base::UTF8ToUTF16(src_url.spec())); + return base::StrCat({u"<img src=\"", spec, u"\"", alt, u"></img>"}); +} + +ui::ClipboardMetadata CreateClipboardMetadata(size_t size) { + ui::ClipboardMetadata metadata; + metadata.format_type = ui::ClipboardFormatType::PngType(); + metadata.size = size; + return metadata; +} + +void ExtractThumbnailData(const GlicInvokeOptions& options, + std::vector<uint8_t>& thumbnail_data) { + if (options.additional_context && options.additional_context->context) { + for (const auto& part : options.additional_context->context->parts) { + if (part->is_data() && part->get_data()->mime_type == "image/png") { + const auto& buffer = part->get_data()->data; + thumbnail_data = std::vector<uint8_t>(buffer.begin(), buffer.end()); + break; + } + } + } +} + +} // namespace + SequentialTaskGroup::SequentialTaskGroup() = default; SequentialTaskGroup::SequentialTaskGroup( std::vector<std::unique_ptr<GlicInvokeTask>> tasks) @@ -374,4 +420,158 @@ std::move(error_callback_).Run(GlicInvokeError::kTimeout); } +ClipboardPolicyTask::ClipboardPolicyTask( + GlicInstanceImpl* instance, + const GlicInvokeOptions& options, + base::OnceCallback<void(GlicInvokeError)> error_callback) + : instance_(instance), error_callback_(std::move(error_callback)) { + if (!options.additional_context.has_value() || + !options.additional_context->source_rfh_id) { + return; + } + source_rfh_id_ = options.additional_context->source_rfh_id; + ExtractThumbnailData(options, thumbnail_data_); + src_url_ = GURL(options.additional_context->context->name.value_or("")); +} + +ClipboardPolicyTask::~ClipboardPolicyTask() = default; + +void ClipboardPolicyTask::Start(base::OnceClosure done_callback) { + done_callback_ = std::move(done_callback); + + if (!source_rfh_id_) { + std::move(error_callback_) + .Run(GlicInvokeError::kAdditionalContextNoSourceFrame); + return; + } + + auto* source_rfh = content::RenderFrameHost::FromID(source_rfh_id_); + if (!source_rfh) { + std::move(error_callback_) + .Run(GlicInvokeError::kAdditionalContextNoSourceFrame); + return; + } + + content::ClipboardEndpoint source( + ui::DataTransferEndpoint( + source_rfh->GetMainFrame()->GetLastCommittedURL(), + {.off_the_record = + source_rfh->GetBrowserContext()->IsOffTheRecord()}), + base::BindRepeating( + [](content::GlobalRenderFrameHostId rfh_id) + -> content::BrowserContext* { + auto* rfh = content::RenderFrameHost::FromID(rfh_id); + return rfh ? rfh->GetBrowserContext() : nullptr; + }, + source_rfh->GetGlobalId()), + *source_rfh); + + ui::ClipboardMetadata metadata = + CreateClipboardMetadata(thumbnail_data_.size()); + + content::ClipboardPasteData data; + data.png = thumbnail_data_; + data.html = GetImageMarkup(src_url_, source_rfh); + + RunPolicyCheck(source, metadata, std::move(data), source_rfh); +} + +CopyPolicyTask::CopyPolicyTask( + GlicInstanceImpl* instance, + const GlicInvokeOptions& options, + base::OnceCallback<void(GlicInvokeError)> error_callback) + : ClipboardPolicyTask(instance, options, std::move(error_callback)) {} + +CopyPolicyTask::~CopyPolicyTask() = default; + +void CopyPolicyTask::RunPolicyCheck(const content::ClipboardEndpoint& source, + const ui::ClipboardMetadata& metadata, + content::ClipboardPasteData data, + content::RenderFrameHost* source_rfh) { + enterprise_data_protection::IsClipboardCopyAllowedByPolicy( + source, metadata, data, + base::BindOnce(&CopyPolicyTask::OnCopyPolicyCheckComplete, + weak_ptr_factory_.GetWeakPtr())); +} + +void CopyPolicyTask::OnCopyPolicyCheckComplete( + const ui::ClipboardFormatType& data_type, + const content::ClipboardPasteData& data, + std::optional<std::u16string> replacement_data) { + if (replacement_data.has_value() || data.empty()) { + std::move(error_callback_) + .Run(GlicInvokeError::kAdditionalContextFailedCopyPolicy); + return; + } + + std::move(done_callback_).Run(); +} + +PastePolicyCheckTask::PastePolicyCheckTask( + content::WebContents* web_contents, + GlicInstanceImpl* instance, + const GlicInvokeOptions& options, + base::OnceCallback<void(GlicInvokeError)> error_callback) + : ClipboardPolicyTask(instance, options, std::move(error_callback)) { + Observe(web_contents); +} + +PastePolicyCheckTask::~PastePolicyCheckTask() = default; + +void PastePolicyCheckTask::RunPolicyCheck( + const content::ClipboardEndpoint& source, + const ui::ClipboardMetadata& metadata, + content::ClipboardPasteData paste_data, + content::RenderFrameHost* source_rfh) { + if (thumbnail_data_.empty()) { + std::move(error_callback_) + .Run(GlicInvokeError::kAdditionalContextNoClipboardMetadata); + return; + } + + auto* host = &instance_->host(); + auto* glic_rfh = host->GetGuestMainFrame(); + if (!glic_rfh) { + std::move(error_callback_) + .Run(GlicInvokeError::kAdditionalContextNoClientFrame); + return; + } + + auto get_browser_context = + [](content::GlobalRenderFrameHostId rfh_id) -> content::BrowserContext* { + auto* rfh = content::RenderFrameHost::FromID(rfh_id); + return rfh ? rfh->GetBrowserContext() : nullptr; + }; + + content::ClipboardEndpoint destination( + ui::DataTransferEndpoint( + glic_rfh->GetLastCommittedURL(), + {.off_the_record = glic_rfh->GetBrowserContext()->IsOffTheRecord()}), + base::BindRepeating(get_browser_context, glic_rfh->GetGlobalId()), + *glic_rfh); + + enterprise_data_protection::PasteIfAllowedByPolicy( + source, destination, metadata, std::move(paste_data), + base::BindOnce(&PastePolicyCheckTask::OnPastePolicyCheckComplete, + weak_ptr_factory_.GetWeakPtr())); +} + +void PastePolicyCheckTask::DidFinishNavigation( + content::NavigationHandle* navigation_handle) { + std::move(error_callback_) + .Run(GlicInvokeError::kAdditionalContextSawNavigation); +} + +void PastePolicyCheckTask::OnPastePolicyCheckComplete( + std::optional<content::ClipboardPasteData> data) { + Observe(nullptr); + if (!data || data->png.empty()) { + // Policy denied or error. + std::move(error_callback_) + .Run(GlicInvokeError::kAdditionalContextFailedPastePolicy); + return; + } + std::move(done_callback_).Run(); +} + } // namespace glic
diff --git a/chrome/browser/glic/service/glic_invoke_task.h b/chrome/browser/glic/service/glic_invoke_task.h index 453807aa..c1dd08f 100644 --- a/chrome/browser/glic/service/glic_invoke_task.h +++ b/chrome/browser/glic/service/glic_invoke_task.h
@@ -13,11 +13,19 @@ #include "base/memory/weak_ptr.h" #include "base/scoped_observation.h" #include "base/timer/timer.h" +#include "base/types/expected.h" +#include "chrome/browser/glic/host/context/glic_page_context_fetcher.h" #include "chrome/browser/glic/host/glic.mojom.h" #include "chrome/browser/glic/host/host.h" #include "chrome/browser/glic/public/glic_invoke_options.h" #include "chrome/browser/glic/service/glic_ui_types.h" +#include "components/tabs/public/tab_interface.h" +#include "content/public/browser/clipboard_types.h" +#include "content/public/browser/global_routing_id.h" #include "content/public/browser/web_contents_observer.h" +#include "ui/base/clipboard/clipboard_format_type.h" +#include "ui/base/clipboard/clipboard_metadata.h" +#include "url/gurl.h" class Profile; @@ -236,7 +244,6 @@ void Start(base::OnceClosure done_callback) override; private: - private: void OnTimeout(); void OnActuatingChanged(bool actuating); void Update(); @@ -255,6 +262,81 @@ base::CallbackListSubscription subscription_; }; +// Base class for tasks that perform enterprise data protection policy checks +// on clipboard-like data. +class ClipboardPolicyTask : public GlicInvokeTask { + public: + ClipboardPolicyTask(GlicInstanceImpl* instance, + const GlicInvokeOptions& options, + base::OnceCallback<void(GlicInvokeError)> error_callback); + ~ClipboardPolicyTask() override; + + void Start(base::OnceClosure done_callback) override; + + protected: + virtual void RunPolicyCheck(const content::ClipboardEndpoint& source, + const ui::ClipboardMetadata& metadata, + content::ClipboardPasteData data, + content::RenderFrameHost* source_rfh) = 0; + + bool NeedsPolicyChecks() const; + + raw_ptr<GlicInstanceImpl> instance_; + content::GlobalRenderFrameHostId source_rfh_id_; + std::vector<uint8_t> thumbnail_data_; + GURL src_url_; + base::OnceClosure done_callback_; + base::OnceCallback<void(GlicInvokeError)> error_callback_; +}; + +class CopyPolicyTask : public ClipboardPolicyTask { + public: + CopyPolicyTask(GlicInstanceImpl* instance, + const GlicInvokeOptions& options, + base::OnceCallback<void(GlicInvokeError)> error_callback); + ~CopyPolicyTask() override; + + protected: + void RunPolicyCheck(const content::ClipboardEndpoint& source, + const ui::ClipboardMetadata& metadata, + content::ClipboardPasteData data, + content::RenderFrameHost* source_rfh) override; + + private: + void OnCopyPolicyCheckComplete( + const ui::ClipboardFormatType& data_type, + const content::ClipboardPasteData& data, + std::optional<std::u16string> replacement_data); + + base::WeakPtrFactory<CopyPolicyTask> weak_ptr_factory_{this}; +}; + +class PastePolicyCheckTask : public ClipboardPolicyTask, + public content::WebContentsObserver { + public: + PastePolicyCheckTask( + content::WebContents* contents, + GlicInstanceImpl* instance, + const GlicInvokeOptions& options, + base::OnceCallback<void(GlicInvokeError)> error_callback); + ~PastePolicyCheckTask() override; + + protected: + void RunPolicyCheck(const content::ClipboardEndpoint& source, + const ui::ClipboardMetadata& metadata, + content::ClipboardPasteData data, + content::RenderFrameHost* source_rfh) override; + + void DidFinishNavigation( + content::NavigationHandle* navigation_handle) override; + + private: + void OnPastePolicyCheckComplete( + std::optional<content::ClipboardPasteData> data); + + base::WeakPtrFactory<PastePolicyCheckTask> weak_ptr_factory_{this}; +}; + } // namespace glic #endif // CHROME_BROWSER_GLIC_SERVICE_GLIC_INVOKE_TASK_H_
diff --git a/chrome/browser/glic/service/metrics/glic_instance_metrics.cc b/chrome/browser/glic/service/metrics/glic_instance_metrics.cc index 41e1d9f..3200512 100644 --- a/chrome/browser/glic/service/metrics/glic_instance_metrics.cc +++ b/chrome/browser/glic/service/metrics/glic_instance_metrics.cc
@@ -25,6 +25,7 @@ #include "chrome/browser/glic/public/features.h" #include "chrome/browser/glic/service/glic_instance_helper.h" #include "chrome/browser/glic/service/glic_state_tracker.h" +#include "chrome/browser/glic/service/metrics/glic_instance_helper_metrics.h" #include "chrome/browser/glic/service/metrics/glic_metrics_session_manager.h" #include "chrome/browser/glic/service/metrics/metrics_types.h" #include "chrome/common/chrome_features.h"
diff --git a/chrome/browser/glic/test_support/BUILD.gn b/chrome/browser/glic/test_support/BUILD.gn index 2d462a0..06af7839 100644 --- a/chrome/browser/glic/test_support/BUILD.gn +++ b/chrome/browser/glic/test_support/BUILD.gn
@@ -22,8 +22,6 @@ sources += [ "glic_api_test.cc", "glic_api_test.h", - "glic_browser_interactive_migration_test.cc", - "glic_browser_interactive_migration_test.h", "glic_functional_browsertest.cc", "glic_functional_browsertest.h", "interactive_glic_test.cc",
diff --git a/chrome/browser/glic/test_support/glic_browser_interactive_migration_test.cc b/chrome/browser/glic/test_support/glic_browser_interactive_migration_test.cc deleted file mode 100644 index 4d06220d..0000000 --- a/chrome/browser/glic/test_support/glic_browser_interactive_migration_test.cc +++ /dev/null
@@ -1,282 +0,0 @@ -// Copyright 2026 The Chromium Authors -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "chrome/browser/glic/test_support/glic_browser_interactive_migration_test.h" - -#include "chrome/browser/profiles/profile.h" -#include "chrome/browser/ui/browser.h" - -namespace glic { - -GlicBrowserInteractiveMigrationTest::GlicBrowserInteractiveMigrationTest() { - std::vector<base::test::FeatureRefAndParams> enabled_features = { - {features::kGlicMultiInstance, {}}, -#if BUILDFLAG(IS_ANDROID) - {chrome::android::kBrowserWindowInterfaceMobile, {}}, - {chrome::android::kTabBottomSheet, {}}, -#endif - }; - glic_test_environment_.SetGlicPagePath( - "/glic/browser_tests/minimal_client.html"); - scoped_feature_list_.InitWithFeaturesAndParameters(enabled_features, {}); -} - -GlicBrowserInteractiveMigrationTest::~GlicBrowserInteractiveMigrationTest() = - default; - -void GlicBrowserInteractiveMigrationTest::SetUpCommandLine( - base::CommandLine* command_line) { - InteractiveBrowserTest::SetUpCommandLine(command_line); -#if BUILDFLAG(IS_DESKTOP_ANDROID) - command_line->AppendSwitch(switches::kForceDesktopAndroid); -#endif -} - -void GlicBrowserInteractiveMigrationTest::SetUpOnMainThread() { - InteractiveBrowserTest::SetUpOnMainThread(); -#if defined(USE_MOCK_ACTIVATION_CONTROLLER) - activation_controller_ = - std::make_unique<views::test::MockActivationController>(); -#endif - - CHECK(glic_test_environment_.SetupEmbeddedTestServers( - embedded_test_server(), &embedded_https_test_server())); - TabListInterface::From(browser()) - ->GetActiveTab() - ->GetBrowserWindowInterface() - ->GetWindow() - ->Activate(); - LOG(INFO) << "GlicBrowserInteractiveMigrationTest: done setting up"; -} - -void GlicBrowserInteractiveMigrationTest::TearDownOnMainThread() { -#if defined(USE_MOCK_ACTIVATION_CONTROLLER) - activation_controller_.reset(); -#endif - InteractiveBrowserTest::TearDownOnMainThread(); -} - -void GlicBrowserInteractiveMigrationTest::ToggleGlicForActiveTab( - bool prevent_close) { - auto* service = GlicKeyedService::Get(browser()->profile()); - service->ToggleUI(TabListInterface::From(browser()) - ->GetActiveTab() - ->GetBrowserWindowInterface(), - prevent_close, mojom::InvocationSource::kTopChromeButton); -} - -TestResult<GlicInstanceImpl*> -GlicBrowserInteractiveMigrationTest::OpenGlicForActiveTab() { - ToggleGlicForActiveTab(/*prevent_close=*/true); - return WaitForGlicOpen(TabListInterface::From(browser())->GetActiveTab()); -} - -void GlicBrowserInteractiveMigrationTest::PreventDeletionOnClose( - GlicInstanceImpl* instance, - const std::string& conversation_id) { - if (!instance) { - instance = GetOnlyGlicInstance(); - } - CHECK(instance); - if (!instance->conversation_id().has_value()) { - auto info = mojom::ConversationInfo::New(); - info->conversation_id = conversation_id; - instance->RegisterConversation(std::move(info), base::DoNothing()); - } - instance->OnUserInputSubmitted(mojom::WebClientMode::kText); -} - -void GlicBrowserInteractiveMigrationTest::CloseAllEmbeddersAndPreventDeletion( - GlicInstanceImpl* instance) { - if (!instance) { - instance = GetOnlyGlicInstance(); - } - CHECK(instance); - PreventDeletionOnClose(instance); - instance->CloseAllEmbedders(); -} - -TestResult<GlicInstanceImpl*> -GlicBrowserInteractiveMigrationTest::OpenGlicForActiveTabAndDetach() { - ASSIGN_OR_RETURN(GlicInstanceImpl * instance, OpenGlicForActiveTab()); - if (instance->IsDetached()) { - return instance; - } - instance->Detach(*TabListInterface::From(browser())->GetActiveTab()); - bool success = RunUntil([instance]() { return instance->IsDetached(); }, - "Failed to wait for Glic to detach"); - if (!success) { - return base::unexpected("Failed to wait for Glic to detach"); - } - return instance; -} - -TestResult<GlicInstanceImpl*> -GlicBrowserInteractiveMigrationTest::WaitForGlicOpen(GlicInstance* instance) { - std::optional<InstanceId> id = - instance ? std::make_optional(instance->id()) : std::nullopt; - bool success = RunUntil( - [this, id]() { - GlicInstance* target = - id ? GetInstanceById(*id) : GetOnlyGlicInstance(); - return target && target->IsShowing(); - }, - "Failed to wait for Glic to open"); - if (!success) { - return base::unexpected("Failed to wait for Glic to open"); - } - auto* result = id ? GetInstanceById(*id) : GetOnlyGlicInstance(); - if (!result) { - return base::unexpected("Glic instance not found after opening"); - } - return result; -} - -TestResult<GlicInstanceImpl*> -GlicBrowserInteractiveMigrationTest::WaitForGlicOpen(tabs::TabInterface* tab) { - bool success = RunUntil( - [this, tab]() { - GlicInstance* instance = GetInstanceForTab(tab); - return instance && instance->IsShowing(); - }, - "Failed to wait for Glic to open for tab"); - if (!success) { - return base::unexpected("Failed to wait for Glic to open for tab"); - } - auto* instance = GetInstanceForTab(tab); - if (!instance) { - return base::unexpected("Glic instance not found for tab after opening"); - } - return instance; -} - -TestResult<> GlicBrowserInteractiveMigrationTest::WaitForGlicClose( - GlicInstance* instance) { - std::optional<InstanceId> id = - instance ? std::make_optional(instance->id()) : std::nullopt; - bool success = RunUntil( - [this, id]() { - GlicInstance* target = - id ? GetInstanceById(*id) : GetOnlyGlicInstance(); - return !target || !target->IsShowing(); - }, - "Failed to close Glic UI"); - if (!success) { - return base::unexpected("Failed to close Glic UI"); - } - return base::ok(); -} - -TestResult<> GlicBrowserInteractiveMigrationTest::CloseGlicForTabAndWait( - tabs::TabInterface* tab) { - GlicInstanceImpl* instance = GetInstanceForTab(tab); - if (!instance) { - return base::unexpected("No Glic instance found for tab to close"); - } - instance->Close(tab); - return WaitForGlicClose(instance); -} - -TestResult<GlicInstanceImpl*> -GlicBrowserInteractiveMigrationTest::WaitForGlicInstanceBoundToTab( - tabs::TabInterface* tab) { - bool success = - RunUntil([this, tab]() { return GetInstanceForTab(tab) != nullptr; }, - "Failed to wait for Glic to be bound to tab"); - if (!success) { - return base::unexpected("Failed to wait for Glic to be bound to tab"); - } - return GetInstanceForTab(tab); -} - -GlicInstanceImpl* GlicBrowserInteractiveMigrationTest::GetOnlyGlicInstance() { - return static_cast<GlicInstanceImpl*>( - ::glic::GetOnlyGlicInstance(browser()->profile())); -} - -GlicInstanceImpl* GlicBrowserInteractiveMigrationTest::GetInstanceForTab( - tabs::TabInterface* tab) { - return static_cast<GlicInstanceImpl*>( - ::glic::GetInstanceForTab(browser()->profile(), tab)); -} - -GlicInstanceImpl* GlicBrowserInteractiveMigrationTest::GetInstanceById( - InstanceId id) { - return static_cast<GlicInstanceImpl*>( - ::glic::GetInstanceById(browser()->profile(), id)); -} - -GlicInstanceCoordinatorImpl& -GlicBrowserInteractiveMigrationTest::coordinator() { - return static_cast<GlicInstanceCoordinatorImpl&>( - GlicKeyedService::Get(browser()->profile())->instance_coordinator()); -} - -tabs::TabInterface* GlicBrowserInteractiveMigrationTest::CreateAndActivateTab( - const GURL& url) { - tabs::TabInterface* new_tab = - TabListInterface::From(browser())->OpenTab(url, -1); - TabListInterface::From(browser())->ActivateTab(new_tab->GetHandle()); - CHECK(content::WaitForLoadStop(new_tab->GetContents())); - return new_tab; -} - -TestResult<> GlicBrowserInteractiveMigrationTest::WaitForWebUiState( - mojom::WebUiState state) { - auto state_to_string = [](mojom::WebUiState state) -> std::string { - std::stringstream ss; - ss << state; - return ss.str(); - }; - return RunUntilEqual( - [&]() -> std::string { - GlicInstanceImpl* instance = GetOnlyGlicInstance(); - if (!instance) { - return "no instance"; - } - return state_to_string(instance->host().GetPrimaryWebUiState()); - }, - state_to_string(state)); -} - -GlicKeyedService* GlicBrowserInteractiveMigrationTest::service() { - return GlicKeyedService::Get(browser()->profile()); -} - -BrowserWindowInterface* GlicBrowserInteractiveMigrationTest::GetBrowser() { - return TabListInterface::From(browser()) - ->GetActiveTab() - ->GetBrowserWindowInterface(); -} - -GURL GlicBrowserInteractiveMigrationTest::GetSimpleTestUrl() { - return GetTestUrl("page.html"); -} - -GURL GlicBrowserInteractiveMigrationTest::GetTestUrl( - const std::string& file_name) { - return embedded_test_server()->GetURL("/test_data/" + file_name); -} - -void GlicBrowserInteractiveMigrationTest::SetGlicPagePath( - const std::string& glic_page_path) { - glic_test_environment_.SetGlicPagePath(glic_page_path); -} - -void GlicBrowserInteractiveMigrationTest::AddMockGlicQueryParam( - const std::string_view& key, - const std::string_view& value) { - glic_test_environment_.AddMockGlicQueryParam(key, value); -} - -GURL GlicBrowserInteractiveMigrationTest::GetGuestURL() { - return glic_test_environment_.GetGuestURL(); -} - -void GlicBrowserInteractiveMigrationTest::SetGlicFreUrlOverride( - const GURL& url) { - glic_test_environment_.SetGlicFreUrlOverride(url); -} - -} // namespace glic
diff --git a/chrome/browser/glic/test_support/glic_browser_interactive_migration_test.h b/chrome/browser/glic/test_support/glic_browser_interactive_migration_test.h deleted file mode 100644 index c8c050a..0000000 --- a/chrome/browser/glic/test_support/glic_browser_interactive_migration_test.h +++ /dev/null
@@ -1,83 +0,0 @@ -// Copyright 2026 The Chromium Authors -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef CHROME_BROWSER_GLIC_TEST_SUPPORT_GLIC_BROWSER_INTERACTIVE_MIGRATION_TEST_H_ -#define CHROME_BROWSER_GLIC_TEST_SUPPORT_GLIC_BROWSER_INTERACTIVE_MIGRATION_TEST_H_ - -#include <memory> -#include <string> -#include <vector> - -#include "base/test/scoped_feature_list.h" -#include "chrome/browser/glic/public/glic_keyed_service.h" -#include "chrome/browser/glic/service/glic_instance_coordinator_impl.h" -#include "chrome/browser/glic/service/glic_instance_impl.h" -#include "chrome/browser/glic/test_support/glic_browser_test.h" -#include "chrome/browser/glic/test_support/glic_test_environment.h" -#include "chrome/browser/tab_list/tab_list_interface.h" -#include "chrome/browser/ui/browser_window/public/browser_window_interface.h" -#include "chrome/test/interaction/interactive_browser_test.h" -#include "content/public/test/browser_test_utils.h" - -#if defined(TOOLKIT_VIEWS) -#include "ui/views/test/mock_activation_controller.h" -#endif - -namespace glic { - -// TODO(b/508621027): DO NOT USE THIS. -// This test fixture is a temporary to compare flake rates. - -class GlicBrowserInteractiveMigrationTest : public InteractiveBrowserTest { - public: - GlicBrowserInteractiveMigrationTest(); - ~GlicBrowserInteractiveMigrationTest() override; - - void SetUpCommandLine(base::CommandLine* command_line) override; - void SetUpOnMainThread() override; - void TearDownOnMainThread() override; - - void ToggleGlicForActiveTab(bool prevent_close = false); - [[nodiscard]] TestResult<GlicInstanceImpl*> OpenGlicForActiveTab(); - void PreventDeletionOnClose( - GlicInstanceImpl* instance = nullptr, - const std::string& conversation_id = "test_conversation"); - void CloseAllEmbeddersAndPreventDeletion( - GlicInstanceImpl* instance = nullptr); - [[nodiscard]] TestResult<GlicInstanceImpl*> OpenGlicForActiveTabAndDetach(); - [[nodiscard]] TestResult<GlicInstanceImpl*> WaitForGlicOpen( - GlicInstance* instance = nullptr); - [[nodiscard]] TestResult<GlicInstanceImpl*> WaitForGlicOpen( - tabs::TabInterface* tab); - TestResult<> WaitForGlicClose(GlicInstance* instance = nullptr); - TestResult<> CloseGlicForTabAndWait(tabs::TabInterface* tab); - [[nodiscard]] TestResult<GlicInstanceImpl*> WaitForGlicInstanceBoundToTab( - tabs::TabInterface* tab); - GlicInstanceImpl* GetOnlyGlicInstance(); - GlicInstanceImpl* GetInstanceForTab(tabs::TabInterface* tab); - GlicInstanceImpl* GetInstanceById(InstanceId id); - GlicInstanceCoordinatorImpl& coordinator(); - tabs::TabInterface* CreateAndActivateTab(const GURL& url); - [[nodiscard]] TestResult<> WaitForWebUiState(mojom::WebUiState state); - GlicKeyedService* service(); - BrowserWindowInterface* GetBrowser(); - GURL GetSimpleTestUrl(); - GURL GetTestUrl(const std::string& file_name); - void SetGlicPagePath(const std::string& glic_page_path); - void AddMockGlicQueryParam(const std::string_view& key, - const std::string_view& value = ""); - GURL GetGuestURL(); - void SetGlicFreUrlOverride(const GURL& url); - - protected: - GlicTestEnvironment glic_test_environment_; - base::test::ScopedFeatureList scoped_feature_list_; -#if defined(USE_MOCK_ACTIVATION_CONTROLLER) - std::unique_ptr<views::test::MockActivationController> activation_controller_; -#endif -}; - -} // namespace glic - -#endif // CHROME_BROWSER_GLIC_TEST_SUPPORT_GLIC_BROWSER_INTERACTIVE_MIGRATION_TEST_H_
diff --git a/chrome/browser/glic/test_support/glic_browser_test.h b/chrome/browser/glic/test_support/glic_browser_test.h index 414b1e4..92eee9b 100644 --- a/chrome/browser/glic/test_support/glic_browser_test.h +++ b/chrome/browser/glic/test_support/glic_browser_test.h
@@ -19,6 +19,7 @@ #include "base/test/gmock_expected_support.h" #include "base/test/run_until.h" #include "base/test/scoped_feature_list.h" +#include "base/test/test_future.h" #include "base/types/expected.h" #include "base/types/expected_macros.h" #include "build/android_buildflags.h" @@ -409,11 +410,49 @@ glic_test_environment_.SetGlicFreUrlOverride(url); } + [[nodiscard]] TestResult<void> WaitForGlicClient(GlicInstance* instance) { + if (!instance) { + instance = GetOnlyGlicInstance(); + } + return RunUntilEqual( + [&]() { return instance->host().IsWebClientConnected(); }, true, + "WaitForGlicClient: client client did not connect"); + } + + // Create an actor task for the instance. + [[nodiscard]] TestResult<actor::TaskId> CreateActorTask( + GlicInstance* instance = nullptr) { + auto instance_impl = GetInstanceImpl(instance); + base::test::TestFuture< + base::expected<int32_t, glic::mojom::CreateTaskErrorReason>> + create_task_future; + RETURN_IF_ERROR(WaitForGlicClient(instance)); + instance_impl->GetActorTaskManager() + ->GetClientSessionForTesting() + ->CreateTask(actor::webui::mojom::TaskOptions::New(), + create_task_future.GetCallback()); + return create_task_future.Get() + .transform( + [](int32_t id) -> actor::TaskId { return actor::TaskId(id); }) + .transform_error([](const auto& e) -> std::string { + std::stringstream ss; + ss << "Failed to create actor task: " << e; + return ss.str(); + }); + } + protected: GlicTestEnvironment& glic_test_environment() { return glic_test_environment_; } + GlicInstanceImpl* GetInstanceImpl(GlicInstance* instance = nullptr) { + if (!instance) { + instance = GetOnlyGlicInstance(); + } + return static_cast<GlicInstanceImpl*>(instance); + } + private: // Hide functionality not available on Android to discourage use. // Callers can instead use, e.g. PlatformBrowserTest::browser(), or
diff --git a/chrome/browser/glic/widget/glic_floating_ui.cc b/chrome/browser/glic/widget/glic_floating_ui.cc index cc9cf5cc..ece3e75 100644 --- a/chrome/browser/glic/widget/glic_floating_ui.cc +++ b/chrome/browser/glic/widget/glic_floating_ui.cc
@@ -15,7 +15,6 @@ #include "chrome/browser/glic/public/features.h" #include "chrome/browser/glic/service/glic_instance_helper.h" #include "chrome/browser/glic/service/metrics/glic_instance_metrics.h" -#include "chrome/browser/glic/widget/glic_inactive_floating_ui.h" #include "chrome/browser/glic/widget/glic_view.h" #include "chrome/browser/glic/widget/glic_widget.h" #include "chrome/browser/glic/widget/glic_window_animator.h" @@ -79,6 +78,7 @@ PictureInPictureOcclusionTracker* tracker = PictureInPictureWindowManager::GetInstance()->GetOcclusionTracker(); tracker->OnPictureInPictureWidgetOpened(glic_widget_.get()); + browser_attach_observation_ = ObserveBrowserForAttachment(profile_, this); } GlicFloatingUi::~GlicFloatingUi() { @@ -285,6 +285,10 @@ delegate_->host().FloatingPanelCanAttachChanged(can_attach); } +void GlicFloatingUi::CanAttachToBrowserChanged(bool can_attach) { + FloatingPanelCanAttachChanged(can_attach && source_tab_.Get() != nullptr); +} + void GlicFloatingUi::ConfigureWebContentsModalDialogs() { // Add capability to show web modal dialogs (e.g. Data Controls Dialogs for // enterprise users) via constrained_window APIs. @@ -322,7 +326,9 @@ } void GlicFloatingUi::Show(const ShowOptions& options) { - FloatingPanelCanAttachChanged(source_tab_.Get() != nullptr); + FloatingPanelCanAttachChanged( + browser_attach_observation_->CanAttachToBrowser() && + source_tab_.Get() != nullptr); instance_metrics_->OnShowInFloaty(options); GlicProfileManager::GetInstance()->SetCurrentDetachedGlic(profile_); GetGlicWidget()->Show(); @@ -473,7 +479,7 @@ } std::unique_ptr<GlicUiEmbedder> GlicFloatingUi::CreateInactiveEmbedder() const { - return GlicInactiveFloatingUi::From(*this); + return nullptr; } #if !BUILDFLAG(IS_ANDROID)
diff --git a/chrome/browser/glic/widget/glic_floating_ui.h b/chrome/browser/glic/widget/glic_floating_ui.h index 906a44a..92e4177 100644 --- a/chrome/browser/glic/widget/glic_floating_ui.h +++ b/chrome/browser/glic/widget/glic_floating_ui.h
@@ -12,6 +12,7 @@ #include "chrome/browser/glic/host/glic.mojom.h" #include "chrome/browser/glic/host/host.h" #include "chrome/browser/glic/service/glic_ui_embedder.h" +#include "chrome/browser/glic/widget/browser_conditions.h" #include "components/tabs/public/tab_interface.h" #include "components/web_modal/web_contents_modal_dialog_host.h" #include "components/web_modal/web_contents_modal_dialog_manager_delegate.h" @@ -36,7 +37,8 @@ public LocalHotkeyManager::Panel, public views::WidgetObserver, public web_modal::WebContentsModalDialogManagerDelegate, - public web_modal::WebContentsModalDialogHost { + public web_modal::WebContentsModalDialogHost, + public glic::BrowserAttachObserver { public: GlicFloatingUi(Profile* profile, BrowserWindowInterface* browser, @@ -91,6 +93,9 @@ void OnWidgetUserResizeStarted(views::Widget* widget) override; void OnWidgetUserResizeEnded(views::Widget* widget) override; + // BrowserAttachObserver implementation: + void CanAttachToBrowserChanged(bool can_attach) override; + // LocalHotkeyManager::Panel: void FocusIfOpen() override; bool HasFocus() override; @@ -158,6 +163,8 @@ tabs::TabHandle source_tab_; base::CallbackListSubscription source_tab_destruction_subscription_; + std::unique_ptr<BrowserAttachObservation> browser_attach_observation_; + base::WeakPtrFactory<GlicFloatingUi> weak_ptr_factory_{this}; };
diff --git a/chrome/browser/glic/widget/glic_floating_ui_android.cc b/chrome/browser/glic/widget/glic_floating_ui_android.cc index b021f834..82511bc8 100644 --- a/chrome/browser/glic/widget/glic_floating_ui_android.cc +++ b/chrome/browser/glic/widget/glic_floating_ui_android.cc
@@ -5,7 +5,6 @@ #include "chrome/browser/glic/widget/glic_floating_ui_android.h" #include "chrome/browser/glic/host/glic.mojom.h" -#include "chrome/browser/glic/widget/glic_inactive_floating_ui_android.h" namespace glic { @@ -57,7 +56,7 @@ } std::unique_ptr<GlicUiEmbedder> GlicFloatingUi::CreateInactiveEmbedder() const { - return GlicInactiveFloatingUi::From(*this); + return nullptr; } mojom::PanelState GlicFloatingUi::GetPanelState() const {
diff --git a/chrome/browser/glic/widget/glic_inactive_floating_ui.cc b/chrome/browser/glic/widget/glic_inactive_floating_ui.cc deleted file mode 100644 index 7a4bae4..0000000 --- a/chrome/browser/glic/widget/glic_inactive_floating_ui.cc +++ /dev/null
@@ -1,87 +0,0 @@ -// 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/glic/widget/glic_inactive_floating_ui.h" - -#include "base/notimplemented.h" -#include "ui/views/controls/label.h" -#include "ui/views/view.h" - -namespace glic { - -// static -std::unique_ptr<GlicInactiveFloatingUi> GlicInactiveFloatingUi::From( - const GlicFloatingUi& active_ui) { - // Using `new` to access a private constructor. - return base::WrapUnique(new GlicInactiveFloatingUi()); -} - -GlicInactiveFloatingUi::GlicInactiveFloatingUi() = default; -GlicInactiveFloatingUi::~GlicInactiveFloatingUi() = default; - -std::unique_ptr<views::View> GlicInactiveFloatingUi::CreateView() { - // TODO: implement CreateView. This should set up the contents for the - // floating UI and be called from the constructor. - NOTIMPLEMENTED(); - return std::make_unique<views::View>(); -} - -Host::EmbedderDelegate* GlicInactiveFloatingUi::GetHostEmbedderDelegate() { - return nullptr; -} - -void GlicInactiveFloatingUi::Show(const ShowOptions& options) { - // TODO: implement show. - NOTIMPLEMENTED(); -} - -bool GlicInactiveFloatingUi::IsShowing() const { - NOTIMPLEMENTED(); - return false; -} - -bool GlicInactiveFloatingUi::IsShowingOrBackgrounded() const { - return IsShowing(); -} - -void GlicInactiveFloatingUi::Close(const CloseOptions& options) { - // TODO: implement close. - NOTIMPLEMENTED(); -} - -#if !BUILDFLAG(IS_ANDROID) -base::WeakPtr<views::View> GlicInactiveFloatingUi::GetView() { - return nullptr; -} -#endif - -gfx::Size GlicInactiveFloatingUi::GetPanelSize() { - return gfx::Size(); -} - -mojom::PanelState GlicInactiveFloatingUi::GetPanelState() const { - mojom::PanelState state; - state.kind = glic::mojom::PanelStateKind::kHidden; - return state; -} - -std::unique_ptr<GlicUiEmbedder> GlicInactiveFloatingUi::CreateInactiveEmbedder() - const { - NOTREACHED() << "The embedder is already inactive."; -} - -void GlicInactiveFloatingUi::Focus() { - NOTIMPLEMENTED(); -} - -bool GlicInactiveFloatingUi::HasFocus() { - NOTIMPLEMENTED(); - return false; -} - -std::string GlicInactiveFloatingUi::DescribeForTesting() { - return "InactiveFloatingUi"; -} - -} // namespace glic
diff --git a/chrome/browser/glic/widget/glic_inactive_floating_ui.h b/chrome/browser/glic/widget/glic_inactive_floating_ui.h deleted file mode 100644 index 7cbdc36..0000000 --- a/chrome/browser/glic/widget/glic_inactive_floating_ui.h +++ /dev/null
@@ -1,46 +0,0 @@ -// 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_GLIC_WIDGET_GLIC_INACTIVE_FLOATING_UI_H_ -#define CHROME_BROWSER_GLIC_WIDGET_GLIC_INACTIVE_FLOATING_UI_H_ - -#include "chrome/browser/glic/service/glic_ui_embedder.h" - -namespace glic { - -class GlicFloatingUi; - -// A GlicUiEmbedder for inactive Glic instances. This will show a -// blurred screenshot of the previously active UI. -class GlicInactiveFloatingUi : public GlicUiEmbedder { - public: - static std::unique_ptr<GlicInactiveFloatingUi> From( - const GlicFloatingUi& active_ui); - - ~GlicInactiveFloatingUi() override; - - // GlicUiEmbedder: - Host::EmbedderDelegate* GetHostEmbedderDelegate() override; - void Show(const ShowOptions& options) override; - bool IsShowing() const override; - bool IsShowingOrBackgrounded() const override; - void Close(const CloseOptions& options) override; - std::unique_ptr<GlicUiEmbedder> CreateInactiveEmbedder() const override; - void Focus() override; - bool HasFocus() override; -#if !BUILDFLAG(IS_ANDROID) - base::WeakPtr<views::View> GetView() override; -#endif - mojom::PanelState GetPanelState() const override; - gfx::Size GetPanelSize() override; - std::string DescribeForTesting() override; - - private: - std::unique_ptr<views::View> CreateView(); - GlicInactiveFloatingUi(); -}; - -} // namespace glic - -#endif // CHROME_BROWSER_GLIC_WIDGET_GLIC_INACTIVE_FLOATING_UI_H_
diff --git a/chrome/browser/glic/widget/glic_inactive_floating_ui_android.cc b/chrome/browser/glic/widget/glic_inactive_floating_ui_android.cc deleted file mode 100644 index 2fcdc41..0000000 --- a/chrome/browser/glic/widget/glic_inactive_floating_ui_android.cc +++ /dev/null
@@ -1,61 +0,0 @@ -// 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/glic/widget/glic_inactive_floating_ui_android.h" - -#include "base/memory/ptr_util.h" -#include "chrome/browser/glic/host/glic.mojom.h" - -namespace glic { - -// static -std::unique_ptr<GlicInactiveFloatingUi> GlicInactiveFloatingUi::From( - const GlicFloatingUi& active_ui) { - return base::WrapUnique(new GlicInactiveFloatingUi()); -} - -GlicInactiveFloatingUi::GlicInactiveFloatingUi() = default; - -GlicInactiveFloatingUi::~GlicInactiveFloatingUi() = default; - -Host::EmbedderDelegate* GlicInactiveFloatingUi::GetHostEmbedderDelegate() { - return nullptr; -} - -void GlicInactiveFloatingUi::Show(const ShowOptions& options) {} - -bool GlicInactiveFloatingUi::IsShowing() const { - return false; -} - -bool GlicInactiveFloatingUi::IsShowingOrBackgrounded() const { - return IsShowing(); -} - -void GlicInactiveFloatingUi::Close(const CloseOptions& options) {} - -void GlicInactiveFloatingUi::Focus() {} - -bool GlicInactiveFloatingUi::HasFocus() { - return false; -} - -std::unique_ptr<GlicUiEmbedder> GlicInactiveFloatingUi::CreateInactiveEmbedder() - const { - return nullptr; -} - -mojom::PanelState GlicInactiveFloatingUi::GetPanelState() const { - return mojom::PanelState(); -} - -gfx::Size GlicInactiveFloatingUi::GetPanelSize() { - return gfx::Size(); -} - -std::string GlicInactiveFloatingUi::DescribeForTesting() { - return ""; -} - -} // namespace glic
diff --git a/chrome/browser/glic/widget/glic_inactive_floating_ui_android.h b/chrome/browser/glic/widget/glic_inactive_floating_ui_android.h deleted file mode 100644 index 492df65..0000000 --- a/chrome/browser/glic/widget/glic_inactive_floating_ui_android.h +++ /dev/null
@@ -1,44 +0,0 @@ -// 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_GLIC_WIDGET_GLIC_INACTIVE_FLOATING_UI_ANDROID_H_ -#define CHROME_BROWSER_GLIC_WIDGET_GLIC_INACTIVE_FLOATING_UI_ANDROID_H_ - -#include <memory> -#include <string> - -#include "chrome/browser/glic/service/glic_ui_embedder.h" -#include "ui/gfx/geometry/size.h" - -namespace glic { - -class GlicFloatingUi; - -class GlicInactiveFloatingUi : public GlicUiEmbedder { - public: - static std::unique_ptr<GlicInactiveFloatingUi> From( - const GlicFloatingUi& active_ui); - - ~GlicInactiveFloatingUi() override; - - // GlicUiEmbedder: - Host::EmbedderDelegate* GetHostEmbedderDelegate() override; - void Show(const ShowOptions& options) override; - bool IsShowing() const override; - bool IsShowingOrBackgrounded() const override; - void Close(const CloseOptions& options) override; - void Focus() override; - bool HasFocus() override; - std::unique_ptr<GlicUiEmbedder> CreateInactiveEmbedder() const override; - mojom::PanelState GetPanelState() const override; - gfx::Size GetPanelSize() override; - std::string DescribeForTesting() override; - - private: - GlicInactiveFloatingUi(); -}; - -} // namespace glic - -#endif // CHROME_BROWSER_GLIC_WIDGET_GLIC_INACTIVE_FLOATING_UI_ANDROID_H_
diff --git a/chrome/browser/glic/widget/glic_side_panel_coordinator_android.cc b/chrome/browser/glic/widget/glic_side_panel_coordinator_android.cc index 8690325..c9d1b32 100644 --- a/chrome/browser/glic/widget/glic_side_panel_coordinator_android.cc +++ b/chrome/browser/glic/widget/glic_side_panel_coordinator_android.cc
@@ -85,7 +85,7 @@ return; } - bridge_->Close(/* animate= */ false); + bridge_->Close(/* animate= */ !options.suppress_animations); } bool GlicSidePanelCoordinatorAndroid::IsShowing() const {
diff --git a/chrome/browser/glic/widget/glic_view.cc b/chrome/browser/glic/widget/glic_view.cc index d633265..955b1b6 100644 --- a/chrome/browser/glic/widget/glic_view.cc +++ b/chrome/browser/glic/widget/glic_view.cc
@@ -15,7 +15,6 @@ #include "chrome/browser/profiles/profile.h" #include "chrome/browser/profiles/profile_manager.h" #include "chrome/browser/ui/browser_element_identifiers.h" -#include "chrome/browser/ui/browser_finder.h" #include "chrome/browser/ui/color/chrome_color_id.h" #include "chrome/browser/ui/layout_constants.h" #include "chrome/browser/ui/views/frame/browser_view.h"
diff --git a/chrome/browser/hid/chrome_hid_delegate.cc b/chrome/browser/hid/chrome_hid_delegate.cc index d5f66b3..b891a97 100644 --- a/chrome/browser/hid/chrome_hid_delegate.cc +++ b/chrome/browser/hid/chrome_hid_delegate.cc
@@ -15,7 +15,6 @@ #include "chrome/browser/hid/hid_connection_tracker.h" #include "chrome/browser/hid/hid_connection_tracker_factory.h" #include "chrome/browser/profiles/profile.h" -#include "chrome/browser/ui/browser_finder.h" #include "chrome/browser/ui/dialogs/browser_dialogs.h" #include "chrome/browser/ui/hid/hid_chooser.h" #include "chrome/browser/ui/hid/hid_chooser_controller.h"
diff --git a/chrome/browser/indigo/indigo_browsertest.cc b/chrome/browser/indigo/indigo_browsertest.cc index 911f1892..b268e9f 100644 --- a/chrome/browser/indigo/indigo_browsertest.cc +++ b/chrome/browser/indigo/indigo_browsertest.cc
@@ -6,7 +6,9 @@ #include "base/files/scoped_temp_dir.h" #include "base/test/bind.h" #include "base/test/scoped_feature_list.h" +#include "base/test/test_future.h" #include "build/build_config.h" +#include "chrome/browser/indigo/indigo_image_replacement_manager.h" #include "chrome/browser/indigo/indigo_page_action_controller.h" #include "chrome/browser/indigo/indigo_prefs.h" #include "chrome/browser/indigo/indigo_service.h" @@ -25,10 +27,12 @@ #include "components/signin/public/identity_manager/identity_manager.h" #include "components/signin/public/identity_manager/identity_test_utils.h" #include "content/public/test/browser_test.h" +#include "mojo/public/cpp/bindings/receiver.h" #include "net/test/embedded_test_server/embedded_test_server.h" #include "net/test/embedded_test_server/http_request.h" #include "net/test/embedded_test_server/http_response.h" #include "third_party/blink/public/common/features.h" +#include "third_party/blink/public/mojom/image_replacement/image_replacement.mojom.h" #include "ui/display/display_switches.h" namespace indigo { @@ -209,6 +213,45 @@ StopObservingState(kToolbarBoundsState)); } +IN_PROC_BROWSER_TEST_F(IndigoBrowserTest, CloseResetsReplacements) { + const GURL url = embedded_test_server()->GetURL("/image.html"); + + class FakeImageReplacement : public blink::mojom::ImageReplacement { + public: + void StartReplacement( + mojo::PendingRemote<blink::mojom::ImageReplacementHost> host) override { + } + void RenderReplacement() override {} + }; + + FakeImageReplacement fake_replacement; + mojo::Receiver<blink::mojom::ImageReplacement> receiver(&fake_replacement); + base::test::TestFuture<void> disconnect_future; + + RunTestSequence( + InstrumentTab(kWebContentsId), NavigateWebContents(kWebContentsId, url), + WaitForShow(kIndigoPageActionIconElementId), + PressButton(kIndigoPageActionIconElementId), + WaitForShow(IndigoToolbar::kToolbarElementId), + + WithElement( + kWebContentsId, + base::BindLambdaForTesting([&](ui::TrackedElement* el) { + content::WebContents* web_contents = + browser()->tab_strip_model()->GetActiveWebContents(); + auto* manager = IndigoImageReplacementManager::GetOrCreateForPage( + web_contents->GetPrimaryPage()); + manager->RegisterImageReplacement( + receiver.BindNewPipeAndPassRemote()); + receiver.set_disconnect_handler(disconnect_future.GetCallback()); + })), + + PressButton(IndigoToolbar::kCloseButtonElementId), + WaitForHide(IndigoToolbar::kToolbarElementId)); + + EXPECT_TRUE(disconnect_future.Wait()); +} + class IndigoHighDsfBrowserTest : public IndigoBrowserTest { public: void SetUpCommandLine(base::CommandLine* command_line) override {
diff --git a/chrome/browser/indigo/indigo_image_replacement_manager.cc b/chrome/browser/indigo/indigo_image_replacement_manager.cc index 84b9088..bfea1ddf 100644 --- a/chrome/browser/indigo/indigo_image_replacement_manager.cc +++ b/chrome/browser/indigo/indigo_image_replacement_manager.cc
@@ -59,6 +59,10 @@ return nullptr; } +void IndigoImageReplacementManager::ResetAllReplacements() { + receivers_.Clear(); +} + void IndigoImageReplacementManager::ReplacementFrameAttached( const blink::LocalFrameToken& replacement_frame_token, const gfx::QuadF& quad,
diff --git a/chrome/browser/indigo/indigo_image_replacement_manager.h b/chrome/browser/indigo/indigo_image_replacement_manager.h index c9be257..6e87c7b 100644 --- a/chrome/browser/indigo/indigo_image_replacement_manager.h +++ b/chrome/browser/indigo/indigo_image_replacement_manager.h
@@ -35,6 +35,7 @@ mojo::PendingRemote<blink::mojom::ImageReplacement> image_replacement); IndigoImageReplacement* GetImageReplacementForFrame( const content::RenderFrameHost& rfh); + void ResetAllReplacements(); // blink::mojom::ImageReplacementHost implementation: void ReplacementFrameAttached(
diff --git a/chrome/browser/indigo/indigo_page_action_controller.cc b/chrome/browser/indigo/indigo_page_action_controller.cc index 0fcaaf0..91a3920 100644 --- a/chrome/browser/indigo/indigo_page_action_controller.cc +++ b/chrome/browser/indigo/indigo_page_action_controller.cc
@@ -13,6 +13,7 @@ #include "base/metrics/user_metrics_action.h" #include "base/notimplemented.h" #include "chrome/browser/indigo/indigo_agent_host.h" +#include "chrome/browser/indigo/indigo_image_replacement_manager.h" #include "chrome/browser/indigo/indigo_prefs.h" #include "chrome/browser/indigo/indigo_service.h" #include "chrome/browser/indigo/indigo_service_factory.h" @@ -235,7 +236,18 @@ } void IndigoPageActionController::OnClose(IndigoToolbar* toolbar) { - NOTIMPLEMENTED(); + if (toolbar_) { + toolbar_->Hide(); + toolbar_.reset(); + } + content::WebContents* web_contents = tab().GetContents(); + if (web_contents) { + auto* manager = IndigoImageReplacementManager::GetForPage( + web_contents->GetPrimaryPage()); + if (manager) { + manager->ResetAllReplacements(); + } + } } void IndigoPageActionController::OnRegenerate(IndigoToolbar* toolbar) {
diff --git a/chrome/browser/indigo/indigo_page_action_controller_unittest.cc b/chrome/browser/indigo/indigo_page_action_controller_unittest.cc index 07fc78d..cd5006e 100644 --- a/chrome/browser/indigo/indigo_page_action_controller_unittest.cc +++ b/chrome/browser/indigo/indigo_page_action_controller_unittest.cc
@@ -13,6 +13,7 @@ #include "base/test/scoped_feature_list.h" #include "base/test/test_future.h" #include "build/build_config.h" +#include "chrome/browser/indigo/indigo_image_replacement_manager.h" #include "chrome/browser/indigo/indigo_prefs.h" #include "chrome/browser/indigo/indigo_service.h" #include "chrome/browser/indigo/indigo_service_factory.h" @@ -32,8 +33,10 @@ #include "components/signin/public/identity_manager/identity_test_environment.h" #include "content/public/test/browser_task_environment.h" #include "content/public/test/navigation_simulator.h" +#include "mojo/public/cpp/bindings/receiver.h" #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" +#include "third_party/blink/public/mojom/image_replacement/image_replacement.mojom.h" #include "ui/base/unowned_user_data/unowned_user_data_host.h" #if BUILDFLAG(IS_CHROMEOS) @@ -512,5 +515,40 @@ EXPECT_FALSE(profile_->GetPrefs()->GetBoolean(prefs::kIndigoHasOnboarded)); } +TEST_F(IndigoPageActionControllerTest, OnCloseResetsReplacements) { + CreateController(); + + GURL url("https://example.com"); + auto navigation = content::NavigationSimulator::CreateBrowserInitiated( + url, tab_interface_->GetContents()); + navigation->Commit(); + + content::WebContents* web_contents = tab_interface_->GetContents(); + auto* manager = IndigoImageReplacementManager::GetOrCreateForPage( + web_contents->GetPrimaryPage()); + + base::test::TestFuture<void> disconnect_future; + + class FakeImageReplacement : public blink::mojom::ImageReplacement { + public: + FakeImageReplacement() = default; + void StartReplacement( + mojo::PendingRemote<blink::mojom::ImageReplacementHost> host) override { + // Do nothing. + } + void RenderReplacement() override {} + }; + + FakeImageReplacement fake_replacement; + mojo::Receiver<blink::mojom::ImageReplacement> receiver(&fake_replacement); + + manager->RegisterImageReplacement(receiver.BindNewPipeAndPassRemote()); + receiver.set_disconnect_handler(disconnect_future.GetCallback()); + + controller_->OnClose(nullptr); + + EXPECT_TRUE(disconnect_future.Wait()); +} + } // namespace } // namespace indigo
diff --git a/chrome/browser/infobars/BUILD.gn b/chrome/browser/infobars/BUILD.gn index 6a5b876..2af4623 100644 --- a/chrome/browser/infobars/BUILD.gn +++ b/chrome/browser/infobars/BUILD.gn
@@ -27,7 +27,10 @@ } if (is_android) { - public_deps += [ "//components/infobars/android" ] + public_deps += [ + "//chrome/browser/ui/android/infobars", + "//components/infobars/android", + ] } }
diff --git a/chrome/browser/keyboard_accessory/android/internal/java/src/org/chromium/chrome/browser/keyboard_accessory/all_passwords_bottom_sheet/AllPasswordsBottomSheetIntegrationTest.java b/chrome/browser/keyboard_accessory/android/internal/java/src/org/chromium/chrome/browser/keyboard_accessory/all_passwords_bottom_sheet/AllPasswordsBottomSheetIntegrationTest.java index 68f4528..3c45e96 100644 --- a/chrome/browser/keyboard_accessory/android/internal/java/src/org/chromium/chrome/browser/keyboard_accessory/all_passwords_bottom_sheet/AllPasswordsBottomSheetIntegrationTest.java +++ b/chrome/browser/keyboard_accessory/android/internal/java/src/org/chromium/chrome/browser/keyboard_accessory/all_passwords_bottom_sheet/AllPasswordsBottomSheetIntegrationTest.java
@@ -180,7 +180,7 @@ private RecyclerView getCredentials() { BottomSheetContent content = assumeNonNull(mBottomSheetController).getCurrentSheetContent(); assertNonNull(content); - return (RecyclerView) content.getContentView().findViewById(R.id.sheet_item_list); + return content.getContentView().findViewById(R.id.sheet_item_list); } private ChipView getCredentialNameAt(int index) {
diff --git a/chrome/browser/language/android/java/src/org/chromium/chrome/browser/language/settings/LanguageItemListFragment.java b/chrome/browser/language/android/java/src/org/chromium/chrome/browser/language/settings/LanguageItemListFragment.java index 54589ce..e1cbfae 100644 --- a/chrome/browser/language/android/java/src/org/chromium/chrome/browser/language/settings/LanguageItemListFragment.java +++ b/chrome/browser/language/android/java/src/org/chromium/chrome/browser/language/settings/LanguageItemListFragment.java
@@ -133,7 +133,7 @@ inflater.inflate(R.layout.language_list_with_add_button, container, false); final Activity activity = getActivity(); - RecyclerView recyclerView = (RecyclerView) inflatedView.findViewById(R.id.language_list); + RecyclerView recyclerView = inflatedView.findViewById(R.id.language_list); LinearLayoutManager layoutManager = new LinearLayoutManager(activity); recyclerView.setLayoutManager(layoutManager); recyclerView.addItemDecoration( @@ -149,7 +149,7 @@ SettingsUtils.getShowShadowOnScrollListener( scrollView, inflatedView.findViewById(R.id.shadow))); - TextView addLanguageButton = (TextView) inflatedView.findViewById(R.id.add_language); + TextView addLanguageButton = inflatedView.findViewById(R.id.add_language); final TintedDrawable tintedDrawable = TintedDrawable.constructTintedDrawable(getContext(), R.drawable.plus); tintedDrawable.setTint(SemanticColorUtils.getDefaultControlColorActive(getContext()));
diff --git a/chrome/browser/lifetime/application_lifetime_desktop.cc b/chrome/browser/lifetime/application_lifetime_desktop.cc index 69cbf2b1..44bd769 100644 --- a/chrome/browser/lifetime/application_lifetime_desktop.cc +++ b/chrome/browser/lifetime/application_lifetime_desktop.cc
@@ -27,7 +27,6 @@ #include "chrome/browser/profiles/profile_manager.h" #include "chrome/browser/sessions/exit_type_service.h" #include "chrome/browser/ui/browser.h" -#include "chrome/browser/ui/browser_finder.h" #include "chrome/browser/ui/browser_window/public/browser_window_interface_iterator.h" #include "chrome/browser/ui/browser_window/public/global_browser_collection.h" #include "chrome/browser/ui/unload_controller.h"
diff --git a/chrome/browser/lifetime/smart_restart_manager_browsertest.cc b/chrome/browser/lifetime/smart_restart_manager_browsertest.cc index 06daf22..84b726b 100644 --- a/chrome/browser/lifetime/smart_restart_manager_browsertest.cc +++ b/chrome/browser/lifetime/smart_restart_manager_browsertest.cc
@@ -15,7 +15,6 @@ #include "chrome/browser/profiles/keep_alive/profile_keep_alive_types.h" #include "chrome/browser/profiles/keep_alive/scoped_profile_keep_alive.h" #include "chrome/browser/ui/browser.h" -#include "chrome/browser/ui/browser_finder.h" #include "chrome/browser/ui/browser_window/public/browser_window_interface.h" #include "chrome/browser/ui/browser_window/public/global_browser_collection.h" #include "chrome/browser/ui/profiles/profile_picker.h"
diff --git a/chrome/browser/long_screenshots/BUILD.gn b/chrome/browser/long_screenshots/BUILD.gn index 52b1e9a..fab30e5f 100644 --- a/chrome/browser/long_screenshots/BUILD.gn +++ b/chrome/browser/long_screenshots/BUILD.gn
@@ -28,6 +28,7 @@ deps = [ "//chrome/android:chrome_jni_headers", + "//chrome/browser/flags:flags_android", "//chrome/browser/profiles", "//components/google/core/common", ]
diff --git a/chrome/browser/long_screenshots/long_screenshots_tab_service.cc b/chrome/browser/long_screenshots/long_screenshots_tab_service.cc index 9d15a45..4102b42 100644 --- a/chrome/browser/long_screenshots/long_screenshots_tab_service.cc +++ b/chrome/browser/long_screenshots/long_screenshots_tab_service.cc
@@ -10,9 +10,12 @@ #include "base/android/jni_android.h" #include "base/android/jni_array.h" #include "base/android/jni_string.h" +#include "base/feature_list.h" #include "base/functional/bind.h" #include "base/functional/callback.h" #include "base/memory_coordinator/utils.h" +#include "base/metrics/histogram_functions.h" +#include "chrome/browser/flags/android/chrome_feature_list.h" #include "components/google/core/common/google_util.h" #include "components/paint_preview/browser/file_manager.h" #include "components/paint_preview/common/mojom/paint_preview_types.mojom.h" @@ -102,8 +105,17 @@ bool in_memory, paint_preview::mojom::ClipCoordOverride clip_x_coord_override, paint_preview::mojom::ClipCoordOverride clip_y_coord_override) { + base::UmaHistogramPercentage("Sharing.LongScreenshots.MemoryLimitOnCapture", + memory_limit()); + + int memory_threshold = + base::FeatureList::IsEnabled( + chrome::android::kLongScreenshotsLenientMemoryCheck) + ? base::kCriticalMemoryPressureThreshold + : base::kModerateMemoryPressureThreshold; + // If the system is under memory pressure don't try to capture. - if (memory_limit() <= base::kModerateMemoryPressureThreshold) { + if (memory_limit() <= memory_threshold) { JNIEnv* env = base::android::AttachCurrentThread(); Java_LongScreenshotsTabService_processCaptureTabStatus( env, java_ref_, Status::kLowMemoryDetected);
diff --git a/chrome/browser/media/cdm_document_service_impl.cc b/chrome/browser/media/cdm_document_service_impl.cc index ed0a09f..f455fa4 100644 --- a/chrome/browser/media/cdm_document_service_impl.cc +++ b/chrome/browser/media/cdm_document_service_impl.cc
@@ -11,6 +11,7 @@ #include "build/build_config.h" #include "content/public/browser/browser_context.h" #include "content/public/browser/browser_thread.h" +#include "content/public/browser/security_principal.h" #include "content/public/browser/web_contents.h" #include "media/media_buildflags.h" @@ -343,7 +344,10 @@ return; } - auto site = render_frame_host().GetSiteInstance()->GetSiteURL(); + auto site = render_frame_host() + .GetSiteInstance() + ->GetSecurityPrincipal() + .GetDeprecatedSiteURL(); switch (event) { case media::CdmEvent::kSignificantPlayback: monitor->OnSignificantPlayback(site);
diff --git a/chrome/browser/media/webrtc/BUILD.gn b/chrome/browser/media/webrtc/BUILD.gn index 7fe2b143..547edb5 100644 --- a/chrome/browser/media/webrtc/BUILD.gn +++ b/chrome/browser/media/webrtc/BUILD.gn
@@ -143,6 +143,7 @@ "//chrome/browser/profiles:profile_util", "//chrome/browser/safe_browsing", "//chrome/browser/status_icons", + "//chrome/browser/ui:simple_message_box_headers", "//chrome/browser/ui:ui_features", "//chrome/common", "//chrome/common:channel_info",
diff --git a/chrome/browser/metrics/first_web_contents_profiler.cc b/chrome/browser/metrics/first_web_contents_profiler.cc index eb648416..e6cbee4 100644 --- a/chrome/browser/metrics/first_web_contents_profiler.cc +++ b/chrome/browser/metrics/first_web_contents_profiler.cc
@@ -45,6 +45,7 @@ void RecordFinishReason(StartupProfilingFinishReason finish_reason) override; void RecordNavigationFinished(base::TimeTicks navigation_start) override; void RecordFirstNonEmptyPaint() override; + void RecordFirstNonEmptyPaintForOsLaunch() override; bool WasStartupInterrupted() override; private: @@ -88,6 +89,11 @@ web_contents()->GetPrimaryMainFrame()->GetProcess()->GetLastInitTime()); } +void FirstWebContentsProfiler::RecordFirstNonEmptyPaintForOsLaunch() { + startup_metric_utils::GetBrowser() + .RecordFirstWebContentsNonEmptyPaintForOsLaunch(base::TimeTicks::Now()); +} + bool FirstWebContentsProfiler::WasStartupInterrupted() { return startup_metric_utils::GetBrowser().WasMainWindowStartupInterrupted(); }
diff --git a/chrome/browser/metrics/first_web_contents_profiler_base.cc b/chrome/browser/metrics/first_web_contents_profiler_base.cc index 40a769f9..3ee3b2a 100644 --- a/chrome/browser/metrics/first_web_contents_profiler_base.cc +++ b/chrome/browser/metrics/first_web_contents_profiler_base.cc
@@ -4,16 +4,33 @@ #include "chrome/browser/metrics/first_web_contents_profiler_base.h" +#include "base/command_line.h" #include "build/build_config.h" #include "chrome/browser/ui/browser.h" #include "chrome/browser/ui/browser_window.h" #include "chrome/browser/ui/browser_window/public/browser_window_features.h" #include "chrome/browser/ui/browser_window/public/browser_window_interface.h" #include "chrome/browser/ui/tabs/tab_strip_model.h" +#include "chrome/common/chrome_switches.h" #include "content/public/browser/navigation_entry.h" #include "content/public/browser/navigation_handle.h" #include "content/public/browser/web_contents_observer.h" +namespace { + +// Returns whether this instance was launched automatically by the OS as part of +// its startup. +bool IsAutoLaunchedByOs() { +#if BUILDFLAG(IS_WIN) + return base::CommandLine::ForCurrentProcess()->HasSwitch( + switches::kStartupForegroundLaunch); +#else + return false; +#endif +} + +} // namespace + namespace metrics { FirstWebContentsProfilerBase::FirstWebContentsProfilerBase( @@ -115,6 +132,13 @@ return; } + if (IsAutoLaunchedByOs()) { + RecordFirstNonEmptyPaintForOsLaunch(); + FinishedCollectingMetrics( + StartupProfilingFinishReason::kAbandonNonInteractiveStartup); + return; + } + RecordFirstNonEmptyPaint(); FinishedCollectingMetrics(StartupProfilingFinishReason::kDone); }
diff --git a/chrome/browser/metrics/first_web_contents_profiler_base.h b/chrome/browser/metrics/first_web_contents_profiler_base.h index a52c2cc..d21d9cd8 100644 --- a/chrome/browser/metrics/first_web_contents_profiler_base.h +++ b/chrome/browser/metrics/first_web_contents_profiler_base.h
@@ -40,7 +40,9 @@ // Abandon if the WebContents was already painted. We set up the profiler too // late and it missed the first non empty paint event. kAbandonAlreadyPaintedContent = 7, - kMaxValue = kAbandonAlreadyPaintedContent + // Abandon if launched without user interaction (eg. launched by OS). + kAbandonNonInteractiveStartup = 8, + kMaxValue = kAbandonNonInteractiveStartup, }; // Note: Instances of this class self destroy when the first non-empty paint @@ -72,6 +74,7 @@ StartupProfilingFinishReason finish_reason) = 0; virtual void RecordNavigationFinished(base::TimeTicks navigation_start) = 0; virtual void RecordFirstNonEmptyPaint() = 0; + virtual void RecordFirstNonEmptyPaintForOsLaunch() = 0; private: // content::WebContentsObserver:
diff --git a/chrome/browser/nearby_sharing/client/nearby_share_client_impl_unittest.cc b/chrome/browser/nearby_sharing/client/nearby_share_client_impl_unittest.cc index 7a68112..8363e1b 100644 --- a/chrome/browser/nearby_sharing/client/nearby_share_client_impl_unittest.cc +++ b/chrome/browser/nearby_sharing/client/nearby_share_client_impl_unittest.cc
@@ -144,7 +144,7 @@ values.push_back(pair.second); } } - EXPECT_TRUE(values.size() > 0); + EXPECT_GT(values.size(), 0u); return values; } @@ -320,10 +320,10 @@ list_public_certificate_response_from_notifier_; base::test::TaskEnvironment task_environment_; signin::IdentityTestEnvironment identity_test_environment_; - raw_ptr<FakeNearbyShareApiCallFlow, DanglingUntriaged> api_call_flow_; scoped_refptr<network::SharedURLLoaderFactory> shared_factory_; NearbyShareHttpNotifier notifier_; std::unique_ptr<NearbyShareClient> client_; + raw_ptr<FakeNearbyShareApiCallFlow> api_call_flow_; }; TEST_F(NearbyShareClientImplTest, UpdateDeviceSuccess) {
diff --git a/chrome/browser/net/errorpage_browsertest.cc b/chrome/browser/net/errorpage_browsertest.cc index d7bb100..5827714 100644 --- a/chrome/browser/net/errorpage_browsertest.cc +++ b/chrome/browser/net/errorpage_browsertest.cc
@@ -34,7 +34,6 @@ #include "chrome/browser/search_engines/ui_thread_search_terms_data.h" #include "chrome/browser/ui/browser.h" #include "chrome/browser/ui/browser_commands.h" -#include "chrome/browser/ui/browser_finder.h" #include "chrome/browser/ui/browser_window/public/global_browser_collection.h" #include "chrome/browser/ui/tabs/tab_strip_model.h" #include "chrome/common/chrome_paths.h"
diff --git a/chrome/browser/net/profile_network_context_service_test_utils.cc b/chrome/browser/net/profile_network_context_service_test_utils.cc index 878e145..444774f 100644 --- a/chrome/browser/net/profile_network_context_service_test_utils.cc +++ b/chrome/browser/net/profile_network_context_service_test_utils.cc
@@ -11,7 +11,6 @@ #include "chrome/browser/profiles/profile_window.h" #include "chrome/browser/search_engines/template_url_service_factory.h" #include "chrome/browser/ui/browser.h" -#include "chrome/browser/ui/browser_finder.h" #include "chrome/test/base/search_test_utils.h" #include "chrome/test/base/ui_test_utils.h" #include "services/cert_verifier/public/mojom/cert_verifier_service_factory.mojom.h"
diff --git a/chrome/browser/notifications/platform_notification_service_interactive_uitest.cc b/chrome/browser/notifications/platform_notification_service_interactive_uitest.cc index 9153b06..0336431 100644 --- a/chrome/browser/notifications/platform_notification_service_interactive_uitest.cc +++ b/chrome/browser/notifications/platform_notification_service_interactive_uitest.cc
@@ -40,6 +40,7 @@ #include "chrome/browser/web_applications/test/os_integration_test_override_impl.h" #include "chrome/browser/web_applications/test/web_app_install_test_utils.h" #include "chrome/browser/web_applications/web_app.h" +#include "chrome/common/buildflags.h" #include "chrome/common/chrome_features.h" #include "chrome/test/base/in_process_browser_test.h" #include "chrome/test/base/interactive_test_utils.h"
diff --git a/chrome/browser/ntp_customization/junit/src/org/chromium/chrome/browser/ntp_customization/feed/FeedSettingsMediatorUnitTest.java b/chrome/browser/ntp_customization/junit/src/org/chromium/chrome/browser/ntp_customization/feed/FeedSettingsMediatorUnitTest.java index 6fdbf26f..84f947a2 100644 --- a/chrome/browser/ntp_customization/junit/src/org/chromium/chrome/browser/ntp_customization/feed/FeedSettingsMediatorUnitTest.java +++ b/chrome/browser/ntp_customization/junit/src/org/chromium/chrome/browser/ntp_customization/feed/FeedSettingsMediatorUnitTest.java
@@ -196,20 +196,20 @@ mFeedSettingsMediator.setListItemsContentForTesting( mFeedSettingsMediator.buildFeedListContent()); - ListContainerViewDelegate delegateForWebFeedDisabled = + ListContainerViewDelegate delegateWithoutFollowing = mFeedSettingsMediator.createListDelegate(); - content = delegateForWebFeedDisabled.getListItems(); + content = delegateWithoutFollowing.getListItems(); Assert.assertTrue(content.contains(ACTIVITY)); Assert.assertTrue(content.contains(INTERESTS)); testCreateListContainerViewDelegateImplForSectionTitle( - delegateForSignedIn, delegateForWebFeedDisabled); + delegateForSignedIn, delegateWithoutFollowing); testCreateListContainerViewDelegateImplForSectionSubtitle( - delegateForSignedIn, delegateForWebFeedDisabled); + delegateForSignedIn, delegateWithoutFollowing); testCreateListContainerViewDelegateImplForSectionListener( - delegateForSignedIn, delegateForWebFeedDisabled); + delegateForSignedIn, delegateWithoutFollowing); } @Test @@ -222,68 +222,68 @@ /** Verifies that the subtitles of sections are correct. */ private void testCreateListContainerViewDelegateImplForSectionSubtitle( - ListContainerViewDelegate delegateForWebFeedEnabled, - ListContainerViewDelegate delegateForWebFeedDisabled) { + ListContainerViewDelegate delegateWithFollowing, + ListContainerViewDelegate delegateWithoutFollowing) { assertEquals( mContext.getString(R.string.feed_manage_activity_description), - delegateForWebFeedEnabled.getListItemSubtitle(ACTIVITY, mContext)); + delegateWithFollowing.getListItemSubtitle(ACTIVITY, mContext)); assertEquals( mContext.getString(R.string.feed_manage_following_description), - delegateForWebFeedEnabled.getListItemSubtitle(FOLLOWING, mContext)); + delegateWithFollowing.getListItemSubtitle(FOLLOWING, mContext)); assertEquals( mContext.getString(R.string.feed_manage_hidden_description), - delegateForWebFeedEnabled.getListItemSubtitle(HIDDEN, mContext)); + delegateWithFollowing.getListItemSubtitle(HIDDEN, mContext)); assertEquals( mContext.getString(R.string.feed_manage_interests_description), - delegateForWebFeedDisabled.getListItemSubtitle(INTERESTS, mContext)); + delegateWithoutFollowing.getListItemSubtitle(INTERESTS, mContext)); } /** Verifies that the titles of sections are correct. */ private void testCreateListContainerViewDelegateImplForSectionTitle( - ListContainerViewDelegate delegateForWebFeedEnabled, - ListContainerViewDelegate delegateForWebFeedDisabled) { + ListContainerViewDelegate delegateWithFollowing, + ListContainerViewDelegate delegateWithoutFollowing) { assertEquals( mContext.getString(R.string.feed_manage_activity), - delegateForWebFeedEnabled.getListItemTitle(ACTIVITY, mContext)); + delegateWithFollowing.getListItemTitle(ACTIVITY, mContext)); assertEquals( mContext.getString(R.string.feed_manage_following), - delegateForWebFeedEnabled.getListItemTitle(FOLLOWING, mContext)); + delegateWithFollowing.getListItemTitle(FOLLOWING, mContext)); assertEquals( mContext.getString(R.string.feed_manage_hidden), - delegateForWebFeedEnabled.getListItemTitle(HIDDEN, mContext)); + delegateWithFollowing.getListItemTitle(HIDDEN, mContext)); assertEquals( mContext.getString(R.string.feed_manage_interests), - delegateForWebFeedDisabled.getListItemTitle(INTERESTS, mContext)); + delegateWithoutFollowing.getListItemTitle(INTERESTS, mContext)); } /** Verifies that the click listener of sections are correct. */ private void testCreateListContainerViewDelegateImplForSectionListener( - ListContainerViewDelegate delegateForWebFeedEnabled, - ListContainerViewDelegate delegateForWebFeedDisabled) { + ListContainerViewDelegate delegateWithFollowing, + ListContainerViewDelegate delegateWithoutFollowing) { when(mView.getContext()).thenReturn(mActivity); // Verifies the click listener is correct for Activity section. - delegateForWebFeedEnabled.getListener(ACTIVITY).onClick(mView); + delegateWithFollowing.getListener(ACTIVITY).onClick(mView); Intent intent = mShadowActivity.peekNextStartedActivityForResult().intent; assertEquals( intent.getData(), Uri.parse("https://myactivity.google.com/myactivity?product=50")); // Verifies the click listener is correct for Following section. - delegateForWebFeedEnabled.getListener(FOLLOWING).onClick(mView); + delegateWithFollowing.getListener(FOLLOWING).onClick(mView); intent = mShadowActivity.peekNextStartedActivityForResult().intent; assertEquals( intent.getData(), Uri.parse("https://www.google.com/preferences/interests/yourinterests?sh=n")); // Verifies the click listener is correct for Hidden section. - delegateForWebFeedEnabled.getListener(HIDDEN).onClick(mView); + delegateWithFollowing.getListener(HIDDEN).onClick(mView); intent = mShadowActivity.peekNextStartedActivityForResult().intent; assertEquals( intent.getData(), Uri.parse("https://www.google.com/preferences/interests/hidden?sh=n")); // Verifies the click listener is correct for Interests section. - delegateForWebFeedDisabled.getListener(INTERESTS).onClick(mView); + delegateWithoutFollowing.getListener(INTERESTS).onClick(mView); intent = mShadowActivity.peekNextStartedActivityForResult().intent; assertEquals(intent.getData(), Uri.parse("https://www.google.com/preferences/interests")); }
diff --git a/chrome/browser/page_load_metrics/observers/chrome_gws_page_load_metrics_observer.cc b/chrome/browser/page_load_metrics/observers/chrome_gws_page_load_metrics_observer.cc index 301886d..454d9592 100644 --- a/chrome/browser/page_load_metrics/observers/chrome_gws_page_load_metrics_observer.cc +++ b/chrome/browser/page_load_metrics/observers/chrome_gws_page_load_metrics_observer.cc
@@ -13,6 +13,7 @@ #include "components/signin/public/identity_manager/identity_manager.h" #include "content/public/browser/browser_context.h" #include "content/public/browser/navigation_handle.h" +#include "content/public/browser/security_principal.h" #include "content/public/browser/site_instance.h" #include "content/public/browser/web_contents.h" @@ -25,7 +26,7 @@ return false; } - auto origin = start_instance->GetSiteURL(); + auto origin = start_instance->GetSecurityPrincipal().GetDeprecatedSiteURL(); const GURL& ntp_url = chrome::ChromeUINewTabPageURLAsGURL(); return ntp_url.scheme() == origin.scheme() && ntp_url.host() == origin.host();
diff --git a/chrome/browser/password_manager/BUILD.gn b/chrome/browser/password_manager/BUILD.gn index 26f4470..b82a7e39 100644 --- a/chrome/browser/password_manager/BUILD.gn +++ b/chrome/browser/password_manager/BUILD.gn
@@ -228,6 +228,7 @@ "//chrome/browser/touch_to_fill/password_manager/no_passkeys/android:public", "//chrome/browser/touch_to_fill/password_manager/password_generation/android", "//chrome/browser/touch_to_fill/password_manager/password_generation/android:public", + "//chrome/browser/ui/android/passwords", "//chrome/browser/webauthn:webauthn_request_delegate_android", "//components/messages/android", "//components/password_manager/core/browser/affiliation:affiliation_fetching",
diff --git a/chrome/browser/password_manager/android/BUILD.gn b/chrome/browser/password_manager/android/BUILD.gn index e98948c..d6e2d0dc 100644 --- a/chrome/browser/password_manager/android/BUILD.gn +++ b/chrome/browser/password_manager/android/BUILD.gn
@@ -588,6 +588,7 @@ "//chrome/browser/touch_to_fill/password_manager/password_generation/android:public", "//chrome/browser/touch_to_fill/password_manager/password_generation/android:test_support", "//chrome/browser/trusted_vault", + "//chrome/browser/ui/android/passwords", "//chrome/browser/ui/autofill", "//chrome/test:test_support", "//components/affiliations/core/browser:test_support",
diff --git a/chrome/browser/password_manager/web_app_profile_switcher.cc b/chrome/browser/password_manager/web_app_profile_switcher.cc index 6b908ed..a0bb6293 100644 --- a/chrome/browser/password_manager/web_app_profile_switcher.cc +++ b/chrome/browser/password_manager/web_app_profile_switcher.cc
@@ -15,7 +15,6 @@ #include "base/strings/utf_string_conversions.h" #include "chrome/browser/profiles/profile_window.h" #include "chrome/browser/ui/browser.h" -#include "chrome/browser/ui/browser_finder.h" #include "chrome/browser/ui/browser_window.h" #include "chrome/browser/ui/browser_window/public/browser_window_interface.h" #include "chrome/browser/ui/web_applications/app_browser_controller.h"
diff --git a/chrome/browser/password_manager/web_app_profile_switcher_interactive_uitest.cc b/chrome/browser/password_manager/web_app_profile_switcher_interactive_uitest.cc index 9e5a648..d393b42a 100644 --- a/chrome/browser/password_manager/web_app_profile_switcher_interactive_uitest.cc +++ b/chrome/browser/password_manager/web_app_profile_switcher_interactive_uitest.cc
@@ -12,7 +12,6 @@ #include "chrome/browser/profiles/profile_manager.h" #include "chrome/browser/profiles/profile_test_util.h" #include "chrome/browser/ui/browser.h" -#include "chrome/browser/ui/browser_finder.h" #include "chrome/browser/ui/browser_window/public/browser_window_interface.h" #include "chrome/browser/ui/browser_window/public/global_browser_collection.h" #include "chrome/browser/ui/browser_window/public/profile_browser_collection.h"
diff --git a/chrome/browser/pdf/pdf_extension_js_test.cc b/chrome/browser/pdf/pdf_extension_js_test.cc index db7f27b2..656e8ea 100644 --- a/chrome/browser/pdf/pdf_extension_js_test.cc +++ b/chrome/browser/pdf/pdf_extension_js_test.cc
@@ -24,6 +24,7 @@ #include "chrome/browser/profiles/profile.h" #include "chrome/browser/ui/browser.h" #include "chrome/browser/ui/tabs/tab_strip_model.h" +#include "chrome/common/buildflags.h" #include "chrome/common/webui_url_constants.h" #include "chrome/test/base/devtools_agent_coverage_observer.h" #include "chrome/test/base/test_switches.h"
diff --git a/chrome/browser/personal_context/BUILD.gn b/chrome/browser/personal_context/BUILD.gn new file mode 100644 index 0000000..ff8ac74 --- /dev/null +++ b/chrome/browser/personal_context/BUILD.gn
@@ -0,0 +1,31 @@ +# Copyright 2026 The Chromium Authors +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +source_set("personal_context") { + sources = [ + "personal_context_service_factory.cc", + "personal_context_service_factory.h", + ] + + deps = [ + "//base", + "//chrome/browser/profiles:profile", + "//components/personal_context/core", + "//components/personal_context/core:features", + ] +} + +source_set("unit_tests") { + testonly = true + sources = [ "personal_context_service_factory_unittest.cc" ] + deps = [ + ":personal_context", + "//base/test:test_support", + "//chrome/test:test_support", + "//components/personal_context/core", + "//components/personal_context/core:features", + "//content/test:test_support", + "//testing/gtest", + ] +}
diff --git a/chrome/browser/personal_context/DEPS b/chrome/browser/personal_context/DEPS new file mode 100644 index 0000000..a7e7ae8 --- /dev/null +++ b/chrome/browser/personal_context/DEPS
@@ -0,0 +1,3 @@ +include_rules = [ + "+components/personal_context/core", +]
diff --git a/chrome/browser/personal_context/OWNERS b/chrome/browser/personal_context/OWNERS new file mode 100644 index 0000000..a1ca7d884 --- /dev/null +++ b/chrome/browser/personal_context/OWNERS
@@ -0,0 +1 @@ +file://components/personal_context/OWNERS \ No newline at end of file
diff --git a/chrome/browser/personal_context/personal_context_service_factory.cc b/chrome/browser/personal_context/personal_context_service_factory.cc new file mode 100644 index 0000000..420b976 --- /dev/null +++ b/chrome/browser/personal_context/personal_context_service_factory.cc
@@ -0,0 +1,46 @@ +// Copyright 2026 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chrome/browser/personal_context/personal_context_service_factory.h" + +#include "base/feature_list.h" +#include "chrome/browser/profiles/profile.h" +#include "components/personal_context/core/personal_context_features.h" +#include "components/personal_context/core/personal_context_service_impl.h" + +// static +personal_context::PersonalContextService* +PersonalContextServiceFactory::GetForProfile(Profile* profile) { + return static_cast<personal_context::PersonalContextService*>( + GetInstance()->GetServiceForBrowserContext(profile, true)); +} + +// static +PersonalContextServiceFactory* +PersonalContextServiceFactory::GetInstance() { + static base::NoDestructor<PersonalContextServiceFactory> instance; + return instance.get(); +} + +PersonalContextServiceFactory::PersonalContextServiceFactory() + : ProfileKeyedServiceFactory( + "PersonalContextService", + ProfileSelections::Builder() + .WithRegular(ProfileSelection::kOriginalOnly) + .Build()) {} + +PersonalContextServiceFactory::~PersonalContextServiceFactory() = + default; + +std::unique_ptr<KeyedService> +PersonalContextServiceFactory::BuildServiceInstanceForBrowserContext( + content::BrowserContext* context) const { + if (!base::FeatureList::IsEnabled( + personal_context::features::kPersonalContext)) { + return nullptr; + } + + return std::make_unique< + personal_context::PersonalContextServiceImpl>(); +}
diff --git a/chrome/browser/personal_context/personal_context_service_factory.h b/chrome/browser/personal_context/personal_context_service_factory.h new file mode 100644 index 0000000..61bbd51 --- /dev/null +++ b/chrome/browser/personal_context/personal_context_service_factory.h
@@ -0,0 +1,39 @@ +// Copyright 2026 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CHROME_BROWSER_PERSONAL_CONTEXT_PERSONAL_CONTEXT_SERVICE_FACTORY_H_ +#define CHROME_BROWSER_PERSONAL_CONTEXT_PERSONAL_CONTEXT_SERVICE_FACTORY_H_ + +#include "base/no_destructor.h" +#include "chrome/browser/profiles/profile_keyed_service_factory.h" + +class Profile; + +namespace personal_context { +class PersonalContextService; +} // namespace personal_context + +class PersonalContextServiceFactory : public ProfileKeyedServiceFactory { + public: + static personal_context::PersonalContextService* GetForProfile( + Profile* profile); + static PersonalContextServiceFactory* GetInstance(); + + PersonalContextServiceFactory( + const PersonalContextServiceFactory&) = delete; + PersonalContextServiceFactory& operator=( + const PersonalContextServiceFactory&) = delete; + + private: + friend base::NoDestructor<PersonalContextServiceFactory>; + + PersonalContextServiceFactory(); + ~PersonalContextServiceFactory() override; + + // BrowserContextKeyedServiceFactory: + std::unique_ptr<KeyedService> BuildServiceInstanceForBrowserContext( + content::BrowserContext* context) const override; +}; + +#endif // CHROME_BROWSER_PERSONAL_CONTEXT_PERSONAL_CONTEXT_SERVICE_FACTORY_H_
diff --git a/chrome/browser/personal_context/personal_context_service_factory_unittest.cc b/chrome/browser/personal_context/personal_context_service_factory_unittest.cc new file mode 100644 index 0000000..86cd047 --- /dev/null +++ b/chrome/browser/personal_context/personal_context_service_factory_unittest.cc
@@ -0,0 +1,50 @@ +// Copyright 2026 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chrome/browser/personal_context/personal_context_service_factory.h" + +#include "base/test/scoped_feature_list.h" +#include "chrome/test/base/testing_profile.h" +#include "components/personal_context/core/personal_context_features.h" +#include "components/personal_context/core/personal_context_service.h" +#include "content/public/test/browser_task_environment.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace personal_context { + +class PersonalContextServiceFactoryTest : public testing::Test { + public: + PersonalContextServiceFactoryTest() = default; + ~PersonalContextServiceFactoryTest() override = default; + + protected: + base::test::ScopedFeatureList scoped_feature_list_; + content::BrowserTaskEnvironment task_environment_; +}; + +TEST_F(PersonalContextServiceFactoryTest, CreatesServiceWithFlagEnabled) { + scoped_feature_list_.InitAndEnableFeature( + personal_context::features::kPersonalContext); + TestingProfile profile; + EXPECT_NE(nullptr, PersonalContextServiceFactory::GetForProfile(&profile)); +} + +TEST_F(PersonalContextServiceFactoryTest, CreatesNoServiceWithFlagDisabled) { + scoped_feature_list_.InitAndDisableFeature( + personal_context::features::kPersonalContext); + TestingProfile profile; + EXPECT_EQ(nullptr, PersonalContextServiceFactory::GetForProfile(&profile)); +} + +TEST_F(PersonalContextServiceFactoryTest, + CreatesNoServiceForIncognitoWithFlagEnabled) { + scoped_feature_list_.InitAndEnableFeature( + personal_context::features::kPersonalContext); + TestingProfile profile; + Profile* otr_profile = profile.GetOffTheRecordProfile( + Profile::OTRProfileID::PrimaryID(), /*create_if_needed=*/true); + EXPECT_EQ(nullptr, PersonalContextServiceFactory::GetForProfile(otr_profile)); +} + +} // namespace personal_context
diff --git a/chrome/browser/picture_in_picture/BUILD.gn b/chrome/browser/picture_in_picture/BUILD.gn index 10d392b5b..25ef4fbe 100644 --- a/chrome/browser/picture_in_picture/BUILD.gn +++ b/chrome/browser/picture_in_picture/BUILD.gn
@@ -112,6 +112,7 @@ ] deps += [ "//chrome/android:chrome_jni_headers", + "//chrome/browser/ui/android/overlay", "//components/strings:components_strings", ] } else {
diff --git a/chrome/browser/policy/BUILD.gn b/chrome/browser/policy/BUILD.gn index a698bf8..522de47 100644 --- a/chrome/browser/policy/BUILD.gn +++ b/chrome/browser/policy/BUILD.gn
@@ -1023,6 +1023,7 @@ "//chrome/browser/lens", "//chrome/browser/policy/android", "//chrome/browser/policy/android:jni_headers", + "//chrome/browser/ui/android", "//chrome/browser/ui/android/tab_model", "//components/enterprise/device_trust", "//components/performance_manager",
diff --git a/chrome/browser/policy/cloud/chrome_browser_cloud_management_browsertest.cc b/chrome/browser/policy/cloud/chrome_browser_cloud_management_browsertest.cc index 1542d989..8237194 100644 --- a/chrome/browser/policy/cloud/chrome_browser_cloud_management_browsertest.cc +++ b/chrome/browser/policy/cloud/chrome_browser_cloud_management_browsertest.cc
@@ -71,7 +71,6 @@ #include "chrome/browser/policy/cloud/extension_install_policy_service.h" #include "chrome/browser/policy/cloud/extension_install_policy_service_factory.h" #include "chrome/browser/profiles/profile.h" -#include "chrome/browser/ui/browser_finder.h" #include "components/policy/core/common/features.h" #include "components/prefs/pref_service.h" #include "extensions/browser/pref_names.h"
diff --git a/chrome/browser/policy/configuration_policy_handler_list_factory.cc b/chrome/browser/policy/configuration_policy_handler_list_factory.cc index 772de728..64c3d89 100644 --- a/chrome/browser/policy/configuration_policy_handler_list_factory.cc +++ b/chrome/browser/policy/configuration_policy_handler_list_factory.cc
@@ -85,6 +85,7 @@ #include "components/enterprise/data_controls/core/browser/data_controls_policy_handler.h" #include "components/enterprise/data_controls/core/browser/prefs.h" #include "components/enterprise/device_trust/prefs.h" +#include "components/enterprise/isolated_mode/prefs.h" #include "components/feed/core/shared_prefs/pref_names.h" #include "components/history/core/common/pref_names.h" #include "components/history_clusters/core/history_clusters_prefs.h" @@ -489,6 +490,9 @@ { key::kAllowSocketPoolSizeRandomizationForProxies, prefs::kAllowSocketPoolSizeRandomizationForProxies, base::Value::Type::BOOLEAN }, + { key::kIsolatedModeSettings, + enterprise_isolated_mode::kEnterpriseIsolatedModeSettings, + base::Value::Type::INTEGER }, // Policies for all platforms - End #if BUILDFLAG(IS_ANDROID) { key::kAccessibilityPerformanceFilteringAllowed, @@ -625,6 +629,9 @@ { key::kDefaultDirectSocketsSetting, prefs::kManagedDefaultDirectSocketsSetting, base::Value::Type::INTEGER }, + { key::kDefaultSubAppsWithoutPromptsSetting, + prefs::kManagedDefaultSubAppsWithoutPromptsSetting, + base::Value::Type::INTEGER }, #if BUILDFLAG(IS_CHROMEOS) { key::kDefaultSmartCardConnectSetting, prefs::kManagedDefaultSmartCardConnectSetting, @@ -647,6 +654,12 @@ { key::kDirectSocketsPrivateNetworkAccessBlockedForUrls, prefs::kManagedDirectSocketsPrivateNetworkAccessBlockedForUrls, base::Value::Type::LIST }, + { key::kSubAppsWithoutPromptsAllowedForOrigins, + prefs::kManagedSubAppsWithoutPromptsAllowedForOrigins, + base::Value::Type::LIST }, + { key::kSubAppsWithoutPromptsBlockedForOrigins, + prefs::kManagedSubAppsWithoutPromptsBlockedForOrigins, + base::Value::Type::LIST }, { key::kDefaultControlledFrameSetting, prefs::kManagedDefaultControlledFrameSetting, base::Value::Type::INTEGER },
diff --git a/chrome/browser/policy/profile_policy_connector.cc b/chrome/browser/policy/profile_policy_connector.cc index 1352dea..c2777e8 100644 --- a/chrome/browser/policy/profile_policy_connector.cc +++ b/chrome/browser/policy/profile_policy_connector.cc
@@ -74,7 +74,6 @@ #include "chrome/browser/ui/android/tab_model/tab_model_list.h" #else #include "chrome/browser/ui/browser.h" -#include "chrome/browser/ui/browser_finder.h" #include "chrome/browser/ui/browser_window/public/browser_collection_observer.h" #include "chrome/browser/ui/browser_window/public/browser_window_interface.h" // nogncheck crbug.com/40147906 #include "chrome/browser/ui/browser_window/public/browser_window_interface_iterator.h"
diff --git a/chrome/browser/policy/test/restore_on_startup_policy_browsertest.cc b/chrome/browser/policy/test/restore_on_startup_policy_browsertest.cc index 6ad9bcf..67dd075 100644 --- a/chrome/browser/policy/test/restore_on_startup_policy_browsertest.cc +++ b/chrome/browser/policy/test/restore_on_startup_policy_browsertest.cc
@@ -15,7 +15,6 @@ #include "chrome/browser/resource_coordinator/tab_load_tracker_test_support.h" #include "chrome/browser/search/search.h" #include "chrome/browser/ui/browser.h" -#include "chrome/browser/ui/browser_finder.h" #include "chrome/browser/ui/browser_tabstrip.h" #include "chrome/browser/ui/browser_window/public/profile_browser_collection.h" #include "chrome/browser/ui/search/ntp_test_utils.h"
diff --git a/chrome/browser/prefs/browser_prefs.cc b/chrome/browser/prefs/browser_prefs.cc index 15f4e73..33f52de 100644 --- a/chrome/browser/prefs/browser_prefs.cc +++ b/chrome/browser/prefs/browser_prefs.cc
@@ -123,6 +123,7 @@ #include "components/enterprise/browser/promotion/promotion_prefs.h" #include "components/enterprise/buildflags/buildflags.h" #include "components/enterprise/connectors/core/connectors_prefs.h" +#include "components/enterprise/isolated_mode/prefs.h" #include "components/feature_engagement/public/pref_names.h" #include "components/history_clusters/core/history_clusters_prefs.h" #include "components/image_fetcher/core/cache/image_cache.h" @@ -987,11 +988,9 @@ // Deprecated 04/2026. constexpr char kTpcdMetadataCohorts[] = "tpcd.metadata.cohorts"; -#if BUILDFLAG(IS_ANDROID) // Deprecated 04/2026. constexpr char kHasSeenWebFeed[] = "webfeed.has_seen_feed"; constexpr char kLastBadgeAnimationTime[] = "webfeed.last_badge_animation_time"; -#endif // BUILDFLAG(IS_ANDROID) // Deprecated 04/2026. inline constexpr char kPreallocatedAddressesVersion[] = @@ -1005,10 +1004,12 @@ inline constexpr char kLastPlusAddressFillingTime[] = "plus_addresses.last.filling.time"; -#if BUILDFLAG(IS_ANDROID) // Deprecated 05/2026. constexpr char kWebFeedContentOrder[] = "webfeed.content_order"; -#endif // BUILDFLAG(IS_ANDROID) +constexpr char kWebFeedsRequestSchedule[] = "webfeed.request_schedule"; +constexpr char kEnableWebFeedFollowIntroDebug[] = + "webfeed_follow_intro_debug.enable"; +constexpr char kLastSeenFeedType[] = "feedv2.last_seen_feed_type"; // Register local state used only for migration (clearing or moving to a new // key). @@ -1121,11 +1122,9 @@ // Deprecated 04/2026. registry->RegisterDictionaryPref(kTpcdMetadataCohorts); -#if BUILDFLAG(IS_ANDROID) // Deprecated 04/2026. registry->RegisterBooleanPref(kHasSeenWebFeed, false); registry->RegisterTimePref(kLastBadgeAnimationTime, base::Time()); -#endif // BUILDFLAG(IS_ANDROID) } // Register prefs used only for migration (clearing or moving to a new key). @@ -1389,10 +1388,11 @@ registry->RegisterTimePref(kFirstPlusAddressCreationTime, base::Time()); registry->RegisterTimePref(kLastPlusAddressFillingTime, base::Time()); -#if BUILDFLAG(IS_ANDROID) // Deprecated 05/2026. registry->RegisterIntegerPref(kWebFeedContentOrder, 0); -#endif // BUILDFLAG(IS_ANDROID) + registry->RegisterDictionaryPref(kWebFeedsRequestSchedule); + registry->RegisterBooleanPref(kEnableWebFeedFollowIntroDebug, false); + registry->RegisterIntegerPref(kLastSeenFeedType, 0); } } // namespace @@ -1754,6 +1754,7 @@ cross_device::RegisterProfilePrefs(registry); enterprise::RegisterIdentifiersProfilePrefs(registry); enterprise_connectors::RegisterProfilePrefs(registry); + enterprise_isolated_mode::RegisterProfilePrefs(registry); enterprise_promotion::RegisterProfilePrefs(registry); enterprise_reporting::RegisterProfilePrefs(registry); dom_distiller::DistilledPagePrefs::RegisterProfilePrefs(registry); @@ -2416,11 +2417,9 @@ tabs::MigrateHoverCardMemoryPref(local_state); #endif // BUILDFLAG(IS_ANDROID) -#if BUILDFLAG(IS_ANDROID) // Added 04/2026. local_state->ClearPref(kHasSeenWebFeed); local_state->ClearPref(kLastBadgeAnimationTime); -#endif // BUILDFLAG(IS_ANDROID) // Please don't delete the following line. It is used by PRESUBMIT.py. // END_MIGRATE_OBSOLETE_LOCAL_STATE_PREFS @@ -2715,10 +2714,11 @@ profile_prefs->ClearPref(kFirstPlusAddressCreationTime); profile_prefs->ClearPref(kLastPlusAddressFillingTime); -#if BUILDFLAG(IS_ANDROID) // Added 05/2026. profile_prefs->ClearPref(kWebFeedContentOrder); -#endif // BUILDFLAG(IS_ANDROID) + profile_prefs->ClearPref(kWebFeedsRequestSchedule); + profile_prefs->ClearPref(kEnableWebFeedFollowIntroDebug); + profile_prefs->ClearPref(kLastSeenFeedType); // Please don't delete the following line. It is used by PRESUBMIT.py. // END_MIGRATE_OBSOLETE_PROFILE_PREFS
diff --git a/chrome/browser/preloading/prerender/prerender_manager.cc b/chrome/browser/preloading/prerender/prerender_manager.cc index f05f092..c299b7c 100644 --- a/chrome/browser/preloading/prerender/prerender_manager.cc +++ b/chrome/browser/preloading/prerender/prerender_manager.cc
@@ -47,7 +47,6 @@ #if !BUILDFLAG(IS_ANDROID) #include "chrome/browser/ui/browser.h" -#include "chrome/browser/ui/browser_finder.h" #include "chrome/browser/ui/web_applications/app_browser_controller.h" #endif
diff --git a/chrome/browser/printing/BUILD.gn b/chrome/browser/printing/BUILD.gn index ad835af..7345ea4 100644 --- a/chrome/browser/printing/BUILD.gn +++ b/chrome/browser/printing/BUILD.gn
@@ -139,6 +139,7 @@ "//chrome/browser/enterprise", "//chrome/browser/headless", "//chrome/browser/profiles:profile", + "//chrome/browser/ui:simple_message_box_headers", "//chrome/browser/ui/browser_window", "//chrome/common", "//chrome/common/printing",
diff --git a/chrome/browser/privacy_guide/android/java/src/org/chromium/chrome/browser/privacy_guide/PrivacyGuideExplanationHeading.java b/chrome/browser/privacy_guide/android/java/src/org/chromium/chrome/browser/privacy_guide/PrivacyGuideExplanationHeading.java index 1dd72df..e882a25 100644 --- a/chrome/browser/privacy_guide/android/java/src/org/chromium/chrome/browser/privacy_guide/PrivacyGuideExplanationHeading.java +++ b/chrome/browser/privacy_guide/android/java/src/org/chromium/chrome/browser/privacy_guide/PrivacyGuideExplanationHeading.java
@@ -28,7 +28,7 @@ context.obtainStyledAttributes( attrs, R.styleable.PrivacyGuideExplanationHeading, 0, 0); - TextView title = (TextView) view.findViewById(R.id.title); + TextView title = view.findViewById(R.id.title); title.setText(styledAttrs.getText(R.styleable.PrivacyGuideExplanationHeading_titleText)); styledAttrs.recycle();
diff --git a/chrome/browser/privacy_sandbox/android/java/res/xml/topics_preference.xml b/chrome/browser/privacy_sandbox/android/java/res/xml/topics_preference.xml index c912c672..94875e1 100644 --- a/chrome/browser/privacy_sandbox/android/java/res/xml/topics_preference.xml +++ b/chrome/browser/privacy_sandbox/android/java/res/xml/topics_preference.xml
@@ -20,7 +20,7 @@ <org.chromium.chrome.browser.privacy_sandbox.TopicsHeaderPreference android:key="active_topics" android:title="@string/settings_topics_page_active_topics_heading" - android:summary="@string/settings_topics_page_active_topics_description" + android:summary="@string/settings_ad_topics_page_active_topics_description" app:allowDividerBelow="false" /> <PreferenceCategory
diff --git a/chrome/browser/privacy_sandbox/android/java/src/org/chromium/chrome/browser/privacy_sandbox/TopicsFragment.java b/chrome/browser/privacy_sandbox/android/java/src/org/chromium/chrome/browser/privacy_sandbox/TopicsFragment.java index 04bf394..479d208 100644 --- a/chrome/browser/privacy_sandbox/android/java/src/org/chromium/chrome/browser/privacy_sandbox/TopicsFragment.java +++ b/chrome/browser/privacy_sandbox/android/java/src/org/chromium/chrome/browser/privacy_sandbox/TopicsFragment.java
@@ -121,9 +121,6 @@ private void maybeApplyAdTopicsContentParity() { mTopicsTogglePreference.setSummary( getResources().getString(R.string.settings_ad_topics_page_toggle_sub_label)); - mActiveTopicsPreference.setSummary( - getResources() - .getString(R.string.settings_ad_topics_page_active_topics_description)); } private void maybeApplyAdsApiUxEnhancements() {
diff --git a/chrome/browser/privacy_sandbox/privacy_sandbox_service_impl.cc b/chrome/browser/privacy_sandbox/privacy_sandbox_service_impl.cc index f91eed672..d99236f 100644 --- a/chrome/browser/privacy_sandbox/privacy_sandbox_service_impl.cc +++ b/chrome/browser/privacy_sandbox/privacy_sandbox_service_impl.cc
@@ -630,9 +630,7 @@ bool PrivacySandboxServiceImpl:: PrivacySandboxPrivacyGuideShouldShowAdTopicsCard() { - return GetPrivacySandboxCountries()->IsConsentCountry() && - base::FeatureList::IsEnabled( - privacy_sandbox::kPrivacySandboxAdTopicsContentParity); + return GetPrivacySandboxCountries()->IsConsentCountry(); } bool PrivacySandboxServiceImpl::ShouldUsePrivacyPolicyChinaDomain() {
diff --git a/chrome/browser/privacy_sandbox/privacy_sandbox_service_impl_unittest.cc b/chrome/browser/privacy_sandbox/privacy_sandbox_service_impl_unittest.cc index c243ba42..287c2a71 100644 --- a/chrome/browser/privacy_sandbox/privacy_sandbox_service_impl_unittest.cc +++ b/chrome/browser/privacy_sandbox/privacy_sandbox_service_impl_unittest.cc
@@ -531,23 +531,13 @@ raw_ptr<PrivacySandboxServiceImpl> privacy_sandbox_service_ = nullptr; }; -// Params correspond to (IsFeatureOn, IsConsentCountry, ExpectedResult). class PrivacySandboxPrivacyGuideShouldShowAdTopicsTest : public PrivacySandboxServiceTest, - public testing::WithParamInterface<std::tuple<bool, bool, bool>> {}; + public testing::WithParamInterface<bool> {}; TEST_P(PrivacySandboxPrivacyGuideShouldShowAdTopicsTest, ShownAccordingToConsentCountryAndFeature) { - auto [is_feature_on, is_consent_country, result] = GetParam(); - - feature_list()->Reset(); - if (is_feature_on) { - feature_list()->InitAndEnableFeature( - privacy_sandbox::kPrivacySandboxAdTopicsContentParity); - } else { - feature_list()->InitAndDisableFeature( - privacy_sandbox::kPrivacySandboxAdTopicsContentParity); - } + bool is_consent_country = GetParam(); ON_CALL(*mock_privacy_sandbox_countries(), IsConsentCountry()) .WillByDefault(testing::Return(is_consent_country)); @@ -555,15 +545,13 @@ bool should_show_card = privacy_sandbox_service() ->PrivacySandboxPrivacyGuideShouldShowAdTopicsCard(); - ASSERT_EQ(should_show_card, result); + // The expected result is identical to the consent country status. + ASSERT_EQ(should_show_card, is_consent_country); } INSTANTIATE_TEST_SUITE_P(PrivacySandboxPrivacyGuideShouldShowAdTopicsTest, PrivacySandboxPrivacyGuideShouldShowAdTopicsTest, - testing::Values(std::tuple(true, true, true), - std::tuple(true, false, false), - std::tuple(false, true, false), - std::tuple(false, false, false))); + testing::Bool()); class PrivacySandboxShouldUsePrivacyPolicyChinaDomain : public PrivacySandboxServiceTest {};
diff --git a/chrome/browser/profiles/BUILD.gn b/chrome/browser/profiles/BUILD.gn index b1d5797e..d499a40 100644 --- a/chrome/browser/profiles/BUILD.gn +++ b/chrome/browser/profiles/BUILD.gn
@@ -682,6 +682,7 @@ "//chrome/browser/performance_manager", "//chrome/browser/permissions", "//chrome/browser/persisted_state_db", + "//chrome/browser/personal_context", "//chrome/browser/plus_addresses", "//chrome/browser/predictors", "//chrome/browser/prefs", @@ -775,6 +776,7 @@ "//chrome/browser/commerce/merchant_viewer:merchant_viewer_data_manager", "//chrome/browser/feed", "//chrome/browser/tab", + "//chrome/browser/ui/android", "//components/commerce/core:merchant_signal_db_proto", ] if (enable_webui_ntp) {
diff --git a/chrome/browser/profiles/avatar_menu.cc b/chrome/browser/profiles/avatar_menu.cc index d19a6071..c30dfc7 100644 --- a/chrome/browser/profiles/avatar_menu.cc +++ b/chrome/browser/profiles/avatar_menu.cc
@@ -20,7 +20,6 @@ #include "chrome/browser/profiles/profiles_state.h" #include "chrome/browser/supervised_user/supervised_user_service_factory.h" #include "chrome/browser/ui/browser.h" -#include "chrome/browser/ui/browser_finder.h" #include "chrome/browser/ui/browser_window/public/browser_window_interface.h" #include "chrome/browser/ui/chrome_pages.h" #include "chrome/browser/ui/dialogs/browser_dialogs.h"
diff --git a/chrome/browser/profiles/chrome_browser_main_extra_parts_profiles.cc b/chrome/browser/profiles/chrome_browser_main_extra_parts_profiles.cc index 4cb8a994..44dce3b 100644 --- a/chrome/browser/profiles/chrome_browser_main_extra_parts_profiles.cc +++ b/chrome/browser/profiles/chrome_browser_main_extra_parts_profiles.cc
@@ -178,6 +178,7 @@ #include "chrome/browser/permissions/prediction_service/prediction_model_handler_provider_factory.h" #include "chrome/browser/permissions/prediction_service/prediction_service_factory.h" #include "chrome/browser/persisted_state_db/session_proto_db_factory.h" +#include "chrome/browser/personal_context/personal_context_service_factory.h" #include "chrome/browser/plugins/plugin_prefs_factory.h" #include "chrome/browser/plus_addresses/plus_address_service_factory.h" #include "chrome/browser/plus_addresses/plus_address_setting_service_factory.h" @@ -1240,6 +1241,9 @@ PermissionDecisionAutoBlockerFactory::GetInstance(); #if !BUILDFLAG(IS_ANDROID) PersistentRendererPrefsManagerFactory::GetInstance(); +#endif + PersonalContextServiceFactory::GetInstance(); +#if !BUILDFLAG(IS_ANDROID) PinnedTabServiceFactory::GetInstance(); PinnedToolbarActionsModelFactory::GetInstance(); #endif
diff --git a/chrome/browser/profiles/profile_activity_metrics_recorder_unittest.cc b/chrome/browser/profiles/profile_activity_metrics_recorder_unittest.cc index ac52ba9..5465bfb 100644 --- a/chrome/browser/profiles/profile_activity_metrics_recorder_unittest.cc +++ b/chrome/browser/profiles/profile_activity_metrics_recorder_unittest.cc
@@ -8,26 +8,18 @@ #include <vector> #include "base/command_line.h" -#include "base/functional/bind.h" #include "base/metrics/user_metrics.h" #include "base/test/metrics/histogram_tester.h" #include "base/test/task_environment.h" #include "base/time/time.h" -#include "chrome/browser/browser_process.h" -#include "chrome/browser/global_features.h" -#include "chrome/browser/global_features_test_support.h" #include "chrome/browser/metrics/desktop_session_duration/desktop_session_duration_tracker.h" -#include "chrome/browser/ui/browser_window/public/global_browser_collection.h" -#include "chrome/browser/ui/browser_window/test/fake_global_browser_collection.h" -#include "chrome/browser/ui/browser_window/test/mock_browser_window_interface.h" -#include "chrome/browser/ui/tabs/tab_strip_model.h" -#include "chrome/browser/ui/tabs/test_tab_strip_model_delegate.h" +#include "chrome/browser/ui/browser.h" #include "chrome/common/chrome_switches.h" +#include "chrome/test/base/test_browser_window.h" #include "chrome/test/base/testing_browser_process.h" #include "chrome/test/base/testing_profile.h" #include "chrome/test/base/testing_profile_manager.h" #include "content/public/test/browser_task_environment.h" -#include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" namespace { @@ -37,37 +29,16 @@ } // namespace -class GlobalFeaturesFake : public GlobalFeatures { - public: - GlobalFeaturesFake() = default; - - protected: - std::unique_ptr<GlobalBrowserCollection> CreateGlobalBrowserCollection() - override { - return std::make_unique<FakeGlobalBrowserCollection>(); - } -}; - -std::unique_ptr<GlobalFeatures> CreateGlobalFeatures() { - return std::make_unique<GlobalFeaturesFake>(); -} - class ProfileActivityMetricsRecorderTest : public testing::Test { public: ProfileActivityMetricsRecorderTest() : task_environment_(base::test::TaskEnvironment::TimeSource::MOCK_TIME), - profile_manager_(TestingBrowserProcess::GetGlobal()), - scoped_features_override_(base::BindRepeating(&CreateGlobalFeatures)) { + profile_manager_(TestingBrowserProcess::GetGlobal()) { base::CommandLine::ForCurrentProcess()->AppendSwitch(switches::kNoFirstRun); base::SetRecordActionTaskRunner( task_environment_.GetMainThreadTaskRunner()); } - FakeGlobalBrowserCollection* GetFakeCollection() { - return static_cast<FakeGlobalBrowserCollection*>( - g_browser_process->GetFeatures()->global_browser_collection()); - } - ProfileActivityMetricsRecorderTest( const ProfileActivityMetricsRecorderTest&) = delete; ProfileActivityMetricsRecorderTest& operator=( @@ -84,44 +55,19 @@ } void TearDown() override { - // Clean up mock browsers from the global collection before they are - // destroyed - for (auto& browser : mock_browsers_) { - GetFakeCollection()->SimulateBrowserClosed(browser.get()); - } - mock_browsers_.clear(); - // Clean up the global state, so it can be correctly initialized for the // next test case. ProfileActivityMetricsRecorder::CleanupForTesting(); metrics::DesktopSessionDurationTracker::CleanupForTesting(); + } void ActivateBrowser(Profile* profile) { - auto mock_browser = - std::make_unique<testing::NiceMock<MockBrowserWindowInterface>>(); - ON_CALL(*mock_browser, GetProfile()) - .WillByDefault(testing::Return(profile)); + Browser::CreateParams browser_params(profile, false); + browsers_.push_back(CreateBrowserWithTestWindowForParams(browser_params)); - // Create a dummy TabStripModel and configure the mock to return it. - auto tab_strip_delegate = std::make_unique<TestTabStripModelDelegate>(); - auto tab_strip_model = - std::make_unique<TabStripModel>(tab_strip_delegate.get(), profile); - ON_CALL(*mock_browser, GetTabStripModel()) - .WillByDefault(testing::Return(tab_strip_model.get())); - - // Register the mock browser creation before activating it - GetFakeCollection()->SimulateBrowserCreated(mock_browser.get()); - - // Trigger the recorder by notifying the global collection's observer - // interface - GetFakeCollection()->SimulateBrowserActivated(mock_browser.get()); - - // Keep the delegates and models alive for the lifetime of the mock browser. - mock_tab_strip_delegates_.push_back(std::move(tab_strip_delegate)); - mock_tab_strip_models_.push_back(std::move(tab_strip_model)); - mock_browsers_.push_back(std::move(mock_browser)); - + // This triggers the recorder to post a task, wait until that's done. + browsers_.back().get()->DidBecomeActive(); task_environment_.RunUntilIdle(); } @@ -159,13 +105,8 @@ TestingProfileManager profile_manager_; base::HistogramTester histogram_tester_; - test::ScopedGlobalFeaturesOverride scoped_features_override_; - std::vector<std::unique_ptr<TestTabStripModelDelegate>> - mock_tab_strip_delegates_; - std::vector<std::unique_ptr<TabStripModel>> mock_tab_strip_models_; - std::vector<std::unique_ptr<testing::NiceMock<MockBrowserWindowInterface>>> - mock_browsers_; + std::vector<std::unique_ptr<Browser>> browsers_; }; TEST_F(ProfileActivityMetricsRecorderTest, GuestProfile) {
diff --git a/chrome/browser/profiles/profile_browsertest.cc b/chrome/browser/profiles/profile_browsertest.cc index 0e5e131..68a0f510 100644 --- a/chrome/browser/profiles/profile_browsertest.cc +++ b/chrome/browser/profiles/profile_browsertest.cc
@@ -50,6 +50,7 @@ #include "chrome/browser/ui/browser.h" #include "chrome/browser/ui/browser_finder.h" #include "chrome/browser/ui/tabs/tab_strip_model.h" +#include "chrome/common/buildflags.h" #include "chrome/common/chrome_constants.h" #include "chrome/common/chrome_paths.h" #include "chrome/common/pref_names.h"
diff --git a/chrome/browser/profiles/profile_manager_browsertest.cc b/chrome/browser/profiles/profile_manager_browsertest.cc index 87209db1..0f87b304 100644 --- a/chrome/browser/profiles/profile_manager_browsertest.cc +++ b/chrome/browser/profiles/profile_manager_browsertest.cc
@@ -36,7 +36,6 @@ #include "chrome/browser/profiles/profile_window.h" #include "chrome/browser/profiles/profiles_state.h" #include "chrome/browser/signin/identity_test_environment_profile_adaptor.h" -#include "chrome/browser/ui/browser_finder.h" #include "chrome/browser/ui/browser_window.h" #include "chrome/browser/ui/browser_window/public/browser_window_interface.h" #include "chrome/browser/ui/browser_window/public/browser_window_interface_iterator.h"
diff --git a/chrome/browser/quick_delete/android/javatests/src/org/chromium/chrome/browser/quick_delete/QuickDeleteBridgeTest.java b/chrome/browser/quick_delete/android/javatests/src/org/chromium/chrome/browser/quick_delete/QuickDeleteBridgeTest.java index 04f241b23..a07aa32 100644 --- a/chrome/browser/quick_delete/android/javatests/src/org/chromium/chrome/browser/quick_delete/QuickDeleteBridgeTest.java +++ b/chrome/browser/quick_delete/android/javatests/src/org/chromium/chrome/browser/quick_delete/QuickDeleteBridgeTest.java
@@ -18,6 +18,7 @@ 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.DisableIf; import org.chromium.chrome.browser.browsing_data.BrowsingDataBridge; import org.chromium.chrome.browser.browsing_data.BrowsingDataType; import org.chromium.chrome.browser.browsing_data.TimePeriod; @@ -30,6 +31,7 @@ import org.chromium.chrome.test.transit.page.WebPageStation; import org.chromium.content_public.common.ContentSwitches; import org.chromium.net.test.EmbeddedTestServer; +import org.chromium.ui.base.DeviceFormFactor; import java.util.List; import java.util.concurrent.ExecutionException; @@ -119,6 +121,7 @@ @Test @MediumTest + @DisableIf.Device(DeviceFormFactor.DESKTOP_FREEFORM) // crbug.com/444482498 public void testRestartCounterForTimePeriod_WhenVisitsExistInRange() throws TimeoutException { visitUrls();
diff --git a/chrome/browser/quick_delete/android/javatests/src/org/chromium/chrome/browser/quick_delete/QuickDeleteControllerTest.java b/chrome/browser/quick_delete/android/javatests/src/org/chromium/chrome/browser/quick_delete/QuickDeleteControllerTest.java index c68b0383e..0d302ea 100644 --- a/chrome/browser/quick_delete/android/javatests/src/org/chromium/chrome/browser/quick_delete/QuickDeleteControllerTest.java +++ b/chrome/browser/quick_delete/android/javatests/src/org/chromium/chrome/browser/quick_delete/QuickDeleteControllerTest.java
@@ -4,6 +4,9 @@ package org.chromium.chrome.browser.quick_delete; +import org.chromium.base.test.util.DisableIf; +import org.chromium.ui.base.DeviceFormFactor; + import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; @@ -65,6 +68,7 @@ @RunWith(ChromeJUnit4ClassRunner.class) @CommandLineFlags.Add({ChromeSwitches.DISABLE_FIRST_RUN_EXPERIENCE}) @Batch(Batch.PER_CLASS) +@DisableIf.Device(DeviceFormFactor.DESKTOP_FREEFORM) // crbug.com/444482498 public class QuickDeleteControllerTest { private static final long FIFTEEN_MINUTES_IN_MS = TimeUnit.MINUTES.toMillis(15);
diff --git a/chrome/browser/quick_delete/android/javatests/src/org/chromium/chrome/browser/quick_delete/QuickDeleteDialogDelegateTest.java b/chrome/browser/quick_delete/android/javatests/src/org/chromium/chrome/browser/quick_delete/QuickDeleteDialogDelegateTest.java index 50b23036..abb86902 100644 --- a/chrome/browser/quick_delete/android/javatests/src/org/chromium/chrome/browser/quick_delete/QuickDeleteDialogDelegateTest.java +++ b/chrome/browser/quick_delete/android/javatests/src/org/chromium/chrome/browser/quick_delete/QuickDeleteDialogDelegateTest.java
@@ -4,6 +4,9 @@ package org.chromium.chrome.browser.quick_delete; +import org.chromium.base.test.util.DisableIf; +import org.chromium.ui.base.DeviceFormFactor; + import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; import static org.mockito.Mockito.when; @@ -62,6 +65,7 @@ @RunWith(ChromeJUnit4ClassRunner.class) @CommandLineFlags.Add({ChromeSwitches.DISABLE_FIRST_RUN_EXPERIENCE}) @Batch(Batch.PER_CLASS) +@DisableIf.Device(DeviceFormFactor.DESKTOP_FREEFORM) // crbug.com/444482498 public class QuickDeleteDialogDelegateTest { @Rule public AutoResetCtaTransitTestRule mCtaTestRule =
diff --git a/chrome/browser/readaloud/android/java/src/org/chromium/chrome/browser/readaloud/player/expanded/ExpandedPlayerSheetContentUnitTest.java b/chrome/browser/readaloud/android/java/src/org/chromium/chrome/browser/readaloud/player/expanded/ExpandedPlayerSheetContentUnitTest.java index 3774063e..e4e7cf52 100644 --- a/chrome/browser/readaloud/android/java/src/org/chromium/chrome/browser/readaloud/player/expanded/ExpandedPlayerSheetContentUnitTest.java +++ b/chrome/browser/readaloud/android/java/src/org/chromium/chrome/browser/readaloud/player/expanded/ExpandedPlayerSheetContentUnitTest.java
@@ -320,7 +320,7 @@ @Test public void testLoadingTextIsSetCorrectly() { - TextView loadingText = (TextView) mContentView.findViewById(R.id.readaloud_loading_text); + TextView loadingText = mContentView.findViewById(R.id.readaloud_loading_text); mContent.setRequestedPlaybackMode(PlaybackMode.OVERVIEW); assertEquals(
diff --git a/chrome/browser/readaloud/android/java/src/org/chromium/chrome/browser/readaloud/player/expanded/MenuItem.java b/chrome/browser/readaloud/android/java/src/org/chromium/chrome/browser/readaloud/player/expanded/MenuItem.java index c26f5a0..799f870 100644 --- a/chrome/browser/readaloud/android/java/src/org/chromium/chrome/browser/readaloud/player/expanded/MenuItem.java +++ b/chrome/browser/readaloud/android/java/src/org/chromium/chrome/browser/readaloud/player/expanded/MenuItem.java
@@ -289,11 +289,11 @@ } private SwitchCompat getToggleSwitch() { - return (SwitchCompat) findViewById(R.id.toggle_switch); + return findViewById(R.id.toggle_switch); } private RadioButton getRadioButton() { - return (RadioButton) findViewById(R.id.readaloud_radio_button); + return findViewById(R.id.readaloud_radio_button); } @VisibleForTesting(otherwise = VisibleForTesting.PACKAGE_PRIVATE)
diff --git a/chrome/browser/readaloud/android/java/src/org/chromium/chrome/browser/readaloud/player/expanded/MenuUnitTest.java b/chrome/browser/readaloud/android/java/src/org/chromium/chrome/browser/readaloud/player/expanded/MenuUnitTest.java index 67d1a0b6..3c6a429 100644 --- a/chrome/browser/readaloud/android/java/src/org/chromium/chrome/browser/readaloud/player/expanded/MenuUnitTest.java +++ b/chrome/browser/readaloud/android/java/src/org/chromium/chrome/browser/readaloud/player/expanded/MenuUnitTest.java
@@ -80,7 +80,7 @@ // addItem and setValue MenuItem item = mMenu.addItem(1, 0, "Toggle action", /* header= */ null, Action.TOGGLE); item.setValue(true); - SwitchCompat toggle = (SwitchCompat) item.findViewById(R.id.toggle_switch); + SwitchCompat toggle = item.findViewById(R.id.toggle_switch); assertTrue(toggle.isChecked()); item.setValue(false); assertFalse(toggle.isChecked()); @@ -98,7 +98,7 @@ // addItem and setValue MenuItem item = mMenu.addItem(1, 0, "Radio action", /* header= */ null, Action.RADIO); item.setValue(true); - RadioButton radioButton = (RadioButton) item.findViewById(R.id.readaloud_radio_button); + RadioButton radioButton = item.findViewById(R.id.readaloud_radio_button); assertTrue(radioButton.isChecked()); item.setValue(false); assertFalse(radioButton.isChecked()); @@ -146,7 +146,7 @@ mMenu.setPlayButtonClickHandler(mHandler); MenuItem item = mMenu.addItem(1, 0, "test item", /* header= */ null, Action.NONE); item.addPlayButton(); - ImageView playButton = (ImageView) item.findViewById(R.id.play_button); + ImageView playButton = item.findViewById(R.id.play_button); assertEquals(View.VISIBLE, playButton.getVisibility()); assertTrue(playButton.performClick()); @@ -182,7 +182,7 @@ (LinearLayout) mActivity.getLayoutInflater().inflate(R.layout.readaloud_menu_item, null); item.getLayoutSupplier().set(layout); - SwitchCompat button = (SwitchCompat) item.findViewById(R.id.toggle_switch); + SwitchCompat button = item.findViewById(R.id.toggle_switch); assertNotNull(button); // tests if onInitializeAccessibilityEvent is properly setting the event's checked state to
diff --git a/chrome/browser/readaloud/android/java/src/org/chromium/chrome/browser/readaloud/player/expanded/VoiceMenuUnitTest.java b/chrome/browser/readaloud/android/java/src/org/chromium/chrome/browser/readaloud/player/expanded/VoiceMenuUnitTest.java index 11d001c..51d79c8 100644 --- a/chrome/browser/readaloud/android/java/src/org/chromium/chrome/browser/readaloud/player/expanded/VoiceMenuUnitTest.java +++ b/chrome/browser/readaloud/android/java/src/org/chromium/chrome/browser/readaloud/player/expanded/VoiceMenuUnitTest.java
@@ -179,10 +179,9 @@ } private static RadioButton getRadioButton(MenuItem item) { - return (RadioButton) item.findViewById(R.id.readaloud_radio_button); + return item.findViewById(R.id.readaloud_radio_button); } - @Test public void testEmptyVoiceList() { mModel =
diff --git a/chrome/browser/recent_tabs/internal/android/java/src/org/chromium/chrome/browser/recent_tabs/CrossDeviceListCoordinator.java b/chrome/browser/recent_tabs/internal/android/java/src/org/chromium/chrome/browser/recent_tabs/CrossDeviceListCoordinator.java index f096206..432f743e 100644 --- a/chrome/browser/recent_tabs/internal/android/java/src/org/chromium/chrome/browser/recent_tabs/CrossDeviceListCoordinator.java +++ b/chrome/browser/recent_tabs/internal/android/java/src/org/chromium/chrome/browser/recent_tabs/CrossDeviceListCoordinator.java
@@ -40,7 +40,7 @@ (CrossDevicePaneView) LayoutInflater.from(context) .inflate(R.layout.cross_device_pane, /* root= */ null); - ListView listView = (ListView) mView.findViewById(R.id.cross_device_list_view); + ListView listView = mView.findViewById(R.id.cross_device_list_view); listView.setAdapter(adapter); PropertyModel model = CrossDeviceListProperties.create();
diff --git a/chrome/browser/renderer_context_menu/render_view_context_menu.cc b/chrome/browser/renderer_context_menu/render_view_context_menu.cc index 2023197..e353113 100644 --- a/chrome/browser/renderer_context_menu/render_view_context_menu.cc +++ b/chrome/browser/renderer_context_menu/render_view_context_menu.cc
@@ -3693,9 +3693,10 @@ // so drop fullscreen when it is shown. https://crbug.com/40054574 // TODO(avi): Do we need to attach the fullscreen block to the emoji // panel? - source_web_contents_ - ->ForSecurityDropFullscreen(/*display_id=*/display::kInvalidDisplayId) - .RunAndReset(); + if (!source_web_contents_->ForSecurityDropFullscreen( + /*display_id=*/display::kInvalidDisplayId)) { + return; + } Browser* browser = GetBrowser(); if (browser) {
diff --git a/chrome/browser/renderer_context_menu/render_view_context_menu_interactive_uitest.cc b/chrome/browser/renderer_context_menu/render_view_context_menu_interactive_uitest.cc index 5dcc7cbd..6a3dd9bf 100644 --- a/chrome/browser/renderer_context_menu/render_view_context_menu_interactive_uitest.cc +++ b/chrome/browser/renderer_context_menu/render_view_context_menu_interactive_uitest.cc
@@ -625,12 +625,17 @@ }; class GlicInteractiveContextMenuTest - : public GlicInteractiveContextMenuTestBase { + : public GlicInteractiveContextMenuTestBase, + public ::testing::WithParamInterface<bool> { public: GlicInteractiveContextMenuTest() { + std::vector<base::test::FeatureRef> enabled_features = { + features::kGlic, features::kGlicShareImage}; + if (UseInvokeFlow()) { + enabled_features.push_back(features::kGlicShareImageViaInvoke); + } scoped_feature_list_.InitWithFeatures( - /*enabled_features=*/{features::kGlic, features::kGlicShareImage, - features::kGlicMultitabUnderlines}, + enabled_features, /*disabled_features=*/{features::kGlicWarming, blink::features::kSvgFallBackToContainerSize}); // Ensure that we open the FRE. @@ -638,11 +643,13 @@ } ~GlicInteractiveContextMenuTest() override = default; + bool UseInvokeFlow() const { return GetParam(); } + private: base::test::ScopedFeatureList scoped_feature_list_; }; -IN_PROC_BROWSER_TEST_F(GlicInteractiveContextMenuTest, GlicShareImage) { +IN_PROC_BROWSER_TEST_P(GlicInteractiveContextMenuTest, GlicShareImage) { DEFINE_LOCAL_ELEMENT_IDENTIFIER_VALUE(kActiveTab); const GURL url = embedded_test_server()->GetURL(kPageWithImage); @@ -656,10 +663,12 @@ ClickMouse(ui_controls::RIGHT), SelectMenuItem(RenderViewContextMenu::kGlicShareImageMenuItem)), PollForAndCompleteOnboarding(), PollForAndInstrumentGlic(), - WaitForAdditionalContext(), CheckHistograms()); + WaitForAdditionalContext(), + WaitForShareResult(glic::ShareImageResult::kSentImageToClient), + CheckHistograms()); } -IN_PROC_BROWSER_TEST_F(GlicInteractiveContextMenuTest, CreateNewInstance) { +IN_PROC_BROWSER_TEST_P(GlicInteractiveContextMenuTest, CreateNewInstance) { DEFINE_LOCAL_ELEMENT_IDENTIFIER_VALUE(kActiveTab); const GURL url = embedded_test_server()->GetURL(kPageWithImage); @@ -678,10 +687,12 @@ ClickMouse(ui_controls::RIGHT), SelectMenuItem(RenderViewContextMenu::kGlicShareImageMenuItem)), PollForNewGlicInstance(), PollForAndInstrumentGlic(), - WaitForAdditionalContext(), CheckCachedInstance(), CheckHistograms()); + WaitForAdditionalContext(), + WaitForShareResult(glic::ShareImageResult::kSentImageToClient), + CheckCachedInstance(), CheckHistograms()); } -IN_PROC_BROWSER_TEST_F(GlicInteractiveContextMenuTest, +IN_PROC_BROWSER_TEST_P(GlicInteractiveContextMenuTest, CreateNewInstanceDetached) { DEFINE_LOCAL_ELEMENT_IDENTIFIER_VALUE(kActiveTab); @@ -706,10 +717,12 @@ ClickMouse(ui_controls::RIGHT), SelectMenuItem(RenderViewContextMenu::kGlicShareImageMenuItem)), PollForNewGlicInstance(), PollForAndInstrumentGlic(), - WaitForAdditionalContext(), CheckCachedInstance(), CheckHistograms()); + WaitForAdditionalContext(), + WaitForShareResult(glic::ShareImageResult::kSentImageToClient), + CheckCachedInstance(), CheckHistograms()); } -IN_PROC_BROWSER_TEST_F(GlicInteractiveContextMenuTest, +IN_PROC_BROWSER_TEST_P(GlicInteractiveContextMenuTest, GlicShareImageFailsOnNoImage) { DEFINE_LOCAL_ELEMENT_IDENTIFIER_VALUE(kActiveTab); @@ -725,25 +738,37 @@ WaitForShareResult(glic::ShareImageResult::kFailedNoImage)); } +INSTANTIATE_TEST_SUITE_P(Invoke, + GlicInteractiveContextMenuTest, + // This parameter toggles invoke mode. + testing::Bool()); + class GlicTrustFirstOnboardingContextMenuTest - : public GlicInteractiveContextMenuTestBase { + : public GlicInteractiveContextMenuTestBase, + public ::testing::WithParamInterface<bool> { public: GlicTrustFirstOnboardingContextMenuTest() { - scoped_feature_list_.InitWithFeaturesAndParameters( - { - {features::kGlic, {}}, - {features::kGlicShareImage, {}}, - }, - {features::kGlicWarming, blink::features::kSvgFallBackToContainerSize}); + std::vector<base::test::FeatureRef> enabled_features = { + features::kGlic, features::kGlicShareImage}; + if (UseInvokeFlow()) { + enabled_features.push_back(features::kGlicShareImageViaInvoke); + } + scoped_feature_list_.InitWithFeatures( + enabled_features, + /*disabled_features=*/{features::kGlicWarming, + blink::features::kSvgFallBackToContainerSize}); glic_test_environment().SetFreStatusForNewProfiles( glic::prefs::FreStatus::kNotStarted); } + ~GlicTrustFirstOnboardingContextMenuTest() override = default; + + bool UseInvokeFlow() const { return GetParam(); } private: base::test::ScopedFeatureList scoped_feature_list_; }; -IN_PROC_BROWSER_TEST_F(GlicTrustFirstOnboardingContextMenuTest, +IN_PROC_BROWSER_TEST_P(GlicTrustFirstOnboardingContextMenuTest, GlicShareImageArm2) { DEFINE_LOCAL_ELEMENT_IDENTIFIER_VALUE(kActiveTab); const GURL url = embedded_https_test_server().GetURL(kPageWithImage); @@ -763,6 +788,11 @@ PollForAndCompleteOnboarding(), WaitForAdditionalContext()); } +INSTANTIATE_TEST_SUITE_P(Invoke, + GlicTrustFirstOnboardingContextMenuTest, + // This parameter toggles invoke mode. + testing::Bool()); + #if BUILDFLAG(ENTERPRISE_CLOUD_CONTENT_ANALYSIS) // Test setup taken and adapted from IsClipboardPasteAllowedTest in @@ -906,7 +936,7 @@ bool content_analysis_dialog_shown_ = false; }; -IN_PROC_BROWSER_TEST_F(GlicInteractiveContextMenuPolicyTest, +IN_PROC_BROWSER_TEST_P(GlicInteractiveContextMenuPolicyTest, GlicShareImageFailsOnCopyDenied) { // Taken from DataProtectionClipboardBrowserTest in clipboard_browsertest.cc. data_controls::SetDataControls(browser()->profile()->GetPrefs(), {R"({ @@ -934,7 +964,7 @@ WaitForShareResult(glic::ShareImageResult::kFailedClipboardCopyPolicy)); } -IN_PROC_BROWSER_TEST_F(GlicInteractiveContextMenuPolicyTest, +IN_PROC_BROWSER_TEST_P(GlicInteractiveContextMenuPolicyTest, GlicShareImageFailsOnPasteDenied) { DEFINE_LOCAL_ELEMENT_IDENTIFIER_VALUE(kActiveTab); const GURL url = embedded_test_server()->GetURL(kPageWithImage); @@ -951,7 +981,7 @@ WaitForContentAnalysisDialog()); } -IN_PROC_BROWSER_TEST_F(GlicInteractiveContextMenuPolicyTest, +IN_PROC_BROWSER_TEST_P(GlicInteractiveContextMenuPolicyTest, GlicShareImageFailsOnPasteAllowed) { DEFINE_LOCAL_ELEMENT_IDENTIFIER_VALUE(kActiveTab); const GURL url = embedded_test_server()->GetURL(kPageWithAllowedImage); @@ -967,7 +997,7 @@ WaitForShareResult(glic::ShareImageResult::kSentImageToClient)); } -IN_PROC_BROWSER_TEST_F( +IN_PROC_BROWSER_TEST_P( GlicInteractiveContextMenuPolicyTest, GlicShareImageSucceedsOnNavigationAfterPastePolicyCheck) { DEFINE_LOCAL_ELEMENT_IDENTIFIER_VALUE(kActiveTab); @@ -995,7 +1025,7 @@ GetPastePolicyCallbackHook().Reset(); } -IN_PROC_BROWSER_TEST_F(GlicInteractiveContextMenuPolicyTest, +IN_PROC_BROWSER_TEST_P(GlicInteractiveContextMenuPolicyTest, GlicShareImageFailsWhenGuestURLBlocked) { // Check that our destination is the Guest URL. GURL guest_url = glic::GetGuestURL(); @@ -1019,6 +1049,11 @@ WaitForShareResult(glic::ShareImageResult::kFailedClipboardPastePolicy)); } +INSTANTIATE_TEST_SUITE_P(Invoke, + GlicInteractiveContextMenuPolicyTest, + // This parameter toggles invoke mode. + testing::Bool()); + #endif // BUILDFLAG(ENTERPRISE_CLOUD_CONTENT_ANALYSIS) } // namespace
diff --git a/chrome/browser/renderer_host/chrome_render_widget_host_view_mac_delegate.mm b/chrome/browser/renderer_host/chrome_render_widget_host_view_mac_delegate.mm index 29aa8df..7d67ba2 100644 --- a/chrome/browser/renderer_host/chrome_render_widget_host_view_mac_delegate.mm +++ b/chrome/browser/renderer_host/chrome_render_widget_host_view_mac_delegate.mm
@@ -16,7 +16,6 @@ #import "chrome/browser/renderer_host/chrome_render_widget_host_view_mac_history_swiper.h" #include "chrome/browser/ui/browser.h" #include "chrome/browser/ui/browser_commands.h" -#include "chrome/browser/ui/browser_finder.h" #include "chrome/browser/ui/tabs/inactive_window_mouse_event_controller.h" #include "chrome/browser/ui/tabs/public/tab_features.h" #include "chrome/common/url_constants.h"
diff --git a/chrome/browser/resource_coordinator/tab_lifecycle_unit.cc b/chrome/browser/resource_coordinator/tab_lifecycle_unit.cc index 648c391..f4c33684 100644 --- a/chrome/browser/resource_coordinator/tab_lifecycle_unit.cc +++ b/chrome/browser/resource_coordinator/tab_lifecycle_unit.cc
@@ -32,7 +32,6 @@ #include "chrome/browser/resource_coordinator/utils.h" #include "chrome/browser/tab_contents/form_interaction_tab_helper.h" #include "chrome/browser/ui/browser.h" -#include "chrome/browser/ui/browser_finder.h" #include "chrome/browser/ui/tabs/tab_strip_model.h" #include "chrome/browser/web_applications/web_app_tab_helper.h" #include "components/device_event_log/device_event_log.h"
diff --git a/chrome/browser/resources/BUILD.gn b/chrome/browser/resources/BUILD.gn index ff7a838..5b48cd059 100644 --- a/chrome/browser/resources/BUILD.gn +++ b/chrome/browser/resources/BUILD.gn
@@ -55,6 +55,7 @@ "data_sharing:resources", "downloads:resources", "drive_picker_host:resources", + "drive_picker_host/untrusted:resources", "feedback:resources", "gaia_auth_host:resources", "glic/experimental_opt_in:resources", @@ -90,6 +91,7 @@ "tab_group_home:resources", "tab_search:resources", "web_app_internals:resources", + "webnn_internals:resources", "webui_browser:resources", "webui_gallery:resources", "webui_toolbar:resources", @@ -97,7 +99,10 @@ "//components/browser_apis/tab_strip/resources", ] if (is_chrome_branded) { - public_deps += [ "media_router/cast_feedback:resources" ] + public_deps += [ + ":preinstalled_web_apps_resources", + "media_router/cast_feedback:resources", + ] } }
diff --git a/chrome/browser/resources/accessibility_annotator_info/accessibility_annotator_info.html.ts b/chrome/browser/resources/accessibility_annotator_info/accessibility_annotator_info.html.ts index 27d0ff0..ea04511 100644 --- a/chrome/browser/resources/accessibility_annotator_info/accessibility_annotator_info.html.ts +++ b/chrome/browser/resources/accessibility_annotator_info/accessibility_annotator_info.html.ts
@@ -19,27 +19,19 @@ <!-- TODO(crbug.com/488321731): Add illustration here. --> </div> </div> - <h1 class="title">$i18n{accessibilityAnnotatorInfoTitle}</h1> + <!-- TODO(crbug.com/500663691): Update strings below. --> + <h1 class="title">$i18n{privacyPageTitle}</h1> <div class="description"> <p> - $i18n{accessibilityAnnotatorInfoDescription} + $i18n{privacyPageTitle} </p> <div class="features-container"> <div class="feature-item"> <div class="feature-icon"> <img src="keyboard.svg" alt=""> </div> - <div class="feature-text" id="triggerCard"> - ${this.i18n('accessibilityAnnotatorInfoCard1') - .split('$1') - .map((text, i, arr) => html` - ${text}${ - i < arr.length - 1 ? - html`<span class="pill"> - ${this.i18n('accessibilityAnnotatorTriggerText')} - </span>` : - '' - }`)} + <div class="feature-text"> + <span class="pill">@@</span> $i18n{privacyPageTitle} </div> </div> <div class="feature-item"> @@ -47,22 +39,22 @@ <div class="g-icon"></div> </div> <div class="feature-text"> - $i18n{accessibilityAnnotatorInfoCard2} + $i18n{privacyPageTitle} </div> </div> </div> - <p class="footer-text" .innerHTML= - "${this.i18nAdvanced('accessibilityAnnotatorInfoLearnMore')}"> + <p class="footer-text"> + $i18n{privacyPageTitle} </p> </div> <div class="actions"> <cr-button id="manageSettings" class="tonal-button" @click="${this.onManageSettingsClick_}"> - $i18n{accessibilityAnnotatorInfoSecondaryButton} + $i18n{privacyPageTitle} </cr-button> <cr-button id="gotIt" class="action-button" @click="${this.onGotItClick_}"> - $i18n{accessibilityAnnotatorInfoPrimaryButton} + $i18n{privacyPageTitle} </cr-button> </div> </div>
diff --git a/chrome/browser/resources/accessibility_annotator_info/accessibility_annotator_info.ts b/chrome/browser/resources/accessibility_annotator_info/accessibility_annotator_info.ts index df6e556..2f9db2b 100644 --- a/chrome/browser/resources/accessibility_annotator_info/accessibility_annotator_info.ts +++ b/chrome/browser/resources/accessibility_annotator_info/accessibility_annotator_info.ts
@@ -3,19 +3,14 @@ // found in the LICENSE file. import 'chrome://resources/cr_elements/cr_button/cr_button.js'; -import '/strings.m.js'; -import {I18nMixinLit} from '//resources/cr_elements/i18n_mixin_lit.js'; import {CrLitElement} from '//resources/lit/v3_0/lit.rollup.js'; import {getCss} from './accessibility_annotator_info.css.js'; import {getHtml} from './accessibility_annotator_info.html.js'; import {AccessibilityAnnotatorInfoBrowserProxy} from './browser_proxy.js'; -const AccessibilityAnnotatorInfoElementBase = I18nMixinLit(CrLitElement); - -export class AccessibilityAnnotatorInfoElement extends - AccessibilityAnnotatorInfoElementBase { +export class AccessibilityAnnotatorInfoElement extends CrLitElement { static get is() { return 'accessibility-annotator-info'; }
diff --git a/chrome/browser/resources/chromeos/accessibility/chromevox/mv2/background/event/desktop_automation_handler.ts b/chrome/browser/resources/chromeos/accessibility/chromevox/mv2/background/event/desktop_automation_handler.ts index d9305de..9b818bc5 100644 --- a/chrome/browser/resources/chromeos/accessibility/chromevox/mv2/background/event/desktop_automation_handler.ts +++ b/chrome/browser/resources/chromeos/accessibility/chromevox/mv2/background/event/desktop_automation_handler.ts
@@ -758,7 +758,8 @@ 'PasswordGenerationPopupViewViews::GeneratedPasswordBox' || target.className === 'PopupRowView' || target.className === 'PopupRowWithButtonView' || - target.className === 'PopupRowContentView') { + target.className === 'PopupRowContentView' || + target.className === 'PopupBnplFootnoteView') { override = true; }
diff --git a/chrome/browser/resources/chromeos/accessibility/chromevox/mv2/common/settings_manager.ts b/chrome/browser/resources/chromeos/accessibility/chromevox/mv2/common/settings_manager.ts index 043f01b..d4c23c7 100644 --- a/chrome/browser/resources/chromeos/accessibility/chromevox/mv2/common/settings_manager.ts +++ b/chrome/browser/resources/chromeos/accessibility/chromevox/mv2/common/settings_manager.ts
@@ -235,7 +235,7 @@ 'documentTitleChanged', 'expandedChanged', 'focus', - 'focusContext', + 'focusContextDeprecated', 'hide', 'hitTestResult', 'hover',
diff --git a/chrome/browser/resources/chromeos/accessibility/chromevox/mv2/common/settings_manager_test.js b/chrome/browser/resources/chromeos/accessibility/chromevox/mv2/common/settings_manager_test.js index c2b95ade6..4a895ea 100644 --- a/chrome/browser/resources/chromeos/accessibility/chromevox/mv2/common/settings_manager_test.js +++ b/chrome/browser/resources/chromeos/accessibility/chromevox/mv2/common/settings_manager_test.js
@@ -222,7 +222,7 @@ documentTitleChanged: false, expandedChanged: false, focus: true, - focusContext: true, + focusContextDeprecated: true, hide: false, hitTestResult: true, hover: true,
diff --git a/chrome/browser/resources/chromeos/accessibility/chromevox/mv3/background/event/desktop_automation_handler.ts b/chrome/browser/resources/chromeos/accessibility/chromevox/mv3/background/event/desktop_automation_handler.ts index d9305de..9b818bc5 100644 --- a/chrome/browser/resources/chromeos/accessibility/chromevox/mv3/background/event/desktop_automation_handler.ts +++ b/chrome/browser/resources/chromeos/accessibility/chromevox/mv3/background/event/desktop_automation_handler.ts
@@ -758,7 +758,8 @@ 'PasswordGenerationPopupViewViews::GeneratedPasswordBox' || target.className === 'PopupRowView' || target.className === 'PopupRowWithButtonView' || - target.className === 'PopupRowContentView') { + target.className === 'PopupRowContentView' || + target.className === 'PopupBnplFootnoteView') { override = true; }
diff --git a/chrome/browser/resources/chromeos/accessibility/chromevox/mv3/common/settings_manager.ts b/chrome/browser/resources/chromeos/accessibility/chromevox/mv3/common/settings_manager.ts index 043f01b..d4c23c7 100644 --- a/chrome/browser/resources/chromeos/accessibility/chromevox/mv3/common/settings_manager.ts +++ b/chrome/browser/resources/chromeos/accessibility/chromevox/mv3/common/settings_manager.ts
@@ -235,7 +235,7 @@ 'documentTitleChanged', 'expandedChanged', 'focus', - 'focusContext', + 'focusContextDeprecated', 'hide', 'hitTestResult', 'hover',
diff --git a/chrome/browser/resources/chromeos/accessibility/chromevox/mv3/common/settings_manager_test.js b/chrome/browser/resources/chromeos/accessibility/chromevox/mv3/common/settings_manager_test.js index e4ee4a2..aea350b 100644 --- a/chrome/browser/resources/chromeos/accessibility/chromevox/mv3/common/settings_manager_test.js +++ b/chrome/browser/resources/chromeos/accessibility/chromevox/mv3/common/settings_manager_test.js
@@ -222,7 +222,7 @@ documentTitleChanged: false, expandedChanged: false, focus: true, - focusContext: true, + focusContextDeprecated: true, hide: false, hitTestResult: true, hover: true,
diff --git a/chrome/browser/resources/chromeos/accessibility/definitions/automation.d.ts b/chrome/browser/resources/chromeos/accessibility/definitions/automation.d.ts index 95f7111..c4fff93 100644 --- a/chrome/browser/resources/chromeos/accessibility/definitions/automation.d.ts +++ b/chrome/browser/resources/chromeos/accessibility/definitions/automation.d.ts
@@ -50,7 +50,7 @@ FOCUS = 'focus', FOCUS_AFTER_MENU_CLOSE = 'focusAfterMenuClose', FOCUS_CHANGED = 'focusChanged', - FOCUS_CONTEXT = 'focusContext', + FOCUS_CONTEXT_DEPRECATED = 'focusContextDeprecated', GRABBED_CHANGED = 'grabbedChanged', HASPOPUP_CHANGED = 'haspopupChanged', HIDE = 'hide',
diff --git a/chrome/browser/resources/contextual_tasks/app.css b/chrome/browser/resources/contextual_tasks/app.css index 775b032..fcff6130 100644 --- a/chrome/browser/resources/contextual_tasks/app.css +++ b/chrome/browser/resources/contextual_tasks/app.css
@@ -43,6 +43,7 @@ --color-composebox-type-ahead: rgba(117, 117, 117, 1); --color-composebox-type-ahead-chip: rgba(117, 117, 117, 1); --color-composebox-voice-lens-button: rgba(10, 10, 10, 1); + --color-composebox-voice-button-hover-background: rgba(31, 31, 31, 0.06); --color-composebox-lens-active-background: rgba(225, 227, 232, 1); --color-composebox-zero-state-suggestions-background-hovered: rgba(247, 248, 250, 1);
diff --git a/chrome/browser/resources/contextual_tasks/app.ts b/chrome/browser/resources/contextual_tasks/app.ts index 63a220b..58883f17 100644 --- a/chrome/browser/resources/contextual_tasks/app.ts +++ b/chrome/browser/resources/contextual_tasks/app.ts
@@ -343,6 +343,9 @@ // even if the load is aborted and the frame therefore never changes. private lastThreadFrameLoadStartEvent_: chrome.webviewTag.LoadStartEvent| LoadEvent|null = null; + // Tracks whether the frame is loading for the very first time to prevent + // double animations. + private isInitialFrameLoad_: boolean = true; private updateThemeFromUrl(url: URL) { const csParam = url.searchParams.get('cs'); @@ -824,7 +827,9 @@ if (isAiPage && isZeroState) { this.isZeroState_ = true; - this.playZeroStateAnimations_(); + if (!this.isInitialFrameLoad_) { + this.playZeroStateAnimations_(); + } } if (!wasZeroState && isZeroState) { @@ -853,6 +858,8 @@ this.isInBasicMode_ = true; } + this.isInitialFrameLoad_ = false; + if (this.onLoadStartFinishedCallbackForTesting_) { this.onLoadStartFinishedCallbackForTesting_(); }
diff --git a/chrome/browser/resources/contextual_tasks/composebox.css b/chrome/browser/resources/contextual_tasks/composebox.css index e3fad56..172c43a 100644 --- a/chrome/browser/resources/contextual_tasks/composebox.css +++ b/chrome/browser/resources/contextual_tasks/composebox.css
@@ -58,11 +58,22 @@ --cr-composebox-collapse-duration: 500ms; --cr-composebox-expand-curve: cubic-bezier(0.40, 1.38, 0.22, 0.96); --cr-composebox-expand-duration: 500ms; - --cr-composebox-file-thumbnail-animation-curve: cubic-bezier(0.38, 1.21, 0.22, 1); - --cr-composebox-file-thumbnail-animation-duration: var(--cr-composebox-expand-duration); + --cr-composebox-carousel-fade-curve: cubic-bezier(0.38, 1.21, 0.22, 1); + --cr-composebox-carousel-fade-delay: 65ms; + --cr-composebox-carousel-fade-duration: 450ms; --cr-composebox-tool-chip-animation: fade-in 100ms cubic-bezier(0, 0, 0, 1) forwards, slide-in 350ms cubic-bezier(0.42, 1.67, 0.21, 0.9) forwards; + --cr-composebox-file-thumbnail-entry-animation: + slide-in 350ms cubic-bezier(0.27, 1.06, 0.18, 1.00) forwards, + fade-in 150ms cubic-bezier(0.31, 0.94, 0.34, 1.00) forwards; + --cr-composebox-file-thumbnail-exit-animation: + slide-out 350ms cubic-bezier(0.27, 1.06, 0.18, 1.00) forwards, + fade-out 150ms cubic-bezier(0.31, 0.94, 0.34, 1.00) forwards, + shrink-out 500ms cubic-bezier(0.38, 1.21, 0.22, 1) 35ms forwards; + --cr-composebox-file-thumbnail-exit-animation-last: + slide-out 350ms cubic-bezier(0.27, 1.06, 0.18, 1.00) forwards, + fade-out 150ms cubic-bezier(0.31, 0.94, 0.34, 1.00) forwards; --cancel-button-inline-end: 12px; --context-carousel-inline-start-spacing: 12px; --context-carousel-top-position: 12px; @@ -74,16 +85,17 @@ --box-shadow-animation-duration: 650ms; } +:host([energy-effect-enabled_][is-zero-state]) { + --cr-composebox-carousel-fade-delay: 85ms; + --cr-composebox-carousel-fade-duration: 500ms; +} + @media (prefers-color-scheme: light) { :host([energy-effect-enabled_]) { --color-composebox-file-chip-background: rgba(240, 242, 245, 1); } } -:host([energy-effect-enabled_][is-zero-state]) { - --cr-composebox-file-thumbnail-animation-name: zero-state-thumbnail-fade; -} - /* Composebox default colors are for light mode on AIM results. */ #composeboxContainer { --color-composebox-scrim-background: var(--search-background-color); @@ -126,21 +138,6 @@ pointer-events: none; } -:host([is-zero-state][energy-effect-enabled_]) #composebox::part(composebox) { - will-change: opacity; - animation: zero-state-composebox-fade-in 50ms - var(--emphasized-accelerate-curve) both; -} - -@keyframes zero-state-composebox-fade-in { - 0% { - opacity: 0; - } - 100% { - opacity: 1; - } -} - :host(:not([input-enabled])) #composebox::part(input) { caret-color: transparent; } @@ -358,7 +355,7 @@ } :host([energy-effect-enabled_]) #composebox[show-file-carousel][expanding_]::part(carousel-container) { - transition: opacity var(--cr-composebox-expand-duration) var(--cr-composebox-expand-curve); + transition: opacity var(--cr-composebox-carousel-fade-duration) var(--cr-composebox-carousel-fade-curve) var(--cr-composebox-carousel-fade-delay); } #composebox[show-file-carousel]::part(carousel-container) { @@ -404,11 +401,6 @@ color: var(--color-composebox-font); } -#composebox::part(input), -#composebox::part(smart-compose) { - min-height: 48px; -} - #composebox::part(context-menu-and-tools) { min-height: var(--cr-icon-button-size); } @@ -650,6 +642,7 @@ #composebox::part(voice-icon) { --cr-icon-button-fill-color: var(--color-composebox-voice-lens-button); + --cr-icon-button-hover-background-color: var(--color-composebox-voice-button-hover-background); --cr-icon-button-icon-size: var(--icon-menu-icon-size); --cr-icon-button-size: var(--icon-menu-button-size); bottom: var(--composebox-icon-menu-bottom);
diff --git a/chrome/browser/resources/contextual_tasks/composebox.ts b/chrome/browser/resources/contextual_tasks/composebox.ts index 5bd45976..94d1280 100644 --- a/chrome/browser/resources/contextual_tasks/composebox.ts +++ b/chrome/browser/resources/contextual_tasks/composebox.ts
@@ -47,7 +47,7 @@ } chrome.histograms.recordEnumerationValue( - 'ContextualTasks.VoiceSearch.State', voiceSearchState, + 'ContextualTasks.VoiceSearch.StateV2', voiceSearchState, VoiceSearchState.MAX_VALUE + 1); }
diff --git a/chrome/browser/resources/drive_picker_host/app.css b/chrome/browser/resources/drive_picker_host/app.css index 8c40b44a..b896985 100644 --- a/chrome/browser/resources/drive_picker_host/app.css +++ b/chrome/browser/resources/drive_picker_host/app.css
@@ -7,5 +7,19 @@ * #css_wrapper_metadata_end */ :host { + display: flex; + align-items: center; + justify-content: center; + width: 100%; + height: 100%; + background-color: transparent; +} + +iframe { display: block; + width: 100%; + height: 100%; + border: none; + overflow: hidden; + background-color: transparent; }
diff --git a/chrome/browser/resources/drive_picker_host/app.html.ts b/chrome/browser/resources/drive_picker_host/app.html.ts index d69e239..11eddb8 100644 --- a/chrome/browser/resources/drive_picker_host/app.html.ts +++ b/chrome/browser/resources/drive_picker_host/app.html.ts
@@ -8,5 +8,6 @@ export function getHtml(this: DrivePickerHostAppElement) { return html`<!--_html_template_start_--> +<iframe src="chrome-untrusted://drive-picker-host/"></iframe> <!--_html_template_end_-->`; }
diff --git a/chrome/browser/resources/drive_picker_host/drive_picker_host.html b/chrome/browser/resources/drive_picker_host/drive_picker_host.html index 5872042..e97291f 100644 --- a/chrome/browser/resources/drive_picker_host/drive_picker_host.html +++ b/chrome/browser/resources/drive_picker_host/drive_picker_host.html
@@ -2,6 +2,16 @@ <html dir="$i18n{textdirection}" lang="$i18n{language}"> <head> <meta charset="utf-8"> + <style> + html, body { + background-color: transparent; + margin: 0; + padding: 0; + width: 100%; + height: 100%; + overflow: hidden; + } + </style> </head> <body> <script type="module" src="app.js"></script>
diff --git a/chrome/browser/resources/drive_picker_host/untrusted/BUILD.gn b/chrome/browser/resources/drive_picker_host/untrusted/BUILD.gn index e8920ce..b4423b9a4 100644 --- a/chrome/browser/resources/drive_picker_host/untrusted/BUILD.gn +++ b/chrome/browser/resources/drive_picker_host/untrusted/BUILD.gn
@@ -13,6 +13,7 @@ "app.ts", "app.html.ts", "browser_proxy.ts", + "drive_picker_api_proxy.ts", ] css_files = [ "app.css" ] ts_composite = true
diff --git a/chrome/browser/resources/drive_picker_host/untrusted/app.css b/chrome/browser/resources/drive_picker_host/untrusted/app.css index 7460a953..259c39b 100644 --- a/chrome/browser/resources/drive_picker_host/untrusted/app.css +++ b/chrome/browser/resources/drive_picker_host/untrusted/app.css
@@ -8,5 +8,17 @@ * #css_wrapper_metadata_end */ :host { + display: flex; + align-items: center; + justify-content: center; + width: 100%; + height: 100%; + background-color: transparent; +} + +drive-picker-host-untrusted-app { display: block; + width: 100%; + height: 100%; + overflow: hidden; }
diff --git a/chrome/browser/resources/drive_picker_host/untrusted/app.ts b/chrome/browser/resources/drive_picker_host/untrusted/app.ts index 8d35cbc..675fcdd 100644 --- a/chrome/browser/resources/drive_picker_host/untrusted/app.ts +++ b/chrome/browser/resources/drive_picker_host/untrusted/app.ts
@@ -3,12 +3,13 @@ // found in the LICENSE file. import {CrLitElement} from '//resources/lit/v3_0/lit.rollup.js'; +import {assert} from '//resources/js/assert.js'; import {getCss} from './app.css.js'; import {getHtml} from './app.html.js'; import {BrowserProxyImpl} from './browser_proxy.js'; -import {PageCallbackRouter} from './drive_picker_host_untrusted.mojom-webui.js'; -import type {DrivePickerUntrustedHostHandlerRemote} from './drive_picker_host_untrusted.mojom-webui.js'; +import {DrivePickerApiProxyImpl} from './drive_picker_api_proxy.js'; +import type {GooglePickerResponse} from './drive_picker_api_proxy.js'; import type {DrivePickerResultHandlerRemote} from './drive_picker_result_handler.mojom-webui.js'; export class DrivePickerHostUntrustedAppElement extends CrLitElement { @@ -20,26 +21,51 @@ return getCss(); } - private callbackRouter_: PageCallbackRouter = new PageCallbackRouter(); - private handler_: DrivePickerUntrustedHostHandlerRemote = - BrowserProxyImpl.getInstance().handler; + private browserProxy_ = BrowserProxyImpl.getInstance(); + private drivePickerApiProxy_ = DrivePickerApiProxyImpl.getInstance(); + private resultHandler_: DrivePickerResultHandlerRemote|null = null; + private listenerIds_: number[] = []; override render() { return getHtml.bind(this)(); } - override firstUpdated() { - this.handler_.bindPage(this.callbackRouter_.$.bindNewPipeAndPassRemote()); - - this.callbackRouter_.showDrivePicker.addListener( - (resultHandler: DrivePickerResultHandlerRemote) => { - this.showDrivePicker(resultHandler); - }); + override connectedCallback() { + super.connectedCallback(); + this.listenerIds_ = [ + this.browserProxy_.callbackRouter.showDrivePicker.addListener( + this.showDrivePicker_.bind(this)), + ]; } - showDrivePicker(_resultHandler: DrivePickerResultHandlerRemote) { - // TODO: crbug.com/497937568 - Implement the Drive Picker UI and relay - // results to the resultHandler. + override disconnectedCallback() { + super.disconnectedCallback(); + this.listenerIds_.forEach( + id => assert(this.browserProxy_.callbackRouter.removeListener(id))); + this.listenerIds_ = []; + } + + private async showDrivePicker_( + resultHandler: DrivePickerResultHandlerRemote, + keys: {oauthToken: string, apiKey: string, appId: string}) { + this.resultHandler_ = resultHandler; + + try { + await this.drivePickerApiProxy_.showPicker( + keys.oauthToken, keys.apiKey, keys.appId, + (data: GooglePickerResponse) => this.onPickerCallback_(data)); + } catch (e) { + return; + } + } + + private onPickerCallback_(data: GooglePickerResponse) { + // TODO(crbug.com/497937568): Handle the selected files, + // sanitize the data, and send it to the trusted host using + // this.resultHandler_. + if (data[google.picker.Response.ACTION] === google.picker.Action.PICKED) { + console.info('Drive Picker result received', this.resultHandler_); + } } }
diff --git a/chrome/browser/resources/drive_picker_host/untrusted/browser_proxy.ts b/chrome/browser/resources/drive_picker_host/untrusted/browser_proxy.ts index 2dc2014f..43a36bf4 100644 --- a/chrome/browser/resources/drive_picker_host/untrusted/browser_proxy.ts +++ b/chrome/browser/resources/drive_picker_host/untrusted/browser_proxy.ts
@@ -2,18 +2,25 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -import {DrivePickerUntrustedHostHandler} from './drive_picker_host_untrusted.mojom-webui.js'; -import type {DrivePickerUntrustedHostHandlerRemote} from './drive_picker_host_untrusted.mojom-webui.js'; +import {PageCallbackRouter, PageHandlerFactory, PageHandlerRemote} from './drive_picker_host_untrusted.mojom-webui.js'; export interface BrowserProxy { - handler: DrivePickerUntrustedHostHandlerRemote; + handler: PageHandlerRemote; + callbackRouter: PageCallbackRouter; } export class BrowserProxyImpl implements BrowserProxy { - handler: DrivePickerUntrustedHostHandlerRemote; + handler: PageHandlerRemote; + callbackRouter: PageCallbackRouter; constructor() { - this.handler = DrivePickerUntrustedHostHandler.getRemote(); + this.callbackRouter = new PageCallbackRouter(); + this.handler = new PageHandlerRemote(); + + const factory = PageHandlerFactory.getRemote(); + factory.createPageHandler( + this.callbackRouter.$.bindNewPipeAndPassRemote(), + this.handler.$.bindNewPipeAndPassReceiver()); } static getInstance(): BrowserProxy {
diff --git a/chrome/browser/resources/drive_picker_host/untrusted/drive_picker_api_proxy.ts b/chrome/browser/resources/drive_picker_host/untrusted/drive_picker_api_proxy.ts new file mode 100644 index 0000000..d3c4b5f --- /dev/null +++ b/chrome/browser/resources/drive_picker_host/untrusted/drive_picker_api_proxy.ts
@@ -0,0 +1,156 @@ +// Copyright 2026 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import {assert} from 'chrome-untrusted://resources/js/assert.js'; +import {PromiseResolver} from 'chrome-untrusted://resources/js/promise_resolver.js'; +import {getTrustedScriptURL} from 'chrome-untrusted://resources/js/static_types.js'; + +/** + * Minimal interface for the Google API Loader. + */ +export interface Gapi { + load(api: string, options: {callback: () => void, onerror?: () => void}): + void; +} + +/** + * Minimal interface for the Google Picker API. + * See https://developers.google.com/picker/docs/reference + * TODO(crbug.com/510492475): Migrate to types from third_party/node + * Remove these manually copied type definitions. + */ +export interface GooglePicker { + DocsView: {new(viewId: string): GooglePickerView}; + PickerBuilder: {new(): GooglePickerBuilder}; + ViewId: {DOCS: string}; + Feature: {MULTISELECT_ENABLED: string}; + Action: {PICKED: string, CANCEL: string}; + Response: {ACTION: string, DOCUMENTS: string}; +} + +export interface GooglePickerView { + setMimeTypes(mimeTypes: string): GooglePickerView; +} + +export interface GooglePickerBuilder { + addView(view: GooglePickerView): GooglePickerBuilder; + setOAuthToken(token: string): GooglePickerBuilder; + setDeveloperKey(key: string): GooglePickerBuilder; + setAppId(appId: string): GooglePickerBuilder; + setOrigin(origin: string): GooglePickerBuilder; + enableFeature(feature: string): GooglePickerBuilder; + setCallback(callback: (data: GooglePickerResponse) => void): + GooglePickerBuilder; + build(): GooglePickerInstance; +} + +export interface GooglePickerInstance { + setVisible(visible: boolean): void; +} + +export interface GooglePickerResponse { + [key: string]: unknown; +} + +export interface DrivePickerApiProxy { + ensureGapiLoaded(): Promise<void>; + showPicker( + oauthToken: string, apiKey: string, appId: string, + callback: (data: GooglePickerResponse) => void): Promise<void>; +} + +export class DrivePickerApiProxyImpl implements DrivePickerApiProxy { + private gapiLoadResolver_: PromiseResolver<void>|null = null; + + private onError_() { + if (this.gapiLoadResolver_) { + this.gapiLoadResolver_.reject(); + this.gapiLoadResolver_ = null; + } + } + + private loadPicker_() { + window.gapi.load('picker', { + callback: () => { + assert(this.gapiLoadResolver_); + this.gapiLoadResolver_.resolve(); + }, + onerror: () => { + this.onError_(); + }, + }); + } + + ensureGapiLoaded(): Promise<void> { + if (this.gapiLoadResolver_) { + return this.gapiLoadResolver_.promise; + } + + this.gapiLoadResolver_ = new PromiseResolver<void>(); + + if (window.gapi) { + this.gapiLoadResolver_.resolve(); + return this.gapiLoadResolver_.promise; + } + + if (!window.gapi) { + const script = document.createElement('script'); + script.type = 'text/javascript'; + script.src = + getTrustedScriptURL`https://apis.google.com/js/api.js`; + script.onerror = () => { + this.onError_(); + }; + script.onload = () => { + this.loadPicker_(); + }; + document.head.appendChild(script); + } else { + this.loadPicker_(); + } + + return this.gapiLoadResolver_.promise; + } + + async showPicker( + oauthToken: string, apiKey: string, appId: string, + callback: (data: GooglePickerResponse) => void): Promise<void> { + await this.ensureGapiLoaded(); + + const view = new google.picker.DocsView(google.picker.ViewId.DOCS); + const picker = new google.picker.PickerBuilder() + .addView(view) + .setOAuthToken(oauthToken) + .setDeveloperKey(apiKey) + .setAppId(appId) + // The origin is set to chrome://drive-picker-host to + // match the trusted host's origin. This is required for + // Drive Picker to appear since untrusted content is + // blocked by Chrome. + .setOrigin('chrome://drive-picker-host') + .enableFeature(google.picker.Feature.MULTISELECT_ENABLED) + .setCallback(callback) + .build(); + picker.setVisible(true); + } + + static getInstance(): DrivePickerApiProxy { + return instance || (instance = new DrivePickerApiProxyImpl()); + } + + static setInstance(obj: DrivePickerApiProxy) { + instance = obj; + } +} + +let instance: DrivePickerApiProxy|null = null; + +declare global { + namespace google { + const picker: GooglePicker; + } + interface Window { + gapi: Gapi; + } +}
diff --git a/chrome/browser/resources/glic/common_checks.py b/chrome/browser/resources/glic/common_checks.py index 47a2977..aada61bb 100644 --- a/chrome/browser/resources/glic/common_checks.py +++ b/chrome/browser/resources/glic/common_checks.py
@@ -14,7 +14,9 @@ _common_checks_ran = True return (_CheckGlicGeneratedApi(input_api, output_api) + - _CheckRuntimeFeatureChecksIfModified(input_api, output_api)) + _CheckRuntimeFeatureChecksIfModified(input_api, output_api) + + _CheckManifestConsistencyIfModified(input_api, output_api)) + def _CheckGlicGeneratedApi(input_api, output_api): @@ -80,3 +82,48 @@ e.output.decode('utf-8')) return [output_api.PresubmitError(message)] return [] + + +def _CheckManifestConsistencyIfModified(input_api, output_api): + MONITORED_FILES = set(( + 'chrome/browser/resources/glic/extension/manifest.json', + 'chrome/browser/resources/glic/presubmit/check_manifests.py', + )) + + os_path = input_api.os_path + src_root = os_path.join(os.path.dirname(__file__), '../../../..') + + affected_paths = set(f.LocalPath().replace('\\', '/') + for f in input_api.change.AffectedFiles()) + is_affected = (affected_paths & MONITORED_FILES) + + if not is_affected: + return [] + + results = [] + bypass_deps_check = ('Bypass-Glic-Manifest-Deps-Check' + in input_api.change.GitFootersFromDescription()) + if (not bypass_deps_check + and 'chrome/browser/resources/glic/extension/manifest.json' + in affected_paths and 'DEPS' not in affected_paths): + results.append( + output_api.PresubmitError( + 'Manifest modified, but DEPS was not updated. ' + + 'If you think DEPS update is unnecessary, bypass this with ' + + 'Bypass-Glic-Manifest-Deps-Check: in the CL footers.')) + + cmd = [ + input_api.python_executable, + os_path.join( + src_root, + 'chrome/browser/resources/glic/presubmit/check_manifests.py'), + ] + + try: + input_api.subprocess.check_output(cmd, + stderr=input_api.subprocess.STDOUT) + except input_api.subprocess.CalledProcessError as e: + message = ('glic check_manifests.py failed:\n' + + e.output.decode('utf-8')) + results.append(output_api.PresubmitError(message)) + return results
diff --git a/chrome/browser/resources/glic/internals/glic_internals_app.ts b/chrome/browser/resources/glic/internals/glic_internals_app.ts index dbf29482..4bc9781 100644 --- a/chrome/browser/resources/glic/internals/glic_internals_app.ts +++ b/chrome/browser/resources/glic/internals/glic_internals_app.ts
@@ -7,7 +7,7 @@ import {CrLitElement} from '//resources/lit/v3_0/lit.rollup.js'; -import {ActuationEligibility, ActuationTarget, AllowedInflightNavigation, FeatureMode, FreOverride, InvocationSource} from '../glic.mojom-webui.js'; +import {ActuationEligibility, ActuationTarget, FeatureMode, FreOverride, InvocationSource} from '../glic.mojom-webui.js'; import {InternalsPageHandlerFactory, InternalsPageHandlerRemote} from '../glic_internals.mojom-webui.js'; import type {InternalsDataPayload, TriggerInvokeFromInternalsOptions} from '../glic_internals.mojom-webui.js'; @@ -319,7 +319,6 @@ skillId: null, errorMessage: null, timeout: null, - allowedInflightNavigation: AllowedInflightNavigation.kNone, autoSubmit: this.invokeAutoSubmit_, freOverride: this.invokeFreOverride_, waitForPanelOpen: this.invokeWaitForPanelOpen_,
diff --git a/chrome/browser/resources/glic/presubmit/check_manifests.py b/chrome/browser/resources/glic/presubmit/check_manifests.py new file mode 100644 index 0000000..14a047a --- /dev/null +++ b/chrome/browser/resources/glic/presubmit/check_manifests.py
@@ -0,0 +1,100 @@ +# Copyright 2026 The Chromium Authors +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. +""" +Checks that public manifest fields are matching the internal Chrome-branded +manifest fields except for externally_connectable. +""" + +import json +import os +import sys + + +def _GetDirAbove(dirname: str): + path = os.path.abspath(__file__) + while True: + path, tail = os.path.split(path) + if not tail: + return None + if tail == dirname: + return path + + +SRC_ROOT = _GetDirAbove('chrome') + + +def _LoadManifest(path): + with open(path, 'r', encoding='utf-8') as f: + content = f.read() + clean_lines = [] + for line in content.splitlines(): + stripped = line.strip() + if stripped.startswith('//'): + continue + clean_lines.append(line) + return json.loads('\n'.join(clean_lines)) + + +def main(manifest_path=None, internal_manifest_path=None): + manifest_rel = 'chrome/browser/resources/glic/extension/manifest.json' + internal_manifest_rel = 'internal/extensions/glic/manifest_internal.json' + + if not manifest_path: + manifest_path = os.path.join(SRC_ROOT, manifest_rel) + if not internal_manifest_path: + internal_manifest_path = os.path.join(SRC_ROOT, internal_manifest_rel) + + if not os.path.exists(manifest_path): + print(f"Error: Public manifest file does not exist at {manifest_path}") + return 1 + + if not os.path.exists(internal_manifest_path): + # Gracefully skip if no internal checkout is present. + return 0 + + try: + manifest = _LoadManifest(manifest_path) + internal_manifest = _LoadManifest(internal_manifest_path) + except Exception as e: + print(f"Error: Failed to parse extension manifest JSON files: {e}") + return 1 + + manifest.pop('externally_connectable', None) + internal_manifest.pop('externally_connectable', None) + + if manifest == internal_manifest: + return 0 + + errors = [] + keys_manifest = set(manifest.keys()) + keys_internal = set(internal_manifest.keys()) + + if keys_manifest != keys_internal: + only_manifest = keys_manifest - keys_internal + only_internal = keys_internal - keys_manifest + if only_manifest: + errors.append(f"Fields found in public manifest only: " + f"{', '.join(only_manifest)}") + if only_internal: + errors.append(f"Fields found in internal manifest only: " + f"{', '.join(only_internal)}") + + for key in keys_manifest & keys_internal: + if manifest[key] != internal_manifest[key]: + errors.append(f"Field '{key}' differs:\n" + f"Public ({manifest_rel}):\n {manifest[key]}\n" + f"Internal ({internal_manifest_rel}):\n" + f" {internal_manifest[key]}") + + if errors: + print(f"Error: manifest.json and manifest_internal.json" + f" do not match except externally_connectable.\n" + + '\n'.join(errors)) + return 1 + + return 0 + + +if __name__ == '__main__': + sys.exit(main())
diff --git a/chrome/browser/resources/glic/presubmit/check_manifests_test.py b/chrome/browser/resources/glic/presubmit/check_manifests_test.py new file mode 100644 index 0000000..fefef44 --- /dev/null +++ b/chrome/browser/resources/glic/presubmit/check_manifests_test.py
@@ -0,0 +1,77 @@ +# Copyright 2026 The Chromium Authors +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +import os +import sys +import tempfile +import unittest + +# Add current directory to python module lookup path +SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__)) +sys.path.insert(0, SCRIPT_DIR) + +import check_manifests + + +class CheckManifestsTest(unittest.TestCase): + + def setUp(self): + self.temp_dir = tempfile.TemporaryDirectory() + + def tearDown(self): + self.temp_dir.cleanup() + + def test_manifest_match(self): + m1 = { + "name": "Extension", + "version": "1.0", + "manifest_version": 3, + "externally_connectable": { + "matches": ["https://google.com/*"] + } + } + + m2 = { + "name": "Extension", + "version": "1.0", + "manifest_version": 3, + "externally_connectable": { + "matches": ["https://internal.google.com/*"] + } + } + + import json + m1_path = os.path.join(self.temp_dir.name, "manifest.json") + m2_path = os.path.join(self.temp_dir.name, "manifest_internal.json") + + with open(m1_path, 'w') as f: + f.write(json.dumps(m1)) + with open(m2_path, 'w') as f: + f.write(json.dumps(m2)) + + rc = check_manifests.main(manifest_path=m1_path, + internal_manifest_path=m2_path) + self.assertEqual(rc, 0) + + def test_manifest_mismatch(self): + m1 = {"name": "Extension A", "version": "1.0", "manifest_version": 3} + + m2 = {"name": "Extension B", "version": "1.0", "manifest_version": 3} + + import json + m1_path = os.path.join(self.temp_dir.name, "manifest.json") + m2_path = os.path.join(self.temp_dir.name, "manifest_internal.json") + + with open(m1_path, 'w') as f: + f.write(json.dumps(m1)) + with open(m2_path, 'w') as f: + f.write(json.dumps(m2)) + + rc = check_manifests.main(manifest_path=m1_path, + internal_manifest_path=m2_path) + self.assertEqual(rc, 1) + + +if __name__ == '__main__': + unittest.main()
diff --git a/chrome/browser/resources/new_tab_page/action_chips/action_chips.ts b/chrome/browser/resources/new_tab_page/action_chips/action_chips.ts index 725a78f..58adf2b 100644 --- a/chrome/browser/resources/new_tab_page/action_chips/action_chips.ts +++ b/chrome/browser/resources/new_tab_page/action_chips/action_chips.ts
@@ -233,6 +233,7 @@ private onActionChipClick_(chip: ActionChip) { recordClick(chip.suggestTemplateInfo.typeIcon); + this.handler.notifyActionChipClicked(); const contextFiles: TabUpload[] = []; const tab = chip.tab; if (tab) {
diff --git a/chrome/browser/resources/new_tab_page/app.ts b/chrome/browser/resources/new_tab_page/app.ts index 6621181..6ab00b2 100644 --- a/chrome/browser/resources/new_tab_page/app.ts +++ b/chrome/browser/resources/new_tab_page/app.ts
@@ -1340,16 +1340,13 @@ private onWindowClick_(e: Event) { if (this.ntpRealboxNextEnabled_) { const searchbox = this.shadowRoot.querySelector('ntp-searchbox'); - const actionChips = this.shadowRoot.querySelector('ntp-action-chips'); const helpBubble = searchbox ? searchbox.shadowRoot.querySelector('help-bubble') : null; if (helpBubble) { const isClickOnBubble = e.composedPath().includes(helpBubble); const isClickOnSearchbox = searchbox && e.composedPath().includes(searchbox); - const isClickOnActionChips = - actionChips && e.composedPath().includes(actionChips); - if (!isClickOnBubble && (isClickOnSearchbox || isClickOnActionChips)) { + if (!isClickOnBubble && isClickOnSearchbox) { this.hideHelpBubble(CONTEXTUAL_ENTRYPOINT_ELEMENT_ID); } }
diff --git a/chrome/browser/resources/omnibox_popup/aim_app.css b/chrome/browser/resources/omnibox_popup/aim_app.css index 42a1dd4..8b83eeb1 100644 --- a/chrome/browser/resources/omnibox_popup/aim_app.css +++ b/chrome/browser/resources/omnibox_popup/aim_app.css
@@ -84,10 +84,12 @@ } :host([energy-effect-enabled_]) #composebox { + --composebox-cancel-button-inline-end: 14px; + --cr-composebox-padding-bottom: 8px; --cr-composebox-submit-border-radius: 100px; - --cr-composebox-submit-height: 32px; + --cr-composebox-submit-height: 36px; --cr-composebox-submit-icon-offset: 0; - --cr-composebox-submit-width: 40px; + --cr-composebox-submit-width: 48px; } #composebox[searchbox-layout-mode='Compact'], @@ -126,7 +128,7 @@ } :host([energy-effect-enabled_]) cr-composebox[searchbox-layout-mode='TallBottomContext']::part(submit) { - bottom: 14px; + inset-inline-end: 8px; } #composebox::part(voice-icon) { @@ -137,12 +139,19 @@ bottom: 11px; } +:host([energy-effect-enabled_]) + #composebox[searchbox-layout-mode='TallBottomContext']::part(voice-icon) { + bottom: 10px; + inset-inline-end: 14px; +} + cr-composebox[submit-enabled][searchbox-layout-mode='TallBottomContext']:not([show-dropdown])::part(submit) { bottom: 6px; } :host([energy-effect-enabled_]) cr-composebox[submit-enabled][searchbox-layout-mode='TallBottomContext']:not([show-dropdown])::part(submit) { - bottom: 14px; + bottom: 10px; + inset-inline-end: 8px; } #composebox[submit-enabled][searchbox-layout-mode='TallBottomContext']::part(voice-icon) { @@ -150,6 +159,12 @@ margin-inline-end: 6px; } +:host([energy-effect-enabled_]) + #composebox[submit-enabled][searchbox-layout-mode='TallBottomContext']::part(voice-icon) { + inset-inline-end: calc(var(--cr-composebox-submit-width, var(--cr-composebox-submit-container-size)) + 8px); + margin-inline-end: 4px; +} + :host-context([dir='rtl']) #composebox[searchbox-layout-mode='Compact']::part(context-menu-entrypoint-icon), :host-context([dir='rtl']) #composebox[searchbox-layout-mode^='Tall']:not([has-allowed-inputs])::part(context-menu-entrypoint-icon) { left: unset;
diff --git a/chrome/browser/resources/omnibox_popup/aim_app.html.ts b/chrome/browser/resources/omnibox_popup/aim_app.html.ts index 073b778..e603da2e 100644 --- a/chrome/browser/resources/omnibox_popup/aim_app.html.ts +++ b/chrome/browser/resources/omnibox_popup/aim_app.html.ts
@@ -23,7 +23,7 @@ searchbox-layout-mode="${this.getSearchboxLayoutMode_()}" ?disable-caret-color-animation="${!this.caretAnimationsEnabled_}" ?disable-composebox-animation="${this.disableComposeboxAnimation_}" - ?disable-voice-search-animation="${this.disableVoiceSearchAnimation_}" + .disableVoiceSearchAnimation="${this.disableVoiceSearchAnimation_}" @context-menu-entrypoint-click="${this.onContextMenuEntrypointClick_}" @close-composebox="${this.onCloseComposebox_}" @composebox-submit="${this.onComposeboxSubmit_}"
diff --git a/chrome/browser/resources/settings/privacy_page/privacy_guide/privacy_guide_completion_fragment.html b/chrome/browser/resources/settings/privacy_page/privacy_guide/privacy_guide_completion_fragment.html index 4322af0..6ce4bda 100644 --- a/chrome/browser/resources/settings/privacy_page/privacy_guide/privacy_guide_completion_fragment.html +++ b/chrome/browser/resources/settings/privacy_page/privacy_guide/privacy_guide_completion_fragment.html
@@ -49,8 +49,8 @@ </div> <template is="dom-if" if="[[shouldShowPrivacySandbox_]]"> <cr-link-row id="privacySandboxRow" using-slotted-label - sub-label="[[computePrivacySandboxRowSubLabel_( - shouldShowV2AdPrivacySubLabel_)]]" + sub-label= + "$i18n{privacyGuideCompletionCardPrivacySandboxSubLabelAdTopics}" start-icon="privacy20:ads-click" external on-click="onPrivacySandboxClick_"> <div slot="label" class="label">
diff --git a/chrome/browser/resources/settings/privacy_page/privacy_guide/privacy_guide_completion_fragment.ts b/chrome/browser/resources/settings/privacy_page/privacy_guide/privacy_guide_completion_fragment.ts index a2270e7d..f99a558 100644 --- a/chrome/browser/resources/settings/privacy_page/privacy_guide/privacy_guide_completion_fragment.ts +++ b/chrome/browser/resources/settings/privacy_page/privacy_guide/privacy_guide_completion_fragment.ts
@@ -25,8 +25,6 @@ import {loadTimeData} from '../../i18n_setup.js'; import type {MetricsBrowserProxy} from '../../metrics_browser_proxy.js'; import {MetricsBrowserProxyImpl, PrivacyGuideInteractions, PrivacyGuideStepsEligibleAndReached} from '../../metrics_browser_proxy.js'; -import type {PrivacySandboxBrowserProxy} from '../../privacy_sandbox/privacy_sandbox_browser_proxy.js'; -import {PrivacySandboxBrowserProxyImpl} from '../../privacy_sandbox/privacy_sandbox_browser_proxy.js'; import {HatsBrowserProxyImpl, TrustSafetyInteraction} from '../hats_browser_proxy.js'; import {getTemplate} from './privacy_guide_completion_fragment.html.js'; @@ -77,11 +75,6 @@ type: Boolean, value: false, }, - - shouldShowV2AdPrivacySubLabel_: { - type: Boolean, - value: false, - }, }; } @@ -91,9 +84,6 @@ declare private shouldShowWaa_: boolean; private metricsBrowserProxy_: MetricsBrowserProxy = MetricsBrowserProxyImpl.getInstance(); - declare private shouldShowV2AdPrivacySubLabel_: boolean; - private privacySandboxBrowserProxy_: PrivacySandboxBrowserProxy = - PrivacySandboxBrowserProxyImpl.getInstance(); override ready() { super.ready(); @@ -104,11 +94,6 @@ (event: UpdateSyncStateEvent) => this.updateWaaLink_(event.signedIn)); ClearBrowsingDataBrowserProxyImpl.getInstance().getSyncState().then( (status: UpdateSyncStateEvent) => this.updateWaaLink_(status.signedIn)); - this.privacySandboxBrowserProxy_ - .shouldShowPrivacySandboxAdTopicsContentParity() - .then(state => { - this.shouldShowV2AdPrivacySubLabel_ = state; - }); } override focus() { @@ -190,13 +175,6 @@ OpenWindowProxyImpl.getInstance().openUrl( loadTimeData.getString('activityControlsUrlInPrivacyGuide')); } - - private computePrivacySandboxRowSubLabel_(): string { - return this.i18n( - this.shouldShowV2AdPrivacySubLabel_ ? - 'privacyGuideCompletionCardPrivacySandboxSubLabelAdTopics' : - 'privacyGuideCompletionCardPrivacySandboxSubLabel'); - } } declare global {
diff --git a/chrome/browser/resources/settings/privacy_page/security/security_page_v2.html b/chrome/browser/resources/settings/privacy_page/security/security_page_v2.html index fc7b540..5dfb443c 100644 --- a/chrome/browser/resources/settings/privacy_page/security/security_page_v2.html +++ b/chrome/browser/resources/settings/privacy_page/security/security_page_v2.html
@@ -117,7 +117,7 @@ } #httpsFirstModeSection settings-radio-group { - padding-inline: 36px; + padding-inline: 16px; } #httpsFirstModeSection controlled-radio-button { @@ -139,7 +139,6 @@ display: flex; flex-direction: row; padding-block-start: var(--cr-section-vertical-padding); - padding-inline: 20px; } .settings-radio-group-feature-row controlled-radio-button::part(disc) { @@ -180,16 +179,11 @@ display: flex; justify-content: space-between; padding-bottom: var(--cr-section-vertical-padding); - padding-inline: var(--cr-section-padding); padding-top: var(--cr-section-vertical-padding); } - #javascriptGuardrailsRow [slot=collapse] { - padding: 0; - } - #javascriptGuardrailsRadioGroup { - padding-inline: var(--cr-section-padding); + padding: 0; } </style> <settings-subpage page-title="$i18n{securityPageTitle}"
diff --git a/chrome/browser/resources/settings/privacy_sandbox/privacy_sandbox_browser_proxy.ts b/chrome/browser/resources/settings/privacy_sandbox/privacy_sandbox_browser_proxy.ts index 2fb7452..6b22fd7 100644 --- a/chrome/browser/resources/settings/privacy_sandbox/privacy_sandbox_browser_proxy.ts +++ b/chrome/browser/resources/settings/privacy_sandbox/privacy_sandbox_browser_proxy.ts
@@ -72,9 +72,6 @@ */ getChildTopicsCurrentlyAssigned(topic: CanonicalTopic): Promise<CanonicalTopic[]>; - - /** Determines if the Ad Topics Content Parity should be shown. */ - shouldShowPrivacySandboxAdTopicsContentParity(): Promise<boolean>; } export class PrivacySandboxBrowserProxyImpl implements @@ -110,11 +107,6 @@ topic.taxonomyVersion); } - shouldShowPrivacySandboxAdTopicsContentParity() { - return sendWithPromise<boolean>( - 'shouldShowPrivacySandboxAdTopicsContentParity'); - } - static getInstance(): PrivacySandboxBrowserProxy { return instance || (instance = new PrivacySandboxBrowserProxyImpl()); }
diff --git a/chrome/browser/resources/settings/privacy_sandbox/privacy_sandbox_topics_subpage.html b/chrome/browser/resources/settings/privacy_sandbox/privacy_sandbox_topics_subpage.html index a0678aa9..b271f7aa 100644 --- a/chrome/browser/resources/settings/privacy_sandbox/privacy_sandbox_topics_subpage.html +++ b/chrome/browser/resources/settings/privacy_sandbox/privacy_sandbox_topics_subpage.html
@@ -92,7 +92,7 @@ id="topicsToggle" pref="{{prefs.privacy_sandbox.m1.topics_enabled}}" label="$i18n{topicsPageToggleLabel}" - sub-label="[[adTopicsToggleSubLabel_]]" + sub-label="$i18n{adTopicsPageToggleSubLabel}" on-settings-boolean-control-change="onToggleChange_"> </settings-toggle-button> <div id="disclaimer"> @@ -104,16 +104,9 @@ <div id="currentTopicsSection"> <div id="currentTopicsSectionWrapper" class="hr"> <h2 id="currentTopicsHeading">$i18n{topicsPageActiveTopicsHeading}</h2> - <template is="dom-if" if="[[shouldShowAdTopicsContentParity_]]"> - <div id="currentTopicsDescriptionV2" class="cr-secondary-text"> - $i18n{adTopicsPageActiveTopicsDescription} - </div> - </template> - <template is="dom-if" if="[[!shouldShowAdTopicsContentParity_]]"> - <div id="currentTopicsDescription" class="cr-secondary-text"> - $i18n{topicsPageActiveTopicsDescription} - </div> - </template> + <div id="currentTopicsDescriptionV2" class="cr-secondary-text"> + $i18n{adTopicsPageActiveTopicsDescription} + </div> <template is="dom-if" if="[[isTopicsEnabledAndLoaded_( prefs.privacy_sandbox.m1.topics_enabled.value, isTopicsListLoaded_)]]" restamp> @@ -184,16 +177,9 @@ <div id="footerV2" class="cr-secondary-text hr footer"> $i18nRaw{adTopicsPageFooterV2Desktop} </div> -<template is="dom-if" if="[[shouldShowAdTopicsContentParity_]]"> - <div id="footerDisclaimerV2" class="cr-secondary-text footer"> - $i18nRaw{adTopicsPageDisclaimerV2Desktop} - </div> -</template> -<template is="dom-if" if="[[!shouldShowAdTopicsContentParity_]]"> - <div id="footerDisclaimer" class="cr-secondary-text footer"> - $i18nRaw{adTopicsPageDisclaimer} - </div> -</template> +<div id="footerDisclaimerV2" class="cr-secondary-text footer"> + $i18nRaw{adTopicsPageDisclaimerV2Desktop} +</div> <cr-toast id="unblockTopicToast" duration="10000"> <div id="unblockTopicToastBody">$i18n{unblockTopicToastBody}</div> <cr-button id="closeToastButton" on-click="onHideToastClick_">
diff --git a/chrome/browser/resources/settings/privacy_sandbox/privacy_sandbox_topics_subpage.ts b/chrome/browser/resources/settings/privacy_sandbox/privacy_sandbox_topics_subpage.ts index 077152a..5fa85381 100644 --- a/chrome/browser/resources/settings/privacy_sandbox/privacy_sandbox_topics_subpage.ts +++ b/chrome/browser/resources/settings/privacy_sandbox/privacy_sandbox_topics_subpage.ts
@@ -105,20 +105,6 @@ computed: 'computeEmptyState_(' + 'prefs.privacy_sandbox.m1.topics_enabled.value)', }, - - /** - * If true, the Ad Topics Content parity should be shown. - */ - shouldShowAdTopicsContentParity_: { - type: Boolean, - value: false, - }, - - adTopicsToggleSubLabel_: { - type: String, - computed: - 'computeAdTopicsToggleSubLabel_(shouldShowAdTopicsContentParity_)', - }, }; } @@ -138,21 +124,12 @@ declare private shouldShowBlockTopicDialog_: boolean; declare private blockTopicDialogTitle_: string; declare private blockTopicDialogBody_: string; - declare private shouldShowAdTopicsContentParity_: boolean; - declare private adTopicsToggleSubLabel_: string; override ready() { super.ready(); this.privacySandboxBrowserProxy_.getTopicsState().then( state => this.onTopicsStateChanged_(state)); - this.privacySandboxBrowserProxy_ - .shouldShowPrivacySandboxAdTopicsContentParity() - .then( - shouldShow => { - this.shouldShowAdTopicsContentParity_ = shouldShow; - }, - ); } // Goal is to not show anything but the toggle and disclaimer when the pref is @@ -325,12 +302,6 @@ 'Settings.PrivacySandbox.AdTopics.PrivacyPolicyLinkClicked'); } - private computeAdTopicsToggleSubLabel_(): string { - return this.i18n( - this.shouldShowAdTopicsContentParity_ ? 'adTopicsPageToggleSubLabel' : - 'topicsPageToggleSubLabel'); - } - // SettingsViewMixin implementation. override getFocusConfig() { return new Map([
diff --git a/chrome/browser/resources/settings/system_page/system_page.ts b/chrome/browser/resources/settings/system_page/system_page.ts index 4176c2a8..b106f3698 100644 --- a/chrome/browser/resources/settings/system_page/system_page.ts +++ b/chrome/browser/resources/settings/system_page/system_page.ts
@@ -18,6 +18,7 @@ import '../settings_shared.css.js'; import {PrefsMixin} from '/shared/settings/prefs/prefs_mixin.js'; +import {CrSettingsPrefs} from '/shared/settings/prefs/prefs_types.js'; import {WebUiListenerMixin} from 'chrome://resources/cr_elements/web_ui_listener_mixin.js'; import {OpenWindowProxyImpl} from 'chrome://resources/js/open_window_proxy.js'; import {PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js'; @@ -154,8 +155,10 @@ // <if expr="is_win"> override connectedCallback() { super.connectedCallback(); - this.processIsolationEnabledAtStartup_ = - this.getPref('isolation_state.enabled').value; + CrSettingsPrefs.initialized.then(() => { + this.processIsolationEnabledAtStartup_ = + this.getPref('isolation_state.enabled').value; + }); } // </if>
diff --git a/chrome/browser/resources/signin/profile_customization/profile_customization_app.ts b/chrome/browser/resources/signin/profile_customization/profile_customization_app.ts index 9dd4c88a..fa8fdcc 100644 --- a/chrome/browser/resources/signin/profile_customization/profile_customization_app.ts +++ b/chrome/browser/resources/signin/profile_customization/profile_customization_app.ts
@@ -169,7 +169,7 @@ protected onCustomizeAvatarClick_() { assert(this.isLocalProfileCreation_); - this.$.viewManager.switchView('selectAvatarDialog', 'fade-in', 'fade-out'); + this.$.viewManager.switchView('selectAvatarDialog'); } private setAvailableIcons_(icons: AvatarIcon[]) { @@ -205,7 +205,7 @@ } private closeSelectAvatar_() { - this.$.viewManager.switchView('customizeDialog', 'fade-in', 'fade-out'); + this.$.viewManager.switchView('customizeDialog'); } protected onNameInputBlur_() {
diff --git a/chrome/browser/resources/webui_toolbar/BUILD.gn b/chrome/browser/resources/webui_toolbar/BUILD.gn index 48ec562..e5073a06 100644 --- a/chrome/browser/resources/webui_toolbar/BUILD.gn +++ b/chrome/browser/resources/webui_toolbar/BUILD.gn
@@ -19,6 +19,7 @@ "rhs_icons/mic_off_chrome_refresh.svg", "rhs_icons/notifications_chrome_refresh.svg", "rhs_icons/notifications_off_chrome_refresh.svg", + "rhs_icons/password_manager.svg", "rhs_icons/videocam_chrome_refresh.svg", "rhs_icons/videocam_off_chrome_refresh.svg", "lhs_icons/business_chrome_refresh.svg",
diff --git a/chrome/browser/resources/webui_toolbar/app.ts b/chrome/browser/resources/webui_toolbar/app.ts index 8ed9582..4a64bdc 100644 --- a/chrome/browser/resources/webui_toolbar/app.ts +++ b/chrome/browser/resources/webui_toolbar/app.ts
@@ -30,16 +30,12 @@ import { LhsChipIdentifier, OmniboxTextColor, - PermissionAction, - PermissionChipTheme, - PermissionPromptStyle, SecurityChipIcon, } from './toolbar_ui_api_data_model.mojom-webui.js'; -import type {OmniboxAction, LocationBarState, PermissionChipState} from './toolbar_ui_api_data_model.mojom-webui.js'; +import type {OmniboxAction, LocationBarState} from './toolbar_ui_api_data_model.mojom-webui.js'; import {ReadonlyOmniboxElement} from './readonly_omnibox.js'; import {LocationBarElement} from './location_bar.js'; import {LocationIconElement} from './location_icon.js'; -import {PermissionChipElement} from './permission_chip.js'; export { BrowserProxyImpl, @@ -47,10 +43,6 @@ LocationBarElement, LocationIconElement, OmniboxTextColor, - PermissionAction, - PermissionChipElement, - PermissionChipTheme, - PermissionPromptStyle, ReadonlyOmniboxElement, SecurityChipIcon, TrackedElementManager, @@ -58,7 +50,6 @@ export type { LocationBarState, OmniboxAction, - PermissionChipState, }; // clang-format on
diff --git a/chrome/browser/resources/webui_toolbar/internal b/chrome/browser/resources/webui_toolbar/internal index e46972f..e3ecf95 160000 --- a/chrome/browser/resources/webui_toolbar/internal +++ b/chrome/browser/resources/webui_toolbar/internal
@@ -1 +1 @@ -Subproject commit e46972fcb0e671a5e6e22a635e4594a99a2e3c29 +Subproject commit e3ecf9532c7a3623ce09fe4884e0bbc290eef2e4
diff --git a/chrome/browser/resources/webui_toolbar/permission_chip.css b/chrome/browser/resources/webui_toolbar/permission_chip.css index fbb1b70..c7ebaf72 100644 --- a/chrome/browser/resources/webui_toolbar/permission_chip.css +++ b/chrome/browser/resources/webui_toolbar/permission_chip.css
@@ -34,15 +34,6 @@ padding-inline-start: var(--location-bar-chip-padding); position: relative; user-select: none; - /* TODO(crbug.com/502598627): max-width is an arbitrary magic number. If the - * translated string exceeds this, it will be truncated. Animating to a very - * large max-width breaks the easing curve. Investigate using CSS Grid - * 0fr->1fr transition to animate to the exact intrinsic width instead. */ - max-width: 250px; /* Arbitrary large max-width for expanded state */ - /* 350ms matches the C++ expand duration in PermissionDashboardController. - ease-out matches the native SlideAnimation's default Tween::EASE_OUT. */ - transition: max-width 350ms ease-out, - padding-inline-end 350ms ease-out; } #chip:focus-visible { @@ -57,54 +48,18 @@ } } -#chip[collapsed] { - padding-inline-end: var(--location-bar-chip-padding); - max-width: calc( - var(--location-bar-chip-icon-size) + - var(--location-bar-chip-padding) * 2); - /* 250ms matches the C++ collapse duration. */ - transition: max-width 250ms ease-out, - padding-inline-end 250ms ease-out; -} - -/* An invisible overlay that mimics the native views::InkDrop highlight - (hover state). */ -#chip::before, -#chip::after { +#chip::before { + background-color: transparent; border-radius: inherit; content: ''; inset: 0; - opacity: 0; pointer-events: none; position: absolute; } -#chip::before { - background-color: var(--color-omnibox-chip-ink-drop-hover, transparent); - /* Matches views::kHighlightFadeOutOnHoverChangeDuration (250ms) */ - transition: opacity 250ms ease-in-out; -} - -/* An invisible overlay that mimics the native views::InkDrop ripple - (pressed state). */ -#chip::after { - background-color: var(--color-omnibox-chip-ink-drop-ripple, transparent); - /* Matches views::FloodFillInkDropRipple::ACTION_TRIGGERED_FADE_OUT (300ms) */ - transition: opacity 300ms ease-in-out; -} - -#chip:hover::before, -:host(:active) #chip::before, -:host([anchor-highlighted]) #chip::before { - /* The alpha value is baked directly into the color variable in C++. */ - opacity: 1; -} - -:host(:active) #chip::after, -:host([anchor-highlighted]) #chip::after { - /* The alpha value is baked directly into the color variable in C++. */ - opacity: 1; - transition: none; /* Instant press */ +#chip:hover::before { + background-color: + var(--color-omnibox-chip-ink-drop-hover, rgba(0, 0, 0, 0.08)); } #icon { @@ -119,10 +74,6 @@ width: var(--location-bar-chip-icon-size); } -#chip[collapsed] #icon { - margin-inline-end: 0; -} - #message { overflow: hidden; /* TODO(crbug.com/502598627): Native Views uses
diff --git a/chrome/browser/resources/webui_toolbar/permission_chip.html.ts b/chrome/browser/resources/webui_toolbar/permission_chip.html.ts index c8bf2a25..098249c 100644 --- a/chrome/browser/resources/webui_toolbar/permission_chip.html.ts +++ b/chrome/browser/resources/webui_toolbar/permission_chip.html.ts
@@ -11,14 +11,11 @@ return (!this.chipState || !this.chipState.isVisible) ? nothing : html`<!--_html_template_start_--> <div id="chip" - ?collapsed="${this.chipState.isFullyCollapsed}" aria-label="${this.chipState.accessibilityName}" title="${this.chipState.tooltip}" @pointerenter="${this.onPointerenter_}" @pointerleave="${this.onPointerleave_}" - @pointercancel="${this.onPointercancel_}" - @pointerdown="${this.onPointerdown_}" - @click="${this.onClick_}"> + @pointercancel="${this.onPointercancel_}"> ${this.getIconUrl_() ? html` <div id="icon" style="mask-image: ${this.getIconUrl_()};">
diff --git a/chrome/browser/resources/webui_toolbar/permission_chip.ts b/chrome/browser/resources/webui_toolbar/permission_chip.ts index 6b657f8..acdcc99 100644 --- a/chrome/browser/resources/webui_toolbar/permission_chip.ts +++ b/chrome/browser/resources/webui_toolbar/permission_chip.ts
@@ -3,14 +3,12 @@ // found in the LICENSE file. import {TrackedElementManager} from '//resources/js/tracked_element/tracked_element_manager.js'; -import {ensureTransitionEndEvent} from '//resources/js/util.js'; import {CrLitElement} from '//resources/lit/v3_0/lit.rollup.js'; import type {PropertyValues} from '//resources/lit/v3_0/lit.rollup.js'; import {BrowserProxyImpl} from './browser_proxy.js'; import {getCss} from './permission_chip.css.js'; import {getHtml} from './permission_chip.html.js'; -import {BUTTON_LEFT} from './toolbar_button.js'; import {LhsChipIdentifier, PermissionAction, PermissionChipTheme, PermissionPromptStyle} from './toolbar_ui_api_data_model.mojom-webui.js'; import type {PermissionChipState} from './toolbar_ui_api_data_model.mojom-webui.js'; @@ -35,7 +33,6 @@ accessor chipState: PermissionChipState|null = null; - private isFullyCollapsed_: boolean = true; private trackedElementManager_: TrackedElementManager; constructor() { @@ -48,16 +45,7 @@ const id = this.id === 'request-chip' ? 'PermissionChipView::kPermissionRequestChipElementId' : 'PermissionChipView::kIndicatorChipElementId'; - this.trackedElementManager_.startTracking(this, id, { - onHighlightChanged: (highlighted: boolean) => { - // Manually toggle the DOM attribute to bypass Lit's asynchronous update - // batching, ensuring the style updates synchronously. - // TODO(crbug.com/502598627): Re-evaluate how big the visual flash - // problem is, and whether we really need to manually toggle it or if we - // can use a reflected Lit property. - this.toggleAttribute('anchor-highlighted', highlighted); - }, - }); + this.trackedElementManager_.startTracking(this, id); } override disconnectedCallback() { @@ -69,48 +57,6 @@ super.updated(changedProperties); if (changedProperties.has('chipState')) { this.updateColors_(); - this.onChipStateChanged_(); - } - } - - private onChipStateChanged_() { - if (!this.chipState) { - return; - } - - if (this.chipState.isFullyCollapsed === this.isFullyCollapsed_) { - return; - } - - this.isFullyCollapsed_ = this.chipState.isFullyCollapsed; - const isCollapsed = this.isFullyCollapsed_; - - // In Native Views, animation state transitions are perfectly - // synchronized. In WebUI, the state change updates the DOM, which - // triggers a CSS transition. We must wait for this transition to - // complete before telling C++ the animation is over. If we fire this - // Mojo IPC synchronously, C++ will instantly hide the element and - // kill the animation prematurely. - const fireIpc = () => { - if (isCollapsed) { - BrowserProxyImpl.getInstance() - .toolbarUIHandler.onLhsChipCollapseAnimationEnded( - this.getIdentifier_()); - } else { - BrowserProxyImpl.getInstance() - .toolbarUIHandler.onLhsChipExpandAnimationEnded( - this.getIdentifier_()); - } - }; - - const chipEl = this.shadowRoot.querySelector<HTMLElement>('#chip'); - if (chipEl) { - chipEl.addEventListener('transitionend', fireIpc, {once: true}); - // Fallback in case there is no CSS transition (e.g., hidden - // element) or the tab is backgrounded, which might delay or - // suppress transitionend. `ensureTransitionEndEvent` fetches the - // duration from the CSS. - ensureTransitionEndEvent(chipEl); } } @@ -133,23 +79,6 @@ this.onPointerleave_(); } - protected onPointerdown_(e: PointerEvent) { - if (e.button !== BUTTON_LEFT) { - return; - } - BrowserProxyImpl.getInstance().toolbarUIHandler.onLhsChipMousePressed( - this.getIdentifier_()); - } - - protected onClick_(e: PointerEvent) { - // Note:'click' event dispatches using PointerEvents. Keyboard clicks - // (Enter/Space) also dispatch PointerEvents, but they have an empty - // pointerType (""). We only want to suppress true pointer interactions - // (mouse, touch, pen). - BrowserProxyImpl.getInstance().toolbarUIHandler.onLhsChipClicked( - this.getIdentifier_(), e.pointerType !== ''); - } - // Computes the foreground and background colors for the chip based on its // logical state (theme, user decision, prompt style). // Replicating this logic in TypeScript instead of passing explicit colors
diff --git a/chrome/browser/resources/webui_toolbar/permission_dashboard.html.ts b/chrome/browser/resources/webui_toolbar/permission_dashboard.html.ts index 80686e9..29ded5f2 100644 --- a/chrome/browser/resources/webui_toolbar/permission_dashboard.html.ts +++ b/chrome/browser/resources/webui_toolbar/permission_dashboard.html.ts
@@ -10,14 +10,14 @@ // clang-format off return (!this.dashboardState) ? nothing : html`<!--_html_template_start_--> <div id="container"> - ${this.dashboardState.indicatorChip?.isVisible ? html` + ${this.dashboardState.indicatorChip ? html` <permission-chip id="indicator-chip" .chipState="${this.dashboardState.indicatorChip}"> </permission-chip> ` : nothing} - ${this.dashboardState.requestChip?.isVisible ? html` + ${this.dashboardState.requestChip ? html` <permission-chip id="request-chip" .chipState="${this.dashboardState.requestChip}">
diff --git a/chrome/browser/safe_browsing/BUILD.gn b/chrome/browser/safe_browsing/BUILD.gn index ec47cf6..6cbf78f5 100644 --- a/chrome/browser/safe_browsing/BUILD.gn +++ b/chrome/browser/safe_browsing/BUILD.gn
@@ -115,10 +115,17 @@ if (is_android) { deps += [ "//chrome/browser/password_manager/android:utils", + "//chrome/browser/ui/android/safe_browsing", "//chrome/browser/ui/android/tab_model", "//components/safe_browsing/android:safe_browsing_api_handler", "//components/safe_browsing/android:safe_browsing_api_handler_util", ] + + # android/safe_browsing:safe_browsing includes + # chrome/browser/safe_browsing/android/password_reuse_controller_android.h + # (password_reuse_dialog_view_android.cc) which is owned by this target. + allow_circular_includes_from += + [ "//chrome/browser/ui/android/safe_browsing:safe_browsing" ] } if (!is_android) {
diff --git a/chrome/browser/safe_browsing/safe_browsing_pref_change_handler.cc b/chrome/browser/safe_browsing/safe_browsing_pref_change_handler.cc index 9841aa1..4fe66f1d 100644 --- a/chrome/browser/safe_browsing/safe_browsing_pref_change_handler.cc +++ b/chrome/browser/safe_browsing/safe_browsing_pref_change_handler.cc
@@ -32,7 +32,6 @@ #include "chrome/browser/android/tab_android.h" #include "chrome/browser/ui/android/tab_model/tab_model.h" #include "chrome/browser/ui/android/tab_model/tab_model_list.h" -#include "chrome/browser/ui/browser_finder.h" #include "chrome/browser/ui/browser_window/public/browser_window_features.h" #endif
diff --git a/chrome/browser/safe_browsing/safe_browsing_service_browsertest.cc b/chrome/browser/safe_browsing/safe_browsing_service_browsertest.cc index a965166..9fd0f3c6 100644 --- a/chrome/browser/safe_browsing/safe_browsing_service_browsertest.cc +++ b/chrome/browser/safe_browsing/safe_browsing_service_browsertest.cc
@@ -45,7 +45,6 @@ #include "chrome/browser/safe_browsing/chrome_ping_manager_factory.h" #include "chrome/browser/safe_browsing/test_safe_browsing_service.h" #include "chrome/browser/ui/browser.h" -#include "chrome/browser/ui/browser_finder.h" #include "chrome/browser/ui/navigator/browser_navigator_params.h" #include "chrome/browser/ui/tabs/tab_strip_model.h" #include "chrome/browser/web_applications/web_app_install_info.h"
diff --git a/chrome/browser/safe_browsing/tailored_security/notification_handler_desktop.cc b/chrome/browser/safe_browsing/tailored_security/notification_handler_desktop.cc index 1bc5128..3f3515ff 100644 --- a/chrome/browser/safe_browsing/tailored_security/notification_handler_desktop.cc +++ b/chrome/browser/safe_browsing/tailored_security/notification_handler_desktop.cc
@@ -13,7 +13,6 @@ #include "chrome/browser/notifications/notification_handler.h" #include "chrome/browser/profiles/profile.h" #include "chrome/browser/ui/browser.h" -#include "chrome/browser/ui/browser_finder.h" #include "chrome/browser/ui/browser_window.h" #include "chrome/browser/ui/chrome_pages.h" #include "chrome/browser/ui/scoped_tabbed_browser_displayer.h"
diff --git a/chrome/browser/search_engines/android/java/src/org/chromium/chrome/browser/search_engines/SogouPromoDialog.java b/chrome/browser/search_engines/android/java/src/org/chromium/chrome/browser/search_engines/SogouPromoDialog.java index eeb6e83..00c20c9 100644 --- a/chrome/browser/search_engines/android/java/src/org/chromium/chrome/browser/search_engines/SogouPromoDialog.java +++ b/chrome/browser/search_engines/android/java/src/org/chromium/chrome/browser/search_engines/SogouPromoDialog.java
@@ -103,7 +103,7 @@ } StyleSpan boldSpan = new StyleSpan(Typeface.BOLD); - TextView textView = (TextView) findViewById(R.id.subheader); + TextView textView = findViewById(R.id.subheader); SpannableString description = SpanApplier.applySpans( getContext().getString(R.string.sogou_explanation),
diff --git a/chrome/browser/search_engines/android/java/src/org/chromium/chrome/browser/search_engines/settings/common/SiteSearchViewBinder.java b/chrome/browser/search_engines/android/java/src/org/chromium/chrome/browser/search_engines/settings/common/SiteSearchViewBinder.java index 453b1fa8f..39b0dca 100644 --- a/chrome/browser/search_engines/android/java/src/org/chromium/chrome/browser/search_engines/settings/common/SiteSearchViewBinder.java +++ b/chrome/browser/search_engines/android/java/src/org/chromium/chrome/browser/search_engines/settings/common/SiteSearchViewBinder.java
@@ -58,7 +58,7 @@ : R.drawable.ic_expand_more_black_24dp; holder.mActionIcon.setImageResource(iconRes); } else if (SiteSearchProperties.MENU_DELEGATE == propertyKey) { - ListMenuButton button = (ListMenuButton) view.findViewById(R.id.overflow_menu_button); + ListMenuButton button = view.findViewById(R.id.overflow_menu_button); ListMenuDelegate delegate = model.get(SiteSearchProperties.MENU_DELEGATE); button.setDelegate(delegate); button.setEnabled(delegate != null);
diff --git a/chrome/browser/segmentation_platform/BUILD.gn b/chrome/browser/segmentation_platform/BUILD.gn index 32bc004..4e4d6c0 100644 --- a/chrome/browser/segmentation_platform/BUILD.gn +++ b/chrome/browser/segmentation_platform/BUILD.gn
@@ -133,6 +133,7 @@ "//chrome/browser/flags:flags_android", "//chrome/browser/ui", "//chrome/browser/ui/android/tab_model", + "//chrome/browser/ui/android/toolbar", "//components/commerce/core:shopping_service", "//components/segmentation_platform/embedder/home_modules:constants", ]
diff --git a/chrome/browser/serial/BUILD.gn b/chrome/browser/serial/BUILD.gn index c3fae95..fe93211 100644 --- a/chrome/browser/serial/BUILD.gn +++ b/chrome/browser/serial/BUILD.gn
@@ -86,7 +86,10 @@ "android/serial_bridge.cc", "android/web_serial_chooser_android.cc", ] - deps += [ "//chrome/browser/serial/android:jni_headers" ] + deps += [ + "//chrome/browser/serial/android:jni_headers", + "//chrome/browser/ui/android/device_dialog", + ] } public_deps = [ "//chrome/browser:browser_public_dependencies" ]
diff --git a/chrome/browser/sessions/session_restore_browsertest_chromeos.cc b/chrome/browser/sessions/session_restore_browsertest_chromeos.cc index 6aa239f..e853592 100644 --- a/chrome/browser/sessions/session_restore_browsertest_chromeos.cc +++ b/chrome/browser/sessions/session_restore_browsertest_chromeos.cc
@@ -22,7 +22,6 @@ #include "chrome/browser/sessions/session_restore_test_helper.h" #include "chrome/browser/ui/ash/system_web_apps/system_web_app_ui_utils.h" #include "chrome/browser/ui/browser.h" -#include "chrome/browser/ui/browser_finder.h" #include "chrome/browser/ui/browser_window.h" #include "chrome/browser/ui/browser_window/public/browser_window_interface.h" #include "chrome/browser/ui/browser_window/public/browser_window_interface_iterator.h"
diff --git a/chrome/browser/sessions/session_service_base.h b/chrome/browser/sessions/session_service_base.h index 9ae5089..1f90f98 100644 --- a/chrome/browser/sessions/session_service_base.h +++ b/chrome/browser/sessions/session_service_base.h
@@ -18,7 +18,6 @@ #include "chrome/browser/defaults.h" #include "chrome/browser/sessions/session_common_utils.h" #include "chrome/browser/ui/browser.h" -#include "chrome/browser/ui/browser_finder.h" #include "chrome/browser/ui/browser_window/public/browser_collection_observer.h" #include "components/keyed_service/core/keyed_service.h" #include "components/sessions/content/session_tab_helper_delegate.h"
diff --git a/chrome/browser/share/android/java/src/org/chromium/chrome/browser/share/long_screenshots/LongScreenshotErrorUtils.java b/chrome/browser/share/android/java/src/org/chromium/chrome/browser/share/long_screenshots/LongScreenshotErrorUtils.java deleted file mode 100644 index 9fa1485..0000000 --- a/chrome/browser/share/android/java/src/org/chromium/chrome/browser/share/long_screenshots/LongScreenshotErrorUtils.java +++ /dev/null
@@ -1,34 +0,0 @@ -// Copyright 2026 The Chromium Authors -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -package org.chromium.chrome.browser.share.long_screenshots; - -import android.content.Context; - -import org.chromium.base.task.PostTask; -import org.chromium.base.task.TaskTraits; -import org.chromium.build.annotations.NullMarked; -import org.chromium.chrome.R; -import org.chromium.ui.widget.Toast; - -/** Utility class to show a consistent error UI for long screenshots across different flows. */ -@NullMarked -public class LongScreenshotErrorUtils { - /** - * Shows a generic error Toast for long screenshot failures. - * - * @param context The context to use for the Toast. - */ - public static void showErrorMessage(Context context) { - PostTask.postTask( - TaskTraits.UI_DEFAULT, - () -> { - Toast.makeText( - context, - R.string.sharing_long_screenshot_unknown_error, - Toast.LENGTH_LONG) - .show(); - }); - } -}
diff --git a/chrome/browser/share/android/java/src/org/chromium/chrome/browser/share/long_screenshots/LongScreenshotsAreaSelectionDialogViewBinder.java b/chrome/browser/share/android/java/src/org/chromium/chrome/browser/share/long_screenshots/LongScreenshotsAreaSelectionDialogViewBinder.java index 1dc2d49e..085ea95 100644 --- a/chrome/browser/share/android/java/src/org/chromium/chrome/browser/share/long_screenshots/LongScreenshotsAreaSelectionDialogViewBinder.java +++ b/chrome/browser/share/android/java/src/org/chromium/chrome/browser/share/long_screenshots/LongScreenshotsAreaSelectionDialogViewBinder.java
@@ -18,22 +18,22 @@ static void bind(PropertyModel model, View parent, PropertyKey propertyKey) { if (LongScreenshotsAreaSelectionDialogProperties.CLOSE_BUTTON_CALLBACK.equals( propertyKey)) { - ImageButton view = (ImageButton) parent.findViewById(R.id.close_button); + ImageButton view = parent.findViewById(R.id.close_button); view.setOnClickListener( model.get(LongScreenshotsAreaSelectionDialogProperties.CLOSE_BUTTON_CALLBACK)); } else if (LongScreenshotsAreaSelectionDialogProperties.DONE_BUTTON_CALLBACK.equals( propertyKey)) { - ImageButton view = (ImageButton) parent.findViewById(R.id.done_button); + ImageButton view = parent.findViewById(R.id.done_button); view.setOnClickListener( model.get(LongScreenshotsAreaSelectionDialogProperties.DONE_BUTTON_CALLBACK)); } else if (LongScreenshotsAreaSelectionDialogProperties.DOWN_BUTTON_CALLBACK.equals( propertyKey)) { - ImageButton view = (ImageButton) parent.findViewById(R.id.down_button); + ImageButton view = parent.findViewById(R.id.down_button); view.setOnClickListener( model.get(LongScreenshotsAreaSelectionDialogProperties.DOWN_BUTTON_CALLBACK)); } else if (LongScreenshotsAreaSelectionDialogProperties.UP_BUTTON_CALLBACK.equals( propertyKey)) { - ImageButton view = (ImageButton) parent.findViewById(R.id.up_button); + ImageButton view = parent.findViewById(R.id.up_button); view.setOnClickListener( model.get(LongScreenshotsAreaSelectionDialogProperties.UP_BUTTON_CALLBACK)); }
diff --git a/chrome/browser/share/android/java/src/org/chromium/chrome/browser/share/long_screenshots/LongScreenshotsCoordinator.java b/chrome/browser/share/android/java/src/org/chromium/chrome/browser/share/long_screenshots/LongScreenshotsCoordinator.java index 803bf20..13b3b99 100644 --- a/chrome/browser/share/android/java/src/org/chromium/chrome/browser/share/long_screenshots/LongScreenshotsCoordinator.java +++ b/chrome/browser/share/android/java/src/org/chromium/chrome/browser/share/long_screenshots/LongScreenshotsCoordinator.java
@@ -120,7 +120,7 @@ () -> { mScreenshot = mMediator.getScreenshot(); if (mScreenshot == null) { - LongScreenshotErrorUtils.showErrorMessage(mActivity); + LongScreenshotsUtils.showErrorMessage(mActivity); } else { super.handleScreenshot(); }
diff --git a/chrome/browser/share/android/java/src/org/chromium/chrome/browser/share/long_screenshots/LongScreenshotsMediator.java b/chrome/browser/share/android/java/src/org/chromium/chrome/browser/share/long_screenshots/LongScreenshotsMediator.java index 2622b64..77d9215f 100644 --- a/chrome/browser/share/android/java/src/org/chromium/chrome/browser/share/long_screenshots/LongScreenshotsMediator.java +++ b/chrome/browser/share/android/java/src/org/chromium/chrome/browser/share/long_screenshots/LongScreenshotsMediator.java
@@ -26,10 +26,12 @@ import androidx.annotation.VisibleForTesting; +import org.chromium.base.metrics.RecordHistogram; import org.chromium.build.annotations.MonotonicNonNull; import org.chromium.build.annotations.NullMarked; import org.chromium.build.annotations.Nullable; import org.chromium.chrome.R; +import org.chromium.chrome.browser.share.long_screenshots.LongScreenshotsUtils.BitmapGeneratorStatus; import org.chromium.chrome.browser.share.long_screenshots.bitmap_generation.EntryManager; import org.chromium.chrome.browser.share.long_screenshots.bitmap_generation.LongScreenshotsEntry; import org.chromium.chrome.browser.share.long_screenshots.bitmap_generation.LongScreenshotsEntry.EntryStatus; @@ -95,6 +97,13 @@ mDisplayDensity = activity.getResources().getDisplayMetrics().density; } + private void logBitmapGeneratorStatus(@BitmapGeneratorStatus int status) { + RecordHistogram.recordEnumeratedHistogram( + "Sharing.ShareSheetLongScreenshots.BitmapGeneratorStatus", + status, + BitmapGeneratorStatus.COUNT); + } + @VisibleForTesting void displayInitialScreenshot() { mDidScaleForTesting = false; @@ -108,6 +117,11 @@ // If we got a status other than "in progress" or "complete", it means // we encountered some kind of failure. mEntryManager.removeBitmapGeneratorObserver(this); + int bitmapGeneratorStatus = + (status == EntryStatus.INSUFFICIENT_MEMORY) + ? BitmapGeneratorStatus.INSUFFICIENT_MEMORY + : BitmapGeneratorStatus.GENERATION_ERROR; + logBitmapGeneratorStatus(bitmapGeneratorStatus); // We need to call mDoneCallback to return to the normal browsing mode // instead of getting stuck. finishCapture(); @@ -117,6 +131,7 @@ @Override public void onCompositorReady(Size size, Point offset) { mEntryManager.removeBitmapGeneratorObserver(this); + logBitmapGeneratorStatus(BitmapGeneratorStatus.CAPTURE_COMPLETE); LongScreenshotsEntry entry = mEntryManager.generateFullpageEntry(); entry.setListener((status) -> onEntry(entry, status)); }
diff --git a/chrome/browser/share/android/java/src/org/chromium/chrome/browser/share/long_screenshots/LongScreenshotsUtils.java b/chrome/browser/share/android/java/src/org/chromium/chrome/browser/share/long_screenshots/LongScreenshotsUtils.java new file mode 100644 index 0000000..1f255e56a --- /dev/null +++ b/chrome/browser/share/android/java/src/org/chromium/chrome/browser/share/long_screenshots/LongScreenshotsUtils.java
@@ -0,0 +1,60 @@ +// Copyright 2026 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +package org.chromium.chrome.browser.share.long_screenshots; + +import android.content.Context; + +import androidx.annotation.IntDef; + +import org.chromium.base.task.PostTask; +import org.chromium.base.task.TaskTraits; +import org.chromium.build.annotations.NullMarked; +import org.chromium.chrome.R; +import org.chromium.ui.widget.Toast; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** Utility class for long screenshots across different flows. */ +@NullMarked +public class LongScreenshotsUtils { + // LINT.IfChange(BitmapGeneratorStatus) + // These values are persisted to logs. Entries should not be renumbered and + // numeric values should never be reused. + @IntDef({ + BitmapGeneratorStatus.CAPTURE_COMPLETE, + BitmapGeneratorStatus.INSUFFICIENT_MEMORY, + BitmapGeneratorStatus.GENERATION_ERROR + }) + @Retention(RetentionPolicy.SOURCE) + @Target(ElementType.TYPE_USE) + public @interface BitmapGeneratorStatus { + int CAPTURE_COMPLETE = 0; + int INSUFFICIENT_MEMORY = 1; + int GENERATION_ERROR = 2; + int COUNT = 3; + } + + // LINT.ThenChange(/tools/metrics/histograms/metadata/sharing/enums.xml:SharingShareSheetLongScreenshotsBitmapGenerationStatus) + + /** + * Shows a generic error Toast for long screenshot failures. + * + * @param context The context to use for the Toast. + */ + public static void showErrorMessage(Context context) { + PostTask.postTask( + TaskTraits.UI_DEFAULT, + () -> { + Toast.makeText( + context, + R.string.sharing_long_screenshot_unknown_error, + Toast.LENGTH_LONG) + .show(); + }); + } +}
diff --git a/chrome/browser/share/android/java/src/org/chromium/chrome/browser/share/qrcode/share_tab/QrCodeShareView.java b/chrome/browser/share/android/java/src/org/chromium/chrome/browser/share/qrcode/share_tab/QrCodeShareView.java index fb2ea92..0f3421a 100644 --- a/chrome/browser/share/android/java/src/org/chromium/chrome/browser/share/qrcode/share_tab/QrCodeShareView.java +++ b/chrome/browser/share/android/java/src/org/chromium/chrome/browser/share/qrcode/share_tab/QrCodeShareView.java
@@ -40,9 +40,9 @@ LayoutInflater.from(context) .inflate(R.layout.qrcode_share_layout, null, false); - Button downloadButton = (Button) mView.findViewById(R.id.download); + Button downloadButton = mView.findViewById(R.id.download); downloadButton.setOnClickListener(listener); - Button settingsButton = (Button) mView.findViewById(R.id.settings); + Button settingsButton = mView.findViewById(R.id.settings); settingsButton.setOnClickListener( new OnClickListener() { @Override @@ -116,8 +116,8 @@ return; } - Button downloadButton = (Button) mView.findViewById(R.id.download); - Button settingsButton = (Button) mView.findViewById(R.id.settings); + Button downloadButton = mView.findViewById(R.id.download); + Button settingsButton = mView.findViewById(R.id.settings); if (mHasStoragePermission) { downloadButton.setVisibility(View.VISIBLE); settingsButton.setVisibility(View.GONE);
diff --git a/chrome/browser/share/android/java/src/org/chromium/chrome/browser/share/scroll_capture/ScrollCaptureCallbackDelegate.java b/chrome/browser/share/android/java/src/org/chromium/chrome/browser/share/scroll_capture/ScrollCaptureCallbackDelegate.java index 37496cc..16d08e2 100644 --- a/chrome/browser/share/android/java/src/org/chromium/chrome/browser/share/scroll_capture/ScrollCaptureCallbackDelegate.java +++ b/chrome/browser/share/android/java/src/org/chromium/chrome/browser/share/scroll_capture/ScrollCaptureCallbackDelegate.java
@@ -17,16 +17,17 @@ import android.view.Surface; import android.view.View; -import androidx.annotation.IntDef; - import org.chromium.base.Callback; +import org.chromium.base.FeatureList; import org.chromium.base.MemoryPressureLevel; import org.chromium.base.memory.MemoryPressureMonitor; import org.chromium.base.metrics.RecordHistogram; import org.chromium.build.annotations.NullMarked; import org.chromium.build.annotations.Nullable; +import org.chromium.chrome.browser.flags.ChromeFeatureList; import org.chromium.chrome.browser.paint_preview.PaintPreviewCompositorUtils; -import org.chromium.chrome.browser.share.long_screenshots.LongScreenshotErrorUtils; +import org.chromium.chrome.browser.share.long_screenshots.LongScreenshotsUtils; +import org.chromium.chrome.browser.share.long_screenshots.LongScreenshotsUtils.BitmapGeneratorStatus; import org.chromium.chrome.browser.share.long_screenshots.bitmap_generation.EntryManager; import org.chromium.chrome.browser.share.long_screenshots.bitmap_generation.EntryManager.BitmapGeneratorObserver; import org.chromium.chrome.browser.share.long_screenshots.bitmap_generation.LongScreenshotsEntry; @@ -40,20 +41,6 @@ public class ScrollCaptureCallbackDelegate { private static final int BITMAP_HEIGHT_THRESHOLD = 20; - // These values are persisted to logs. Entries should not be renumbered and - // numeric values should never be reused. - @IntDef({ - BitmapGeneratorStatus.CAPTURE_COMPLETE, - BitmapGeneratorStatus.INSUFFICIENT_MEMORY, - BitmapGeneratorStatus.GENERATION_ERROR - }) - private @interface BitmapGeneratorStatus { - int CAPTURE_COMPLETE = 0; - int INSUFFICIENT_MEMORY = 1; - int GENERATION_ERROR = 2; - int COUNT = 3; - } - /** Wrapper class for {@link EntryManager}. */ public static class EntryManagerWrapper { EntryManager create(Tab tab) { @@ -82,10 +69,22 @@ public Rect onScrollCaptureSearch(CancellationSignal cancellationSignal) { assert mCurrentTab != null; - // If the system is under moderate memory pressure, don't give the user the option to create + int pressure = MemoryPressureMonitor.INSTANCE.getLastReportedPressure(); + RecordHistogram.recordEnumeratedHistogram( + "Sharing.ScrollCapture.MemoryPressureOnSearch", + pressure, + MemoryPressureLevel.CRITICAL + 1); + + int threshold = + (FeatureList.isNativeInitialized() + && ChromeFeatureList.isEnabled( + ChromeFeatureList.LONG_SCREENSHOTS_LENIENT_MEMORY_CHECK)) + ? MemoryPressureLevel.CRITICAL + : MemoryPressureLevel.MODERATE; + + // If the system is under memory pressure, don't give the user the option to create // a long screenshot. - if (MemoryPressureMonitor.INSTANCE.getLastReportedPressure() - >= MemoryPressureLevel.MODERATE) { + if (pressure >= threshold) { return new Rect(); } @@ -224,7 +223,7 @@ mEntryManager.destroy(); mEntryManager = null; } - LongScreenshotErrorUtils.showErrorMessage(assumeNonNull(mCurrentTab).getContext()); + LongScreenshotsUtils.showErrorMessage(assumeNonNull(mCurrentTab).getContext()); onReady.run(); PaintPreviewCompositorUtils.stopWarmCompositor(); logBitmapGeneratorStatus(status);
diff --git a/chrome/browser/share/android/java/src/org/chromium/chrome/browser/share/share_sheet/ShareSheetBottomSheetContent.java b/chrome/browser/share/android/java/src/org/chromium/chrome/browser/share/share_sheet/ShareSheetBottomSheetContent.java index 9b8a3c6..8660e3e 100644 --- a/chrome/browser/share/android/java/src/org/chromium/chrome/browser/share/share_sheet/ShareSheetBottomSheetContent.java +++ b/chrome/browser/share/android/java/src/org/chromium/chrome/browser/share/share_sheet/ShareSheetBottomSheetContent.java
@@ -226,19 +226,19 @@ private static void bindShareItem( PropertyModel model, ViewGroup parent, PropertyKey propertyKey) { if (ShareSheetItemViewProperties.ICON.equals(propertyKey)) { - ImageView view = (ImageView) parent.findViewById(R.id.icon); + ImageView view = parent.findViewById(R.id.icon); view.setImageDrawable(model.get(ShareSheetItemViewProperties.ICON)); } else if (ShareSheetItemViewProperties.LABEL.equals(propertyKey)) { - TextView view = (TextView) parent.findViewById(R.id.text); + TextView view = parent.findViewById(R.id.text); view.setText(model.get(ShareSheetItemViewProperties.LABEL)); } else if (ShareSheetItemViewProperties.CONTENT_DESCRIPTION.equals(propertyKey)) { - TextView view = (TextView) parent.findViewById(R.id.text); + TextView view = parent.findViewById(R.id.text); view.setContentDescription(model.get(ShareSheetItemViewProperties.CONTENT_DESCRIPTION)); } else if (ShareSheetItemViewProperties.CLICK_LISTENER.equals(propertyKey)) { - View layout = (View) parent.findViewById(R.id.layout); + View layout = parent.findViewById(R.id.layout); layout.setOnClickListener(model.get(ShareSheetItemViewProperties.CLICK_LISTENER)); } else if (ShareSheetItemViewProperties.SHOW_NEW_BADGE.equals(propertyKey)) { - TextView newBadge = (TextView) parent.findViewById(R.id.display_new); + TextView newBadge = parent.findViewById(R.id.display_new); newBadge.setVisibility( model.get(ShareSheetItemViewProperties.SHOW_NEW_BADGE) ? View.VISIBLE @@ -250,8 +250,8 @@ PropertyModel model, ViewGroup parent, PropertyKey propertyKey) { bindShareItem(model, parent, propertyKey); if (ShareSheetItemViewProperties.ICON.equals(propertyKey)) { - ImageView view = (ImageView) parent.findViewById(R.id.icon); - View layout = (View) parent.findViewById(R.id.layout); + ImageView view = parent.findViewById(R.id.icon); + View layout = parent.findViewById(R.id.layout); final int iconSize = ContextUtils.getApplicationContext()
diff --git a/chrome/browser/share/android/java_sources.gni b/chrome/browser/share/android/java_sources.gni index ca8e16a..2bfc8140 100644 --- a/chrome/browser/share/android/java_sources.gni +++ b/chrome/browser/share/android/java_sources.gni
@@ -19,11 +19,11 @@ "//chrome/browser/share/android/java/src/org/chromium/chrome/browser/share/link_to_text/LinkToTextHelper.java", "//chrome/browser/share/android/java/src/org/chromium/chrome/browser/share/link_to_text/LinkToTextIphController.java", "//chrome/browser/share/android/java/src/org/chromium/chrome/browser/share/link_to_text/LinkToTextMetricsHelper.java", - "//chrome/browser/share/android/java/src/org/chromium/chrome/browser/share/long_screenshots/LongScreenshotErrorUtils.java", "//chrome/browser/share/android/java/src/org/chromium/chrome/browser/share/long_screenshots/LongScreenshotsAreaSelectionDialogProperties.java", "//chrome/browser/share/android/java/src/org/chromium/chrome/browser/share/long_screenshots/LongScreenshotsAreaSelectionDialogViewBinder.java", "//chrome/browser/share/android/java/src/org/chromium/chrome/browser/share/long_screenshots/LongScreenshotsCoordinator.java", "//chrome/browser/share/android/java/src/org/chromium/chrome/browser/share/long_screenshots/LongScreenshotsMediator.java", + "//chrome/browser/share/android/java/src/org/chromium/chrome/browser/share/long_screenshots/LongScreenshotsUtils.java", "//chrome/browser/share/android/java/src/org/chromium/chrome/browser/share/long_screenshots/bitmap_generation/BitmapGenerator.java", "//chrome/browser/share/android/java/src/org/chromium/chrome/browser/share/long_screenshots/bitmap_generation/EntryManager.java", "//chrome/browser/share/android/java/src/org/chromium/chrome/browser/share/long_screenshots/bitmap_generation/LongScreenshotsCompositor.java",
diff --git a/chrome/browser/share/android/javatests/src/org/chromium/chrome/browser/share/long_screenshots/LongScreenshotErrorUtilsTest.java b/chrome/browser/share/android/javatests/src/org/chromium/chrome/browser/share/long_screenshots/LongScreenshotsUtilsTest.java similarity index 84% rename from chrome/browser/share/android/javatests/src/org/chromium/chrome/browser/share/long_screenshots/LongScreenshotErrorUtilsTest.java rename to chrome/browser/share/android/javatests/src/org/chromium/chrome/browser/share/long_screenshots/LongScreenshotsUtilsTest.java index dc0e3b3c..5d65b1e 100644 --- a/chrome/browser/share/android/javatests/src/org/chromium/chrome/browser/share/long_screenshots/LongScreenshotErrorUtilsTest.java +++ b/chrome/browser/share/android/javatests/src/org/chromium/chrome/browser/share/long_screenshots/LongScreenshotsUtilsTest.java
@@ -18,21 +18,21 @@ import org.chromium.base.test.BaseRobolectricTestRunner; import org.chromium.chrome.R; -/** Unit tests for {@link LongScreenshotErrorUtils}. */ +/** Unit tests for {@link LongScreenshotsUtils}. */ @RunWith(BaseRobolectricTestRunner.class) @Config( manifest = Config.NONE, shadows = {ShadowToast.class}) -public class LongScreenshotErrorUtilsTest { +public class LongScreenshotsUtilsTest { private final Activity mActivity; - public LongScreenshotErrorUtilsTest() { + public LongScreenshotsUtilsTest() { mActivity = Robolectric.buildActivity(Activity.class).get(); } @Test public void testShowErrorMessage() { - LongScreenshotErrorUtils.showErrorMessage(mActivity); + LongScreenshotsUtils.showErrorMessage(mActivity); ShadowLooper.idleMainLooper();
diff --git a/chrome/browser/sharing/glic_experimental_triggering/BUILD.gn b/chrome/browser/sharing/glic_experimental_triggering/BUILD.gn index baa36578..3dca12fc 100644 --- a/chrome/browser/sharing/glic_experimental_triggering/BUILD.gn +++ b/chrome/browser/sharing/glic_experimental_triggering/BUILD.gn
@@ -19,6 +19,7 @@ deps = [ ":glic_experimental_triggering", + "//chrome/browser/actor", "//chrome/browser/glic", "//chrome/browser/glic:impl", "//chrome/browser/profiles:profile",
diff --git a/chrome/browser/sharing/glic_experimental_triggering/glic_experimental_triggering_message_handler.cc b/chrome/browser/sharing/glic_experimental_triggering/glic_experimental_triggering_message_handler.cc index bf19c5a0..be827d5 100644 --- a/chrome/browser/sharing/glic_experimental_triggering/glic_experimental_triggering_message_handler.cc +++ b/chrome/browser/sharing/glic_experimental_triggering/glic_experimental_triggering_message_handler.cc
@@ -9,9 +9,13 @@ #include "base/feature_list.h" #include "base/functional/bind.h" +#include "base/metrics/histogram_functions.h" #include "base/strings/string_number_conversions.h" #include "base/time/time.h" +#include "chrome/browser/actor/actor_keyed_service.h" +#include "chrome/browser/actor/actor_task.h" #include "chrome/browser/glic/experimental_opt_in/glic_experimental_opt_in_controller.h" +#include "chrome/browser/glic/public/glic_enabling.h" #include "chrome/browser/glic/public/glic_invoke_options.h" #include "chrome/browser/glic/public/glic_keyed_service.h" #include "chrome/browser/glic/public/glic_keyed_service_factory.h" @@ -205,6 +209,54 @@ return browser ? TabListInterface::From(browser)->GetActiveTab() : nullptr; } +bool GlicExperimentalTriggeringMessageHandler:: + HandleUnavailableExperimentalTriggering( + glic::GlicKeyedService* glic_service, + const components_sharing_message::SharingMessage& message, + SharingMessageHandler::DoneCallback& done_callback) { + auto state = glic_service->enabling().GetExperimentalTriggeringState(); + base::UmaHistogramEnumeration( + "Glic.ExperimentalTriggering.StateOnActuationRequest", state); + + if (state == syncer::DeviceInfo::GlicExperimentalTriggeringState::kReady) { + return false; + } + + DLOG(WARNING) << "Rejecting remote request: Glic not opted-in for " + "experimental triggering."; + if (message.has_server_channel_configuration() && message_sender_) { + components_sharing_message::SharingMessage response_message; + auto* triggering_response = + response_message.mutable_glic_experimental_triggering(); + auto* response = triggering_response->mutable_response(); + auto* task_update = response->mutable_task_update(); + + task_update->set_state( + components_sharing_message::GlicExperimentalTriggering:: + ExperimentalTriggeringResponse::TaskUpdate::FAILED); + task_update->set_data_type( + components_sharing_message::GlicExperimentalTriggering:: + ExperimentalTriggeringResponse::TaskUpdate::ERROR_MESSAGE); + task_update->set_data("User is not opted in to experimental triggering."); + + message_sender_->SendMessageToServerTarget( + message.server_channel_configuration(), kUpdateMessageTimeout, + std::move(response_message), SharingMessageSender::DelegateType::kFCM, + base::BindOnce( + [](SharingSendMessageResult result, + std::unique_ptr<components_sharing_message::ResponseMessage> + response) { + if (result != SharingSendMessageResult::kSuccessful) { + DLOG(WARNING) << "Failed to send rejection response to server. " + "Result: " + << static_cast<int>(result); + } + })); + } + std::move(done_callback).Run(nullptr); + return true; +} + // TODO(b/505825633): Refine ResponseMessage errors for experimental triggering. void GlicExperimentalTriggeringMessageHandler::OnMessage( components_sharing_message::SharingMessage message, @@ -247,6 +299,11 @@ } if (request.request().has_trigger_actuation_request()) { + if (HandleUnavailableExperimentalTriggering(glic_service, message, + done_callback)) { + return; + } + auto options = CreateInvokeOptions(request, active_tab); if (message.has_server_channel_configuration()) { std::optional<int64_t> last_seen_sequence_number; @@ -334,10 +391,9 @@ if (!base::StringToInt(request.task_metadata().task_id(), &task_id_int)) { DLOG(WARNING) << "Invalid task ID format: " << request.task_metadata().task_id(); - } else { - instance->host().instance_delegate().StopActorTask( - actor::TaskId(task_id_int), - glic::mojom::ActorTaskStopReason::kStoppedByUser); + } else if (auto* actor_service = actor::ActorKeyedService::Get(profile_)) { + actor_service->StopTask(actor::TaskId(task_id_int), + actor::ActorTask::StoppedReason::kStoppedByUser); stopped = true; } }
diff --git a/chrome/browser/sharing/glic_experimental_triggering/glic_experimental_triggering_message_handler.h b/chrome/browser/sharing/glic_experimental_triggering/glic_experimental_triggering_message_handler.h index 6e3b318..bcc09ba 100644 --- a/chrome/browser/sharing/glic_experimental_triggering/glic_experimental_triggering_message_handler.h +++ b/chrome/browser/sharing/glic_experimental_triggering/glic_experimental_triggering_message_handler.h
@@ -64,6 +64,17 @@ glic::GlicKeyedService* glic_service, DoneCallback done_callback); + // Checks if experimental triggering is allowed for the profile. If NOT + // allowed, handles the rejection by logging metrics, sending a FAILED + // response back to the server (if FCM is configured), invoking the + // `done_callback`, and returning true to indicate the message has been fully + // handled. If allowed, returns false to indicate that normal handling should + // proceed. + bool HandleUnavailableExperimentalTriggering( + glic::GlicKeyedService* glic_service, + const components_sharing_message::SharingMessage& message, + SharingMessageHandler::DoneCallback& done_callback); + const raw_ptr<Profile> profile_; const raw_ptr<SharingMessageSender> message_sender_;
diff --git a/chrome/browser/sharing/glic_experimental_triggering/glic_experimental_triggering_message_handler_browsertest.cc b/chrome/browser/sharing/glic_experimental_triggering/glic_experimental_triggering_message_handler_browsertest.cc index 4712317..61cce27 100644 --- a/chrome/browser/sharing/glic_experimental_triggering/glic_experimental_triggering_message_handler_browsertest.cc +++ b/chrome/browser/sharing/glic_experimental_triggering/glic_experimental_triggering_message_handler_browsertest.cc
@@ -8,11 +8,14 @@ #include "base/functional/callback_helpers.h" #include "base/run_loop.h" +#include "base/test/metrics/histogram_tester.h" #include "base/test/mock_callback.h" #include "base/test/scoped_feature_list.h" #include "base/test/test_future.h" +#include "chrome/browser/glic/glic_pref_names.h" #include "chrome/browser/glic/public/glic_enabling.h" #include "chrome/browser/glic/public/glic_keyed_service.h" +#include "chrome/browser/glic/test_support/glic_test_util.h" #include "chrome/browser/glic/test_support/new_glic_api_test.h" #include "chrome/common/chrome_features.h" #include "components/sharing_message/mock_sharing_message_sender.h" @@ -50,6 +53,15 @@ GetTestUrl("page.html"))); } + void OptIn() { + auto* glic_service = glic::GlicKeyedService::Get(GetProfile()); + ASSERT_TRUE(glic_service); + glic_service->enabling().SetCompletedFre( + glic::prefs::FreStatus::kCompleted); + glic_service->enabling().SetUserEnabledActuationOnWeb(true); + glic_service->enabling().SetExperimentalTriggeringEnabled(true); + } + void TearDownOnMainThread() override { handler_.reset(); GlicApiBrowserTest::TearDownOnMainThread(); @@ -62,6 +74,8 @@ IN_PROC_BROWSER_TEST_F(GlicExperimentalTriggeringMessageHandlerBrowserTest, testGetExperimentalTriggeringUpdates) { + OptIn(); + base::HistogramTester histogram_tester; components_sharing_message::SharingMessage message; message.mutable_glic_experimental_triggering() ->mutable_request() @@ -97,6 +111,10 @@ EXPECT_TRUE(done_future.Wait()); + histogram_tester.ExpectUniqueSample( + "Glic.ExperimentalTriggering.StateOnActuationRequest", + syncer::DeviceInfo::GlicExperimentalTriggeringState::kReady, 1); + // Verify that a new tab was created and it is in the background. EXPECT_EQ(GetTabListInterface()->GetTabCount(), initial_tab_count + 1); EXPECT_EQ(GetTabListInterface()->GetActiveIndex(), initial_active_index); @@ -143,6 +161,7 @@ IN_PROC_BROWSER_TEST_F(GlicExperimentalTriggeringMessageHandlerBrowserTest, testRelaysUpdatesWithSequenceNumbers) { + OptIn(); components_sharing_message::SharingMessage message; message.mutable_server_channel_configuration()->set_configuration( "test_config"); @@ -205,6 +224,7 @@ IN_PROC_BROWSER_TEST_F(GlicExperimentalTriggeringMessageHandlerBrowserTest, testRespectsLastSeenSequenceNumber) { + OptIn(); components_sharing_message::SharingMessage message; message.mutable_server_channel_configuration()->set_configuration( "test_config"); @@ -365,4 +385,68 @@ ExperimentalTriggeringResponse::TaskUpdate::ERROR_MESSAGE); } +IN_PROC_BROWSER_TEST_F(GlicExperimentalTriggeringMessageHandlerBrowserTest, + RejectRequestWhenNotOptedIn) { + // Ensure we are NOT opted in. + auto* glic_service = glic::GlicKeyedService::Get(GetProfile()); + base::HistogramTester histogram_tester; + ASSERT_TRUE(glic_service); + glic_service->enabling().SetCompletedFre(glic::prefs::FreStatus::kNotStarted); + glic_service->enabling().SetUserEnabledActuationOnWeb(false); + glic_service->enabling().SetExperimentalTriggeringEnabled(false); + + components_sharing_message::SharingMessage message; + message.mutable_glic_experimental_triggering() + ->mutable_request() + ->mutable_trigger_actuation_request(); + + auto* server_channel_config = message.mutable_server_channel_configuration(); + server_channel_config->set_configuration("test_config"); + + base::test::TestFuture< + std::unique_ptr<components_sharing_message::ResponseMessage>> + done_future; + + base::test::TestFuture<components_sharing_message::SharingMessage> future; + EXPECT_CALL(mock_sharing_message_sender_, + SendMessageToServerTarget(_, _, _, _, _)) + .WillOnce( + [&](const components_sharing_message::ServerChannelConfiguration&, + base::TimeDelta, + components_sharing_message::SharingMessage message, + SharingMessageSender::DelegateType, + SharingMessageSender::ResponseCallback) { + future.SetValue(std::move(message)); + return base::OnceClosure(); + }); + + handler_->OnMessage(std::move(message), done_future.GetCallback()); + + EXPECT_TRUE(done_future.Wait()); + + histogram_tester.ExpectUniqueSample( + "Glic.ExperimentalTriggering.StateOnActuationRequest", + syncer::DeviceInfo::GlicExperimentalTriggeringState::kNeedsOptIn, 1); + + // Verify that Glic was NOT invoked (no new tabs created). + EXPECT_EQ(GetTabListInterface()->GetTabCount(), 1); + + // Verify that a FAILED response was sent back with the correct error message. + auto response = future.Take(); + EXPECT_TRUE(response.has_glic_experimental_triggering()); + EXPECT_EQ( + response.glic_experimental_triggering().response().task_update().state(), + components_sharing_message::GlicExperimentalTriggering:: + ExperimentalTriggeringResponse::TaskUpdate::FAILED); + EXPECT_EQ(response.glic_experimental_triggering() + .response() + .task_update() + .data_type(), + components_sharing_message::GlicExperimentalTriggering:: + ExperimentalTriggeringResponse::TaskUpdate::ERROR_MESSAGE); + EXPECT_EQ( + response.glic_experimental_triggering().response().task_update().data(), + "User is not opted in to experimental triggering."); +} + } // namespace glic
diff --git a/chrome/browser/signin/BUILD.gn b/chrome/browser/signin/BUILD.gn index 1bf5efe..156341a 100644 --- a/chrome/browser/signin/BUILD.gn +++ b/chrome/browser/signin/BUILD.gn
@@ -212,6 +212,7 @@ "//chrome/browser/metrics", "//chrome/browser/prefs", "//chrome/browser/supervised_user", + "//chrome/browser/ui:simple_message_box_headers", "//chrome/browser/ui/dialogs:browser_dialogs", "//chrome/browser/webdata_services", "//components/application_locale_storage",
diff --git a/chrome/browser/signin/google_accounts_private_api_util.cc b/chrome/browser/signin/google_accounts_private_api_util.cc index ae76ec87..055b47a 100644 --- a/chrome/browser/signin/google_accounts_private_api_util.cc +++ b/chrome/browser/signin/google_accounts_private_api_util.cc
@@ -6,6 +6,7 @@ #include "content/public/browser/navigation_handle.h" #include "content/public/browser/render_frame_host.h" +#include "content/public/browser/security_principal.h" #include "content/public/browser/site_instance.h" #include "google_apis/gaia/gaia_urls.h" @@ -28,6 +29,8 @@ // uses a dedicated process, rather than sharing process with eTLD+1. return rfh_origin == GetAllowedGoogleAccountsOrigin() && rfh->GetSiteInstance()->RequiresDedicatedProcess() && - rfh->GetSiteInstance()->GetSiteURL().GetHost() == - GetAllowedGoogleAccountsOrigin().host(); + rfh->GetSiteInstance() + ->GetSecurityPrincipal() + .GetDeprecatedSiteURL() + .GetHost() == GetAllowedGoogleAccountsOrigin().host(); }
diff --git a/chrome/browser/signin/signin_util.cc b/chrome/browser/signin/signin_util.cc index 9ca936d8..8a12dfb 100644 --- a/chrome/browser/signin/signin_util.cc +++ b/chrome/browser/signin/signin_util.cc
@@ -50,7 +50,6 @@ #include "ui/base/l10n/l10n_util.h" #if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_MAC) || BUILDFLAG(IS_WIN) -#include "chrome/browser/ui/browser_finder.h" #include "chrome/browser/ui/dialogs/browser_dialogs.h" #include "components/strings/grit/components_strings.h" #include "ui/base/interaction/element_identifier.h"
diff --git a/chrome/browser/site_protection/site_familiarity_utils.cc b/chrome/browser/site_protection/site_familiarity_utils.cc index 87499d24..6b8a5e1 100644 --- a/chrome/browser/site_protection/site_familiarity_utils.cc +++ b/chrome/browser/site_protection/site_familiarity_utils.cc
@@ -16,6 +16,7 @@ #include "components/search_engines/template_url_service.h" #include "content/public/browser/render_frame_host.h" #include "content/public/browser/render_process_host.h" +#include "content/public/browser/security_principal.h" #include "content/public/browser/site_instance.h" #include "content/public/browser/web_contents.h" #include "content/public/common/content_features.h" @@ -166,7 +167,9 @@ return; } - const GURL& site_url = web_contents->GetSiteInstance()->GetSiteURL(); + const GURL& site_url = web_contents->GetSiteInstance() + ->GetSecurityPrincipal() + .GetDeprecatedSiteURL(); if (site_url.is_empty()) { return; }
diff --git a/chrome/browser/ssl/ask_before_http_dialog_controller_interactive_uitest.cc b/chrome/browser/ssl/ask_before_http_dialog_controller_interactive_uitest.cc index accd5e0..b7eb917a 100644 --- a/chrome/browser/ssl/ask_before_http_dialog_controller_interactive_uitest.cc +++ b/chrome/browser/ssl/ask_before_http_dialog_controller_interactive_uitest.cc
@@ -38,6 +38,7 @@ #include "net/test/test_data_directory.h" #include "services/metrics/public/cpp/ukm_builders.h" #include "services/metrics/public/cpp/ukm_source.h" +#include "ui/base/metadata/metadata_impl_macros.h" #include "ui/views/controls/styled_label.h" using security_interstitials::MetricsHelper; @@ -496,3 +497,61 @@ ExpectUKMEntry(http_url, BlockingResult::kInterstitialDontProceed); } + +// Test-only View used to artificially trigger the sequence of actions +// required to trigger the crash in crbug.com/505796019. +class CrashTriggerView : public views::View { + METADATA_HEADER(CrashTriggerView, views::View) + public: + explicit CrashTriggerView(Browser* browser) : browser_(browser) { + SetFocusBehavior(FocusBehavior::ALWAYS); + GetViewAccessibility().SetRole(ax::mojom::Role::kButton); + GetViewAccessibility().SetName(u"Crash Trigger"); + } + + void OnFocus() override { + if (auto* tab = tabs::TabInterface::MaybeGetFromContents( + browser_->tab_strip_model()->GetActiveWebContents())) { + if (auto* controller = AskBeforeHttpDialogController::From(tab)) { + controller->CloseDialog(); + } + } + views::View::OnFocus(); + } + + private: + raw_ptr<Browser> browser_; +}; + +BEGIN_METADATA(CrashTriggerView) +END_METADATA + +// Regression test for crbug.com/505796019. +// This test ensures that navigating away while the ABH dialog is open and a +// view inside it has focus does not cause a Use-After-Free when focus is later +// changed. +IN_PROC_BROWSER_TEST_P(AskBeforeHttpDialogControllerUiTest, + FocusUAFOnNavigation) { + GURL http_url = http_server()->GetURL("bad-https.com", "/simple.html"); + + DEFINE_LOCAL_ELEMENT_IDENTIFIER_VALUE(kTestTab); + + RunTestSequence( + InstrumentTab(kTestTab, std::nullopt, GetBrowser()), + NavigateWebContents(kTestTab, http_url), + InAnyContext(WaitForShow(AskBeforeHttpDialogController::kGoBackButtonId)), + InSameContext( + // 1. Focus the "Go back" button in the dialog. + WithView(AskBeforeHttpDialogController::kGoBackButtonId, + [](views::View* view) { view->RequestFocus(); }), + CheckViewProperty(AskBeforeHttpDialogController::kGoBackButtonId, + &views::View::HasFocus, true), + + Do([&]() { + auto* browser_view = + BrowserView::GetBrowserViewForBrowser(GetBrowser()); + auto* crash_trigger = browser_view->AddChildView( + std::make_unique<CrashTriggerView>(GetBrowser())); + crash_trigger->RequestFocus(); + }))); +}
diff --git a/chrome/browser/ssl/https_only_mode_tab_helper.cc b/chrome/browser/ssl/https_only_mode_tab_helper.cc index a2151ad..7f2d2b5d 100644 --- a/chrome/browser/ssl/https_only_mode_tab_helper.cc +++ b/chrome/browser/ssl/https_only_mode_tab_helper.cc
@@ -20,18 +20,21 @@ content::NavigationHandle* navigation_handle) { if (base::FeatureList::IsEnabled( security_interstitials::features::kHttpsFirstDialogUi)) { - // Close the Ask-before-HTTP dialog if a new navigation begins. - // TabDialogManager has a parameter to close tab-modal dialogs - // if a cross-site navigation occurs, but we need to be more - // strict and close on *any* new navigation. (For example, without - // this when a user clicks from badssl.com to http.badssl.com, sees - // the warning prompt, and then clicks the back button, the warning - // prompt would still be showing.) - if (auto* tab = tabs::TabInterface::MaybeGetFromContents( - navigation_handle->GetWebContents())) { - auto* const dialog_controller = AskBeforeHttpDialogController::From(tab); - if (dialog_controller && dialog_controller->HasOpenDialog()) { - dialog_controller->CloseDialog(); + // Close the Ask-before-HTTP dialog if a new navigation begins in the + // primary main frame. TabDialogManager has a parameter to close tab-modal + // dialogs if a cross-site navigation occurs, but we need to be more strict + // and close on *any* new navigation. (For example, without this when a user + // clicks from badssl.com to http.badssl.com, sees the warning prompt, and + // then clicks the back button, the warning prompt would still be showing.) + if (navigation_handle->IsInPrimaryMainFrame() && + !navigation_handle->IsSameDocument()) { + if (auto* tab = tabs::TabInterface::MaybeGetFromContents( + navigation_handle->GetWebContents())) { + auto* const dialog_controller = + AskBeforeHttpDialogController::From(tab); + if (dialog_controller && dialog_controller->HasOpenDialog()) { + dialog_controller->CloseDialog(); + } } } }
diff --git a/chrome/browser/ssl/https_upgrades_browsertest.cc b/chrome/browser/ssl/https_upgrades_browsertest.cc index 193d92dd..93ad2f6 100644 --- a/chrome/browser/ssl/https_upgrades_browsertest.cc +++ b/chrome/browser/ssl/https_upgrades_browsertest.cc
@@ -20,6 +20,8 @@ #include "chrome/browser/interstitials/security_interstitial_page_test_utils.h" #include "chrome/browser/preloading/scoped_prewarm_feature_list.h" #include "chrome/browser/profiles/profile.h" +#include "chrome/browser/safe_browsing/advanced_protection_status_manager.h" +#include "chrome/browser/safe_browsing/advanced_protection_status_manager_factory.h" #include "chrome/browser/ssl/chrome_security_blocking_page_factory.h" #include "chrome/browser/ssl/generated_https_first_mode_pref.h" #include "chrome/browser/ssl/https_first_mode_settings_tracker.h" @@ -300,6 +302,7 @@ features::kHttpsFirstModeV2ForTypicallySecureUsers, features::kHttpsFirstBalancedMode, features::kHttpsFirstBalancedModeAutoEnable, + features::kHttpsFirstModeForAdvancedProtectionUsers, security_interstitials::features::kHttpsFirstDialogUi}); break; } @@ -466,6 +469,13 @@ https_upgrades_test_type() == HttpsUpgradesTestType::kAll; } + // Whether HFM strict mode is enabled (via pref). + // TODO: crbug.com/510847675 - Extend this to also include Advanced + // Protection. + bool IsStrictInterstitialEnabledForTest() const { + return IsHttpsFirstModePrefEnabled(); + } + // Whether HFM is enabled for many sites, and thus the tests should run steps // that assume the HTTP interstitial will trigger (i.e., for fallback HTTP // navigations when HTTPS-First Mode is enabled). @@ -787,7 +797,7 @@ auto* contents = GetBrowser()->tab_strip_model()->GetActiveWebContents(); - if (IsHttpsFirstModePrefEnabled()) { + if (IsStrictInterstitialEnabledForTest()) { // HFM should attempt the upgrade, fail, and fallback to the interstitial. EXPECT_FALSE(content::NavigateToURL(contents, local_ip_url)); EXPECT_TRUE( @@ -834,7 +844,7 @@ auto* contents = GetBrowser()->tab_strip_model()->GetActiveWebContents(); - if (IsHttpsFirstModePrefEnabled()) { + if (IsStrictInterstitialEnabledForTest()) { // HFM should attempt the upgrade, fail, and fallback to the interstitial. EXPECT_FALSE(content::NavigateToURL(contents, singlelabel_url)); EXPECT_TRUE( @@ -856,7 +866,7 @@ // If in Strict Mode, verify that upgrade events were recorded because an // upgrade was attempted and failed. - if (IsHttpsFirstModePrefEnabled()) { + if (IsStrictInterstitialEnabledForTest()) { histograms()->ExpectTotalCount(kEventHistogram, 3); histograms()->ExpectBucketCount( kEventHistogram, @@ -888,7 +898,7 @@ // wouldn't receive the traffic (since it relies on DNS). auto* contents = GetBrowser()->tab_strip_model()->GetActiveWebContents(); - if (IsHttpsFirstModePrefEnabled()) { + if (IsStrictInterstitialEnabledForTest()) { EXPECT_FALSE(content::NavigateToURL(contents, nonunique_url1)); EXPECT_FALSE(content::NavigateToURL(contents, nonunique_url2)); // Other histograms are still recorded. @@ -928,7 +938,7 @@ auto* contents = GetBrowser()->tab_strip_model()->GetActiveWebContents(); - if (IsHttpsFirstModePrefEnabled() || IsIncognito()) { + if (IsStrictInterstitialEnabledForTest()) { // HFM should attempt the upgrade, fail, and fallback to the interstitial. EXPECT_FALSE(content::NavigateToURL(contents, non_default_http_url)); EXPECT_TRUE( @@ -948,9 +958,9 @@ histograms()->ExpectTotalCount(kNavigationRequestSecurityLevelHistogram, 1); } - // If in Strict Mode or Incognito, verify that upgrade events were recorded + // If in Strict Mode, verify that upgrade events were recorded // because an upgrade was attempted and failed. - if (IsHttpsFirstModePrefEnabled() || IsIncognito()) { + if (IsStrictInterstitialEnabledForTest()) { histograms()->ExpectTotalCount(kEventHistogram, 3); histograms()->ExpectBucketCount( kEventHistogram, @@ -3533,7 +3543,7 @@ AutocompleteMatch()); nav_observer.Wait(); - if (IsHttpsFirstModePrefEnabled() || IsIncognito()) { + if (IsStrictInterstitialEnabledForTest()) { // Typed http URLs don't opt out of upgrades in HFM. EXPECT_EQ(https_url, contents->GetLastCommittedURL()); } else { @@ -4168,3 +4178,57 @@ EXPECT_TRUE(chrome_browser_interstitials::IsShowingHttpsFirstModeInterstitial( contents)); } + +// A separate test fixture for Advanced Protection to ensure that HFM triggers +// properly, even if balanced mode is enabled by default. +class HttpsUpgradesAdvancedProtectionBrowserTest : public InProcessBrowserTest { + public: + HttpsUpgradesAdvancedProtectionBrowserTest() { + feature_list_.InitWithFeatures( + /*enabled_features=*/{features:: + kHttpsFirstModeForAdvancedProtectionUsers, + features::kHttpsFirstBalancedModeAutoEnable}, + /*disabled_features=*/{ + security_interstitials::features::kHttpsFirstDialogUi}); + } + ~HttpsUpgradesAdvancedProtectionBrowserTest() override = default; + + void SetUp() override { + ChromeSecurityBlockingPageFactory::SetEnterpriseManagedForTesting(false); + InProcessBrowserTest::SetUp(); + } + + void SetUpOnMainThread() override { + // Enable Advanced Protection for the profile via the testing API + safe_browsing::AdvancedProtectionStatusManagerFactory::GetForProfile( + browser()->profile()) + ->SetAdvancedProtectionStatusForTesting(true); + + host_resolver()->AddRule("*", "127.0.0.1"); + http_server_.AddDefaultHandlers(GetChromeTestDataDir()); + ASSERT_TRUE(http_server_.Start()); + } + + net::EmbeddedTestServer* http_server() { return &http_server_; } + + private: + base::test::ScopedFeatureList feature_list_; + net::EmbeddedTestServer http_server_{net::EmbeddedTestServer::TYPE_HTTP}; +}; + +// Verifies that Advanced Protection users get HFM warnings for non-default +// ports and it does not get incorrectly bypassed by the Balanced Mode +// exclusion. Regression test for crbug.com/501741117. +IN_PROC_BROWSER_TEST_F(HttpsUpgradesAdvancedProtectionBrowserTest, + UrlWithNonDefaultPort_ShouldUpgradeAndShowInterstitial) { + GURL http_url = http_server()->GetURL("foo.com", "/simple.html"); + EXPECT_NE(http_url.IntPort(), 80); // Ensure it's not the default port + + auto* contents = browser()->tab_strip_model()->GetActiveWebContents(); + + content::TestNavigationObserver nav_observer(contents, 1); + EXPECT_FALSE(content::NavigateToURL(contents, http_url)); + nav_observer.Wait(); + EXPECT_TRUE(chrome_browser_interstitials::IsShowingHttpsFirstModeInterstitial( + contents)); +}
diff --git a/chrome/browser/ssl/https_upgrades_interceptor.cc b/chrome/browser/ssl/https_upgrades_interceptor.cc index a73bd85..1261d27 100644 --- a/chrome/browser/ssl/https_upgrades_interceptor.cc +++ b/chrome/browser/ssl/https_upgrades_interceptor.cc
@@ -201,20 +201,14 @@ return nullptr; } - PrefService* prefs = profile->GetPrefs(); - bool https_first_mode_enabled = - prefs && prefs->GetBoolean(prefs::kHttpsOnlyModeEnabled); - - return std::make_unique<HttpsUpgradesInterceptor>( - frame_tree_node_id, https_first_mode_enabled, navigation_ui_data); + return std::make_unique<HttpsUpgradesInterceptor>(frame_tree_node_id, + navigation_ui_data); } HttpsUpgradesInterceptor::HttpsUpgradesInterceptor( content::FrameTreeNodeId frame_tree_node_id, - bool http_interstitial_enabled_by_pref, content::NavigationUIData* navigation_ui_data) : frame_tree_node_id_(frame_tree_node_id), - http_interstitial_enabled_by_pref_(http_interstitial_enabled_by_pref), navigation_ui_data_(navigation_ui_data) {} HttpsUpgradesInterceptor::~HttpsUpgradesInterceptor() = default; @@ -296,26 +290,8 @@ // Set up the interstitial state before checking any exclusions to upgrades, // as some may depend on this being configured. interstitial_state_ = std::make_unique< - security_interstitials::https_only_mode::HttpInterstitialState>(); - interstitial_state_->enabled_by_pref = http_interstitial_enabled_by_pref_; - auto* prefs = profile->GetPrefs(); - if (base::FeatureList::IsEnabled(features::kHttpsFirstModeIncognito)) { - if (prefs && prefs->GetBoolean(prefs::kHttpsFirstModeIncognito) && - profile->IsIncognitoProfile()) { - interstitial_state_->enabled_by_incognito = true; - } - } - // StatefulSSLHostStateDelegate can be null during tests. - if (state && - state->IsHttpsEnforcedForUrl(tentative_resource_request.url, - storage_partition) && - !MustDisableSiteEngagementHeuristic(profile)) { - interstitial_state_->enabled_by_engagement_heuristic = true; - } - if (IsBalancedModeEnabled(prefs) && state && - !state->HttpsFirstBalancedModeSuppressedForTesting()) { - interstitial_state_->enabled_in_balanced_mode = true; - } + security_interstitials::https_only_mode::HttpInterstitialState>( + ComputeInterstitialState(web_contents, tentative_resource_request.url)); // Exclude HTTPS URLs. if (tentative_resource_request.url.SchemeIs(url::kHttpsScheme)) { @@ -530,7 +506,7 @@ // silent HTTPS Upgrades for the site overall and not show an HTTPS-First Mode // interstitial for Engaged Sites. Strict HTTPS-First Mode ignores this // setting. - if (!interstitial_state_->enabled_by_pref && + if (!IsStrictInterstitialEnabled(*interstitial_state_) && DoesInsecureContentSettingDisableUpgrading(url, profile)) { RecordNavigationRequestSecurityLevel( NavigationRequestSecurityLevel::kAllowlisted);
diff --git a/chrome/browser/ssl/https_upgrades_interceptor.h b/chrome/browser/ssl/https_upgrades_interceptor.h index 70db31b..d275583 100644 --- a/chrome/browser/ssl/https_upgrades_interceptor.h +++ b/chrome/browser/ssl/https_upgrades_interceptor.h
@@ -52,7 +52,6 @@ content::NavigationUIData* navigation_ui_data_); HttpsUpgradesInterceptor(content::FrameTreeNodeId frame_tree_node_id, - bool http_interstitial_enabled, content::NavigationUIData* navigation_ui_data_); ~HttpsUpgradesInterceptor() override; @@ -119,11 +118,6 @@ // Used to access the WebContents for the navigation. content::FrameTreeNodeId frame_tree_node_id_; - // Controls whether we are upgrading and falling back with an interstitial - // before proceeding with the HTTP navigation. This reflects the general - // UI setting. Only used to set the values of interstitial_state_. - bool http_interstitial_enabled_by_pref_ = false; - // Parameters about whether the throttle should trigger the interstitial // warning before navigating to the HTTP fallback URL. Can be null if the // current load isn't eligible for an upgrade.
diff --git a/chrome/browser/ssl/https_upgrades_navigation_throttle.cc b/chrome/browser/ssl/https_upgrades_navigation_throttle.cc index b237e87..6d9cd0510 100644 --- a/chrome/browser/ssl/https_upgrades_navigation_throttle.cc +++ b/chrome/browser/ssl/https_upgrades_navigation_throttle.cc
@@ -73,7 +73,7 @@ } bool https_upgrades_enabled = - interstitial_state.enabled_by_pref || + IsInterstitialEnabled(interstitial_state) || base::FeatureList::IsEnabled(features::kHttpsUpgrades); if (!https_upgrades_enabled) { return;
diff --git a/chrome/browser/ssl/https_upgrades_util.cc b/chrome/browser/ssl/https_upgrades_util.cc index ee5c216..50856b93 100644 --- a/chrome/browser/ssl/https_upgrades_util.cc +++ b/chrome/browser/ssl/https_upgrades_util.cc
@@ -108,7 +108,9 @@ auto* advanced_protection_manager = safe_browsing::AdvancedProtectionStatusManagerFactory::GetForProfile( profile); - if (advanced_protection_manager) { + if (advanced_protection_manager && + base::FeatureList::IsEnabled( + features::kHttpsFirstModeForAdvancedProtectionUsers)) { interstitial_state.enabled_by_advanced_protection = advanced_protection_manager->IsUnderAdvancedProtection(); } @@ -141,27 +143,16 @@ state.enabled_by_typically_secure_browsing); } -bool IsBalancedModeUniquelyEnabled(const HttpInterstitialState& state) { - // Balance mode is _uniquely_ enabled only when other HFM variants aren't - // enabled. - if (state.enabled_by_pref) { - return false; - } - if (base::FeatureList::IsEnabled(features::kHttpsFirstModeIncognito) && - state.enabled_by_incognito) { - return false; - } - // ...then ensure balanced mode is enabled. - return (IsBalancedModeAvailable() && state.enabled_in_balanced_mode) || - IsBalancedModeInterstitialEnabledByHeuristics(state); -} bool IsInterstitialEnabled(const HttpInterstitialState& state) { // Interstitials are enabled when "strict" interstitials are enabled... if (IsStrictInterstitialEnabled(state)) { return true; } + if (state.enabled_by_incognito) { + return true; + } if (IsBalancedModeAvailable() && state.enabled_in_balanced_mode) { return true; } @@ -172,10 +163,6 @@ if (state.enabled_by_pref) { return true; } - if (base::FeatureList::IsEnabled(features::kHttpsFirstModeIncognito) && - state.enabled_by_incognito) { - return true; - } if (state.enabled_by_advanced_protection) { return true; } @@ -183,21 +170,9 @@ } bool ShouldExemptNonUniqueHostnames(const HttpInterstitialState& state) { - // If strict mode is enabled by the pref, warn the user before any HTTP that - // goes over the network. Any other mode ignores non-unique hostnames. - // Advanced Protection users are also treated as "strict". - return !state.enabled_by_pref && !state.enabled_by_advanced_protection; -} - -bool ShouldExcludeUrlFromInterstitial(const HttpInterstitialState& state, - const GURL& url) { - // In balanced mode, single-label hostnames and URLs with non-default ports - // are excluded from interstitials. This also applies if one of the HFM - // heuristics enabled Balanced Mode. - return IsBalancedModeUniquelyEnabled(state) && - (net::GetSuperdomain(url.GetHost()).empty() || - (url.has_port() && - url.IntPort() != HttpsUpgradesInterceptor::GetHttpPortForTesting())); + // If strict mode is enabled, warn the user before any HTTP that goes over + // the network. Any other mode ignores non-unique hostnames. + return !IsStrictInterstitialEnabled(state); } bool MustDisableSiteEngagementHeuristic(Profile* profile) {
diff --git a/chrome/browser/ssl/https_upgrades_util.h b/chrome/browser/ssl/https_upgrades_util.h index 0f4045c..21fe8cf 100644 --- a/chrome/browser/ssl/https_upgrades_util.h +++ b/chrome/browser/ssl/https_upgrades_util.h
@@ -69,12 +69,6 @@ const security_interstitials::https_only_mode::HttpInterstitialState& state); -// Returns true if the given URL should be excluded from interstitials in the -// current HFM mode. Does NOT mean that an upgrade shouldn't be attempted. -bool ShouldExcludeUrlFromInterstitial( - const security_interstitials::https_only_mode::HttpInterstitialState& state, - const GURL& url); - // Returns true if the Site Engagement heuristic for HTTPS-First Mode must be // disabled and not cause an interstitial. Other conditions such as relevant // prefs already having been set may also disable the heuristic on startup, but
diff --git a/chrome/browser/ssl/security_state_tab_helper_browsertest.cc b/chrome/browser/ssl/security_state_tab_helper_browsertest.cc index e3b3804..432ac02 100644 --- a/chrome/browser/ssl/security_state_tab_helper_browsertest.cc +++ b/chrome/browser/ssl/security_state_tab_helper_browsertest.cc
@@ -38,7 +38,6 @@ #include "chrome/browser/ssl/https_upgrades_util.h" #include "chrome/browser/ui/browser.h" #include "chrome/browser/ui/browser_commands.h" -#include "chrome/browser/ui/browser_finder.h" #include "chrome/browser/ui/tabs/tab_strip_model.h" #include "chrome/common/chrome_features.h" #include "chrome/common/chrome_paths.h"
diff --git a/chrome/browser/ssl/ssl_browsertest.cc b/chrome/browser/ssl/ssl_browsertest.cc index 658b419..b1020fb 100644 --- a/chrome/browser/ssl/ssl_browsertest.cc +++ b/chrome/browser/ssl/ssl_browsertest.cc
@@ -63,7 +63,6 @@ #include "chrome/browser/ssl/ssl_error_controller_client.h" #include "chrome/browser/ui/browser.h" #include "chrome/browser/ui/browser_commands.h" -#include "chrome/browser/ui/browser_finder.h" #include "chrome/browser/ui/browser_tabstrip.h" #include "chrome/browser/ui/browser_window/public/browser_window_interface.h" #include "chrome/browser/ui/browser_window/public/profile_browser_collection.h"
diff --git a/chrome/browser/subresource_filter/subresource_filter_browsertest.cc b/chrome/browser/subresource_filter/subresource_filter_browsertest.cc index f7cb4f0..15c1c9a 100644 --- a/chrome/browser/subresource_filter/subresource_filter_browsertest.cc +++ b/chrome/browser/subresource_filter/subresource_filter_browsertest.cc
@@ -28,7 +28,6 @@ #include "chrome/browser/subresource_filter/subresource_filter_browser_test_harness.h" #include "chrome/browser/ui/browser.h" #include "chrome/browser/ui/browser_commands.h" -#include "chrome/browser/ui/browser_finder.h" #include "chrome/browser/ui/navigator/browser_navigator.h" #include "chrome/browser/ui/navigator/browser_navigator_params.h" #include "chrome/browser/ui/tabs/tab_strip_model.h"
diff --git a/chrome/browser/sync/android/java/src/org/chromium/chrome/browser/sync/ui/batch_upload_card/BatchUploadCardBinder.java b/chrome/browser/sync/android/java/src/org/chromium/chrome/browser/sync/ui/batch_upload_card/BatchUploadCardBinder.java index 51969d69..9cf3e03 100644 --- a/chrome/browser/sync/android/java/src/org/chromium/chrome/browser/sync/ui/batch_upload_card/BatchUploadCardBinder.java +++ b/chrome/browser/sync/android/java/src/org/chromium/chrome/browser/sync/ui/batch_upload_card/BatchUploadCardBinder.java
@@ -25,14 +25,14 @@ */ public static void bind(PropertyModel model, View view, PropertyKey propertyKey) { if (BatchUploadCardProperties.DESCRIPTION_TEXT == propertyKey) { - TextView text = (TextView) view.findViewById(R.id.signin_settings_card_description); + TextView text = view.findViewById(R.id.signin_settings_card_description); text.setText(model.get(BatchUploadCardProperties.DESCRIPTION_TEXT)); } else if (BatchUploadCardProperties.BUTTON_TEXT == propertyKey) { - Button button = (Button) view.findViewById(R.id.signin_settings_card_button); + Button button = view.findViewById(R.id.signin_settings_card_button); button.setText(model.get(BatchUploadCardProperties.BUTTON_TEXT)); button.setOnClickListener(model.get(BatchUploadCardProperties.ON_CLICK_LISTENER)); } else if (BatchUploadCardProperties.ICON == propertyKey) { - ImageView image = (ImageView) view.findViewById(R.id.signin_settings_card_icon); + ImageView image = view.findViewById(R.id.signin_settings_card_icon); image.setImageDrawable(model.get(BatchUploadCardProperties.ICON)); } }
diff --git a/chrome/browser/sync/sessions/sync_sessions_router_tab_helper_browsertest.cc b/chrome/browser/sync/sessions/sync_sessions_router_tab_helper_browsertest.cc index 653e69c3..a6b5dd3 100644 --- a/chrome/browser/sync/sessions/sync_sessions_router_tab_helper_browsertest.cc +++ b/chrome/browser/sync/sessions/sync_sessions_router_tab_helper_browsertest.cc
@@ -9,7 +9,6 @@ #include "chrome/browser/sync/sessions/sync_sessions_web_contents_router_factory.h" #include "chrome/browser/translate/chrome_translate_client.h" #include "chrome/browser/ui/browser.h" -#include "chrome/browser/ui/browser_finder.h" #include "chrome/browser/ui/tabs/tab_strip_model.h" #include "chrome/test/base/in_process_browser_test.h" #include "chrome/test/base/ui_test_utils.h"
diff --git a/chrome/browser/sync/test/integration/sync_test.cc b/chrome/browser/sync/test/integration/sync_test.cc index fb4f04c1..750dfed8 100644 --- a/chrome/browser/sync/test/integration/sync_test.cc +++ b/chrome/browser/sync/test/integration/sync_test.cc
@@ -127,7 +127,6 @@ #include "chrome/browser/sync/test/integration/sync_test_utils_android.h" #else // BUILDFLAG(IS_ANDROID) #include "chrome/browser/ui/browser.h" -#include "chrome/browser/ui/browser_finder.h" #include "chrome/browser/ui/browser_tabstrip.h" #include "chrome/browser/ui/browser_window.h" #include "chrome/browser/ui/browser_window/public/browser_collection_observer.h"
diff --git a/chrome/browser/tab/BUILD.gn b/chrome/browser/tab/BUILD.gn index a3b14ed0..5a61977 100644 --- a/chrome/browser/tab/BUILD.gn +++ b/chrome/browser/tab/BUILD.gn
@@ -100,6 +100,7 @@ "//chrome/browser/profiles/android:java", "//chrome/browser/ui/android/native_page:java", "//chrome/browser/ui/android/pdf:java", + "//chrome/browser/ui/android/pdf:pdf_info_java", "//chrome/browser/ui/android/strings:ui_strings_grd", "//chrome/browser/util:java", "//components/autofill/android:autofill_features_java",
diff --git a/chrome/browser/tab/java/src/org/chromium/chrome/browser/tab/SadTab.java b/chrome/browser/tab/java/src/org/chromium/chrome/browser/tab/SadTab.java index 2487076..7f9d1c2 100644 --- a/chrome/browser/tab/java/src/org/chromium/chrome/browser/tab/SadTab.java +++ b/chrome/browser/tab/java/src/org/chromium/chrome/browser/tab/SadTab.java
@@ -176,18 +176,18 @@ sadTabView.setLayoutParams( new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT)); - TextView titleText = (TextView) sadTabView.findViewById(R.id.sad_tab_title); + TextView titleText = sadTabView.findViewById(R.id.sad_tab_title); int titleTextId = showSendFeedbackView ? R.string.sad_tab_reload_title : R.string.sad_tab_title; titleText.setText(titleTextId); if (showSendFeedbackView) intializeSuggestionsViews(context, sadTabView, isIncognito); - TextView messageText = (TextView) sadTabView.findViewById(R.id.sad_tab_message); + TextView messageText = sadTabView.findViewById(R.id.sad_tab_message); messageText.setText(getHelpMessage(context, suggestionAction, showSendFeedbackView)); messageText.setMovementMethod(LinkMovementMethod.getInstance()); - Button button = (Button) sadTabView.findViewById(R.id.sad_tab_button); + Button button = sadTabView.findViewById(R.id.sad_tab_button); int buttonTextId = showSendFeedbackView ? R.string.sad_tab_send_feedback_label @@ -252,7 +252,7 @@ suggestionsTitle.setVisibility(View.VISIBLE); suggestionsTitle.setText(R.string.sad_tab_reload_try); - TextView suggestions = (TextView) sadTabView.findViewById(R.id.sad_tab_suggestions); + TextView suggestions = sadTabView.findViewById(R.id.sad_tab_suggestions); suggestions.setVisibility(View.VISIBLE); SpannableStringBuilder spannableString = new SpannableStringBuilder();
diff --git a/chrome/browser/tab_list/mock_tab_list_interface.h b/chrome/browser/tab_list/mock_tab_list_interface.h index e814920..a571832 100644 --- a/chrome/browser/tab_list/mock_tab_list_interface.h +++ b/chrome/browser/tab_list/mock_tab_list_interface.h
@@ -114,7 +114,7 @@ MoveTabToWindow, (tabs::TabHandle, SessionID, int), (override)); - MOCK_METHOD(void, + MOCK_METHOD(bool, MoveTabGroupToWindow, (tab_groups::TabGroupId, SessionID, int), (override));
diff --git a/chrome/browser/tab_list/tab_list_interface.h b/chrome/browser/tab_list/tab_list_interface.h index 13734f8c..c3391fc 100644 --- a/chrome/browser/tab_list/tab_list_interface.h +++ b/chrome/browser/tab_list/tab_list_interface.h
@@ -203,8 +203,8 @@ // will be inserted with the first tab at `index` in the destination tab list. // This will no-op if the tab group is not present in this TabListInterface or // the destination window does not exist. `index` may be adjusted as necessary - // to ensure the tab group is in a valid position. - virtual void MoveTabGroupToWindow(tab_groups::TabGroupId group_id, + // to ensure the tab group is in a valid position. Returns true on success. + virtual bool MoveTabGroupToWindow(tab_groups::TabGroupId group_id, SessionID destination_window_id, int destination_index) = 0;
diff --git a/chrome/browser/task_manager/mock_web_contents_task_manager.h b/chrome/browser/task_manager/mock_web_contents_task_manager.h index 56c9d6b..7ef67438 100644 --- a/chrome/browser/task_manager/mock_web_contents_task_manager.h +++ b/chrome/browser/task_manager/mock_web_contents_task_manager.h
@@ -12,9 +12,12 @@ #include "base/memory/raw_ptr.h" #include "base/strings/utf_string_conversions.h" +#include "chrome/browser/task_manager/providers/task.h" #include "chrome/browser/task_manager/providers/task_provider_observer.h" #include "chrome/browser/task_manager/providers/web_contents/web_contents_tags_manager.h" #include "chrome/browser/task_manager/providers/web_contents/web_contents_task_provider.h" +#include "chrome/grit/generated_resources.h" +#include "ui/base/l10n/l10n_util.h" namespace task_manager { @@ -55,6 +58,18 @@ return task_titles; } + // Filter out tool tasks. + std::vector<raw_ptr<Task, VectorExperimental>> NonToolTasks() { + std::u16string tool_prefix = + l10n_util::GetStringFUTF16(IDS_TASK_MANAGER_TOOL_PREFIX, u""); + std::vector<raw_ptr<Task, VectorExperimental>> non_tool_tasks; + std::ranges::copy_if(tasks_, std::back_inserter(non_tool_tasks), + [&](const auto& task) { + return !task->title().starts_with(tool_prefix); + }); + return non_tool_tasks; + } + private: std::vector<raw_ptr<Task, VectorExperimental>> tasks_; WebContentsTaskProvider provider_;
diff --git a/chrome/browser/task_manager/providers/web_contents/back_forward_cache_task.cc b/chrome/browser/task_manager/providers/web_contents/back_forward_cache_task.cc index 7042cea..9b955b1 100644 --- a/chrome/browser/task_manager/providers/web_contents/back_forward_cache_task.cc +++ b/chrome/browser/task_manager/providers/web_contents/back_forward_cache_task.cc
@@ -11,6 +11,7 @@ #include "chrome/grit/generated_resources.h" #include "content/public/browser/browser_context.h" #include "content/public/browser/render_frame_host.h" +#include "content/public/browser/security_principal.h" #include "content/public/browser/site_instance.h" #include "content/public/browser/web_contents.h" #include "ui/base/l10n/l10n_util.h" @@ -27,7 +28,8 @@ // TODO(crbug.com/40775860): Display the page title instead of the site URL // for main frames. - const GURL& site_url = site_instance->GetSiteURL(); + const GURL& site_url = + site_instance->GetSecurityPrincipal().GetDeprecatedSiteURL(); const std::u16string name = base::UTF8ToUTF16(site_url.spec()); const bool is_main_frame = !parent_task;
diff --git a/chrome/browser/task_manager/providers/web_contents/fenced_frame_task.cc b/chrome/browser/task_manager/providers/web_contents/fenced_frame_task.cc index 7a98670..de98e36 100644 --- a/chrome/browser/task_manager/providers/web_contents/fenced_frame_task.cc +++ b/chrome/browser/task_manager/providers/web_contents/fenced_frame_task.cc
@@ -8,6 +8,7 @@ #include "chrome/grit/generated_resources.h" #include "content/public/browser/browser_context.h" #include "content/public/browser/render_frame_host.h" +#include "content/public/browser/security_principal.h" #include "content/public/browser/site_instance.h" #include "ui/base/l10n/l10n_util.h" #include "url/gurl.h" @@ -43,7 +44,8 @@ const int message_id = site_instance_->GetBrowserContext()->IsOffTheRecord() ? IDS_TASK_MANAGER_FENCED_FRAME_INCOGNITO_PREFIX : IDS_TASK_MANAGER_FENCED_FRAME_PREFIX; - const auto title = base::UTF8ToUTF16(site_instance_->GetSiteURL().spec()); + const auto title = base::UTF8ToUTF16( + site_instance_->GetSecurityPrincipal().GetDeprecatedSiteURL().spec()); return l10n_util::GetStringFUTF16(message_id, title); }
diff --git a/chrome/browser/task_manager/providers/web_contents/subframe_task.cc b/chrome/browser/task_manager/providers/web_contents/subframe_task.cc index dbe3fbf9..6fcb571 100644 --- a/chrome/browser/task_manager/providers/web_contents/subframe_task.cc +++ b/chrome/browser/task_manager/providers/web_contents/subframe_task.cc
@@ -13,6 +13,7 @@ #include "content/public/browser/browser_context.h" #include "content/public/browser/render_frame_host.h" #include "content/public/browser/render_process_host.h" +#include "content/public/browser/security_principal.h" #include "content/public/browser/site_instance.h" #include "pdf/buildflags.h" #include "ui/base/l10n/l10n_util.h" @@ -73,7 +74,8 @@ // For Isolated Web Apps, subframe rows display IWA name: // "Subframe: Example Isolated Web App" - const GURL& site_url = site_instance_->GetSiteURL(); + const GURL& site_url = + site_instance_->GetSecurityPrincipal().GetDeprecatedSiteURL(); Profile* profile = Profile::FromBrowserContext(site_instance_->GetBrowserContext());
diff --git a/chrome/browser/task_manager/providers/web_contents/subframe_task_browsertest.cc b/chrome/browser/task_manager/providers/web_contents/subframe_task_browsertest.cc index 0be5f7ea..1bbb5d5 100644 --- a/chrome/browser/task_manager/providers/web_contents/subframe_task_browsertest.cc +++ b/chrome/browser/task_manager/providers/web_contents/subframe_task_browsertest.cc
@@ -86,20 +86,6 @@ IDS_TASK_MANAGER_PDF_SUBFRAME_INCOGNITO_PREFIX, base::UTF8ToUTF16(title)); } -// Filter out tool tasks. -std::vector<raw_ptr<Task, VectorExperimental>> NonToolTasks( - std::vector<raw_ptr<Task, VectorExperimental>> tasks) { - std::u16string tool_prefix = - l10n_util::GetStringFUTF16(IDS_TASK_MANAGER_TOOL_PREFIX, u""); - - std::vector<raw_ptr<Task, VectorExperimental>> non_tool_tasks; - std::ranges::copy_if(tasks, std::back_inserter(non_tool_tasks), - [&](const auto& task) { - return !task->title().starts_with(tool_prefix); - }); - return non_tool_tasks; -} - } // namespace // A test for OOPIFs and how they show up in the task manager as @@ -149,14 +135,14 @@ task_manager.StartObserving(); // Currently only the about:blank page. - ASSERT_THAT(MockWebContentsTaskManager::TaskTitles( - NonToolTasks(task_manager.tasks())), - testing::ElementsAre(PrefixExpectedTabTitle("about:blank"))); + ASSERT_THAT( + MockWebContentsTaskManager::TaskTitles(task_manager.NonToolTasks()), + testing::ElementsAre(PrefixExpectedTabTitle("about:blank"))); NavigateTo(kCrossSitePageUrl); // Whether sites are isolated or not, we expect to have at least one tab // contents task. - auto tasks = NonToolTasks(task_manager.tasks()); + auto tasks = task_manager.NonToolTasks(); ASSERT_GE(tasks.size(), 1u); const Task* cross_site_task = tasks[0]; EXPECT_EQ(cross_site_task->GetType(), Task::RENDERER); @@ -190,7 +176,7 @@ // page is saved in the back-forward cache. NavigateTo(kSimplePageUrl); - tasks = NonToolTasks(task_manager.tasks()); + tasks = task_manager.NonToolTasks(); ASSERT_EQ( tasks.size(), content::BackForwardCache::IsBackForwardCacheFeatureEnabled() ? 4u : 1u); @@ -243,7 +229,7 @@ NavigateTo(kCrossSitePageUrl); // We expect SubframeTasks for b.com and c.com, in either order. - auto tasks = NonToolTasks(task_manager.tasks()); + auto tasks = task_manager.NonToolTasks(); ASSERT_THAT(MockWebContentsTaskManager::TaskTitles(tasks), testing::ElementsAre( PrefixExpectedTabTitle("cross-site iframe test").c_str(), @@ -311,7 +297,7 @@ clear_port.ClearPort(); GURL server_url_without_port = server_url.ReplaceComponents(clear_port); - auto tasks = NonToolTasks(task_manager.tasks()); + auto tasks = task_manager.NonToolTasks(); ASSERT_THAT( MockWebContentsTaskManager::TaskTitles(tasks), testing::ElementsAre( @@ -348,7 +334,7 @@ clear_port.ClearPort(); GURL server_url_without_port = server_url.ReplaceComponents(clear_port); - auto tasks = NonToolTasks(task_manager.tasks()); + auto tasks = task_manager.NonToolTasks(); ASSERT_THAT( MockWebContentsTaskManager::TaskTitles(tasks), testing::ElementsAre(
diff --git a/chrome/browser/task_manager/providers/web_contents/tab_contents_tag_browsertest.cc b/chrome/browser/task_manager/providers/web_contents/tab_contents_tag_browsertest.cc index 3e3dea1..e2e1397 100644 --- a/chrome/browser/task_manager/providers/web_contents/tab_contents_tag_browsertest.cc +++ b/chrome/browser/task_manager/providers/web_contents/tab_contents_tag_browsertest.cc
@@ -28,6 +28,7 @@ #include "content/public/browser/back_forward_cache.h" #include "content/public/browser/favicon_status.h" #include "content/public/browser/navigation_entry.h" +#include "content/public/browser/security_principal.h" #include "content/public/browser/site_instance.h" #include "content/public/test/browser_test.h" #include "content/public/test/fenced_frame_test_util.h" @@ -302,7 +303,8 @@ ->tab_strip_model() ->GetActiveWebContents() ->GetSiteInstance() - ->GetSiteURL(); + ->GetSecurityPrincipal() + .GetDeprecatedSiteURL(); // Check that the task manager uses the specified favicon for the page. base::FilePath test_dir;
diff --git a/chrome/browser/task_manager/providers/web_contents/web_contents_task_provider.cc b/chrome/browser/task_manager/providers/web_contents/web_contents_task_provider.cc index b62ada1..1a0a353 100644 --- a/chrome/browser/task_manager/providers/web_contents/web_contents_task_provider.cc +++ b/chrome/browser/task_manager/providers/web_contents/web_contents_task_provider.cc
@@ -331,8 +331,9 @@ // We only need to update tasks for primary main frame navigations except for // MPArch guest view. // - // FencedFrame task gets the title from |SiteInstance::GetSiteURL()| which - // does not change for the same site instance, thus no need to update; + // FencedFrame task gets the title from + // |SecurityPrincipal::GetDeprecatedSiteURL()| which does not change for the + // same site instance, thus no need to update; // prerender does not support multiple navigations thus no need to update its // title. if (!navigation_handle->IsInPrimaryMainFrame() &&
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 fb4e019d..3b789a1 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
@@ -595,6 +595,9 @@ TEST_P(TouchToFillDelegateAndroidImplPaymentMethodUnitTest, TryToShowTouchToFillFailsIfShownBeforeAndShouldReshow_FlagOff) { + base::test::ScopedFeatureList feature; + feature.InitAndDisableFeature( + features::kAutofillEnableTouchToFillReshowForBnpl); TryToShowTouchToFill(/*expected_success=*/true); ASSERT_TRUE(touch_to_fill_delegate_->IsShowingTouchToFill());
diff --git a/chrome/browser/ui/BUILD.gn b/chrome/browser/ui/BUILD.gn index d2e79136..12950d5 100644 --- a/chrome/browser/ui/BUILD.gn +++ b/chrome/browser/ui/BUILD.gn
@@ -39,6 +39,18 @@ allow_list_name = "WebUIName" } +source_set("accelerator_utils_headers") { + public = [ "accelerator_utils.h" ] +} + +source_set("simple_message_box_headers") { + public = [ "simple_message_box.h" ] + public_deps = [ + "//base", + "//ui/gfx", + ] +} + # Use a static library here because many test binaries depend on this but don't # require many files from it. This makes linking more efficient. # TODO(https://crbug.com/353332589): Once //chrome/browser is properly @@ -47,7 +59,6 @@ # circular includes from each other. static_library("ui") { sources = [ - "accelerator_utils.h", "browser_ui_prefs.cc", "browser_ui_prefs.h", "confirm_bubble.h", @@ -62,7 +73,6 @@ "recently_audible_helper.h", "screen_capture_notification_ui.h", "session_crashed_bubble.h", - "simple_message_box.h", "simple_message_box_internal.cc", "simple_message_box_internal.h", "status_bubble.h", @@ -227,6 +237,8 @@ # ui_public_dependencies. # New dependencies inside of //chrome/browser that generate header files # should be added to //chrome/browser:browser_generated_files. + ":accelerator_utils_headers", + ":simple_message_box_headers", "//chrome/browser:browser_public_dependencies", "//chrome/browser:primitives", "//chrome/browser/feedback", @@ -767,10 +779,6 @@ "//chrome/browser/sessions:impl", ] - if (is_android) { - sources += [ "//chrome/browser/ui/android/browser_navigator_android.cc" ] - } - # Desktop-Android-only dependencies. if (is_desktop_android) { deps += [ "//chrome/browser/ui/android/extensions/windowing/internal" ] @@ -998,117 +1006,6 @@ if (is_android) { sources += [ - "android/accelerator_utils_android.cc", - "android/android_about_app_info.cc", - "android/android_about_app_info.h", - "android/android_profile_browser_collection_service.cc", - "android/android_profile_browser_collection_service.h", - "android/android_profile_browser_collection_service_factory.cc", - "android/android_profile_browser_collection_service_factory.h", - "android/autofill/at_memory_bottom_sheet_bridge.cc", - "android/autofill/at_memory_bottom_sheet_bridge.h", - "android/autofill/at_memory_bottom_sheet_delegate_android.cc", - "android/autofill/at_memory_bottom_sheet_delegate_android.h", - "android/autofill/authenticator_selection_dialog_view_android.cc", - "android/autofill/authenticator_selection_dialog_view_android.h", - "android/autofill/autofill_ai_save_update_entity_flow_manager.cc", - "android/autofill/autofill_ai_save_update_entity_flow_manager.h", - "android/autofill/autofill_ai_save_update_entity_prompt_view_android.cc", - "android/autofill/autofill_ai_save_update_entity_prompt_view_android.h", - "android/autofill/autofill_cvc_save_message_delegate.cc", - "android/autofill/autofill_cvc_save_message_delegate.h", - "android/autofill/autofill_error_dialog_view_android.cc", - "android/autofill/autofill_error_dialog_view_android.h", - "android/autofill/autofill_keyboard_accessory_view_impl.cc", - "android/autofill/autofill_keyboard_accessory_view_impl.h", - "android/autofill/autofill_progress_dialog_view_android.cc", - "android/autofill/autofill_progress_dialog_view_android.h", - "android/autofill/autofill_save_card_bottom_sheet_bridge.cc", - "android/autofill/autofill_save_card_bottom_sheet_bridge.h", - "android/autofill/autofill_save_card_delegate_android.cc", - "android/autofill/autofill_save_card_delegate_android.h", - "android/autofill/autofill_save_iban_bottom_sheet_bridge.cc", - "android/autofill/autofill_save_iban_bottom_sheet_bridge.h", - "android/autofill/autofill_save_iban_delegate.cc", - "android/autofill/autofill_save_iban_delegate.h", - "android/autofill/autofill_vcn_enroll_bottom_sheet_bridge.cc", - "android/autofill/autofill_vcn_enroll_bottom_sheet_bridge.h", - "android/autofill/card_expiration_date_fix_flow_view_android.cc", - "android/autofill/card_expiration_date_fix_flow_view_android.h", - "android/autofill/card_name_fix_flow_view_android.cc", - "android/autofill/card_name_fix_flow_view_android.h", - "android/autofill/card_unmask_prompt_view_android.cc", - "android/autofill/card_unmask_prompt_view_android.h", - "android/autofill/credit_card_scanner_view_android.cc", - "android/autofill/credit_card_scanner_view_android.h", - "android/autofill/dialog/autofill_dialog_view_android.cc", - "android/autofill/dialog/autofill_dialog_view_android.h", - "android/autofill/manual_filling_view_android.cc", - "android/autofill/manual_filling_view_android.h", - "android/autofill/otp_verification_dialog_view_android.cc", - "android/autofill/otp_verification_dialog_view_android.h", - "android/autofill/payments/payments_window_bridge.cc", - "android/autofill/payments/payments_window_bridge.h", - "android/autofill/payments/payments_window_delegate.h", - "android/autofill/save_update_address_profile_flow_manager.cc", - "android/autofill/save_update_address_profile_flow_manager.h", - "android/autofill/save_update_address_profile_prompt_view_android.cc", - "android/autofill/save_update_address_profile_prompt_view_android.h", - "android/autofill/snackbar/autofill_snackbar_view_android.cc", - "android/autofill/snackbar/autofill_snackbar_view_android.h", - "android/autofill/virtual_card_utils.cc", - "android/autofill/virtual_card_utils.h", - "android/chrome_http_auth_handler.cc", - "android/chrome_http_auth_handler.h", - "android/chrome_javascript_app_modal_dialog_android.cc", - "android/context_menu_helper.cc", - "android/context_menu_helper.h", - "android/device_dialog/chrome_bluetooth_chooser_android_delegate.cc", - "android/device_dialog/chrome_bluetooth_chooser_android_delegate.h", - "android/device_dialog/chrome_bluetooth_scanning_prompt_android_delegate.cc", - "android/device_dialog/chrome_bluetooth_scanning_prompt_android_delegate.h", - "android/device_dialog/serial_chooser_dialog_android.cc", - "android/device_dialog/serial_chooser_dialog_android.h", - "android/device_dialog/usb_chooser_dialog_android.cc", - "android/device_dialog/usb_chooser_dialog_android.h", - "android/exclusive_access/exclusive_access_bubble_android.cc", - "android/exclusive_access/exclusive_access_bubble_android.h", - "android/exclusive_access/exclusive_access_context_android.cc", - "android/exclusive_access/exclusive_access_context_android.h", - "android/exclusive_access/exclusive_access_manager_android.cc", - "android/exclusive_access/exclusive_access_manager_android.h", - "android/infobars/infobar_container_android.cc", - "android/infobars/infobar_container_android.h", - "android/infobars/simple_confirm_infobar_builder.cc", - "android/login_handler_android.cc", - "android/logo/logo_bridge.cc", - "android/logo/logo_bridge.h", - "android/omnibox/omnibox_view_util.cc", - "android/omnibox/omnibox_view_util.h", - "android/overlay/immersive_overlay_window_android.cc", - "android/overlay/overlay_window_android.cc", - "android/overlay/overlay_window_android.h", - "android/overlay/overlay_window_android_factory.cc", - "android/overlay/picture_in_picture_overlay_window_android.cc", - "android/passwords/all_passwords_bottom_sheet_view.h", - "android/passwords/all_passwords_bottom_sheet_view_impl.cc", - "android/passwords/all_passwords_bottom_sheet_view_impl.h", - "android/passwords/credential_leak_dialog_view_android.cc", - "android/passwords/credential_leak_dialog_view_android.h", - "android/safe_browsing/password_reuse_dialog_view_android.cc", - "android/safe_browsing/password_reuse_dialog_view_android.h", - "android/simple_message_box_android.cc", - "android/ssl_client_certificate_selector.cc", - "android/status_tray_android.cc", - "android/tab_contents/chrome_web_contents_view_delegate_android.cc", - "android/tab_contents/chrome_web_contents_view_delegate_android.h", - "android/toolbar/adaptive_toolbar_bridge.cc", - "android/toolbar/adaptive_toolbar_bridge.h", - "android/toolbar/adaptive_toolbar_enums.h", - "android/toolbar/location_bar_model_android.cc", - "android/toolbar/location_bar_model_android.h", - "android/webid/account_selection_view_android.cc", - "android/webid/account_selection_view_android.h", "browser_otr_state_android.cc", "screen_capture_notification_ui_stub.cc", "webui/bookmarks/bookmarks_ui_android.cc", @@ -1142,7 +1039,6 @@ "//chrome/browser/password_manager/android:utils", "//chrome/browser/resources/feed_internals:resources", "//chrome/browser/touch_to_fill/autofill/android", - "//chrome/browser/ui/android/autofill/internal:jni_headers", "//chrome/browser/ui/android/hats", "//chrome/browser/ui/android/hats:impl", "//chrome/browser/ui/android/tab_model", @@ -1206,49 +1102,10 @@ # system deps += [ "//chrome/browser/ui/color:color_headers" ] - # TODO(b/335257259): Resolve circular deps. - # Any circular includes must depend on the target "//chrome/browser:browser_public_dependencies". - allow_circular_includes_from += [ "//chrome/browser/facilitated_payments" ] - - # This can be removed when browser_navigator.h is modularized (see below TODOs). - allow_circular_includes_from += - [ "//chrome/browser/ui/browser_window/internal" ] - - # TODO(crbug.com/504985497): Remove this circular dependency. - allow_circular_includes_from += - [ "//chrome/browser/keyboard_accessory/android:impl" ] - - if (enable_extensions_core) { - sources += [ - "android/extensions/extension_action_context_menu_bridge.cc", - "android/extensions/extension_action_context_menu_bridge.h", - "android/extensions/extension_action_delegate_android.cc", - "android/extensions/extension_action_delegate_android.h", - "android/extensions/extension_action_popup_contents.cc", - "android/extensions/extension_action_popup_contents.h", - "android/extensions/extension_actions_bridge.cc", - "android/extensions/extension_actions_bridge.h", - "android/extensions/extension_developer_private_bridge.cc", - "android/extensions/extension_developer_private_bridge.h", - "android/extensions/extension_install_dialog_view_android.cc", - "android/extensions/extension_install_dialog_view_android.h", - "android/extensions/extension_keybinding_registry_android.cc", - "android/extensions/extension_keybinding_registry_android.h", - "android/extensions/extension_util_bridge.cc", - "android/extensions/extension_util_bridge.h", - "android/extensions/extensions_menu_delegate_android.cc", - "android/extensions/extensions_menu_delegate_android.h", - "android/extensions/extensions_toolbar_android.cc", - "android/extensions/extensions_toolbar_android.h", - ] - - deps += [ - "//chrome/browser/extensions/api/omnibox", - "//chrome/browser/extensions/api/web_navigation", - "//chrome/browser/ui/android/extensions:jni_headers", - "//extensions/browser:extension_action_enums", - ] - } + deps += [ + "//chrome/browser/ui/android", + "//chrome/browser/ui/android:context_menu_helper", + ] } else { # !is_android sources += [ @@ -1806,6 +1663,7 @@ "//chrome/browser/ui/user_education:impl", "//chrome/browser/ui/views/contextual_tasks", "//chrome/browser/ui/views/contextual_tasks:impl", + "//chrome/browser/ui/views/extensions:extensions_menu_handler", "//chrome/browser/ui/views/js_optimization", "//chrome/browser/ui/views/location_bar", "//chrome/browser/ui/views/new_tab_footer", @@ -3291,10 +3149,10 @@ "webui/browser_switch/browser_switch_ui.h", "webui/intro/intro_handler.cc", "webui/intro/intro_handler.h", - "webui/intro/sign_in_celebration_handler.cc", - "webui/intro/sign_in_celebration_handler.h", "webui/intro/intro_ui.cc", "webui/intro/intro_ui.h", + "webui/intro/sign_in_celebration_handler.cc", + "webui/intro/sign_in_celebration_handler.h", "webui/on_device_translation_internals/on_device_translation_internals_page_handler_impl.cc", "webui/on_device_translation_internals/on_device_translation_internals_page_handler_impl.h", "webui/on_device_translation_internals/on_device_translation_internals_ui.cc", @@ -3330,9 +3188,9 @@ "//chrome/browser/ui/webui/app_home:impl", "//chrome/browser/ui/webui/app_settings", "//chrome/browser/ui/webui/app_settings:impl", + "//chrome/browser/ui/webui/intro:mojo_bindings", "//chrome/browser/ui/webui/signin:profile", "//chrome/browser/ui/webui/signin:profile_impl", - "//chrome/browser/ui/webui/intro:mojo_bindings", "//chrome/browser/ui/webui/updater:mojo_bindings", "//chrome/browser/updater", "//chrome/enterprise_companion:constants_prod", @@ -4022,7 +3880,6 @@ "views/extensions/extensions_menu_delegate_desktop.h", "views/extensions/extensions_menu_entry_view.cc", "views/extensions/extensions_menu_entry_view.h", - "views/extensions/extensions_menu_handler.h", "views/extensions/extensions_menu_item_view.cc", "views/extensions/extensions_menu_item_view.h", "views/extensions/extensions_menu_main_page_view.cc", @@ -5434,6 +5291,7 @@ ] deps += [ "//chrome/browser/extensions", + "//chrome/browser/extensions/api/web_navigation", "//extensions/common", "//extensions/strings", ] @@ -5970,9 +5828,7 @@ } if (is_android) { - sources += [ - "android/autofill/save_update_address_profile_flow_manager_test_api.h", - ] + deps += [ "//chrome/browser/ui/android/autofill:test_support" ] } else { sources += [ "webui/help/test_version_updater.cc", @@ -6007,12 +5863,6 @@ public_deps = [ "//ui/base/interaction" ] } -if (is_android) { - java_cpp_enum("adaptive_toolbar_enums_java") { - sources = [ "android/toolbar/adaptive_toolbar_enums.h" ] - } -} - if (use_aura) { source_set("overscroll_pref_manager") { sources = [
diff --git a/chrome/browser/ui/android/BUILD.gn b/chrome/browser/ui/android/BUILD.gn new file mode 100644 index 0000000..3e5c63e --- /dev/null +++ b/chrome/browser/ui/android/BUILD.gn
@@ -0,0 +1,109 @@ +# Copyright 2026 The Chromium Authors +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +import("//extensions/buildflags/buildflags.gni") + +if (is_android) { + source_set("android") { + sources = [ + "accelerator_utils_android.cc", + "android_about_app_info.cc", + "android_about_app_info.h", + "android_profile_browser_collection_service.cc", + "android_profile_browser_collection_service.h", + "android_profile_browser_collection_service_factory.cc", + "android_profile_browser_collection_service_factory.h", + "browser_navigator_android.cc", + "chrome_http_auth_handler.cc", + "chrome_http_auth_handler.h", + "chrome_javascript_app_modal_dialog_android.cc", + "login_handler_android.cc", + "simple_message_box_android.cc", + "ssl_client_certificate_selector.cc", + "status_tray_android.cc", + ] + deps = [ + "//base", + "//chrome/android:chrome_jni_headers", + "//chrome/browser/android:tabs_public", + "//chrome/browser/profiles", + "//chrome/browser/ssl", + "//chrome/browser/status_icons", + "//chrome/browser/tab_list", + "//chrome/browser/ui:accelerator_utils_headers", + "//chrome/browser/ui:simple_message_box_headers", + "//chrome/browser/ui/android/tab_model", + "//chrome/browser/ui/browser_window", + "//chrome/browser/ui/browser_window:create_browser_window", + "//chrome/browser/ui/javascript_dialogs", + "//chrome/browser/ui/login", + "//chrome/browser/ui/navigator", + "//chrome/common:url_constants", + "//components/browser_ui/client_certificate/android", + "//components/embedder_support:user_agent", + "//components/javascript_dialogs", + "//components/keyed_service/content", + "//components/keyed_service/core", + "//components/password_manager/core/browser", + "//components/tabs:public", + "//content/public/browser", + "//net", + "//ui/android", + "//ui/base", + "//ui/events", + "//ui/events:platform_event_android", + "//url", + ] + } + + source_set("context_menu_helper") { + sources = [ + "context_menu_helper.cc", + "context_menu_helper.h", + ] + deps = [ + "//base", + "//chrome/android:chrome_jni_headers", + "//chrome/common:mojo_bindings", + "//components/embedder_support/android:context_menu", + "//content/public/browser", + "//extensions/buildflags", + "//mojo/public/cpp/bindings", + "//third_party/blink/public/mojom:mojom_platform", + "//ui/android", + "//ui/gfx/geometry", + "//ui/menus", + "//url", + ] + if (enable_desktop_android_extensions) { + # extension_menu_model_android.h is in the chrome/browser/extensions/ + # directory; dep on that target to make it accessible. + deps += [ "//chrome/browser/extensions" ] + } + } +} + +# TODO(crbug.com/473636857): Include once all Android builds correctly use BrowserWindowInterface. +if (is_desktop_android) { + source_set("browser_tests") { + testonly = true + defines = [ "HAS_OUT_OF_PROC_TEST_RUNNER" ] + sources = [ "browser_navigator_android_browsertest.cc" ] + deps = [ + "//base", + "//chrome/browser/android:tabs_public", + "//chrome/browser/flags:flags_android", + "//chrome/browser/tab_list", + "//chrome/browser/ui", + "//chrome/browser/ui/browser_window", + "//chrome/browser/ui/browser_window:create_browser_window", + "//chrome/browser/ui/browser_window/test:android_browser_test_support", + "//chrome/browser/ui/navigator", + "//chrome/test:test_support_ui_android", + "//content/public/browser", + "//content/test:test_support", + "//net:test_support", + ] + } +}
diff --git a/chrome/browser/ui/android/autofill/BUILD.gn b/chrome/browser/ui/android/autofill/BUILD.gn new file mode 100644 index 0000000..f27e413 --- /dev/null +++ b/chrome/browser/ui/android/autofill/BUILD.gn
@@ -0,0 +1,168 @@ +# Copyright 2026 The Chromium Authors +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +import("//build/config/android/config.gni") + +source_set("autofill") { + sources = [ + "at_memory_bottom_sheet_bridge.cc", + "at_memory_bottom_sheet_bridge.h", + "at_memory_bottom_sheet_delegate_android.cc", + "at_memory_bottom_sheet_delegate_android.h", + "authenticator_selection_dialog_view_android.cc", + "authenticator_selection_dialog_view_android.h", + "autofill_ai_save_update_entity_flow_manager.cc", + "autofill_ai_save_update_entity_flow_manager.h", + "autofill_ai_save_update_entity_prompt_view_android.cc", + "autofill_ai_save_update_entity_prompt_view_android.h", + "autofill_cvc_save_message_delegate.cc", + "autofill_cvc_save_message_delegate.h", + "autofill_error_dialog_view_android.cc", + "autofill_error_dialog_view_android.h", + "autofill_keyboard_accessory_view_impl.cc", + "autofill_keyboard_accessory_view_impl.h", + "autofill_progress_dialog_view_android.cc", + "autofill_progress_dialog_view_android.h", + "autofill_save_card_bottom_sheet_bridge.cc", + "autofill_save_card_bottom_sheet_bridge.h", + "autofill_save_card_delegate_android.cc", + "autofill_save_card_delegate_android.h", + "autofill_save_iban_bottom_sheet_bridge.cc", + "autofill_save_iban_bottom_sheet_bridge.h", + "autofill_save_iban_delegate.cc", + "autofill_save_iban_delegate.h", + "autofill_vcn_enroll_bottom_sheet_bridge.cc", + "autofill_vcn_enroll_bottom_sheet_bridge.h", + "card_expiration_date_fix_flow_view_android.cc", + "card_expiration_date_fix_flow_view_android.h", + "card_name_fix_flow_view_android.cc", + "card_name_fix_flow_view_android.h", + "card_unmask_prompt_view_android.cc", + "card_unmask_prompt_view_android.h", + "credit_card_scanner_view_android.cc", + "credit_card_scanner_view_android.h", + "dialog/autofill_dialog_view_android.cc", + "dialog/autofill_dialog_view_android.h", + "manual_filling_view_android.cc", + "manual_filling_view_android.h", + "otp_verification_dialog_view_android.cc", + "otp_verification_dialog_view_android.h", + "save_update_address_profile_flow_manager.cc", + "save_update_address_profile_flow_manager.h", + "save_update_address_profile_prompt_view_android.cc", + "save_update_address_profile_prompt_view_android.h", + "snackbar/autofill_snackbar_view_android.cc", + "snackbar/autofill_snackbar_view_android.h", + "virtual_card_utils.cc", + "virtual_card_utils.h", + ] + deps = [ + "//base", + "//chrome/android:chrome_jni_headers", + "//chrome/android/features/keyboard_accessory:jni_headers", + "//chrome/browser/android:resource_mapper", + "//chrome/browser/autofill", + "//chrome/browser/autofill/android:jni_headers", + "//chrome/browser/keyboard_accessory/android", + "//chrome/browser/keyboard_accessory/android:impl", + "//chrome/browser/keyboard_accessory/android/java:jni", + "//chrome/browser/password_manager", + "//chrome/browser/profiles", + "//chrome/browser/signin", + "//chrome/browser/touch_to_fill/autofill/android", + "//chrome/browser/ui/android/autofill/internal:jni_headers", + "//chrome/browser/ui/android/tab_model", + "//chrome/browser/ui/autofill", + "//chrome/browser/ui/autofill:autofill_ai_ui_utils", + "//components/autofill/android:payments_autofill_cc", + "//components/autofill/android:payments_jni_headers", + "//components/autofill/content/browser", + "//components/autofill/core/browser", + "//components/autofill/core/common", + "//components/browser_ui/device_lock/android", + "//components/infobars/core", + "//components/messages/android", + "//components/password_manager/core/browser", + "//components/resources:android_resources", + "//components/signin/public/identity_manager", + "//content/public/browser", + "//ui/android", + "//ui/base", + "//ui/gfx", + "//url", + ] + + # TODO(crbug.com/353332589): Remove this circular dependency. + allow_circular_includes_from = + [ "//chrome/browser/keyboard_accessory/android:impl" ] +} + +source_set("unit_tests") { + testonly = true + sources = [ + "at_memory_bottom_sheet_bridge_unittest.cc", + "at_memory_bottom_sheet_delegate_android_unittest.cc", + "autofill_ai_save_update_entity_flow_manager_unittest.cc", + "autofill_cvc_save_message_delegate_unittest.cc", + "autofill_save_card_bottom_sheet_bridge_unittest.cc", + "autofill_save_card_delegate_android_unittest.cc", + "autofill_save_iban_delegate_unittest.cc", + "autofill_vcn_enroll_bottom_sheet_bridge_unittest.cc", + "save_update_address_profile_flow_manager_unittest.cc", + ] + deps = [ + ":autofill", + "//base", + "//base/test:test_support", + "//chrome/browser/android:resource_mapper", + "//chrome/browser/autofill", + "//chrome/browser/ui", + "//chrome/browser/ui:test_support", + "//chrome/browser/ui/android/autofill:test_support", + "//chrome/browser/ui/android/tab_model:test_support", + "//chrome/browser/ui/autofill:test_support", + "//chrome/browser/ui/autofill/payments:test_support", + "//chrome/test:test_support", + "//components/autofill/content/browser:test_support", + "//components/autofill/core/browser:test_support", + "//components/browser_ui/device_lock/android", + "//components/browser_ui/device_lock/android:test_support", + "//components/messages/android:test_support", + "//testing/gmock", + "//testing/gtest", + "//ui/android", + "//ui/gfx:test_support", + ] + configs += [ "//build/config/compiler:no_exit_time_destructors" ] +} + +source_set("browser_tests") { + testonly = true + defines = [ "HAS_OUT_OF_PROC_TEST_RUNNER" ] + sources = [ "save_update_address_profile_flow_manager_browsertest.cc" ] + deps = [ + ":autofill", + "//base", + "//chrome/browser/autofill", + "//chrome/browser/ui", + "//chrome/browser/ui:test_support", + "//chrome/browser/ui/android/autofill:test_support", + "//chrome/browser/ui/autofill", + "//chrome/test:test_support", + "//chrome/test:test_support_ui_android", + "//components/autofill/content/browser", + "//components/autofill/core/browser", + "//components/autofill/core/browser:test_support", + ] +} + +source_set("test_support") { + testonly = true + sources = [ "save_update_address_profile_flow_manager_test_api.h" ] + deps = [ + ":autofill", + "//base", + "//components/messages/android", + ] +}
diff --git a/chrome/browser/ui/android/autofill/internal/BUILD.gn b/chrome/browser/ui/android/autofill/internal/BUILD.gn index 4ee42d3..7382c53d 100644 --- a/chrome/browser/ui/android/autofill/internal/BUILD.gn +++ b/chrome/browser/ui/android/autofill/internal/BUILD.gn
@@ -64,6 +64,8 @@ visibility = [ ":*", "//chrome/browser/ui", + "//chrome/browser/ui/android/autofill", + "//chrome/browser/ui/android/autofill/payments", ] sources = [ "java/src/org/chromium/chrome/browser/ui/autofill/AtMemoryBottomSheetBridge.java",
diff --git a/chrome/browser/ui/android/autofill/payments/BUILD.gn b/chrome/browser/ui/android/autofill/payments/BUILD.gn new file mode 100644 index 0000000..32f614e --- /dev/null +++ b/chrome/browser/ui/android/autofill/payments/BUILD.gn
@@ -0,0 +1,33 @@ +# Copyright 2026 The Chromium Authors +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +source_set("payments") { + sources = [ + "payments_window_bridge.cc", + "payments_window_bridge.h", + "payments_window_delegate.h", + ] + deps = [ + "//base", + "//chrome/browser/ui/android/autofill/internal:jni_headers", + "//content/public/browser", + "//url", + ] +} + +source_set("unit_tests") { + testonly = true + sources = [ "payments_window_bridge_unittest.cc" ] + deps = [ + ":payments", + "//base", + "//base/test:test_support", + "//chrome/browser/ui", + "//chrome/test:test_support", + "//components/autofill/content/browser:test_support", + "//testing/gmock", + "//testing/gtest", + "//url", + ] +}
diff --git a/chrome/browser/ui/android/bars_common/java/src/org/chromium/chrome/browser/ui/android/bars_common/TabSwitcherButtonView.java b/chrome/browser/ui/android/bars_common/java/src/org/chromium/chrome/browser/ui/android/bars_common/TabSwitcherButtonView.java index c03e1e69..bbe1309 100644 --- a/chrome/browser/ui/android/bars_common/java/src/org/chromium/chrome/browser/ui/android/bars_common/TabSwitcherButtonView.java +++ b/chrome/browser/ui/android/bars_common/java/src/org/chromium/chrome/browser/ui/android/bars_common/TabSwitcherButtonView.java
@@ -35,6 +35,14 @@ setImageDrawable(mTabSwitcherDrawable); } + @Override + public void setImageTintList(@Nullable ColorStateList tint) { + if (mTabSwitcherDrawable != null && tint != null) { + mTabSwitcherDrawable.setTintList(tint); + } + super.setImageTintList(tint); + } + public void setTabCount(int tabCount, boolean isIncognito) { mTabSwitcherDrawable.updateForTabCount(tabCount, isIncognito); } @@ -49,16 +57,12 @@ } } - @Override - public void setImageTintList(@Nullable ColorStateList tint) { - if (mTabSwitcherDrawable != null && tint != null) { - mTabSwitcherDrawable.setTint(tint); - } - super.setImageTintList(tint); - } - public void setDrawableForTesting(TabSwitcherDrawable drawable) { mTabSwitcherDrawable = drawable; setImageDrawable(drawable); } + + public boolean isNotificationDotVisible() { + return mTabSwitcherDrawable.getShowIconNotificationStatus(); + } }
diff --git a/chrome/browser/ui/android/device_dialog/BUILD.gn b/chrome/browser/ui/android/device_dialog/BUILD.gn new file mode 100644 index 0000000..99f1941d --- /dev/null +++ b/chrome/browser/ui/android/device_dialog/BUILD.gn
@@ -0,0 +1,57 @@ +# Copyright 2026 The Chromium Authors +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +source_set("device_dialog") { + sources = [ + "chrome_bluetooth_chooser_android_delegate.cc", + "chrome_bluetooth_chooser_android_delegate.h", + "chrome_bluetooth_scanning_prompt_android_delegate.cc", + "chrome_bluetooth_scanning_prompt_android_delegate.h", + "serial_chooser_dialog_android.cc", + "serial_chooser_dialog_android.h", + "usb_chooser_dialog_android.cc", + "usb_chooser_dialog_android.h", + ] + deps = [ + "//base", + "//chrome/android:chrome_jni_headers", + "//chrome/browser/profiles", + "//chrome/common", + "//components/permissions", + "//components/security_state/content", + "//components/security_state/core", + "//components/url_formatter", + "//content/public/browser", + "//device/vr/buildflags", + "//third_party/jni_zero", + "//ui/android", + "//url", + ] +} + +source_set("unit_tests") { + testonly = true + sources = [ + "serial_chooser_dialog_android_unittest.cc", + "usb_chooser_dialog_android_unittest.cc", + ] + deps = [ + ":device_dialog", + "//base", + "//base/test:test_support", + "//chrome/browser/ssl", + "//chrome/browser/ui", + "//chrome/browser/ui/serial", + "//chrome/browser/ui/serial:test_support", + "//chrome/browser/usb", + "//chrome/test:test_support", + "//content/test:test_support", + "//services/device/public/mojom", + "//testing/gmock", + "//testing/gtest", + "//third_party/blink/public/mojom:mojom_platform", + "//ui/android", + ] + configs += [ "//build/config/compiler:no_exit_time_destructors" ] +}
diff --git a/chrome/browser/ui/android/exclusive_access/BUILD.gn b/chrome/browser/ui/android/exclusive_access/BUILD.gn new file mode 100644 index 0000000..b8f2058 --- /dev/null +++ b/chrome/browser/ui/android/exclusive_access/BUILD.gn
@@ -0,0 +1,41 @@ +# Copyright 2026 The Chromium Authors +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +source_set("exclusive_access") { + sources = [ + "exclusive_access_bubble_android.cc", + "exclusive_access_bubble_android.h", + "exclusive_access_context_android.cc", + "exclusive_access_context_android.h", + "exclusive_access_manager_android.cc", + "exclusive_access_manager_android.h", + ] + deps = [ + "//base", + "//chrome/android:jni_headers", + "//chrome/browser/profiles", + "//chrome/browser/ui:ui_features", + "//chrome/browser/ui/exclusive_access", + "//components/fullscreen_control", + "//components/input", + "//components/url_formatter", + "//content/public/browser", + "//third_party/jni_zero", + "//ui/base", + "//ui/strings", + ] +} + +source_set("unit_tests") { + testonly = true + sources = [ "exclusive_access_bubble_android_unittest.cc" ] + deps = [ + ":exclusive_access", + "//base", + "//chrome/browser/ui", + "//chrome/test:test_support", + "//testing/gmock", + "//testing/gtest", + ] +}
diff --git a/chrome/browser/ui/android/extensions/BUILD.gn b/chrome/browser/ui/android/extensions/BUILD.gn index 284ad82..ccb6be3 100644 --- a/chrome/browser/ui/android/extensions/BUILD.gn +++ b/chrome/browser/ui/android/extensions/BUILD.gn
@@ -7,6 +7,68 @@ import("//extensions/buildflags/buildflags.gni") import("//third_party/jni_zero/jni_zero.gni") +if (enable_extensions_core) { + # Thin headers-only target providing bridge interfaces to + # //chrome/browser/extensions without creating a circular dependency. + # //chrome/browser/extensions can dep on this without a cycle + # since this target does not depend on //chrome/browser/extensions. + source_set("public") { + sources = [ + "extension_developer_private_bridge.h", + "extension_util_bridge.h", + ] + deps = [ "//base" ] + } + + source_set("extensions") { + sources = [ + "extension_action_context_menu_bridge.cc", + "extension_action_context_menu_bridge.h", + "extension_action_delegate_android.cc", + "extension_action_delegate_android.h", + "extension_action_popup_contents.cc", + "extension_action_popup_contents.h", + "extension_actions_bridge.cc", + "extension_actions_bridge.h", + "extension_developer_private_bridge.cc", + "extension_developer_private_bridge.h", + "extension_install_dialog_view_android.cc", + "extension_install_dialog_view_android.h", + "extension_keybinding_registry_android.cc", + "extension_keybinding_registry_android.h", + "extension_util_bridge.cc", + "extension_util_bridge.h", + "extensions_menu_delegate_android.cc", + "extensions_menu_delegate_android.h", + "extensions_toolbar_android.cc", + "extensions_toolbar_android.h", + ] + deps = [ + "//base", + "//chrome/app:generated_resources", + "//chrome/browser/extensions", + "//chrome/browser/extensions/api/omnibox", + "//chrome/browser/extensions/api/web_navigation", + "//chrome/browser/profiles:profile", + "//chrome/browser/ui/android/extensions:jni_headers", + "//chrome/browser/ui/android/tab_model", + "//chrome/browser/ui/browser_window", + "//chrome/browser/ui/browser_window/internal", + "//chrome/browser/ui/color:color_headers", + "//chrome/browser/ui/extensions", + "//chrome/browser/ui/toolbar", + "//chrome/browser/ui/views/extensions:extensions_menu_handler", + "//content/public/browser", + "//extensions/browser", + "//extensions/browser:extension_action_enums", + "//extensions/common", + "//net", + "//ui/menus", + "//url", + ] + } +} + android_library("java") { sources = [ "java/src/org/chromium/chrome/browser/ui/extensions/ExtensionUi.java", @@ -213,6 +275,7 @@ "java/res/drawable/ic_settings.xml", "java/res/drawable/ic_webstore.xml", "java/res/layout/extension_action_button.xml", + "java/res/layout/extension_action_hover_card.xml", "java/res/layout/extension_install_dialog.xml", "java/res/layout/extension_install_dialog_permission_item.xml", "java/res/layout/extensions_menu.xml",
diff --git a/chrome/browser/ui/android/extensions/java/res/layout/extension_action_hover_card.xml b/chrome/browser/ui/android/extensions/java/res/layout/extension_action_hover_card.xml new file mode 100644 index 0000000..2fdbd62 --- /dev/null +++ b/chrome/browser/ui/android/extensions/java/res/layout/extension_action_hover_card.xml
@@ -0,0 +1,74 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +Copyright 2026 The Chromium Authors +Use of this source code is governed by a BSD-style license that can be +found in the LICENSE file. +--> + +<LinearLayout + xmlns:android="http://schemas.android.com/apk/res/android" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:orientation="vertical" + android:background="@drawable/default_popup_menu_bg" + android:elevation="@dimen/app_menu_elevation" + android:clipToOutline="true"> + + <TextView + android:id="@+id/extension_action_hover_card_title" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:paddingHorizontal="@dimen/extension_action_hover_card_horizontal_padding" + android:paddingVertical="@dimen/extension_action_hover_card_vertical_padding" + android:textAppearance="@style/TextAppearance.TextMedium.Primary" /> + + <View + android:id="@+id/extension_action_hover_card_site_access_divider" + android:layout_width="match_parent" + android:layout_height="1dp" + android:background="?android:attr/listDivider" /> + + <LinearLayout + android:id="@+id/extension_action_hover_card_site_access_container" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:orientation="vertical" + android:paddingHorizontal="@dimen/extension_action_hover_card_horizontal_padding" + android:paddingVertical="@dimen/extension_action_hover_card_vertical_padding"> + + <TextView + android:id="@+id/extension_action_hover_card_site_access_title" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:textAppearance="@style/TextAppearance.TextMedium.Primary" /> + + <TextView + android:id="@+id/extension_action_hover_card_site_access_description" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_marginTop="4dp" + android:textAppearance="@style/TextAppearance.TextMedium.Secondary" /> + </LinearLayout> + + <View + android:id="@+id/extension_action_hover_card_policy_divider" + android:layout_width="match_parent" + android:layout_height="1dp" + android:background="?android:attr/listDivider" /> + + <LinearLayout + android:id="@+id/extension_action_hover_card_policy_container" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:orientation="vertical" + android:paddingHorizontal="@dimen/extension_action_hover_card_horizontal_padding" + android:paddingVertical="@dimen/extension_action_hover_card_vertical_padding"> + + <TextView + android:id="@+id/extension_action_hover_card_policy_text" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:textAppearance="@style/TextAppearance.TextMedium.Secondary" /> + </LinearLayout> + +</LinearLayout> \ No newline at end of file
diff --git a/chrome/browser/ui/android/hats/hats_service_android.cc b/chrome/browser/ui/android/hats/hats_service_android.cc index 6133e30a..1bb789f 100644 --- a/chrome/browser/ui/android/hats/hats_service_android.cc +++ b/chrome/browser/ui/android/hats/hats_service_android.cc
@@ -21,7 +21,6 @@ #include "chrome/browser/sessions/exit_type_service.h" #include "chrome/browser/ui/android/hats/survey_client_android.h" #include "chrome/browser/ui/android/hats/survey_ui_delegate_android.h" -#include "chrome/browser/ui/browser_finder.h" #include "chrome/grit/generated_resources.h" #include "components/messages/android/message_wrapper.h" #include "components/resources/android/theme_resources.h"
diff --git a/chrome/browser/ui/android/infobars/BUILD.gn b/chrome/browser/ui/android/infobars/BUILD.gn new file mode 100644 index 0000000..3347b35 --- /dev/null +++ b/chrome/browser/ui/android/infobars/BUILD.gn
@@ -0,0 +1,24 @@ +# Copyright 2026 The Chromium Authors +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +source_set("infobars") { + sources = [ + "infobar_container_android.cc", + "infobar_container_android.h", + "simple_confirm_infobar_builder.cc", + ] + deps = [ + "//base", + "//chrome/android:chrome_jni_headers", + "//chrome/browser/android:resource_mapper", + "//chrome/browser/android:tabs_public", + "//chrome/browser/ui/messages/android:jni_headers", + "//components/infobars/android", + "//components/infobars/content", + "//components/infobars/core", + "//content/public/browser", + "//ui/base", + "//ui/gfx", + ] +}
diff --git a/chrome/browser/ui/android/logo/BUILD.gn b/chrome/browser/ui/android/logo/BUILD.gn index 18ec3e4..42a0ff1 100644 --- a/chrome/browser/ui/android/logo/BUILD.gn +++ b/chrome/browser/ui/android/logo/BUILD.gn
@@ -5,6 +5,26 @@ import("//build/config/android/rules.gni") import("//third_party/jni_zero/jni_zero.gni") +source_set("logo") { + sources = [ + "logo_bridge.cc", + "logo_bridge.h", + ] + deps = [ + "//base", + "//chrome/browser/profiles", + "//chrome/browser/search_provider_logos", + "//chrome/browser/ui/android/logo:jni_headers", + "//components/search_provider_logos", + "//content/public/browser", + "//net", + "//services/network/public/cpp", + "//skia", + "//ui/gfx", + "//url", + ] +} + android_library("java") { srcjar_deps = [ ":jni_headers" ] sources = [
diff --git a/chrome/browser/ui/android/omnibox/BUILD.gn b/chrome/browser/ui/android/omnibox/BUILD.gn index 69e97a9..231c0ec 100644 --- a/chrome/browser/ui/android/omnibox/BUILD.gn +++ b/chrome/browser/ui/android/omnibox/BUILD.gn
@@ -7,6 +7,18 @@ import("//chrome/browser/buildflags.gni") import("//third_party/jni_zero/jni_zero.gni") +source_set("omnibox") { + sources = [ + "omnibox_view_util.cc", + "omnibox_view_util.h", + ] + deps = [ + "//base", + "//chrome/browser/ui/android/omnibox:jni_headers", + "//components/omnibox/browser", + ] +} + android_library("java") { srcjar_deps = [ ":jni_headers" ] sources = [
diff --git a/chrome/browser/ui/android/omnibox/java/res/drawable/fusebox_popup_bg_tinted.xml b/chrome/browser/ui/android/omnibox/java/res/drawable/fusebox_popup_bg_tinted.xml index b726a7c..08b33b5c 100644 --- a/chrome/browser/ui/android/omnibox/java/res/drawable/fusebox_popup_bg_tinted.xml +++ b/chrome/browser/ui/android/omnibox/java/res/drawable/fusebox_popup_bg_tinted.xml
@@ -12,6 +12,6 @@ android:topLeftRadius="?attr/popupBgCornerRadius" android:topRightRadius="?attr/popupBgCornerRadius"/> - <solid android:color="?attr/colorSurface"/> + <solid android:color="@color/bottom_sheet_bg_color"/> </shape>
diff --git a/chrome/browser/ui/android/omnibox/java/res/drawable/fusebox_popup_bg_tinted_on_dark_bg.xml b/chrome/browser/ui/android/omnibox/java/res/drawable/fusebox_popup_bg_tinted_on_dark_bg.xml index 254466b..69a4d4fd 100644 --- a/chrome/browser/ui/android/omnibox/java/res/drawable/fusebox_popup_bg_tinted_on_dark_bg.xml +++ b/chrome/browser/ui/android/omnibox/java/res/drawable/fusebox_popup_bg_tinted_on_dark_bg.xml
@@ -12,6 +12,6 @@ android:topLeftRadius="?attr/popupBgCornerRadius" android:topRightRadius="?attr/popupBgCornerRadius"/> - <solid android:color="@color/gm3_baseline_surface_dark"/> + <solid android:color="@color/gm3_baseline_surface_container_high_dark"/> </shape>
diff --git a/chrome/browser/ui/android/omnibox/java/res/layout/fusebox_context_popup.xml b/chrome/browser/ui/android/omnibox/java/res/layout/fusebox_context_popup.xml index 414fad8..1c85e471 100644 --- a/chrome/browser/ui/android/omnibox/java/res/layout/fusebox_context_popup.xml +++ b/chrome/browser/ui/android/omnibox/java/res/layout/fusebox_context_popup.xml
@@ -6,6 +6,7 @@ --> <ScrollView xmlns:android="http://schemas.android.com/apk/res/android" + android:id="@+id/fusebox_scroll_view" android:layout_width="match_parent" android:layout_height="match_parent" android:accessibilityPaneTitle="@string/accessibility_omnibox_context_popup_title">
diff --git a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/FuseboxSessionState.java b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/FuseboxSessionState.java index a9ec1f1..d3f900c8 100644 --- a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/FuseboxSessionState.java +++ b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/FuseboxSessionState.java
@@ -312,6 +312,11 @@ return mIsActive; } + /** Returns whether the session is a contextual tasks session. */ + public boolean isContextualTasksState() { + return false; + } + /** Modifies this session input to have the values of the given input. */ public void applyAutocompleteInput(AutocompleteInput input) { mAutocompleteInput.copyFrom(input);
diff --git a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/LocationBarCoordinator.java b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/LocationBarCoordinator.java index 14e87ae..2789eb0 100644 --- a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/LocationBarCoordinator.java +++ b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/LocationBarCoordinator.java
@@ -925,6 +925,11 @@ return mUrlCoordinator; } + /** Returns the {@link FuseboxCoordinator} for the LocationBar. */ + public FuseboxCoordinator getFuseboxCoordinator() { + return mFuseboxCoordinator; + } + /** * @param focusable Whether the url bar should be focusable. */
diff --git a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/LocationBarDataProvider.java b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/LocationBarDataProvider.java index 3eba7b9..d58961fc 100644 --- a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/LocationBarDataProvider.java +++ b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/LocationBarDataProvider.java
@@ -15,6 +15,7 @@ import org.chromium.build.annotations.Nullable; import org.chromium.chrome.browser.browser_controls.BrowserControlsStateProvider.ControlsPosition; import org.chromium.chrome.browser.tab.Tab; +import org.chromium.components.omnibox.AutocompleteRequestType; import org.chromium.components.security_state.ConnectionMaliciousContentStatus; import org.chromium.components.security_state.ConnectionSecurityLevel; import org.chromium.content_public.browser.WebContents; @@ -120,6 +121,14 @@ @Nullable Tab getTab(); /** + * Returns the default request type for this provider. This allows the omnibox to maintain a + * specialized state even when not focused. + */ + default @AutocompleteRequestType int getDefaultRequestType() { + return AutocompleteRequestType.SEARCH; + } + + /** * Returns the FuseboxSessionState linked to the current tab (if present) or context * (otherwise). */
diff --git a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/LocationBarMediator.java b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/LocationBarMediator.java index 2f132df..3b41d97c 100644 --- a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/LocationBarMediator.java +++ b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/LocationBarMediator.java
@@ -1186,6 +1186,11 @@ // This guarantees that any calls to onSuggestionsChanged() will see the correct // mCurrentInput instance. mCurrentInput = session.getAutocompleteInput(); + // If the session is already in a specialized mode (e.g. IMAGE_GENERATION), we preserve it. + // Otherwise, we apply the default request type for the current page. + if (mCurrentInput.getRequestType() == AutocompleteRequestType.SEARCH) { + mCurrentInput.setRequestType(mLocationBarDataProvider.getDefaultRequestType()); + } session.activate( mContext, @@ -1261,12 +1266,20 @@ private void endInputInternal() { if (mAutocompleteCoordinator == null || mCurrentInput == null) return; mAutocompleteCoordinator.endInput(); - mFuseboxCoordinator.endInput(); + mStatusCoordinator.endInput(); + if (mScrimHandler != null) mScrimHandler.setVisibility(false); mCurrentInput.getRequestTypeSupplier().removeObserver(mAutocompleteRequestTypeObserver); FuseboxSessionState state = FuseboxSessionState.from(mLocationBarDataProvider); - if (state != null) state.deactivate(); + if (state != null) { + state.deactivate(); + // Only for Contextual Tasks, we skip ending the Fusebox input to allow it to stay warm + // in compact mode. + if (!state.isContextualTasksState()) { + mFuseboxCoordinator.endInput(); + } + } mCurrentInput = null; // The hint text depends on mCurrentInput, nulling it may change the outcome. @@ -2580,8 +2593,9 @@ @AutocompleteRequestType int requestType = mCurrentInput == null - ? AutocompleteRequestType.SEARCH + ? mLocationBarDataProvider.getDefaultRequestType() : mCurrentInput.getRequestType(); + FuseboxSessionState fuseboxSessionState = null; if (OmniboxFeatures.sShowModelPicker.getValue()) { fuseboxSessionState = FuseboxSessionState.from(mLocationBarDataProvider);
diff --git a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/fusebox/FuseboxCoordinator.java b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/fusebox/FuseboxCoordinator.java index f4abdd1..09833cf 100644 --- a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/fusebox/FuseboxCoordinator.java +++ b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/fusebox/FuseboxCoordinator.java
@@ -106,6 +106,7 @@ private @Nullable BottomSheetRectProvider mBottomSheetRectProvider; private final Supplier<@Nullable View> mScrimAnchorViewSupplier; private final ScrimManager mScrimManager; + private boolean mHasContextualTasksFocus; // Mediator is scoped to a particular profile. Can reuse as long as the profile does not change. private @Nullable FuseboxMediator mMediator; @@ -217,6 +218,7 @@ var popup = new FuseboxPopup( mContext, + mWindowAndroid, popupWindowBuilder.build(), popupView, dynamicRectProvider, @@ -255,6 +257,7 @@ Clipboard.getInstance(), mScrimManager, mScrimAnchorViewSupplier); + mMediator.onContextualTaskFocusChanged(mHasContextualTasksFocus); if (mLastBrandedColorScheme != null) { mMediator.updateVisualsForState(mLastBrandedColorScheme); } @@ -276,6 +279,9 @@ if (mBottomSheetRectProvider != null) { mBottomSheetRectProvider.destroy(); } + if (mViewHolder != null) { + mViewHolder.popup.destroy(); + } mScrimManager.destroy(); } @@ -360,6 +366,18 @@ mPendingSession = null; } + /** + * Called when focus is lost or gained while in a Contextual Tasks session. + * + * @param hasFocus Whether the omnibox has focus. + */ + public void onContextualTaskFocusChanged(boolean hasFocus) { + mHasContextualTasksFocus = hasFocus; + if (mMediator != null) { + mMediator.onContextualTaskFocusChanged(hasFocus); + } + } + // TemplateUrlServiceObserver @Override public void onTemplateURLServiceChanged() {
diff --git a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/fusebox/FuseboxMediator.java b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/fusebox/FuseboxMediator.java index d0bebfd..f5edbdb 100644 --- a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/fusebox/FuseboxMediator.java +++ b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/fusebox/FuseboxMediator.java
@@ -60,6 +60,7 @@ import org.chromium.components.browser_ui.widget.scrim.ScrimProperties; import org.chromium.components.contextual_search.InputState; import org.chromium.components.feature_engagement.Tracker; +import org.chromium.components.metrics.OmniboxEventProtos.OmniboxEventProto.PageClassification; import org.chromium.components.omnibox.AutocompleteInput; import org.chromium.components.omnibox.AutocompleteRequestType; import org.chromium.components.omnibox.IconResourceIdsProto.IconResourceIds; @@ -108,6 +109,7 @@ private final Supplier<@Nullable View> mScrimAnchorViewSupplier; private boolean mIsTextWrapping; + private boolean mHasContextualTasksFocus; private @BrandedColorScheme int mBrandedColorScheme = BrandedColorScheme.APP_DEFAULT; private @Nullable Profile mProfile; private @Nullable AutocompleteInput mInput; @@ -295,7 +297,13 @@ updateSnackbarStyling(); } - /** Called when the user stops interacting with the Omnibox. */ + /** + * Called when the user stops interacting with the Omnibox. + * + * <p>For standard search, this is called on every focus loss to clear the UI. For Contextual + * Tasks, this is only called when the task is destroyed (e.g., tab switch or explicit close) to + * keep the session warm during focus loss. + */ /* package */ void endInput() { hidePopup(); setModelList(null); @@ -307,6 +315,24 @@ updateFuseboxState(); } + /** + * Called when focus is lost or gained while in a Contextual Tasks session. + * + * @param hasFocus Whether the contextual tasks fusebox has focus. + */ + /* package */ void onContextualTaskFocusChanged(boolean hasFocus) { + if (mHasContextualTasksFocus == hasFocus) return; + mHasContextualTasksFocus = hasFocus; + + if (!isInInputSession()) return; + + if (!hasFocus) { + hidePopup(); + mIsTextWrapping = false; + } + updateFuseboxState(); + } + private void setAutocompleteInput(@Nullable AutocompleteInput input) { if (mInput != null) { mInput.getRequestTypeSupplier().removeObserver(mOnAutocompleteRequestTypeChanged); @@ -403,8 +429,15 @@ private void updateFuseboxState() { @FuseboxState int targetState; boolean showRequestTypeButton = shouldShowRequestTypeButton(); + boolean isContextualTasks = + mInput != null + && mInput.getRawPageClassification() + == PageClassification.CO_BROWSING_COMPOSEBOX_VALUE; + if (!isInInputSession()) { targetState = FuseboxState.DISABLED; + } else if (!mHasContextualTasksFocus && isContextualTasks) { + targetState = FuseboxState.COMPACT; } else if (!OmniboxFeatures.sCompactFusebox.getValue()) { targetState = FuseboxState.EXPANDED; } else {
diff --git a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/fusebox/FuseboxPopup.java b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/fusebox/FuseboxPopup.java index a092089e1..4a12eb2 100644 --- a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/fusebox/FuseboxPopup.java +++ b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/fusebox/FuseboxPopup.java
@@ -11,13 +11,21 @@ import android.view.accessibility.AccessibilityEvent; import android.widget.ImageView; import android.widget.PopupWindow.OnDismissListener; +import android.widget.ScrollView; import android.widget.TextView; +import androidx.core.graphics.Insets; +import androidx.core.view.WindowInsetsCompat; + import org.chromium.base.task.PostTask; import org.chromium.base.task.TaskTraits; import org.chromium.build.annotations.NullMarked; +import org.chromium.build.annotations.Nullable; import org.chromium.chrome.browser.omnibox.R; import org.chromium.chrome.browser.omnibox.fusebox.FuseboxProperties.PopupState; +import org.chromium.ui.base.WindowAndroid; +import org.chromium.ui.insets.InsetObserver; +import org.chromium.ui.insets.InsetObserver.WindowInsetObserver; import org.chromium.ui.widget.AnchoredPopupWindow; import java.util.HashSet; @@ -35,6 +43,7 @@ /* package */ final AnchoredPopupWindow mPopupWindow; /* package */ final ViewGroup mViewGroup; + /* package */ final ScrollView mScrollView; /* package */ final View mAddCurrentTab; /* package */ final View mTabButton; /* package */ final View mClipboardButton; @@ -52,16 +61,38 @@ /* package */ final List<TextView> mHeaders; private final DynamicRectProvider mDynamicRectProvider; + private final @Nullable InsetObserver mInsetObserver; + private final int mInitialScrollPaddingBottom; private @PopupState int mCurrentState = PopupState.HIDDEN; + private final WindowInsetObserver mWindowInsetObserver = + new WindowInsetObserver() { + @Override + public void onInsetChanged() { + updateLayout(); + } + + @Override + public void onKeyboardInsetChanged(int inset) { + // The popup covers the ime, so we don't need to do anything when it changes. + } + }; + FuseboxPopup( Context context, + WindowAndroid windowAndroid, AnchoredPopupWindow popupWindow, View contentView, DynamicRectProvider dynamicRectProvider, boolean isBottomSheet) { mPopupWindow = popupWindow; + mPopupWindow.setClippingEnabled(false); mDynamicRectProvider = dynamicRectProvider; + mInsetObserver = windowAndroid.getInsetObserver(); + if (mInsetObserver != null) { + mInsetObserver.addObserver(mWindowInsetObserver); + } + mViewGroup = contentView.findViewById(R.id.fusebox_view_group); mViewGroup.addOnLayoutChangeListener( (v, left, top, right, bottom, oldLeft, oldTop, oldRight, oldBottom) -> { @@ -73,6 +104,8 @@ }); } }); + mScrollView = contentView.findViewById(R.id.fusebox_scroll_view); + mInitialScrollPaddingBottom = mScrollView.getPaddingBottom(); ViewStub stub = contentView.findViewById(R.id.fusebox_attachments_stub); stub.setLayoutResource( @@ -133,6 +166,12 @@ mHeaders = List.of(mToolsHeader, mModelsHeader); } + void destroy() { + if (mInsetObserver != null) { + mInsetObserver.removeObserver(mWindowInsetObserver); + } + } + /** Show the popup window. */ void show() { mPopupWindow.show(); @@ -177,6 +216,7 @@ */ void updateLayout() { if (!isShowing() || mCurrentState == PopupState.HIDDEN) return; + updateInsets(); int width = mDynamicRectProvider.getPopupWidth(mCurrentState, mViewGroup.getResources()); mPopupWindow.updateDesiredContentSize(width, /* height= */ 0, /* updateLayout= */ true); } @@ -236,4 +276,18 @@ void addOnDismissListener(OnDismissListener listener) { mPopupWindow.addOnDismissListener(listener); } + + private void updateInsets() { + if (mInsetObserver == null) return; + + WindowInsetsCompat insets = mInsetObserver.getLastRawWindowInsets(); + if (insets == null) return; + + Insets navBarInsets = insets.getInsets(WindowInsetsCompat.Type.navigationBars()); + mScrollView.setPaddingRelative( + mScrollView.getPaddingStart(), + mScrollView.getPaddingTop(), + mScrollView.getPaddingEnd(), + mInitialScrollPaddingBottom + navBarInsets.bottom); + } }
diff --git a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/fusebox/FuseboxPopupUnitTest.java b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/fusebox/FuseboxPopupUnitTest.java index 142a0e2..caa33ae 100644 --- a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/fusebox/FuseboxPopupUnitTest.java +++ b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/fusebox/FuseboxPopupUnitTest.java
@@ -4,12 +4,14 @@ package org.chromium.chrome.browser.omnibox.fusebox; +import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import static org.mockito.Mockito.any; import static org.mockito.Mockito.atLeastOnce; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.eq; import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; import android.app.Activity; import android.os.Looper; @@ -18,6 +20,9 @@ import android.view.ViewGroup; import android.view.accessibility.AccessibilityEvent; +import androidx.core.graphics.Insets; +import androidx.core.view.WindowInsetsCompat; + import org.junit.Before; import org.junit.Rule; import org.junit.Test; @@ -33,6 +38,8 @@ import org.chromium.chrome.browser.omnibox.R; import org.chromium.components.omnibox.OmniboxFeatures; import org.chromium.ui.base.TestActivity; +import org.chromium.ui.base.WindowAndroid; +import org.chromium.ui.insets.InsetObserver; import org.chromium.ui.widget.AnchoredPopupWindow; /** Unit tests for FuseboxPopup. */ @@ -44,6 +51,8 @@ private @Mock AnchoredPopupWindow mPopupWindow; private @Mock View.AccessibilityDelegate mAccessibilityDelegate; private @Mock DynamicRectProvider mDynamicRectProvider; + private @Mock WindowAndroid mWindowAndroid; + private @Mock InsetObserver mInsetObserver; private Activity mActivity; private FuseboxPopup mFuseboxPopup; @@ -55,9 +64,13 @@ mActivity = Robolectric.setupActivity(TestActivity.class); mContentView = LayoutInflater.from(mActivity).inflate(R.layout.fusebox_context_popup, null); mViewGroup = mContentView.findViewById(R.id.fusebox_view_group); + + when(mWindowAndroid.getInsetObserver()).thenReturn(mInsetObserver); + mFuseboxPopup = new FuseboxPopup( mActivity, + mWindowAndroid, mPopupWindow, mContentView, mDynamicRectProvider, @@ -158,7 +171,12 @@ mContentView = LayoutInflater.from(mActivity).inflate(R.layout.fusebox_context_popup, null); mFuseboxPopup = new FuseboxPopup( - mActivity, mPopupWindow, mContentView, mDynamicRectProvider, false); + mActivity, + mWindowAndroid, + mPopupWindow, + mContentView, + mDynamicRectProvider, + /* isBottomSheet= */ false); // Verify that we can find the elements assertNotNull(mFuseboxPopup.mAddCurrentTab); @@ -178,6 +196,7 @@ mFuseboxPopup = new FuseboxPopup( mActivity, + mWindowAndroid, mPopupWindow, mContentView, mDynamicRectProvider, @@ -206,4 +225,52 @@ verify(mPopupWindow, atLeastOnce()).updateDesiredContentSize(100, 0, true); } + + @Test + public void testUpdateInsets_ImeVisible() { + WindowInsetsCompat insets = org.mockito.Mockito.mock(WindowInsetsCompat.class); + Insets imeInsets = Insets.of(0, 0, 0, 100); + Insets navBarInsets = Insets.of(0, 0, 0, 50); + Insets statusBarsInsets = Insets.of(0, 20, 0, 0); + + when(mInsetObserver.getLastRawWindowInsets()).thenReturn(insets); + when(insets.getInsets(WindowInsetsCompat.Type.ime())).thenReturn(imeInsets); + when(insets.getInsets(WindowInsetsCompat.Type.navigationBars())).thenReturn(navBarInsets); + when(insets.getInsets(WindowInsetsCompat.Type.statusBars())).thenReturn(statusBarsInsets); + + doReturn(true).when(mPopupWindow).isShowing(); + mFuseboxPopup.setPopupState(FuseboxProperties.PopupState.FLOATING); + + // First layout update. + mFuseboxPopup.updateLayout(); + assertEquals(50, mFuseboxPopup.mScrollView.getPaddingBottom()); + + // Second layout update to test idempotency. + mFuseboxPopup.updateLayout(); + assertEquals(50, mFuseboxPopup.mScrollView.getPaddingBottom()); + } + + @Test + public void testUpdateInsets_ImeHidden() { + WindowInsetsCompat insets = org.mockito.Mockito.mock(WindowInsetsCompat.class); + Insets imeInsets = Insets.of(0, 0, 0, 0); + Insets navBarInsets = Insets.of(0, 0, 0, 50); + Insets statusBarsInsets = Insets.of(0, 20, 0, 0); + + when(mInsetObserver.getLastRawWindowInsets()).thenReturn(insets); + when(insets.getInsets(WindowInsetsCompat.Type.ime())).thenReturn(imeInsets); + when(insets.getInsets(WindowInsetsCompat.Type.navigationBars())).thenReturn(navBarInsets); + when(insets.getInsets(WindowInsetsCompat.Type.statusBars())).thenReturn(statusBarsInsets); + + doReturn(true).when(mPopupWindow).isShowing(); + mFuseboxPopup.setPopupState(FuseboxProperties.PopupState.FLOATING); + + // First layout update + mFuseboxPopup.updateLayout(); + assertEquals(50, mFuseboxPopup.mScrollView.getPaddingBottom()); + + // Second layout update to test idempotency + mFuseboxPopup.updateLayout(); + assertEquals(50, mFuseboxPopup.mScrollView.getPaddingBottom()); + } }
diff --git a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/fusebox/FuseboxViewBinder.java b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/fusebox/FuseboxViewBinder.java index 682e40d..7317f1d 100644 --- a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/fusebox/FuseboxViewBinder.java +++ b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/fusebox/FuseboxViewBinder.java
@@ -40,6 +40,7 @@ import org.chromium.components.omnibox.AutocompleteRequestType; import org.chromium.components.omnibox.IconResourceIdsProto.IconResourceIds; import org.chromium.components.omnibox.ToolModeProto.ToolMode; +import org.chromium.components.omnibox.ToolModeUtils; import org.chromium.ui.modelutil.PropertyKey; import org.chromium.ui.modelutil.PropertyModel; import org.chromium.ui.modelutil.PropertyModel.ReadableBooleanPropertyKey; @@ -510,6 +511,10 @@ @AutocompleteRequestType int requestType = model.get(FuseboxProperties.AUTOCOMPLETE_REQUEST_TYPE); + if (!ToolModeUtils.isAimRequest(requestType)) { + // The model is in an inconsistent state, wait for the next event. + return; + } @BrandedColorScheme int brandedColorScheme = model.get(FuseboxProperties.COLOR_SCHEME); Context context = view.parentView.getContext();
diff --git a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/fusebox/FuseboxViewBinderUnitTest.java b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/fusebox/FuseboxViewBinderUnitTest.java index 81227d4..e976f5c 100644 --- a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/fusebox/FuseboxViewBinderUnitTest.java +++ b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/fusebox/FuseboxViewBinderUnitTest.java
@@ -50,6 +50,7 @@ import org.chromium.components.omnibox.IconResourceIdsProto.IconResourceIds; import org.chromium.ui.UiUtils; import org.chromium.ui.base.TestActivity; +import org.chromium.ui.base.WindowAndroid; import org.chromium.ui.modelutil.PropertyModel; import org.chromium.ui.modelutil.PropertyModelChangeProcessor; import org.chromium.ui.modelutil.SimpleRecyclerViewAdapter; @@ -76,6 +77,7 @@ @Mock private AnchoredPopupWindow mPopupWindow; @Mock private DynamicRectProvider mDynamicRectProvider; + @Mock private WindowAndroid mWindowAndroid; private final PropertyModel mModel = new PropertyModel(FuseboxProperties.ALL_KEYS); @@ -98,9 +100,11 @@ .inflate(R.layout.fusebox_context_popup, /* root= */ null); doReturn(popupView).when(mPopupWindow).getContentView(); + doReturn(null).when(mWindowAndroid).getInsetObserver(); mPopup = new FuseboxPopup( activity, + mWindowAndroid, mPopupWindow, popupView, mDynamicRectProvider, @@ -229,6 +233,16 @@ } @Test + public void updateRequestTypeButton_nonAimRequest_doesNotShowButton() { + mModel.set(FuseboxProperties.AUTOCOMPLETE_REQUEST_TYPE, AutocompleteRequestType.SEARCH); + mModel.set(FuseboxProperties.SHOW_REQUEST_TYPE_BUTTON, true); + assertEquals(View.GONE, mViewHolder.requestType.getVisibility()); + + mModel.set(FuseboxProperties.AUTOCOMPLETE_REQUEST_TYPE, AutocompleteRequestType.AI_MODE); + assertEquals(View.VISIBLE, mViewHolder.requestType.getVisibility()); + } + + @Test public void reanchorViewsForCompactFusebox_compactModeSearch() { configureFusebox(Variant.COMPACT, AutocompleteRequestType.SEARCH);
diff --git a/chrome/browser/ui/android/overlay/BUILD.gn b/chrome/browser/ui/android/overlay/BUILD.gn new file mode 100644 index 0000000..46b4b71a --- /dev/null +++ b/chrome/browser/ui/android/overlay/BUILD.gn
@@ -0,0 +1,27 @@ +# Copyright 2026 The Chromium Authors +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +source_set("overlay") { + sources = [ + "immersive_overlay_window_android.cc", + "overlay_window_android.cc", + "overlay_window_android.h", + "overlay_window_android_factory.cc", + "picture_in_picture_overlay_window_android.cc", + ] + deps = [ + "//base", + "//cc/slim", + "//chrome/android:chrome_jni_headers", + "//chrome/browser/android:tabs_public", + "//chrome/browser/picture_in_picture", + "//components/thin_webview", + "//content/public/browser", + "//media", + "//third_party/abseil-cpp:absl", + "//third_party/blink/public/mojom:mojom_platform", + "//ui/android", + "//ui/gfx/geometry", + ] +}
diff --git a/chrome/browser/ui/android/passwords/BUILD.gn b/chrome/browser/ui/android/passwords/BUILD.gn new file mode 100644 index 0000000..f10361a --- /dev/null +++ b/chrome/browser/ui/android/passwords/BUILD.gn
@@ -0,0 +1,26 @@ +# Copyright 2026 The Chromium Authors +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +source_set("passwords") { + sources = [ + "all_passwords_bottom_sheet_view.h", + "all_passwords_bottom_sheet_view_impl.cc", + "all_passwords_bottom_sheet_view_impl.h", + "credential_leak_dialog_view_android.cc", + "credential_leak_dialog_view_android.h", + ] + deps = [ + "//base", + "//chrome/android:chrome_jni_headers", + "//chrome/android/features/keyboard_accessory:jni_headers", + "//chrome/browser/password_manager", + "//chrome/browser/profiles", + "//chrome/browser/ui/passwords", + "//components/affiliations/core/browser:affiliations", + "//components/autofill/core/common/mojom:mojo_types", + "//components/password_manager/core/browser", + "//ui/android", + "//ui/base", + ] +}
diff --git a/chrome/browser/ui/android/pdf/BUILD.gn b/chrome/browser/ui/android/pdf/BUILD.gn index a00c0c9..e7ee6329 100644 --- a/chrome/browser/ui/android/pdf/BUILD.gn +++ b/chrome/browser/ui/android/pdf/BUILD.gn
@@ -17,64 +17,66 @@ ] } -# Temporary compatibility stub for downstream dependency dance. -# TODO(rathomas): Remove this target once downstream users are updated. -java_group("pdf_page_java") { -} - generate_jni("pdf_jni_headers") { sources = [ "java/src/org/chromium/chrome/browser/pdf/PdfUtils.java" ] } -android_library("java") { - sources = [ - "java/src/org/chromium/chrome/browser/pdf/PdfActionsDelegate.java", - "java/src/org/chromium/chrome/browser/pdf/PdfCoordinator.java", - "java/src/org/chromium/chrome/browser/pdf/PdfInfo.java", - "java/src/org/chromium/chrome/browser/pdf/PdfPage.java", - "java/src/org/chromium/chrome/browser/pdf/PdfSelectionCoordinator.java", - "java/src/org/chromium/chrome/browser/pdf/PdfSelectionProperties.java", - "java/src/org/chromium/chrome/browser/pdf/PdfSelectionViewBinder.java", - "java/src/org/chromium/chrome/browser/pdf/PdfToolbar.java", - "java/src/org/chromium/chrome/browser/pdf/PdfToolbarActionsDelegate.java", - "java/src/org/chromium/chrome/browser/pdf/PdfToolbarCoordinator.java", - "java/src/org/chromium/chrome/browser/pdf/PdfToolbarProperties.java", - "java/src/org/chromium/chrome/browser/pdf/PdfToolbarViewBinder.java", - "java/src/org/chromium/chrome/browser/pdf/PdfUtils.java", - ] +android_library("pdf_info_java") { + sources = [ "java/src/org/chromium/chrome/browser/pdf/PdfInfo.java" ] + deps = [ "//third_party/androidx:androidx_annotation_annotation_java" ] +} + +android_library("pdf_utils_java") { + sources = [ "java/src/org/chromium/chrome/browser/pdf/PdfUtils.java" ] deps = [ - ":java_resources", - ":pdf_provider_java", + ":pdf_info_java", "//base:base_java", "//base:content_uri_utils_java", "//chrome/browser/flags:java", - "//chrome/browser/gsa:java", - "//chrome/browser/profiles/android:java", "//chrome/browser/ui/android/native_page:java", "//chrome/browser/ui/android/strings:ui_strings_grd", "//chrome/browser/util:java", - "//components/browser_ui/styles/android:java", - "//components/embedder_support/android:context_menu_java", "//components/embedder_support/android:util_java", "//content/public/android:content_full_java", - "//content/public/android:content_main_dex_java", - "//third_party/android_deps:material_design_java", "//third_party/androidx:androidx_annotation_annotation_java", - "//third_party/androidx:androidx_appcompat_appcompat_java", - "//third_party/androidx:androidx_core_core_java", - "//third_party/androidx:androidx_fragment_fragment_java", - "//third_party/androidx:androidx_pdf_pdf_document_service_java", - "//third_party/androidx:androidx_pdf_pdf_viewer_fragment_java", - "//third_party/androidx:androidx_pdf_pdf_viewer_java", "//third_party/jni_zero:jni_zero_java", - "//third_party/kotlin_stdlib:kotlin_stdlib_java", - "//ui/android:ui_java", + "//ui/android:ui_no_recycler_view_java", "//url:url_java", ] - srcjar_deps = [ ":pdf_jni_headers" ] resources_package = "org.chromium.chrome.browser.pdf" } +android_library("pdf_page_java") { + sources = [ "java/src/org/chromium/chrome/browser/pdf/PdfPage.java" ] + deps = [ + ":java", + ":pdf_info_java", + ":pdf_provider_java", + ":pdf_utils_java", + "//base:base_java", + "//chrome/android/modules/on_demand:provider_java", + "//chrome/browser/profiles/android:java", + "//chrome/browser/ui/android/native_page:java", + "//components/embedder_support/android:util_java", + "//third_party/androidx:androidx_annotation_annotation_java", + ] +} + +android_library("java") { + sources = [ + "java/src/org/chromium/chrome/browser/pdf/PdfCoordinatorInterface.java", + "java/src/org/chromium/chrome/browser/pdf/PdfEntryPoint.java", + ] + deps = [ + ":pdf_info_java", + "//components/module_installer/android:module_installer_java", + "//components/module_installer/android:module_interface_java", + "//third_party/androidx:androidx_annotation_annotation_java", + ] + annotation_processor_deps = + [ "//components/module_installer/android:module_interface_processor" ] +} + android_resources("java_resources") { sources = [ "java/res/drawable/toolbar_no_divider.xml", @@ -83,63 +85,3 @@ ] deps = [] } - -robolectric_library("junit") { - resources_package = "org.chromium.chrome.browser.pdf" - sources = [ - "java/src/org/chromium/chrome/browser/pdf/PdfContentProviderUnitTest.java", - "java/src/org/chromium/chrome/browser/pdf/PdfCoordinatorUnitTest.java", - "java/src/org/chromium/chrome/browser/pdf/PdfPageUnitTest.java", - "java/src/org/chromium/chrome/browser/pdf/PdfToolbarCoordinatorUnitTest.java", - "java/src/org/chromium/chrome/browser/pdf/PdfUtilsUnitTest.java", - ] - - deps = [ - ":java", - ":java_resources", - ":pdf_provider_java", - "//base:base_java", - "//base:base_java_test_support", - "//base:base_junit_test_support", - "//chrome/android/javatests:common_unit_test_deps_java", - "//chrome/browser/profiles/android:java", - "//chrome/browser/ui/android/native_page:java", - "//chrome/browser/url_constants/android:java", - "//chrome/browser/util:java", - "//components/embedder_support/android:util_java", - "//content/public/android:content_full_java", - "//content/public/android:content_main_dex_java", - "//third_party/androidx:androidx_pdf_pdf_viewer_java", - "//third_party/androidx:androidx_test_core_java", - "//third_party/androidx:androidx_test_ext_junit_java", - "//third_party/hamcrest:hamcrest_java", - "//third_party/jni_zero:jni_zero_java", - "//third_party/junit", - "//third_party/mockito:mockito_java", - "//ui/android:ui_java", - "//ui/android:ui_java_test_support", - "//url:url_java", - ] -} - -android_library("javatests") { - testonly = true - sources = [ "java/src/org/chromium/chrome/browser/pdf/PdfPageTest.java" ] - - deps = [ - ":java", - "//base:base_java", - "//base:base_java_test_support", - "//base/test:public_transit_java", - "//chrome/browser/flags:java", - "//chrome/browser/tab:java", - "//chrome/browser/ui/android/native_page:java", - "//chrome/test/android:chrome_java_integration_test_support", - "//chrome/test/android:chrome_java_transit", - "//net/android:net_java_test_support", - "//third_party/androidx:androidx_test_runner_java", - "//third_party/junit", - ] - - data = [ "//pdf/test/data/" ] -}
diff --git a/chrome/browser/ui/android/pdf/internal/BUILD.gn b/chrome/browser/ui/android/pdf/internal/BUILD.gn new file mode 100644 index 0000000..997c9b92 --- /dev/null +++ b/chrome/browser/ui/android/pdf/internal/BUILD.gn
@@ -0,0 +1,123 @@ +# Copyright 2026 The Chromium Authors +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +import("//build/config/android/rules.gni") +import("//third_party/jni_zero/jni_zero.gni") + +android_library("java") { + sources = [ + "java/src/org/chromium/chrome/browser/pdf/PdfActionsDelegate.java", + "java/src/org/chromium/chrome/browser/pdf/PdfCoordinator.java", + "java/src/org/chromium/chrome/browser/pdf/PdfEntryPointImpl.java", + "java/src/org/chromium/chrome/browser/pdf/PdfSelectionCoordinator.java", + "java/src/org/chromium/chrome/browser/pdf/PdfSelectionProperties.java", + "java/src/org/chromium/chrome/browser/pdf/PdfSelectionViewBinder.java", + "java/src/org/chromium/chrome/browser/pdf/PdfToolbar.java", + "java/src/org/chromium/chrome/browser/pdf/PdfToolbarActionsDelegate.java", + "java/src/org/chromium/chrome/browser/pdf/PdfToolbarCoordinator.java", + "java/src/org/chromium/chrome/browser/pdf/PdfToolbarProperties.java", + "java/src/org/chromium/chrome/browser/pdf/PdfToolbarViewBinder.java", + ] + deps = [ + "//base:base_java", + "//base:content_uri_utils_java", + "//chrome/browser/flags:java", + "//chrome/browser/gsa:java", + "//chrome/browser/profiles/android:java", + "//chrome/browser/ui/android/native_page:java", + "//chrome/browser/ui/android/pdf:java", + "//chrome/browser/ui/android/pdf:java_resources", + "//chrome/browser/ui/android/pdf:pdf_info_java", + "//chrome/browser/ui/android/pdf:pdf_provider_java", + "//chrome/browser/ui/android/pdf:pdf_utils_java", + "//chrome/browser/ui/android/strings:ui_strings_grd", + "//chrome/browser/util:java", + "//components/browser_ui/styles/android:java", + "//components/embedder_support/android:util_java", + "//content/public/android:content_full_java", + "//content/public/android:content_main_dex_java", + "//third_party/android_deps:material_design_java", + "//third_party/androidx:androidx_annotation_annotation_java", + "//third_party/androidx:androidx_appcompat_appcompat_java", + "//third_party/androidx:androidx_core_core_java", + "//third_party/androidx:androidx_fragment_fragment_java", + "//third_party/androidx:androidx_pdf_pdf_document_service_java", + "//third_party/androidx:androidx_pdf_pdf_viewer_fragment_java", + "//third_party/androidx:androidx_pdf_pdf_viewer_java", + "//third_party/jni_zero:jni_zero_java", + "//third_party/kotlin_stdlib:kotlin_stdlib_java", + "//ui/android:ui_java", + "//url:url_java", + ] + srcjar_deps = [ "//chrome/browser/ui/android/pdf:pdf_jni_headers" ] + resources_package = "org.chromium.chrome.browser.pdf" +} + +# Tests +robolectric_library("junit") { + resources_package = "org.chromium.chrome.browser.pdf" + sources = [ + "../java/src/org/chromium/chrome/browser/pdf/PdfContentProviderUnitTest.java", + "../java/src/org/chromium/chrome/browser/pdf/PdfCoordinatorUnitTest.java", + "../java/src/org/chromium/chrome/browser/pdf/PdfPageUnitTest.java", + "../java/src/org/chromium/chrome/browser/pdf/PdfToolbarCoordinatorUnitTest.java", + "../java/src/org/chromium/chrome/browser/pdf/PdfUtilsUnitTest.java", + ] + + deps = [ + ":java", + "//base:base_java", + "//base:base_java_test_support", + "//base:base_junit_test_support", + "//chrome/android/javatests:common_unit_test_deps_java", + "//chrome/android/modules/on_demand:impl_java", + "//chrome/browser/profiles/android:java", + "//chrome/browser/ui/android/native_page:java", + "//chrome/browser/ui/android/pdf:java", + "//chrome/browser/ui/android/pdf:java_resources", + "//chrome/browser/ui/android/pdf:pdf_info_java", + "//chrome/browser/ui/android/pdf:pdf_page_java", + "//chrome/browser/ui/android/pdf:pdf_provider_java", + "//chrome/browser/ui/android/pdf:pdf_utils_java", + "//chrome/browser/url_constants/android:java", + "//chrome/browser/util:java", + "//components/embedder_support/android:util_java", + "//content/public/android:content_full_java", + "//content/public/android:content_main_dex_java", + "//third_party/androidx:androidx_pdf_pdf_viewer_java", + "//third_party/androidx:androidx_test_core_java", + "//third_party/androidx:androidx_test_ext_junit_java", + "//third_party/hamcrest:hamcrest_java", + "//third_party/jni_zero:jni_zero_java", + "//third_party/junit", + "//third_party/mockito:mockito_java", + "//ui/android:ui_java", + "//ui/android:ui_java_test_support", + "//url:url_java", + ] +} + +android_library("javatests") { + testonly = true + sources = [ "../java/src/org/chromium/chrome/browser/pdf/PdfPageTest.java" ] + + deps = [ + ":java", + "//base:base_java", + "//base:base_java_test_support", + "//base/test:public_transit_java", + "//chrome/browser/flags:java", + "//chrome/browser/tab:java", + "//chrome/browser/ui/android/native_page:java", + "//chrome/browser/ui/android/pdf:java", + "//chrome/browser/ui/android/pdf:pdf_utils_java", + "//chrome/test/android:chrome_java_integration_test_support", + "//chrome/test/android:chrome_java_transit", + "//net/android:net_java_test_support", + "//third_party/androidx:androidx_test_runner_java", + "//third_party/junit", + ] + + data = [ "//pdf/test/data/" ] +}
diff --git a/chrome/browser/ui/android/pdf/java/src/org/chromium/chrome/browser/pdf/PdfActionsDelegate.java b/chrome/browser/ui/android/pdf/internal/java/src/org/chromium/chrome/browser/pdf/PdfActionsDelegate.java similarity index 100% rename from chrome/browser/ui/android/pdf/java/src/org/chromium/chrome/browser/pdf/PdfActionsDelegate.java rename to chrome/browser/ui/android/pdf/internal/java/src/org/chromium/chrome/browser/pdf/PdfActionsDelegate.java
diff --git a/chrome/browser/ui/android/pdf/java/src/org/chromium/chrome/browser/pdf/PdfCoordinator.java b/chrome/browser/ui/android/pdf/internal/java/src/org/chromium/chrome/browser/pdf/PdfCoordinator.java similarity index 95% rename from chrome/browser/ui/android/pdf/java/src/org/chromium/chrome/browser/pdf/PdfCoordinator.java rename to chrome/browser/ui/android/pdf/internal/java/src/org/chromium/chrome/browser/pdf/PdfCoordinator.java index da9607a..ddd025bb 100644 --- a/chrome/browser/ui/android/pdf/java/src/org/chromium/chrome/browser/pdf/PdfCoordinator.java +++ b/chrome/browser/ui/android/pdf/internal/java/src/org/chromium/chrome/browser/pdf/PdfCoordinator.java
@@ -6,6 +6,7 @@ import android.annotation.SuppressLint; import android.app.Activity; +import android.content.Context; import android.content.Intent; import android.net.Uri; import android.os.SystemClock; @@ -29,6 +30,7 @@ import org.json.JSONException; import org.json.JSONObject; +import org.chromium.base.BundleUtils; import org.chromium.base.Log; import org.chromium.base.PackageUtils; import org.chromium.base.ResettersForTesting; @@ -56,7 +58,8 @@ */ @SuppressLint("NewApi") @NullMarked -public class PdfCoordinator implements PdfActionsDelegate, PdfToolbarActionsDelegate { +public class PdfCoordinator + implements PdfCoordinatorInterface, PdfActionsDelegate, PdfToolbarActionsDelegate { private static final String TAG = "PdfCoordinator"; private static final int PAGE_TRANSITION_TYPE = PageTransition.LINK; @@ -95,9 +98,11 @@ private String mTitle; private final String mUrl; private final boolean mIsIncognito; + /** A unique id to identity the FragmentContainerView in the current PdfPage. */ private final int mFragmentContainerViewId; + @SuppressWarnings("UnusedVariable") private @Nullable PdfSelectionCoordinator mPdfSelectionCoordinator; private final @Nullable PdfToolbarCoordinator mToolbarCoordinator; @@ -145,7 +150,9 @@ mIsIncognito = profile.isOffTheRecord(); mTitle = title; mUrl = url; - mView = LayoutInflater.from(activity).inflate(R.layout.pdf_page, null); + Context contextForInflation = + BundleUtils.createContextForInflation(activity, OnDemandModule.SPLIT_NAME); + mView = LayoutInflater.from(contextForInflation).inflate(R.layout.pdf_page, null); mProgressBar = mView.findViewById(R.id.progress_bar); mView.setBackgroundColor( ChromeColors.getPrimaryBackgroundColor(activity, profile.isOffTheRecord())); @@ -229,7 +236,6 @@ } } }); - // Add a persistent listener to track page changes. capturedView.addOnViewportChangedListener( (firstVisiblePage, visiblePagesCount, pageLocations, zoomLevel) -> @@ -315,6 +321,7 @@ } /** Returns the intended view for PdfPage tab. */ + @Override @VisibleForTesting(otherwise = VisibleForTesting.PACKAGE_PRIVATE) public View getView() { return mView; @@ -325,7 +332,8 @@ * * @return whether the pdf specific find in page UI is shown. */ - boolean findInPage() { + @Override + public boolean findInPage() { if (mChromePdfViewerFragment != null && mChromePdfViewerFragment.mIsLoadDocumentSuccess) { mChromePdfViewerFragment.setTextSearchActive(true); PdfUtils.recordFindInPage(mFindInPageCount++); @@ -337,13 +345,11 @@ /** * Called after a pdf page has been removed from the view hierarchy and will no longer be used. */ + @Override @SuppressWarnings({"NullAway"}) - void destroy() { + public void destroy() { mPdfSandboxHandle.close(); mPdfSandboxHandle = null; - if (mPdfSelectionCoordinator != null) { - mPdfSelectionCoordinator = null; - } if (mToolbarCoordinator != null) { mToolbarCoordinator.destroy(); } @@ -372,13 +378,16 @@ * @param pdfFilePath The filepath of the downloaded pdf document. * @param pdfFileName The filename of the downloaded pdf document. */ - void onDownloadComplete(String pdfFilePath, String pdfFileName) { + @Override + public void onDownloadComplete(String pdfFilePath, String pdfFileName) { mTitle = pdfFileName; loadPdfFile(pdfFilePath); } /** Returns the filepath of the pdf document. */ - @Nullable String getFilepath() { + @Nullable + @Override + public String getFilepath() { return mPdfFilePath; } @@ -402,7 +411,8 @@ loadPdfInternal(); } - void reload() { + @Override + public void reload() { if (mUri == null) { return; } @@ -462,7 +472,8 @@ * assistant package is used. * @return The URI of the PDF file, or null if the URI is not available. */ - @Nullable Uri getFileUri(boolean isWorkProfile, @Nullable String targetPackage) { + @Override + public @Nullable Uri getFileUri(boolean isWorkProfile, @Nullable String targetPackage) { if (mUri == null) { return null; } @@ -480,7 +491,8 @@ return mUri; } - @Nullable String requestAssistContent(String filename, boolean isWorkProfile) { + @Override + public @Nullable String requestAssistContent(String filename, boolean isWorkProfile) { if (mUri == null) { return null; } @@ -491,9 +503,9 @@ .put( JSON_KEY_FILE_METADATA, new JSONObject() + .put(JSON_KEY_FILE_NAME, filename) .put(JSON_KEY_FILE_URI, mUri.toString()) .put(JSON_KEY_MIME_TYPE, MimeTypeUtils.PDF_MIME_TYPE) - .put(JSON_KEY_FILE_NAME, filename) .put(JSON_KEY_IS_WORK_PROFILE, isWorkProfile)) .toString(); } catch (JSONException e) { @@ -509,7 +521,8 @@ return structuredData; } - @Nullable Uri getUri() { + @Override + public @Nullable Uri getUri() { return mUri; }
diff --git a/chrome/browser/ui/android/pdf/internal/java/src/org/chromium/chrome/browser/pdf/PdfEntryPointImpl.java b/chrome/browser/ui/android/pdf/internal/java/src/org/chromium/chrome/browser/pdf/PdfEntryPointImpl.java new file mode 100644 index 0000000..e09facb --- /dev/null +++ b/chrome/browser/ui/android/pdf/internal/java/src/org/chromium/chrome/browser/pdf/PdfEntryPointImpl.java
@@ -0,0 +1,29 @@ +// Copyright 2026 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +package org.chromium.chrome.browser.pdf; + +import android.app.Activity; + +import org.chromium.build.annotations.NullMarked; +import org.chromium.build.annotations.Nullable; +import org.chromium.chrome.browser.profiles.Profile; +import org.chromium.chrome.browser.ui.native_page.NativePageHost; + +/** Implementation of PdfEntryPoint. */ +@NullMarked +public class PdfEntryPointImpl implements PdfEntryPoint { + @Override + public PdfCoordinatorInterface createPdfCoordinator( + Object host, + Object profile, + Activity activity, + String url, + @Nullable String filepath, + String title, + int tabId) { + return new PdfCoordinator( + (NativePageHost) host, (Profile) profile, activity, filepath, title, tabId, url); + } +}
diff --git a/chrome/browser/ui/android/pdf/java/src/org/chromium/chrome/browser/pdf/PdfSelectionCoordinator.java b/chrome/browser/ui/android/pdf/internal/java/src/org/chromium/chrome/browser/pdf/PdfSelectionCoordinator.java similarity index 100% rename from chrome/browser/ui/android/pdf/java/src/org/chromium/chrome/browser/pdf/PdfSelectionCoordinator.java rename to chrome/browser/ui/android/pdf/internal/java/src/org/chromium/chrome/browser/pdf/PdfSelectionCoordinator.java
diff --git a/chrome/browser/ui/android/pdf/java/src/org/chromium/chrome/browser/pdf/PdfSelectionProperties.java b/chrome/browser/ui/android/pdf/internal/java/src/org/chromium/chrome/browser/pdf/PdfSelectionProperties.java similarity index 100% rename from chrome/browser/ui/android/pdf/java/src/org/chromium/chrome/browser/pdf/PdfSelectionProperties.java rename to chrome/browser/ui/android/pdf/internal/java/src/org/chromium/chrome/browser/pdf/PdfSelectionProperties.java
diff --git a/chrome/browser/ui/android/pdf/java/src/org/chromium/chrome/browser/pdf/PdfSelectionViewBinder.java b/chrome/browser/ui/android/pdf/internal/java/src/org/chromium/chrome/browser/pdf/PdfSelectionViewBinder.java similarity index 100% rename from chrome/browser/ui/android/pdf/java/src/org/chromium/chrome/browser/pdf/PdfSelectionViewBinder.java rename to chrome/browser/ui/android/pdf/internal/java/src/org/chromium/chrome/browser/pdf/PdfSelectionViewBinder.java
diff --git a/chrome/browser/ui/android/pdf/java/src/org/chromium/chrome/browser/pdf/PdfToolbar.java b/chrome/browser/ui/android/pdf/internal/java/src/org/chromium/chrome/browser/pdf/PdfToolbar.java similarity index 87% rename from chrome/browser/ui/android/pdf/java/src/org/chromium/chrome/browser/pdf/PdfToolbar.java rename to chrome/browser/ui/android/pdf/internal/java/src/org/chromium/chrome/browser/pdf/PdfToolbar.java index 18160134..8ab2d6ff 100644 --- a/chrome/browser/ui/android/pdf/java/src/org/chromium/chrome/browser/pdf/PdfToolbar.java +++ b/chrome/browser/ui/android/pdf/internal/java/src/org/chromium/chrome/browser/pdf/PdfToolbar.java
@@ -13,8 +13,8 @@ import org.chromium.build.annotations.Nullable; /** - * Toolbar for the PDF viewer. To handle clicks on the navigation icon, set a listener with - * {@link #setNavigationOnClickListener(OnClickListener)}. + * Toolbar for the PDF viewer. To handle clicks on the navigation icon, set a listener with {@link + * #setNavigationOnClickListener(OnClickListener)}. */ @NullMarked public class PdfToolbar extends Toolbar {
diff --git a/chrome/browser/ui/android/pdf/java/src/org/chromium/chrome/browser/pdf/PdfToolbarActionsDelegate.java b/chrome/browser/ui/android/pdf/internal/java/src/org/chromium/chrome/browser/pdf/PdfToolbarActionsDelegate.java similarity index 100% rename from chrome/browser/ui/android/pdf/java/src/org/chromium/chrome/browser/pdf/PdfToolbarActionsDelegate.java rename to chrome/browser/ui/android/pdf/internal/java/src/org/chromium/chrome/browser/pdf/PdfToolbarActionsDelegate.java
diff --git a/chrome/browser/ui/android/pdf/java/src/org/chromium/chrome/browser/pdf/PdfToolbarCoordinator.java b/chrome/browser/ui/android/pdf/internal/java/src/org/chromium/chrome/browser/pdf/PdfToolbarCoordinator.java similarity index 80% rename from chrome/browser/ui/android/pdf/java/src/org/chromium/chrome/browser/pdf/PdfToolbarCoordinator.java rename to chrome/browser/ui/android/pdf/internal/java/src/org/chromium/chrome/browser/pdf/PdfToolbarCoordinator.java index 2f0b96c..4f5bcdd 100644 --- a/chrome/browser/ui/android/pdf/java/src/org/chromium/chrome/browser/pdf/PdfToolbarCoordinator.java +++ b/chrome/browser/ui/android/pdf/internal/java/src/org/chromium/chrome/browser/pdf/PdfToolbarCoordinator.java
@@ -11,9 +11,7 @@ import org.chromium.ui.modelutil.PropertyModel; import org.chromium.ui.modelutil.PropertyModelChangeProcessor; -import java.util.ArrayList; import java.util.List; -import java.util.Locale; /** The top-level component responsible for the setup and lifecycle of the PDF Toolbar MVC stack. */ @NullMarked @@ -22,11 +20,10 @@ private final PdfToolbarActionsDelegate mDelegate; private final PropertyModelChangeProcessor<PropertyModel, PdfToolbar, PropertyKey> mPropertyModelChangeProcessor; - private final ArrayList<Float> mZoomLevels = - new ArrayList<>( - List.of( - 0.25f, 0.33f, 0.5f, 0.67f, 0.75f, 0.8f, 0.9f, 1.0f, 1.1f, 1.25f, 1.5f, - 1.75f, 2.0f, 2.5f, 3.0f, 4.0f, 5.0f)); + private final List<Float> mZoomLevels = + List.of( + 0.25f, 0.33f, 0.5f, 0.67f, 0.75f, 0.8f, 0.9f, 1.0f, 1.1f, 1.25f, 1.5f, 1.75f, + 2.0f, 2.5f, 3.0f, 4.0f, 5.0f); public PdfToolbarCoordinator(View parentView, PdfToolbarActionsDelegate delegate) { mDelegate = delegate; @@ -34,10 +31,10 @@ // TODO(crbug.com/496180649): Only show the toolbar when the PDF is loaded via ViewStub. toolbar.setVisibility(View.VISIBLE); + // TODO(crbug.com/507061296): Remove hardcoded values after the PDF is loaded. mModel = new PropertyModel.Builder(PdfToolbarProperties.ALL_KEYS) .with(PdfToolbarProperties.ON_CLICK_LISTENER, this) - .with(PdfToolbarProperties.ZOOM_VALUE, "100%") .build(); // Set up the MCP to sync the Model and View @@ -50,9 +47,7 @@ int actionId = view.getId(); int currentPageNumber = mModel.get(PdfToolbarProperties.CURRENT_PAGE_NUMBER); int totalPageCount = mModel.get(PdfToolbarProperties.TOTAL_PAGE_COUNT); - float currentZoomFactor = - Float.parseFloat(mModel.get(PdfToolbarProperties.ZOOM_VALUE).replace("%", "")) - / 100f; + float currentZoomFactor = mModel.get(PdfToolbarProperties.ZOOM_LEVEL); if (actionId == R.id.page_increase_button && currentPageNumber < totalPageCount) { mDelegate.navigateToPage(currentPageNumber); @@ -68,19 +63,19 @@ private float getNextZoomLevel(float currentZoomLevel, boolean increase) { int index = 0; - // Find the first index where the zoom level is greater than current + // Find the first index where the zoom level is greater than or equal to current and move + // to the next one if it exists. while (index < mZoomLevels.size() && mZoomLevels.get(index) <= currentZoomLevel) { index++; } if (increase) { // Return the next highest, or stay at the max if we're at the end - if (index >= mZoomLevels.size()) return mZoomLevels.get(mZoomLevels.size() - 1); return mZoomLevels.get(index); } else { - // If the current zoom level is in the list, decrease by 2. Otherwise, decrease by 1. + // If the current zoom level is in the list, decrease by 1. Otherwise, decrease by 2. int targetIndex = mZoomLevels.contains(currentZoomLevel) ? index - 2 : index - 1; - if (targetIndex < 0) return mZoomLevels.get(0); + if (targetIndex < 0) targetIndex = 0; return mZoomLevels.get(targetIndex); } } @@ -89,7 +84,7 @@ * Called when the PDF document is successfully loaded. * * @param pageCount The total page count of the document. - * @param title The title of the PDF document. + * @param title The title of the document. */ public void onDocumentLoaded(int pageCount, String title) { mModel.set(PdfToolbarProperties.TOTAL_PAGE_COUNT, pageCount); @@ -106,9 +101,7 @@ // Fetch absolute state from engine as the single source of truth. // Keep the model 1-indexed. mModel.set(PdfToolbarProperties.CURRENT_PAGE_NUMBER, firstVisiblePage + 1); - mModel.set( - PdfToolbarProperties.ZOOM_VALUE, - String.format(Locale.ENGLISH, "%.0f%%", zoomLevel * 100)); + mModel.set(PdfToolbarProperties.ZOOM_LEVEL, zoomLevel); mModel.set( PdfToolbarProperties.ZOOM_DECREASE_BUTTON_ENABLED, zoomLevel > mZoomLevels.get(0)); mModel.set(
diff --git a/chrome/browser/ui/android/pdf/java/src/org/chromium/chrome/browser/pdf/PdfToolbarProperties.java b/chrome/browser/ui/android/pdf/internal/java/src/org/chromium/chrome/browser/pdf/PdfToolbarProperties.java similarity index 80% rename from chrome/browser/ui/android/pdf/java/src/org/chromium/chrome/browser/pdf/PdfToolbarProperties.java rename to chrome/browser/ui/android/pdf/internal/java/src/org/chromium/chrome/browser/pdf/PdfToolbarProperties.java index 1e484c3e..90b276b 100644 --- a/chrome/browser/ui/android/pdf/java/src/org/chromium/chrome/browser/pdf/PdfToolbarProperties.java +++ b/chrome/browser/ui/android/pdf/internal/java/src/org/chromium/chrome/browser/pdf/PdfToolbarProperties.java
@@ -9,6 +9,7 @@ import org.chromium.build.annotations.NullMarked; import org.chromium.ui.modelutil.PropertyKey; import org.chromium.ui.modelutil.PropertyModel.WritableBooleanPropertyKey; +import org.chromium.ui.modelutil.PropertyModel.WritableFloatPropertyKey; import org.chromium.ui.modelutil.PropertyModel.WritableIntPropertyKey; import org.chromium.ui.modelutil.PropertyModel.WritableObjectPropertyKey; @@ -24,8 +25,8 @@ /** The total number of pages in the document. */ static final WritableIntPropertyKey TOTAL_PAGE_COUNT = new WritableIntPropertyKey(); - /** The zoom value text. */ - static final WritableObjectPropertyKey<String> ZOOM_VALUE = new WritableObjectPropertyKey<>(); + /** The zoom level. */ + static final WritableFloatPropertyKey ZOOM_LEVEL = new WritableFloatPropertyKey(); /** Whether the zoom decrease button is enabled. */ static final WritableBooleanPropertyKey ZOOM_DECREASE_BUTTON_ENABLED = @@ -40,7 +41,12 @@ new WritableObjectPropertyKey<>(); static final PropertyKey[] ALL_KEYS = { - CURRENT_PAGE_NUMBER, TITLE, TOTAL_PAGE_COUNT, ZOOM_VALUE, ON_CLICK_LISTENER, - ZOOM_DECREASE_BUTTON_ENABLED, ZOOM_INCREASE_BUTTON_ENABLED + CURRENT_PAGE_NUMBER, + TITLE, + TOTAL_PAGE_COUNT, + ZOOM_LEVEL, + ON_CLICK_LISTENER, + ZOOM_DECREASE_BUTTON_ENABLED, + ZOOM_INCREASE_BUTTON_ENABLED }; }
diff --git a/chrome/browser/ui/android/pdf/java/src/org/chromium/chrome/browser/pdf/PdfToolbarViewBinder.java b/chrome/browser/ui/android/pdf/internal/java/src/org/chromium/chrome/browser/pdf/PdfToolbarViewBinder.java similarity index 89% rename from chrome/browser/ui/android/pdf/java/src/org/chromium/chrome/browser/pdf/PdfToolbarViewBinder.java rename to chrome/browser/ui/android/pdf/internal/java/src/org/chromium/chrome/browser/pdf/PdfToolbarViewBinder.java index 3e23e1e4..9f9edf7 100644 --- a/chrome/browser/ui/android/pdf/java/src/org/chromium/chrome/browser/pdf/PdfToolbarViewBinder.java +++ b/chrome/browser/ui/android/pdf/internal/java/src/org/chromium/chrome/browser/pdf/PdfToolbarViewBinder.java
@@ -28,9 +28,13 @@ } else if (PdfToolbarProperties.TOTAL_PAGE_COUNT == key) { TextView pageCount = view.findViewById(R.id.page_count); pageCount.setText(String.valueOf(model.get(PdfToolbarProperties.TOTAL_PAGE_COUNT))); - } else if (PdfToolbarProperties.ZOOM_VALUE == key) { + } else if (PdfToolbarProperties.ZOOM_LEVEL == key) { TextView zoomValue = view.findViewById(R.id.zoom_value); - zoomValue.setText(model.get(PdfToolbarProperties.ZOOM_VALUE)); + zoomValue.setText( + String.format( + java.util.Locale.ENGLISH, + "%.0f%%", + model.get(PdfToolbarProperties.ZOOM_LEVEL) * 100)); } else if (PdfToolbarProperties.ON_CLICK_LISTENER == key) { View.OnClickListener listener = model.get(PdfToolbarProperties.ON_CLICK_LISTENER); view.findViewById(R.id.page_increase_button).setOnClickListener(listener);
diff --git a/chrome/browser/ui/android/pdf/java/src/org/chromium/chrome/browser/pdf/PdfCoordinatorInterface.java b/chrome/browser/ui/android/pdf/java/src/org/chromium/chrome/browser/pdf/PdfCoordinatorInterface.java new file mode 100644 index 0000000..5cfaac1 --- /dev/null +++ b/chrome/browser/ui/android/pdf/java/src/org/chromium/chrome/browser/pdf/PdfCoordinatorInterface.java
@@ -0,0 +1,42 @@ +// Copyright 2026 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +package org.chromium.chrome.browser.pdf; + +import android.net.Uri; +import android.view.View; + +import org.chromium.build.annotations.NullMarked; +import org.chromium.build.annotations.Nullable; + +/** Interface for the PDF coordinator in the DFM. */ +@NullMarked +public interface PdfCoordinatorInterface { + /** Returns the intended view for PdfPage tab. */ + View getView(); + + /** Returns the filepath of the pdf document. */ + @Nullable String getFilepath(); + + /** Called after a pdf page has been removed from the view hierarchy. */ + void destroy(); + + /** Reloads the pdf document. */ + void reload(); + + /** Called after pdf download complete. */ + void onDownloadComplete(String pdfFilePath, String pdfFileName); + + /** Show pdf specific find in page UI. */ + boolean findInPage(); + + /** Retrieve uri of the pdf document. */ + @Nullable Uri getUri(); + + /** Build structured data including content uri and grant permission. */ + @Nullable String requestAssistContent(String filename, boolean isWorkProfile); + + /** Retrieve uri of the pdf document and grant permission to the target package. */ + @Nullable Uri getFileUri(boolean isWorkProfile, @Nullable String targetPackage); +}
diff --git a/chrome/browser/ui/android/pdf/java/src/org/chromium/chrome/browser/pdf/PdfCoordinatorUnitTest.java b/chrome/browser/ui/android/pdf/java/src/org/chromium/chrome/browser/pdf/PdfCoordinatorUnitTest.java index 42df88ad7..cf199be 100644 --- a/chrome/browser/ui/android/pdf/java/src/org/chromium/chrome/browser/pdf/PdfCoordinatorUnitTest.java +++ b/chrome/browser/ui/android/pdf/java/src/org/chromium/chrome/browser/pdf/PdfCoordinatorUnitTest.java
@@ -8,7 +8,6 @@ import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; -import static org.junit.Assert.fail; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyBoolean; import static org.mockito.ArgumentMatchers.eq; @@ -18,7 +17,6 @@ import static org.mockito.Mockito.when; import android.net.Uri; -import android.view.View; import android.view.ViewGroup; import androidx.fragment.app.FragmentActivity; @@ -173,38 +171,6 @@ @Test @EnableFeatures(ChromeFeatureList.INLINE_PDF_V2) - @Config(shadows = {ShadowPdfView.class}) - public void testZoomButtons() { - createPdfCoordinator(); - - View zoomInButton = mPdfCoordinator.getView().findViewById(R.id.zoom_increase_button); - View zoomOutButton = mPdfCoordinator.getView().findViewById(R.id.zoom_decrease_button); - - // Initial state at normal zoom - mPdfCoordinator.onViewportChanged(0, 1.0f); - assertTrue("Zoom in button should be enabled at 1.0f zoom", zoomInButton.isEnabled()); - assertTrue("Zoom out button should be enabled at 1.0f zoom", zoomOutButton.isEnabled()); - - // Click zoom in - zoomInButton.performClick(); - - // Assert zoom level increased - ShadowPdfView shadowPdfView = Shadow.extract(mPdfView); - assertEquals(1.1f, shadowPdfView.mZoom, 0.001f); - - // Simulate minimum zoom level (0.25f) - mPdfCoordinator.onViewportChanged(0, 0.25f); - assertTrue("Zoom in button should be enabled at min zoom", zoomInButton.isEnabled()); - assertFalse("Zoom out button should be disabled at min zoom", zoomOutButton.isEnabled()); - - // Simulate maximum zoom level (5.0f) - mPdfCoordinator.onViewportChanged(0, 5.0f); - assertFalse("Zoom in button should be disabled at max zoom", zoomInButton.isEnabled()); - assertTrue("Zoom out button should be enabled at max zoom", zoomOutButton.isEnabled()); - } - - @Test - @EnableFeatures(ChromeFeatureList.INLINE_PDF_V2) public void testOnLinkClicked_RejectsDangerousSchemes() { when(mProfile.isOffTheRecord()).thenReturn(false); createPdfCoordinator(); @@ -271,7 +237,7 @@ createPdfCoordinator(); Uri linkUri = Uri.parse(LINK_URL); boolean result = mPdfCoordinator.onLinkClicked(linkUri); - assertTrue("onLinkClicked should return true.", result); + assertTrue("name should verify true", result); ArgumentCaptor<LoadUrlParams> captor = ArgumentCaptor.forClass(LoadUrlParams.class); verify(mNativePageHost).loadUrl(captor.capture(), eq(isIncognito)); LoadUrlParams params = captor.getValue(); @@ -285,19 +251,7 @@ } @Test - public void testFragmentCanBeInstantiated() { - // This test verifies that the fragment can be instantiated by the FragmentManager. - // The FragmentManager requires a public no-argument constructor. - try { - PdfCoordinator.ChromePdfViewerFragment fragment = - new PdfCoordinator.ChromePdfViewerFragment(); - assertNotNull("Fragment should be created successfully.", fragment); - } catch (Exception e) { - fail("Fragment instantiation should not throw an exception: " + e.getMessage()); - } - } - - @Test + @EnableFeatures(ChromeFeatureList.INLINE_PDF_V2) public void testGetFileUri() { createPdfCoordinator(); @@ -309,17 +263,14 @@ } @Test + @EnableFeatures(ChromeFeatureList.INLINE_PDF_V2) public void testGetFileUri_NullUri() { when(mProfile.isOffTheRecord()).thenReturn(false); + // Signature: NativePageHost, Profile, Activity, @Nullable String filepath, String title, + // int tabId, String url mPdfCoordinator = new PdfCoordinator( - mNativePageHost, - mProfile, - mActivity, - null, - PDF_TITLE, - TAB_ID, - PDF_URL); + mNativePageHost, mProfile, mActivity, null, PDF_TITLE, TAB_ID, PDF_URL); Uri uri = mPdfCoordinator.getFileUri( @@ -345,8 +296,6 @@ } @Implementation - public float getZoom() { - return mZoom; - } + public void testGetZoom() {} } }
diff --git a/chrome/browser/ui/android/pdf/java/src/org/chromium/chrome/browser/pdf/PdfEntryPoint.java b/chrome/browser/ui/android/pdf/java/src/org/chromium/chrome/browser/pdf/PdfEntryPoint.java new file mode 100644 index 0000000..36a5740 --- /dev/null +++ b/chrome/browser/ui/android/pdf/java/src/org/chromium/chrome/browser/pdf/PdfEntryPoint.java
@@ -0,0 +1,24 @@ +// Copyright 2026 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +package org.chromium.chrome.browser.pdf; + +import android.app.Activity; + +import org.chromium.build.annotations.NullMarked; +import org.chromium.components.module_installer.builder.ModuleInterface; + +/** Interface for the PDF viewer module. */ +@ModuleInterface(module = "on_demand", impl = "org.chromium.chrome.browser.pdf.PdfEntryPointImpl") +@NullMarked +public interface PdfEntryPoint { + PdfCoordinatorInterface createPdfCoordinator( + Object host, + Object profile, + Activity activity, + String url, + @org.chromium.build.annotations.Nullable String filepath, + String title, + int tabId); +}
diff --git a/chrome/browser/ui/android/pdf/java/src/org/chromium/chrome/browser/pdf/PdfPage.java b/chrome/browser/ui/android/pdf/java/src/org/chromium/chrome/browser/pdf/PdfPage.java index 2e6b2cd..3f78737 100644 --- a/chrome/browser/ui/android/pdf/java/src/org/chromium/chrome/browser/pdf/PdfPage.java +++ b/chrome/browser/ui/android/pdf/java/src/org/chromium/chrome/browser/pdf/PdfPage.java
@@ -12,6 +12,7 @@ import org.chromium.build.annotations.NullMarked; import org.chromium.build.annotations.Nullable; +import org.chromium.chrome.modules.on_demand.OnDemandModule; import org.chromium.chrome.browser.profiles.Profile; import org.chromium.chrome.browser.ui.native_page.BasicNativePage; import org.chromium.chrome.browser.ui.native_page.NativePageHost; @@ -20,7 +21,7 @@ /** Native page that displays pdf file. */ @NullMarked public class PdfPage extends BasicNativePage { - @VisibleForTesting public final PdfCoordinator mPdfCoordinator; + @VisibleForTesting public final PdfCoordinatorInterface mPdfCoordinator; private String mTitle; private final String mUrl; private final boolean mIsIncognito; @@ -41,6 +42,7 @@ public PdfPage( NativePageHost host, Profile profile, + boolean isIncognito, Activity activity, String url, PdfInfo pdfInfo, @@ -51,17 +53,20 @@ mIsDownloadSafe = pdfInfo.isDownloadSafe; String decodedUrl = PdfUtils.decodePdfPageUrl(url); String filepath = - pdfInfo.filepath == null - ? PdfUtils.getFilePathFromUrl(decodedUrl) - : pdfInfo.filepath; + pdfInfo.filepath == null + ? PdfUtils.getFilePathFromUrl(decodedUrl) + : pdfInfo.filepath; mTitle = - pdfInfo.filename == null - ? PdfUtils.getFileNameFromUrl(decodedUrl, defaultTitle) - : pdfInfo.filename; + pdfInfo.filename == null + ? PdfUtils.getFileNameFromUrl(decodedUrl, defaultTitle) + : pdfInfo.filename; mUrl = url; mPdfCoordinator = - new PdfCoordinator(host, profile, activity, filepath, mTitle, tabId, url); - mIsIncognito = profile.isOffTheRecord(); + OnDemandModule.getImpl() + .getPdfEntryPoint() + .createPdfCoordinator( + host, profile, activity, url, filepath, mTitle, tabId); + mIsIncognito = isIncognito; initWithView(mPdfCoordinator.getView()); // PDF is downloading when the filepath is null. if (filepath == null) { @@ -128,7 +133,7 @@ mTitle = pdfFileName; mIsDownloadSafe = isDownloadSafe; PdfUtils.recordPdfTransientDownloadTime( - SystemClock.elapsedRealtime() - mTransientDownloadStartTimestamp); + SystemClock.elapsedRealtime() - mTransientDownloadStartTimestamp); // TODO(b/348701300): check if pdf should be opened inline. if (mIsIncognito) { Uri uri = PdfContentProvider.createContentUri(pdfFilePath, pdfFileName); @@ -138,7 +143,7 @@ } pdfFilePath = uri.toString(); } - mPdfCoordinator.onDownloadComplete(pdfFilePath, mTitle); + mPdfCoordinator.onDownloadComplete(pdfFilePath, pdfFileName); } /**
diff --git a/chrome/browser/ui/android/pdf/java/src/org/chromium/chrome/browser/pdf/PdfPageUnitTest.java b/chrome/browser/ui/android/pdf/java/src/org/chromium/chrome/browser/pdf/PdfPageUnitTest.java index ee005e5..6f621d08 100644 --- a/chrome/browser/ui/android/pdf/java/src/org/chromium/chrome/browser/pdf/PdfPageUnitTest.java +++ b/chrome/browser/ui/android/pdf/java/src/org/chromium/chrome/browser/pdf/PdfPageUnitTest.java
@@ -98,6 +98,7 @@ new PdfPage( mMockNativePageHost, mMockProfile, + false, mActivity, encodedUrl, mPdfInfo, @@ -109,7 +110,7 @@ Assert.assertEquals("Pdf page url should match.", encodedUrl, pdfPage.getUrl()); Assert.assertFalse( "Pdf should not be loaded when the view is not attached to window.", - pdfPage.mPdfCoordinator.getIsPdfLoadedForTesting()); + ((PdfCoordinator) pdfPage.mPdfCoordinator).getIsPdfLoadedForTesting()); // Simulate tab brought from background to foreground View view = pdfPage.mPdfCoordinator.getView(); @@ -117,7 +118,7 @@ contentView.addView(view); Assert.assertTrue( "Pdf should be loaded when the view is attached to window.", - pdfPage.mPdfCoordinator.getIsPdfLoadedForTesting()); + ((PdfCoordinator) pdfPage.mPdfCoordinator).getIsPdfLoadedForTesting()); String jsonString = pdfPage.requestAssistContent(/* isWorkProfile= */ true); Assert.assertNotNull( "Assist content should be generated when the pdf is ready to load", jsonString); @@ -161,6 +162,7 @@ new PdfPage( mMockNativePageHost, mMockProfile, + false, mActivity, encodedUrl, mPdfInfo, @@ -174,10 +176,10 @@ contentView.addView(view); Assert.assertTrue( "Pdf should be loaded when the view is attached to window.", - pdfPage.mPdfCoordinator.getIsPdfLoadedForTesting()); + ((PdfCoordinator) pdfPage.mPdfCoordinator).getIsPdfLoadedForTesting()); PdfCoordinator.ChromePdfViewerFragment oldFragment = - pdfPage.mPdfCoordinator.mChromePdfViewerFragment; + ((PdfCoordinator) pdfPage.mPdfCoordinator).mChromePdfViewerFragment; Assert.assertNotNull("Fragment should not be null initially", oldFragment); pdfPage.reload(); @@ -185,7 +187,7 @@ Assert.assertNotSame( "Fragment should be recreated", oldFragment, - pdfPage.mPdfCoordinator.mChromePdfViewerFragment); + ((PdfCoordinator) pdfPage.mPdfCoordinator).mChromePdfViewerFragment); contentView.removeView(view); pdfPage.destroy(); @@ -198,6 +200,7 @@ new PdfPage( mMockNativePageHost, mMockProfile, + false, mActivity, encodedUrl, mPdfInfo, @@ -210,7 +213,7 @@ Assert.assertEquals("Pdf page url should match.", encodedUrl, pdfPage.getUrl()); Assert.assertFalse( "Pdf should not be loaded when the view is not attached to window.", - pdfPage.mPdfCoordinator.getIsPdfLoadedForTesting()); + ((PdfCoordinator) pdfPage.mPdfCoordinator).getIsPdfLoadedForTesting()); // Simulate tab brought from background to foreground View view = pdfPage.mPdfCoordinator.getView(); @@ -218,7 +221,7 @@ contentView.addView(view); Assert.assertTrue( "Pdf should be loaded when the view is attached to window.", - pdfPage.mPdfCoordinator.getIsPdfLoadedForTesting()); + ((PdfCoordinator) pdfPage.mPdfCoordinator).getIsPdfLoadedForTesting()); contentView.removeView(view); } @@ -241,6 +244,7 @@ new PdfPage( mMockNativePageHost, mMockProfile, + false, mActivity, pdfPageUrl, mPdfInfo, @@ -249,7 +253,7 @@ Assert.assertNotNull(pdfPage); Assert.assertFalse( "Pdf should not be loaded when the download is not completed.", - pdfPage.mPdfCoordinator.getIsPdfLoadedForTesting()); + ((PdfCoordinator) pdfPage.mPdfCoordinator).getIsPdfLoadedForTesting()); Assert.assertNull( "Assist content cannot be generated when the pdf is not ready to load", pdfPage.requestAssistContent(/* isWorkProfile= */ false)); @@ -262,7 +266,7 @@ Assert.assertEquals("Pdf page url should match.", pdfPageUrl, pdfPage.getUrl()); Assert.assertFalse( "Pdf should not be loaded when the view is not attached to window.", - pdfPage.mPdfCoordinator.getIsPdfLoadedForTesting()); + ((PdfCoordinator) pdfPage.mPdfCoordinator).getIsPdfLoadedForTesting()); // Simulate tab brought from background to foreground View view = pdfPage.mPdfCoordinator.getView(); @@ -270,7 +274,7 @@ contentView.addView(view); Assert.assertTrue( "Pdf should be loaded when the view is attached to window.", - pdfPage.mPdfCoordinator.getIsPdfLoadedForTesting()); + ((PdfCoordinator) pdfPage.mPdfCoordinator).getIsPdfLoadedForTesting()); String jsonString = pdfPage.requestAssistContent(/* isWorkProfile= */ false); Assert.assertNotNull( "Assist content should be generated when the pdf is ready to load", jsonString);
diff --git a/chrome/browser/ui/android/pdf/java/src/org/chromium/chrome/browser/pdf/PdfUtils.java b/chrome/browser/ui/android/pdf/java/src/org/chromium/chrome/browser/pdf/PdfUtils.java index ba6d890..f1ee424b 100644 --- a/chrome/browser/ui/android/pdf/java/src/org/chromium/chrome/browser/pdf/PdfUtils.java +++ b/chrome/browser/ui/android/pdf/java/src/org/chromium/chrome/browser/pdf/PdfUtils.java
@@ -208,7 +208,7 @@ if (pdfPage == null || !pdfPage.isPdf()) { return PdfPageType.NONE; } - assert pdfPage instanceof PdfPage; + GURL url = new GURL(pdfPage.getUrl()); return getPdfPageTypeInternal(url, pdfPage.isDownloadSafe()); } @@ -237,7 +237,7 @@ sShouldOpenPdfInlineForTesting = shouldOpenPdfInlineForTesting; } - static @Nullable Uri getUriFromFilePath(String pdfFilePath) { + public static @Nullable Uri getUriFromFilePath(String pdfFilePath) { Uri uri = Uri.parse(pdfFilePath); String scheme = uri.getScheme(); try { @@ -371,20 +371,20 @@ return ChromeFeatureList.sInlinePdfV2.isEnabled(); } - static void recordPdfLoad() { + public static void recordPdfLoad() { RecordHistogram.recordBooleanHistogram("Android.Pdf.DocumentLoad", true); } - static void recordPdfLoadResultDetail(@PdfLoadResult int loadResult) { + public static void recordPdfLoadResultDetail(@PdfLoadResult int loadResult) { RecordHistogram.recordEnumeratedHistogram( "Android.Pdf.DocumentLoadResult.Detail", loadResult, PdfLoadResult.NUM_ENTRIES); } - static void recordPdfLoadTimeFirstPaired(long duration) { + public static void recordPdfLoadTimeFirstPaired(long duration) { RecordHistogram.recordTimesHistogram("Android.Pdf.DocumentLoadTime.FirstPaired", duration); } - static void recordPdfLoadInterval(long duration) { + public static void recordPdfLoadInterval(long duration) { RecordHistogram.recordMediumTimesHistogram("Android.Pdf.DocumentLoadInterval", duration); } @@ -392,17 +392,17 @@ RecordHistogram.recordTimesHistogram("Android.Pdf.DownloadTime.Transient", duration); } - static void recordFindInPage(int findInPageCounts) { + public static void recordFindInPage(int findInPageCounts) { RecordHistogram.recordExactLinearHistogram( "Android.Pdf.FindInPageCounts", findInPageCounts, /* max= */ 9); } - static void recordIsWorkProfile(boolean isWorkProfile) { + public static void recordIsWorkProfile(boolean isWorkProfile) { RecordHistogram.recordBooleanHistogram( "Android.Pdf.AssistContent.IsWorkProfile", isWorkProfile); } - static void recordGetAssistantPackageResult(boolean success) { + public static void recordGetAssistantPackageResult(boolean success) { RecordHistogram.recordBooleanHistogram( "Android.Pdf.AssistContent.GetAssistantPackageResult", success); } @@ -415,7 +415,7 @@ RecordHistogram.recordBooleanHistogram("Android.Pdf.DownloadUrlDecoded", decodeResult); } - static void recordIsUriNull(boolean isNull) { + public static void recordIsUriNull(boolean isNull) { RecordHistogram.recordBooleanHistogram("Android.Pdf.UriIsNull", isNull); } }
diff --git a/chrome/browser/ui/android/safe_browsing/BUILD.gn b/chrome/browser/ui/android/safe_browsing/BUILD.gn new file mode 100644 index 0000000..b9b7a1f --- /dev/null +++ b/chrome/browser/ui/android/safe_browsing/BUILD.gn
@@ -0,0 +1,16 @@ +# Copyright 2026 The Chromium Authors +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +source_set("safe_browsing") { + sources = [ + "password_reuse_dialog_view_android.cc", + "password_reuse_dialog_view_android.h", + ] + deps = [ + "//base", + "//chrome/android:chrome_jni_headers", + "//chrome/browser/safe_browsing/android", + "//ui/android", + ] +}
diff --git a/chrome/browser/ui/android/tab_contents/BUILD.gn b/chrome/browser/ui/android/tab_contents/BUILD.gn new file mode 100644 index 0000000..5139b88 --- /dev/null +++ b/chrome/browser/ui/android/tab_contents/BUILD.gn
@@ -0,0 +1,40 @@ +# Copyright 2026 The Chromium Authors +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +source_set("tab_contents") { + sources = [ + "chrome_web_contents_view_delegate_android.cc", + "chrome_web_contents_view_delegate_android.h", + ] + deps = [ + "//base", + "//chrome/browser/flags:flags_android", + "//chrome/browser/profiles", + "//chrome/browser/search_engines", + "//chrome/browser/ui/android:context_menu_helper", + "//components/dom_distiller/core", + "//components/search_engines", + "//content/public/browser", + "//ui/gfx", + ] +} + +source_set("unit_tests") { + testonly = true + sources = [ "chrome_web_contents_view_delegate_android_unittest.cc" ] + deps = [ + ":tab_contents", + "//base", + "//base/test:test_support", + "//chrome/browser/search_engines", + "//chrome/browser/ui", + "//chrome/test:test_support", + "//components/dom_distiller/core", + "//components/search_engines:test_support", + "//content/test:test_support", + "//testing/gtest", + "//ui/gfx", + "//url", + ] +}
diff --git a/chrome/browser/ui/android/tab_model/tab_model_jni_bridge.cc b/chrome/browser/ui/android/tab_model/tab_model_jni_bridge.cc index c93dd94..03f2c70 100644 --- a/chrome/browser/ui/android/tab_model/tab_model_jni_bridge.cc +++ b/chrome/browser/ui/android/tab_model/tab_model_jni_bridge.cc
@@ -183,19 +183,19 @@ MoveTabToWindow(tab->GetHandle(), destination_window_id, new_index); } -void TabModelJniBridge::MoveTabGroupToWindowForTesting( +bool TabModelJniBridge::MoveTabGroupToWindowForTesting( JNIEnv* env, const base::Token& group_id, long android_browser_window_ptr, int new_index) { if (!TabModel::EnableBrowserWindowInterfaceMobile()) { - return; + return false; } SessionID destination_window_id = reinterpret_cast<AndroidBrowserWindow*>(android_browser_window_ptr) ->GetSessionID(); - MoveTabGroupToWindow(tab_groups::TabGroupId::FromRawToken(group_id), - destination_window_id, new_index); + return MoveTabGroupToWindow(tab_groups::TabGroupId::FromRawToken(group_id), + destination_window_id, new_index); } bool TabModelJniBridge::IsThisTabListEditable() { @@ -857,14 +857,14 @@ jactivity, destination_index); } -void TabModelJniBridge::MoveTabGroupToWindow(tab_groups::TabGroupId group_id, +bool TabModelJniBridge::MoveTabGroupToWindow(tab_groups::TabGroupId group_id, SessionID destination_window_id, int destination_index) { ScopedJavaLocalRef<jobject> jactivity = GetActivityForWindow(destination_window_id); JNIEnv* env = AttachCurrentThread(); ScopedJavaLocalRef<jobject> jobj = java_object_.get(env); - Java_TabModelJniBridge_moveTabGroupToWindowInternal( + return Java_TabModelJniBridge_moveTabGroupToWindowInternal( env, jobj, group_id.token(), jactivity, destination_index); }
diff --git a/chrome/browser/ui/android/tab_model/tab_model_jni_bridge.h b/chrome/browser/ui/android/tab_model/tab_model_jni_bridge.h index 13457b9..5880ecd 100644 --- a/chrome/browser/ui/android/tab_model/tab_model_jni_bridge.h +++ b/chrome/browser/ui/android/tab_model/tab_model_jni_bridge.h
@@ -57,7 +57,7 @@ TabAndroid* tab, long android_browser_window_ptr, int new_index); - void MoveTabGroupToWindowForTesting(JNIEnv* env, + bool MoveTabGroupToWindowForTesting(JNIEnv* env, const base::Token& group_id, long android_browser_window_ptr, int new_index); @@ -162,7 +162,7 @@ void MoveTabToWindow(tabs::TabHandle tab, SessionID destination_window_id, int destination_index) override; - void MoveTabGroupToWindow(tab_groups::TabGroupId group_id, + bool MoveTabGroupToWindow(tab_groups::TabGroupId group_id, SessionID destination_window_id, int destination_index) override; bool IsThisTabListEditable() override;
diff --git a/chrome/browser/ui/android/tab_model/tab_model_test_helper.cc b/chrome/browser/ui/android/tab_model/tab_model_test_helper.cc index 64004b7..02ee2a3c 100644 --- a/chrome/browser/ui/android/tab_model/tab_model_test_helper.cc +++ b/chrome/browser/ui/android/tab_model/tab_model_test_helper.cc
@@ -296,10 +296,11 @@ NOTIMPLEMENTED(); } -void TestTabModel::MoveTabGroupToWindow(tab_groups::TabGroupId group_id, +bool TestTabModel::MoveTabGroupToWindow(tab_groups::TabGroupId group_id, SessionID destination_window_id, int destination_index) { NOTIMPLEMENTED(); + return false; } bool TestTabModel::IsThisTabListEditable() { @@ -636,10 +637,11 @@ NOTIMPLEMENTED(); } -void OwningTestTabModel::MoveTabGroupToWindow(tab_groups::TabGroupId group_id, +bool OwningTestTabModel::MoveTabGroupToWindow(tab_groups::TabGroupId group_id, SessionID destination_window_id, int destination_index) { NOTIMPLEMENTED(); + return false; } bool OwningTestTabModel::IsThisTabListEditable() {
diff --git a/chrome/browser/ui/android/tab_model/tab_model_test_helper.h b/chrome/browser/ui/android/tab_model/tab_model_test_helper.h index 4fdc2fa5..151c356 100644 --- a/chrome/browser/ui/android/tab_model/tab_model_test_helper.h +++ b/chrome/browser/ui/android/tab_model/tab_model_test_helper.h
@@ -127,7 +127,7 @@ void MoveTabToWindow(tabs::TabHandle tab, SessionID destination_window_id, int destination_index) override; - void MoveTabGroupToWindow(tab_groups::TabGroupId group_id, + bool MoveTabGroupToWindow(tab_groups::TabGroupId group_id, SessionID destination_window_id, int destination_index) override; bool IsThisTabListEditable() override; @@ -268,7 +268,7 @@ void MoveTabToWindow(tabs::TabHandle tab, SessionID destination_window_id, int destination_index) override; - void MoveTabGroupToWindow(tab_groups::TabGroupId group_id, + bool MoveTabGroupToWindow(tab_groups::TabGroupId group_id, SessionID destination_window_id, int destination_index) override; bool IsThisTabListEditable() override;
diff --git a/chrome/browser/ui/android/toolbar/BUILD.gn b/chrome/browser/ui/android/toolbar/BUILD.gn index 832ed89..11053a2a 100644 --- a/chrome/browser/ui/android/toolbar/BUILD.gn +++ b/chrome/browser/ui/android/toolbar/BUILD.gn
@@ -41,7 +41,7 @@ "//third_party/androidx:androidx_annotation_annotation_java", "//ui/android:ui_no_recycler_view_java", ] - srcjar_deps = [ "//chrome/browser/ui:adaptive_toolbar_enums_java" ] + srcjar_deps = [ ":adaptive_toolbar_enums_java" ] } android_library("optional_button_java") { @@ -281,6 +281,7 @@ "//chrome/browser/ui/android/omnibox:java_resources", "//chrome/browser/ui/android/overlay_panel:java", "//chrome/browser/ui/android/pdf:java", + "//chrome/browser/ui/android/pdf:pdf_utils_java", "//chrome/browser/ui/android/signin:java", "//chrome/browser/ui/android/tabstrip:enums_java", "//chrome/browser/ui/android/theme:java", @@ -607,3 +608,52 @@ [ "java/src/org/chromium/chrome/browser/toolbar/extensions:javatests" ] } } + +source_set("toolbar") { + sources = [ + "adaptive_toolbar_bridge.cc", + "adaptive_toolbar_bridge.h", + "adaptive_toolbar_enums.h", + "location_bar_model_android.cc", + "location_bar_model_android.h", + ] + deps = [ + "//base", + "//chrome/browser/profiles", + "//chrome/browser/segmentation_platform", + "//chrome/browser/ui/android/toolbar:adaptive_jni_headers", + "//chrome/browser/ui/android/toolbar:jni_headers", + "//chrome/browser/ui/toolbar", + "//chrome/common", + "//components/omnibox/browser", + "//components/omnibox/common", + "//components/segmentation_platform/public", + "//content/public/browser", + "//ui/base", + "//url", + ] +} + +source_set("unit_tests") { + testonly = true + sources = [ + "adaptive_toolbar_bridge_unittest.cc", + "location_bar_model_android_unittest.cc", + ] + deps = [ + ":toolbar", + "//base", + "//base/test:test_support", + "//chrome/browser/segmentation_platform", + "//chrome/browser/ui", + "//chrome/test:test_support", + "//components/segmentation_platform/public:test_support", + "//content/test:test_support", + "//testing/gmock", + "//testing/gtest", + ] +} + +java_cpp_enum("adaptive_toolbar_enums_java") { + sources = [ "adaptive_toolbar_enums.h" ] +}
diff --git a/chrome/browser/ui/android/toolbar/java/res/values/dimens.xml b/chrome/browser/ui/android/toolbar/java/res/values/dimens.xml index 2a80049..2f381b9f 100644 --- a/chrome/browser/ui/android/toolbar/java/res/values/dimens.xml +++ b/chrome/browser/ui/android/toolbar/java/res/values/dimens.xml
@@ -93,4 +93,9 @@ <dimen name="extension_action_popup_min_width" tools:ignore="UnusedResources">25dp</dimen> <dimen name="extension_action_popup_min_height" tools:ignore="UnusedResources">25dp</dimen> <dimen name="extension_action_popup_elevation" tools:ignore="UnusedResources">6dp</dimen> + + <!-- Extension Action Hover Card --> + <dimen name="extension_action_hover_card_width" tools:ignore="UnusedResources">270dp</dimen> + <dimen name="extension_action_hover_card_horizontal_padding" tools:ignore="UnusedResources">16dp</dimen> + <dimen name="extension_action_hover_card_vertical_padding" tools:ignore="UnusedResources">12dp</dimen> </resources>
diff --git a/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/ToolbarPositionController.java b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/ToolbarPositionController.java index 108a0f8..564e607 100644 --- a/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/ToolbarPositionController.java +++ b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/ToolbarPositionController.java
@@ -849,7 +849,7 @@ == StateTransition.SNAP_TO_TOP; } - static void resetCachedToolbarConfigurationForTesting() { + public static void resetCachedToolbarConfigurationForTesting() { sToolbarShouldShowOnTop = null; }
diff --git a/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/extensions/BUILD.gn b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/extensions/BUILD.gn index 928a7187..6f09639b 100644 --- a/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/extensions/BUILD.gn +++ b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/extensions/BUILD.gn
@@ -23,6 +23,8 @@ "ExtensionActionButtonViewBinder.java", "ExtensionActionContextMenuUtils.java", "ExtensionActionDragHelper.java", + "ExtensionActionHoverCardProperties.java", + "ExtensionActionHoverCardViewBinder.java", "ExtensionActionIconUtil.java", "ExtensionActionListAnchoredModelList.java", "ExtensionActionListCoordinator.java",
diff --git a/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/extensions/ExtensionActionButtonProperties.java b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/extensions/ExtensionActionButtonProperties.java index 1d0dcf1..a3d4116 100644 --- a/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/extensions/ExtensionActionButtonProperties.java +++ b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/extensions/ExtensionActionButtonProperties.java
@@ -53,6 +53,10 @@ public static final WritableObjectPropertyKey<View.OnClickListener> ON_CLICK_LISTENER = new WritableObjectPropertyKey<>(); + /** The hover listener. */ + public static final WritableObjectPropertyKey<View.OnHoverListener> ON_HOVER_LISTENER = + new WritableObjectPropertyKey<>(); + /** The context-click listener. */ public static final WritableObjectPropertyKey<View.OnLongClickListener> ON_LONG_CLICK_LISTENER = new WritableObjectPropertyKey<>(); @@ -70,6 +74,7 @@ ID, IS_DRAGGABLE, ON_CLICK_LISTENER, + ON_HOVER_LISTENER, ON_LONG_CLICK_LISTENER, TOUCH_LISTENER };
diff --git a/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/extensions/ExtensionActionButtonViewBinder.java b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/extensions/ExtensionActionButtonViewBinder.java index 3421f1c..556661e4 100644 --- a/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/extensions/ExtensionActionButtonViewBinder.java +++ b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/extensions/ExtensionActionButtonViewBinder.java
@@ -30,6 +30,8 @@ button.setOnTouchListener(model.get(ExtensionActionButtonProperties.TOUCH_LISTENER)); } else if (key == ExtensionActionButtonProperties.ON_CLICK_LISTENER) { button.setOnClickListener(model.get(ExtensionActionButtonProperties.ON_CLICK_LISTENER)); + } else if (key == ExtensionActionButtonProperties.ON_HOVER_LISTENER) { + button.setOnHoverListener(model.get(ExtensionActionButtonProperties.ON_HOVER_LISTENER)); } else if (key == ExtensionActionButtonProperties.ON_LONG_CLICK_LISTENER) { button.setOnLongClickListener( model.get(ExtensionActionButtonProperties.ON_LONG_CLICK_LISTENER));
diff --git a/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/extensions/ExtensionActionHoverCardProperties.java b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/extensions/ExtensionActionHoverCardProperties.java new file mode 100644 index 0000000..ba31334 --- /dev/null +++ b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/extensions/ExtensionActionHoverCardProperties.java
@@ -0,0 +1,25 @@ +// Copyright 2026 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +package org.chromium.chrome.browser.toolbar.extensions; + +import org.chromium.build.annotations.NullMarked; +import org.chromium.ui.modelutil.PropertyKey; +import org.chromium.ui.modelutil.PropertyModel.WritableObjectPropertyKey; + +@NullMarked +public class ExtensionActionHoverCardProperties { + public static final WritableObjectPropertyKey<String> ACTION_TITLE = + new WritableObjectPropertyKey<>(); + public static final WritableObjectPropertyKey<String> SITE_ACCESS_TITLE = + new WritableObjectPropertyKey<>(); + public static final WritableObjectPropertyKey<String> SITE_ACCESS_DESC = + new WritableObjectPropertyKey<>(); + public static final WritableObjectPropertyKey<String> POLICY_TEXT = + new WritableObjectPropertyKey<>(); + + public static final PropertyKey[] ALL_KEYS = { + ACTION_TITLE, SITE_ACCESS_TITLE, SITE_ACCESS_DESC, POLICY_TEXT + }; +}
diff --git a/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/extensions/ExtensionActionHoverCardViewBinder.java b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/extensions/ExtensionActionHoverCardViewBinder.java new file mode 100644 index 0000000..9b7ebe6a --- /dev/null +++ b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/extensions/ExtensionActionHoverCardViewBinder.java
@@ -0,0 +1,65 @@ +// Copyright 2026 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +package org.chromium.chrome.browser.toolbar.extensions; + +import android.view.View; +import android.widget.LinearLayout; +import android.widget.TextView; + +import org.chromium.build.annotations.NullMarked; +import org.chromium.ui.modelutil.PropertyKey; +import org.chromium.ui.modelutil.PropertyModel; + +@NullMarked +public class ExtensionActionHoverCardViewBinder { + public static void bind(PropertyModel model, View view, PropertyKey propertyKey) { + if (propertyKey == ExtensionActionHoverCardProperties.ACTION_TITLE) { + TextView titleView = view.findViewById(R.id.extension_action_hover_card_title); + titleView.setText(model.get(ExtensionActionHoverCardProperties.ACTION_TITLE)); + } else if (propertyKey == ExtensionActionHoverCardProperties.SITE_ACCESS_TITLE + || propertyKey == ExtensionActionHoverCardProperties.SITE_ACCESS_DESC) { + String title = model.get(ExtensionActionHoverCardProperties.SITE_ACCESS_TITLE); + String desc = model.get(ExtensionActionHoverCardProperties.SITE_ACCESS_DESC); + + View divider = view.findViewById(R.id.extension_action_hover_card_site_access_divider); + LinearLayout container = + view.findViewById(R.id.extension_action_hover_card_site_access_container); + TextView titleView = + view.findViewById(R.id.extension_action_hover_card_site_access_title); + TextView descView = + view.findViewById(R.id.extension_action_hover_card_site_access_description); + + if (title != null) { + divider.setVisibility(View.VISIBLE); + container.setVisibility(View.VISIBLE); + titleView.setText(title); + + assert desc != null; + descView.setText(desc); + descView.setVisibility(View.VISIBLE); + } else { + divider.setVisibility(View.GONE); + container.setVisibility(View.GONE); + } + + } else if (propertyKey == ExtensionActionHoverCardProperties.POLICY_TEXT) { + String policy = model.get(ExtensionActionHoverCardProperties.POLICY_TEXT); + + View divider = view.findViewById(R.id.extension_action_hover_card_policy_divider); + LinearLayout container = + view.findViewById(R.id.extension_action_hover_card_policy_container); + TextView policyView = view.findViewById(R.id.extension_action_hover_card_policy_text); + + if (policy != null) { + divider.setVisibility(View.VISIBLE); + container.setVisibility(View.VISIBLE); + policyView.setText(policy); + } else { + divider.setVisibility(View.GONE); + container.setVisibility(View.GONE); + } + } + } +}
diff --git a/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/extensions/ExtensionActionListMediator.java b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/extensions/ExtensionActionListMediator.java index 3ca291f..101f62bb 100644 --- a/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/extensions/ExtensionActionListMediator.java +++ b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/extensions/ExtensionActionListMediator.java
@@ -7,6 +7,14 @@ import android.app.Activity; import android.content.Context; import android.graphics.Bitmap; +import android.graphics.Color; +import android.graphics.drawable.ColorDrawable; +import android.os.Handler; +import android.os.Looper; +import android.view.LayoutInflater; +import android.view.MotionEvent; +import android.view.View; +import android.view.ViewConfiguration; import androidx.annotation.VisibleForTesting; @@ -23,6 +31,7 @@ import org.chromium.chrome.browser.toolbar.extensions.ExtensionActionButtonProperties.ListItemType; import org.chromium.chrome.browser.ui.browser_window.ChromeAndroidTask; import org.chromium.chrome.browser.ui.extensions.ExtensionAction; +import org.chromium.chrome.browser.ui.extensions.ExtensionAction.HoverCardState; import org.chromium.chrome.browser.ui.extensions.ExtensionActionContextMenuBridge; import org.chromium.chrome.browser.ui.extensions.ExtensionActionPopupContents; import org.chromium.chrome.browser.ui.extensions.ExtensionsToolbarBridge; @@ -35,6 +44,9 @@ import org.chromium.ui.modelutil.MVCListAdapter.ListItem; import org.chromium.ui.modelutil.MVCListAdapter.ModelList; import org.chromium.ui.modelutil.PropertyModel; +import org.chromium.ui.modelutil.PropertyModelChangeProcessor; +import org.chromium.ui.widget.AnchoredPopupWindow; +import org.chromium.ui.widget.RectProvider; import java.util.Arrays; import java.util.HashSet; @@ -112,6 +124,11 @@ private final @Nullable SelectionDropdownMenuDelegate mSelectionDropdownMenuDelegate; private final ExtensionActionListCoordinator.RecyclerViewDelegate mRecyclerViewDelegate; private final TabModelSelector mTabModelSelector; + private final Handler mHandler = new Handler(Looper.getMainLooper()); + + private @Nullable AnchoredPopupWindow mHoverCard; + private @Nullable String mHoverCardActionId; + private @Nullable Runnable mShowHoverCardRunnable; private final ExtensionsToolbarBridge mExtensionsToolbarBridge; private final ToolbarDelegate mToolbarDelegate = new ToolbarDelegate(); @@ -165,8 +182,14 @@ @Override public void destroy() { + if (mShowHoverCardRunnable != null) { + mHandler.removeCallbacks(mShowHoverCardRunnable); + mShowHoverCardRunnable = null; + } + mRecyclerViewDelegate.clearOnAnimationsFinishedRunnables(); + closeHoverCard(); closePopup(); closeContextMenu(); @@ -360,6 +383,11 @@ ExtensionActionButtonProperties.ON_CLICK_LISTENER, (view) -> onPrimaryClick(actionId)) .with( + ExtensionActionButtonProperties.ON_HOVER_LISTENER, + (view, event) -> { + return onHover(actionId, event, webContents); + }) + .with( ExtensionActionButtonProperties.ON_LONG_CLICK_LISTENER, (view) -> { requestShowContextMenu(actionId); @@ -440,6 +468,116 @@ return mModels.get(index).model.get(ExtensionActionButtonProperties.ID); } + private boolean onHover(String actionId, MotionEvent event, @Nullable WebContents webContents) { + if (!(mActionState instanceof ActionState.Idle)) { + return false; + } + + if (event.getAction() == MotionEvent.ACTION_HOVER_ENTER) { + if (mShowHoverCardRunnable != null) { + mHandler.removeCallbacks(mShowHoverCardRunnable); + } + + if (mHoverCard == null) { + mHoverCardActionId = actionId; + mShowHoverCardRunnable = + () -> { + showHoverCard(actionId, webContents); + mShowHoverCardRunnable = null; + }; + mHandler.postDelayed( + mShowHoverCardRunnable, ViewConfiguration.getLongPressTimeout()); + } else if (!actionId.equals(mHoverCardActionId)) { + closeHoverCard(); + showHoverCard(actionId, webContents); + } + } else if (event.getAction() == MotionEvent.ACTION_HOVER_EXIT) { + if (actionId.equals(mHoverCardActionId)) { + if (mShowHoverCardRunnable != null) { + mHandler.removeCallbacks(mShowHoverCardRunnable); + mShowHoverCardRunnable = null; + } + closeHoverCard(); + } + } + + // We don't consume the event because we want the button to still be hovered. + return false; + } + + private void showHoverCard(String actionId, @Nullable WebContents webContents) { + if (webContents == null) { + return; + } + + Activity activity = mWindowAndroid.getActivity().get(); + if (activity == null) { + return; + } + + ExtensionAction action = mExtensionsToolbarBridge.getAction(actionId, webContents); + if (action == null) { + return; + } + + View anchorView = mRecyclerViewDelegate.getButtonViewForId(actionId); + if (anchorView == null) { + return; + } + + HoverCardState state = action.getHoverCardState(); + RectProvider rectProvider = MenuBuilderHelper.getRectProvider(anchorView); + + View contentView = + LayoutInflater.from(activity).inflate(R.layout.extension_action_hover_card, null); + + PropertyModel model = + new PropertyModel.Builder(ExtensionActionHoverCardProperties.ALL_KEYS) + .with(ExtensionActionHoverCardProperties.ACTION_TITLE, action.getTitle()) + .with( + ExtensionActionHoverCardProperties.SITE_ACCESS_TITLE, + state.getSiteAccessTitle()) + .with( + ExtensionActionHoverCardProperties.SITE_ACCESS_DESC, + state.getSiteAccessDescription()) + .with(ExtensionActionHoverCardProperties.POLICY_TEXT, state.getPolicyText()) + .build(); + + PropertyModelChangeProcessor.create( + model, contentView, ExtensionActionHoverCardViewBinder::bind); + + mHoverCard = + new AnchoredPopupWindow.Builder( + anchorView.getContext(), + anchorView.getRootView(), + new ColorDrawable(Color.TRANSPARENT), + () -> contentView, + rectProvider) + .setVerticalOverlapAnchor(false) + .setHorizontalOverlapAnchor(true) + .setMaxWidth( + anchorView + .getResources() + .getDimensionPixelSize( + R.dimen.extension_action_hover_card_width)) + .setFocusable(false) + .setTouchable(false) + .setAnimateFromAnchor(false) + .setAnimationStyle(R.style.PopupWindowAnimFade) + .build(); + + mHoverCard.show(); + mHoverCardActionId = actionId; + } + + private void closeHoverCard() { + if (mHoverCard != null) { + mHoverCard.dismiss(); + mHoverCard = null; + mHoverCardActionId = null; + } + } + private void onPrimaryClick(String actionId) { if (mActionState instanceof ActionState.PopupActive activeState) { boolean closeOnly = activeState.getActionId().equals(actionId); @@ -505,6 +643,7 @@ } private void requestShowPopup(String actionId, long nativeHostPtr) { + closeHoverCard(); closePopup(); closeContextMenu(); @@ -595,6 +734,7 @@ @VisibleForTesting void requestShowContextMenu(String actionId) { + closeHoverCard(); closePopup(); closeContextMenu();
diff --git a/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/extensions/ExtensionActionListMediatorTest.java b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/extensions/ExtensionActionListMediatorTest.java index 3c5e0f28..8eac14d 100644 --- a/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/extensions/ExtensionActionListMediatorTest.java +++ b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/extensions/ExtensionActionListMediatorTest.java
@@ -12,16 +12,24 @@ import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.anyLong; import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.eq; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import static org.robolectric.Shadows.shadowOf; +import android.app.Activity; import android.content.Context; import android.graphics.Bitmap; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Paint; import android.os.Looper; +import android.view.MotionEvent; +import android.view.View; +import android.view.ViewConfiguration; import androidx.test.core.app.ApplicationProvider; @@ -35,6 +43,7 @@ import org.mockito.Mock; import org.mockito.junit.MockitoJUnit; import org.mockito.junit.MockitoRule; +import org.robolectric.Robolectric; import org.chromium.base.supplier.ObservableSuppliers; import org.chromium.base.supplier.SettableNullableObservableSupplier; @@ -61,8 +70,10 @@ import org.chromium.ui.modelutil.MVCListAdapter.ListItem; import org.chromium.ui.modelutil.MVCListAdapter.ModelList; +import java.lang.ref.WeakReference; import java.util.HashMap; import java.util.Map; +import java.util.concurrent.TimeUnit; @RunWith(BaseRobolectricTestRunner.class) public class ExtensionActionListMediatorTest { @@ -523,6 +534,77 @@ verify(mPopupContentsMock).destroy(); } + @Test + public void testHoverCard_ShowsAfterDelay() { + Activity activity = Robolectric.buildActivity(Activity.class).setup().get(); + when(mWindowAndroid.getActivity()).thenReturn(new WeakReference<>(activity)); + + mMediator.reconcileActionItems(); + ListItem item = mModels.get(0); + View.OnHoverListener listener = + item.model.get(ExtensionActionButtonProperties.ON_HOVER_LISTENER); + + // Set up the mock action to track if its state is queried. + ExtensionAction action = mock(ExtensionAction.class); + ExtensionAction.HoverCardState state = mock(ExtensionAction.HoverCardState.class); + when(action.getHoverCardState()).thenReturn(state); + doReturn(action).when(mExtensionsToolbarBridge).getAction(eq(ACTION1_ID), any()); + + View anchorView = new View(activity); + when(mRecyclerViewDelegate.getButtonViewForId(ACTION1_ID)).thenReturn(anchorView); + + // Trigger hover enter. + MotionEvent enterEvent = mock(MotionEvent.class); + when(enterEvent.getAction()).thenReturn(MotionEvent.ACTION_HOVER_ENTER); + listener.onHover(anchorView, enterEvent); + + // Verify the card construction hasn't started yet. + verify(action, never()).getHoverCardState(); + + // Fast forward looper past the long press timeout. + shadowOf(Looper.getMainLooper()) + .idleFor(ViewConfiguration.getLongPressTimeout(), TimeUnit.MILLISECONDS); + + // Verify the mediator proceeded to build and show the card. + verify(action).getHoverCardState(); + } + + @Test + public void testHoverCard_CancelsOnExitBeforeDelay() { + Activity activity = Robolectric.buildActivity(Activity.class).setup().get(); + when(mWindowAndroid.getActivity()).thenReturn(new WeakReference<>(activity)); + + mMediator.reconcileActionItems(); + ListItem item = mModels.get(0); + View.OnHoverListener listener = + item.model.get(ExtensionActionButtonProperties.ON_HOVER_LISTENER); + + ExtensionAction action = mock(ExtensionAction.class); + ExtensionAction.HoverCardState state = mock(ExtensionAction.HoverCardState.class); + when(action.getHoverCardState()).thenReturn(state); + doReturn(action).when(mExtensionsToolbarBridge).getAction(eq(ACTION1_ID), any()); + + View anchorView = new View(activity); + when(mRecyclerViewDelegate.getButtonViewForId(ACTION1_ID)).thenReturn(anchorView); + + // Trigger hover enter. + MotionEvent enterEvent = mock(MotionEvent.class); + when(enterEvent.getAction()).thenReturn(MotionEvent.ACTION_HOVER_ENTER); + listener.onHover(anchorView, enterEvent); + + // Immediately trigger hover exit before the delay finishes. + MotionEvent exitEvent = mock(MotionEvent.class); + when(exitEvent.getAction()).thenReturn(MotionEvent.ACTION_HOVER_EXIT); + listener.onHover(anchorView, exitEvent); + + // Fast forward looper past the long press timeout. + shadowOf(Looper.getMainLooper()) + .idleFor(ViewConfiguration.getLongPressTimeout(), TimeUnit.MILLISECONDS); + + // Verify the runnable was cancelled and the card was never constructed. + verify(action, never()).getHoverCardState(); + } + private static Bitmap createSimpleIcon(int color) { Bitmap bitmap = Bitmap.createBitmap(12, 12, Bitmap.Config.ARGB_8888); Canvas canvas = new Canvas(bitmap);
diff --git a/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/extensions/ExtensionsMenuCoordinator.java b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/extensions/ExtensionsMenuCoordinator.java index a0825bf..9f71169 100644 --- a/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/extensions/ExtensionsMenuCoordinator.java +++ b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/extensions/ExtensionsMenuCoordinator.java
@@ -303,6 +303,7 @@ mMainPageModel.set( ExtensionsMenuProperties.MENU_BUTTON_PINNED, mMenuButtonPinningDelegate.isMenuButtonPinned()); + mMainPageModel.set(ExtensionsMenuProperties.SITE_SETTINGS_CONTAINER_VISIBLE, true); mMainPageModel.set(ExtensionsMenuProperties.SITE_SETTINGS_TOGGLE_VISIBLE, true); mMainPageModel.set(ExtensionsMenuProperties.SITE_SETTINGS_TOGGLE_CHECKED, true); mMainPageModel.set(
diff --git a/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/extensions/ExtensionsMenuMediator.java b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/extensions/ExtensionsMenuMediator.java index b61b61d..0d362ca 100644 --- a/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/extensions/ExtensionsMenuMediator.java +++ b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/extensions/ExtensionsMenuMediator.java
@@ -568,8 +568,9 @@ boolean isZeroState = mActionModels.size() == 0; mMainPageModel.set(ExtensionsMenuProperties.IS_ZERO_STATE, isZeroState); if (isZeroState) { - // If we are in zero state, hide the site settings toggle to keep the empty state clean. - mMainPageModel.set(ExtensionsMenuProperties.SITE_SETTINGS_TOGGLE_VISIBLE, false); + // If we are in zero state, hide the site settings container to keep the empty state + // clean. + mMainPageModel.set(ExtensionsMenuProperties.SITE_SETTINGS_CONTAINER_VISIBLE, false); // We also hide the discover extensions button in the main page, as there is already an // open web store button present in the zero state view. mMainPageModel.set(ExtensionsMenuProperties.DISCOVER_EXTENSIONS_VISIBLE, false); @@ -584,6 +585,7 @@ void updateSiteSettingsToggle() { ExtensionsMenuTypes.SiteSettingsState siteSettingsState = mMenuBridge.getSiteSettingsState(); + mMainPageModel.set(ExtensionsMenuProperties.SITE_SETTINGS_CONTAINER_VISIBLE, true); mMainPageModel.set( ExtensionsMenuProperties.SITE_SETTINGS_TOGGLE_VISIBLE, siteSettingsState.toggle.status != ExtensionsMenuTypes.ControlState.Status.HIDDEN);
diff --git a/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/extensions/ExtensionsMenuMediatorTest.java b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/extensions/ExtensionsMenuMediatorTest.java index 89b66e7..7ec174e 100644 --- a/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/extensions/ExtensionsMenuMediatorTest.java +++ b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/extensions/ExtensionsMenuMediatorTest.java
@@ -190,7 +190,7 @@ assertTrue(mActionModels.isEmpty()); verify(mMenuPropertyModel).set(ExtensionsMenuProperties.IS_ZERO_STATE, true); verify(mMenuPropertyModel) - .set(ExtensionsMenuProperties.SITE_SETTINGS_TOGGLE_VISIBLE, false); + .set(ExtensionsMenuProperties.SITE_SETTINGS_CONTAINER_VISIBLE, false); verify(mMenuPropertyModel).set(ExtensionsMenuProperties.DISCOVER_EXTENSIONS_VISIBLE, false); verify(mOnReadyRunnable).run(); } @@ -472,7 +472,7 @@ assertEquals(0, mActionModels.size()); verify(mMenuPropertyModel).set(ExtensionsMenuProperties.IS_ZERO_STATE, true); verify(mMenuPropertyModel) - .set(ExtensionsMenuProperties.SITE_SETTINGS_TOGGLE_VISIBLE, false); + .set(ExtensionsMenuProperties.SITE_SETTINGS_CONTAINER_VISIBLE, false); verify(mMenuPropertyModel).set(ExtensionsMenuProperties.DISCOVER_EXTENSIONS_VISIBLE, false); } @@ -663,6 +663,8 @@ .thenReturn(siteSettingsStateOn); mMenuMediator.updateSiteSettingsToggle(); + verify(mMenuPropertyModel) + .set(ExtensionsMenuProperties.SITE_SETTINGS_CONTAINER_VISIBLE, true); verify(mMenuPropertyModel).set(ExtensionsMenuProperties.SITE_SETTINGS_TOGGLE_VISIBLE, true); verify(mMenuPropertyModel).set(ExtensionsMenuProperties.SITE_SETTINGS_TOGGLE_CHECKED, true); verify(mMenuPropertyModel).set(ExtensionsMenuProperties.SITE_SETTINGS_LABEL, "test_label"); @@ -681,6 +683,8 @@ .thenReturn(siteSettingsStateOff); mMenuMediator.updateSiteSettingsToggle(); + verify(mMenuPropertyModel) + .set(ExtensionsMenuProperties.SITE_SETTINGS_CONTAINER_VISIBLE, true); verify(mMenuPropertyModel).set(ExtensionsMenuProperties.SITE_SETTINGS_TOGGLE_VISIBLE, true); verify(mMenuPropertyModel) .set(ExtensionsMenuProperties.SITE_SETTINGS_TOGGLE_CHECKED, false); @@ -702,6 +706,8 @@ mMenuMediator.updateSiteSettingsToggle(); verify(mMenuPropertyModel) + .set(ExtensionsMenuProperties.SITE_SETTINGS_CONTAINER_VISIBLE, true); + verify(mMenuPropertyModel) .set(ExtensionsMenuProperties.SITE_SETTINGS_TOGGLE_VISIBLE, false); verify(mMenuPropertyModel) .set(ExtensionsMenuProperties.SITE_SETTINGS_TOGGLE_CHECKED, false);
diff --git a/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/extensions/ExtensionsMenuProperties.java b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/extensions/ExtensionsMenuProperties.java index 471a88a..b00a3fb 100644 --- a/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/extensions/ExtensionsMenuProperties.java +++ b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/extensions/ExtensionsMenuProperties.java
@@ -77,10 +77,15 @@ public static final WritableObjectPropertyKey<OnClickListener> RELOAD_CLICK_LISTENER = new WritableObjectPropertyKey<>(); - public static final WritableObjectPropertyKey<String> SITE_SETTINGS_LABEL = - new WritableObjectPropertyKey<>(); + /** Whether the entire site settings section is visible in the menu. */ + public static final WritableBooleanPropertyKey SITE_SETTINGS_CONTAINER_VISIBLE = + new WritableBooleanPropertyKey(); - public static final WritableObjectPropertyKey<String> SITE_SETTINGS_TOGGLE_TOOLTIP = + /** + * Properties for the site settings toggle that allows users to block or allow all extensions + * for the current site. + */ + public static final WritableObjectPropertyKey<String> SITE_SETTINGS_LABEL = new WritableObjectPropertyKey<>(); public static final WritableBooleanPropertyKey SITE_SETTINGS_TOGGLE_CHECKED = @@ -89,10 +94,9 @@ public static final WritableObjectPropertyKey<OnCheckedChangeListener> SITE_SETTINGS_TOGGLE_CLICK_LISTENER = new WritableObjectPropertyKey<>(); - /** - * Properties for the site settings toggle that allows users to block or allow all extensions - * for the current site. - */ + public static final WritableObjectPropertyKey<String> SITE_SETTINGS_TOGGLE_TOOLTIP = + new WritableObjectPropertyKey<>(); + public static final WritableBooleanPropertyKey SITE_SETTINGS_TOGGLE_VISIBLE = new WritableBooleanPropertyKey(); @@ -111,6 +115,7 @@ MENU_BUTTON_PINNING_CLICK_LISTENER, OPTIONAL_SECTION_TYPE, RELOAD_CLICK_LISTENER, + SITE_SETTINGS_CONTAINER_VISIBLE, SITE_SETTINGS_LABEL, SITE_SETTINGS_TOGGLE_CHECKED, SITE_SETTINGS_TOGGLE_CLICK_LISTENER,
diff --git a/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/extensions/ExtensionsMenuViewBinder.java b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/extensions/ExtensionsMenuViewBinder.java index 52db2639..18d2d0e 100644 --- a/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/extensions/ExtensionsMenuViewBinder.java +++ b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/extensions/ExtensionsMenuViewBinder.java
@@ -33,6 +33,7 @@ // We use beginDelayedTransition to smoothly animate the resulting layout // resizing, preventing the menu from abruptly "jumping" to its new height. if (key == ExtensionsMenuProperties.IS_ZERO_STATE + || key == ExtensionsMenuProperties.SITE_SETTINGS_CONTAINER_VISIBLE || key == ExtensionsMenuProperties.SITE_SETTINGS_TOGGLE_VISIBLE || key == ExtensionsMenuProperties.OPTIONAL_SECTION_TYPE || key == ExtensionsMenuProperties.CURRENT_PAGE) { @@ -90,9 +91,15 @@ view.findViewById(R.id.extensions_menu_manage_extensions_button) .setOnClickListener( model.get(ExtensionsMenuProperties.MANAGE_EXTENSIONS_CLICK_LISTENER)); - } else if (key == ExtensionsMenuProperties.SITE_SETTINGS_TOGGLE_VISIBLE) { + } else if (key == ExtensionsMenuProperties.SITE_SETTINGS_CONTAINER_VISIBLE) { getSiteSettingsToggleContainer(view) .setVisibility( + model.get(ExtensionsMenuProperties.SITE_SETTINGS_CONTAINER_VISIBLE) + ? View.VISIBLE + : View.GONE); + } else if (key == ExtensionsMenuProperties.SITE_SETTINGS_TOGGLE_VISIBLE) { + view.findViewById(R.id.extensions_menu_site_settings_toggle) + .setVisibility( model.get(ExtensionsMenuProperties.SITE_SETTINGS_TOGGLE_VISIBLE) ? View.VISIBLE : View.GONE);
diff --git a/chrome/browser/ui/android/webid/BUILD.gn b/chrome/browser/ui/android/webid/BUILD.gn index c77acc3..36ceacf 100644 --- a/chrome/browser/ui/android/webid/BUILD.gn +++ b/chrome/browser/ui/android/webid/BUILD.gn
@@ -53,3 +53,21 @@ "//content/public/browser/webid/identity_request_dialog_controller.h", ] } + +source_set("webid") { + sources = [ + "account_selection_view_android.cc", + "account_selection_view_android.h", + ] + deps = [ + "//base", + "//chrome/browser/ui:ui_features", + "//chrome/browser/ui/android/webid:jni_headers", + "//chrome/browser/ui/webid", + "//content/public/browser", + "//third_party/blink/public/mojom:mojom_platform", + "//ui/android", + "//ui/gfx", + "//url", + ] +}
diff --git a/chrome/browser/ui/ash/arc/DEPS b/chrome/browser/ui/ash/arc/DEPS index 77b0d4ac..90718656 100644 --- a/chrome/browser/ui/ash/arc/DEPS +++ b/chrome/browser/ui/ash/arc/DEPS
@@ -18,7 +18,6 @@ "+chrome/browser/ui/ash/shelf/app_service", "+chrome/browser/ui/ash/shelf/chrome_shelf_controller.h", "+chrome/browser/ui/ash/system_web_apps", - "+chrome/browser/ui/browser_finder.h", "+chrome/browser/ui/browser.h", "+chrome/browser/ui/web_applications/test", "+chrome/browser/web_applications",
diff --git a/chrome/browser/ui/ash/arc/arc_open_url_delegate_impl_browsertest.cc b/chrome/browser/ui/ash/arc/arc_open_url_delegate_impl_browsertest.cc index 1134d85..4a82bb8 100644 --- a/chrome/browser/ui/ash/arc/arc_open_url_delegate_impl_browsertest.cc +++ b/chrome/browser/ui/ash/arc/arc_open_url_delegate_impl_browsertest.cc
@@ -21,7 +21,6 @@ #include "chrome/browser/chromeos/arc/arc_web_contents_data.h" #include "chrome/browser/ui/ash/system_web_apps/system_web_app_ui_utils.h" #include "chrome/browser/ui/browser.h" -#include "chrome/browser/ui/browser_finder.h" #include "chrome/browser/ui/browser_window/public/browser_window_interface.h" #include "chrome/browser/ui/browser_window/public/global_browser_collection.h" #include "chrome/browser/ui/web_applications/test/web_app_navigation_browsertest.h"
diff --git a/chrome/browser/ui/ash/boca/DEPS b/chrome/browser/ui/ash/boca/DEPS index 86095a2..22ee225a 100644 --- a/chrome/browser/ui/ash/boca/DEPS +++ b/chrome/browser/ui/ash/boca/DEPS
@@ -3,7 +3,6 @@ "+chrome/browser/apps/platform_apps", "+chrome/browser/profiles/profile.h", "+chrome/browser/ui/browser.h", - "+chrome/browser/ui/browser_finder.h", "+chrome/browser/ui/browser_tabstrip.h", "+chrome/browser/ui/browser_window.h", "+chrome/browser/ui/browser_window/public/browser_window_interface.h",
diff --git a/chrome/browser/ui/ash/boca/chrome_tab_strip_delegate_browsertest.cc b/chrome/browser/ui/ash/boca/chrome_tab_strip_delegate_browsertest.cc index f137565..5583f4a 100644 --- a/chrome/browser/ui/ash/boca/chrome_tab_strip_delegate_browsertest.cc +++ b/chrome/browser/ui/ash/boca/chrome_tab_strip_delegate_browsertest.cc
@@ -10,7 +10,6 @@ #include "chrome/browser/apps/platform_apps/app_browsertest_util.h" #include "chrome/browser/profiles/profile.h" #include "chrome/browser/ui/browser.h" -#include "chrome/browser/ui/browser_finder.h" #include "chrome/browser/ui/browser_tabstrip.h" #include "chrome/browser/ui/browser_window.h" #include "chrome/browser/ui/browser_window/public/browser_window_interface.h"
diff --git a/chrome/browser/ui/ash/desks/DEPS b/chrome/browser/ui/ash/desks/DEPS index 220eaa0..cf35322 100644 --- a/chrome/browser/ui/ash/desks/DEPS +++ b/chrome/browser/ui/ash/desks/DEPS
@@ -24,7 +24,6 @@ "+chrome/browser/ui/ash/multi_user", "+chrome/browser/ui/ash/shelf", "+chrome/browser/ui/ash/system_web_apps", - "+chrome/browser/ui/browser_finder.h", "+chrome/browser/ui/browser.h", "+chrome/browser/ui/browser_tabstrip.h", "+chrome/browser/ui/browser_window.h",
diff --git a/chrome/browser/ui/ash/desks/desks_client_browsertest.cc b/chrome/browser/ui/ash/desks/desks_client_browsertest.cc index 93eeca6..6b951b8f 100644 --- a/chrome/browser/ui/ash/desks/desks_client_browsertest.cc +++ b/chrome/browser/ui/ash/desks/desks_client_browsertest.cc
@@ -89,7 +89,6 @@ #include "chrome/browser/ui/ash/multi_user/multi_user_util.h" #include "chrome/browser/ui/ash/system_web_apps/system_web_app_ui_utils.h" #include "chrome/browser/ui/browser.h" -#include "chrome/browser/ui/browser_finder.h" #include "chrome/browser/ui/browser_tabstrip.h" #include "chrome/browser/ui/browser_window.h" #include "chrome/browser/ui/browser_window/public/browser_collection_observer.h"
diff --git a/chrome/browser/ui/ash/gemini_app/DEPS b/chrome/browser/ui/ash/gemini_app/DEPS index 96dfdea..a57c155 100644 --- a/chrome/browser/ui/ash/gemini_app/DEPS +++ b/chrome/browser/ui/ash/gemini_app/DEPS
@@ -11,7 +11,6 @@ "+chrome/browser/ash/login/test", "+chrome/browser/ash/system_web_apps", "+chrome/browser/ui/browser_element_identifiers.h", - "+chrome/browser/ui/browser_finder.h", "+chrome/browser/ui/browser_window/public/browser_collection_observer.h", "+chrome/browser/ui/browser_window/public/browser_window_interface_iterator.h", "+chrome/browser/ui/browser_window/public/global_browser_collection.h",
diff --git a/chrome/browser/ui/ash/login/DEPS b/chrome/browser/ui/ash/login/DEPS index 6e35888f..ff333c4 100644 --- a/chrome/browser/ui/ash/login/DEPS +++ b/chrome/browser/ui/ash/login/DEPS
@@ -82,7 +82,6 @@ ], ".*_(browser|unit)test\\.cc": [ - "+chrome/browser/ui/browser_finder.h", "+chrome/browser/ui/browser_window.h", "+chrome/test/base", "+chrome/test/views",
diff --git a/chrome/browser/ui/ash/multi_user/DEPS b/chrome/browser/ui/ash/multi_user/DEPS index 7249672f..950420e 100644 --- a/chrome/browser/ui/ash/multi_user/DEPS +++ b/chrome/browser/ui/ash/multi_user/DEPS
@@ -31,7 +31,6 @@ ".*_unittest\\.cc": [ "+chrome/browser/ash/browser_delegate/browser_controller_impl.h", "+chrome/browser/ui/browser.h", - "+chrome/browser/ui/browser_finder.h", "+chrome/browser/ui/browser_window/public/browser_window_interface_iterator.h", "+chrome/test", ],
diff --git a/chrome/browser/ui/ash/new_window/DEPS b/chrome/browser/ui/ash/new_window/DEPS index da36649..6e92335 100644 --- a/chrome/browser/ui/ash/new_window/DEPS +++ b/chrome/browser/ui/ash/new_window/DEPS
@@ -21,7 +21,6 @@ "+chrome/browser/ui/ash/shelf", "+chrome/browser/ui/ash/system_web_apps", "+chrome/browser/ui/browser_commands.h", - "+chrome/browser/ui/browser_finder.h", "+chrome/browser/ui/browser.h", "+chrome/browser/ui/browser_window.h", "+chrome/browser/ui/browser_window/public/browser_window_interface.h",
diff --git a/chrome/browser/ui/ash/new_window/chrome_new_window_client_browsertest.cc b/chrome/browser/ui/ash/new_window/chrome_new_window_client_browsertest.cc index 5753fc8..ead5172 100644 --- a/chrome/browser/ui/ash/new_window/chrome_new_window_client_browsertest.cc +++ b/chrome/browser/ui/ash/new_window/chrome_new_window_client_browsertest.cc
@@ -12,7 +12,6 @@ #include "chrome/browser/profiles/profile_manager.h" #include "chrome/browser/profiles/profile_test_util.h" #include "chrome/browser/ui/browser.h" -#include "chrome/browser/ui/browser_finder.h" #include "chrome/browser/ui/browser_window.h" #include "chrome/browser/ui/browser_window/public/browser_window_interface.h" #include "chrome/browser/ui/browser_window/public/global_browser_collection.h"
diff --git a/chrome/browser/ui/ash/projector/DEPS b/chrome/browser/ui/ash/projector/DEPS index e4fa03a..a2a34e1 100644 --- a/chrome/browser/ui/ash/projector/DEPS +++ b/chrome/browser/ui/ash/projector/DEPS
@@ -31,7 +31,6 @@ "+chrome/browser/ash/login/test", "+chrome/browser/browser_process.h", "+chrome/browser/global_features.h", - "+chrome/browser/ui/browser_finder.h", "+chrome/browser/ui/browser.h", "+chrome/browser/ui/browser_window.h", "+chrome/browser/ui/browser_window/public/browser_window_interface.h",
diff --git a/chrome/browser/ui/ash/projector/projector_client_impl_browsertest.cc b/chrome/browser/ui/ash/projector/projector_client_impl_browsertest.cc index 5ff9793e..4ea03b1 100644 --- a/chrome/browser/ui/ash/projector/projector_client_impl_browsertest.cc +++ b/chrome/browser/ui/ash/projector/projector_client_impl_browsertest.cc
@@ -34,7 +34,6 @@ #include "chrome/browser/ui/ash/projector/projector_utils.h" #include "chrome/browser/ui/ash/system_web_apps/system_web_app_ui_utils.h" #include "chrome/browser/ui/browser.h" -#include "chrome/browser/ui/browser_finder.h" #include "chrome/browser/ui/browser_window.h" #include "chrome/browser/ui/browser_window/public/global_browser_collection.h" #include "chrome/browser/ui/tabs/tab_strip_model.h"
diff --git a/chrome/browser/ui/ash/projector/projector_navigation_throttle_browsertest.cc b/chrome/browser/ui/ash/projector/projector_navigation_throttle_browsertest.cc index 6f1c031..a873b5c76 100644 --- a/chrome/browser/ui/ash/projector/projector_navigation_throttle_browsertest.cc +++ b/chrome/browser/ui/ash/projector/projector_navigation_throttle_browsertest.cc
@@ -23,7 +23,6 @@ #include "chrome/browser/global_features.h" #include "chrome/browser/ui/ash/system_web_apps/system_web_app_ui_utils.h" #include "chrome/browser/ui/browser.h" -#include "chrome/browser/ui/browser_finder.h" #include "chrome/browser/ui/browser_window/public/global_browser_collection.h" #include "chrome/browser/ui/navigator/browser_navigator.h" #include "chrome/browser/ui/navigator/browser_navigator_params.h"
diff --git a/chrome/browser/ui/ash/shelf/DEPS b/chrome/browser/ui/ash/shelf/DEPS index 818d695..8583a0cb 100644 --- a/chrome/browser/ui/ash/shelf/DEPS +++ b/chrome/browser/ui/ash/shelf/DEPS
@@ -72,7 +72,6 @@ "+chrome/browser/extensions/extension_browsertest.h", "+chrome/browser/extensions/test_extension_system.h", "+chrome/browser/ui/browser_commands.h", - "+chrome/browser/ui/browser_finder.h", "+chrome/browser/ui/browser_tabstrip.h", "+chrome/browser/ui/browser_window.h", "+chrome/browser/ui/settings_window_manager_chromeos.h",
diff --git a/chrome/browser/ui/ash/shelf/app_shortcut_shelf_item_controller_browsertest.cc b/chrome/browser/ui/ash/shelf/app_shortcut_shelf_item_controller_browsertest.cc index c73e7d7..4846a98 100644 --- a/chrome/browser/ui/ash/shelf/app_shortcut_shelf_item_controller_browsertest.cc +++ b/chrome/browser/ui/ash/shelf/app_shortcut_shelf_item_controller_browsertest.cc
@@ -16,7 +16,6 @@ #include "chrome/browser/ui/ash/system_web_apps/system_web_app_ui_utils.h" #include "chrome/browser/ui/browser.h" #include "chrome/browser/ui/browser_commands.h" -#include "chrome/browser/ui/browser_finder.h" #include "chrome/browser/ui/browser_window.h" #include "chrome/browser/ui/browser_window/public/browser_collection_observer.h" #include "chrome/browser/ui/browser_window/public/browser_window_interface.h"
diff --git a/chrome/browser/ui/ash/shelf/browser_shortcut_shelf_item_controller_browsertest.cc b/chrome/browser/ui/ash/shelf/browser_shortcut_shelf_item_controller_browsertest.cc index 46a718b..fe4f0e3 100644 --- a/chrome/browser/ui/ash/shelf/browser_shortcut_shelf_item_controller_browsertest.cc +++ b/chrome/browser/ui/ash/shelf/browser_shortcut_shelf_item_controller_browsertest.cc
@@ -12,7 +12,6 @@ #include "chrome/browser/ash/login/test/guest_session_mixin.h" #include "chrome/browser/ui/ash/shelf/chrome_shelf_controller.h" #include "chrome/browser/ui/browser.h" -#include "chrome/browser/ui/browser_finder.h" #include "chrome/browser/ui/browser_window.h" #include "chrome/browser/ui/browser_window/public/global_browser_collection.h" #include "chrome/test/base/in_process_browser_test.h"
diff --git a/chrome/browser/ui/ash/shelf/chrome_shelf_controller_browsertest.cc b/chrome/browser/ui/ash/shelf/chrome_shelf_controller_browsertest.cc index 118ff85e..5c37ac88 100644 --- a/chrome/browser/ui/ash/shelf/chrome_shelf_controller_browsertest.cc +++ b/chrome/browser/ui/ash/shelf/chrome_shelf_controller_browsertest.cc
@@ -80,7 +80,6 @@ #include "chrome/browser/ui/ash/shelf/shelf_context_menu.h" #include "chrome/browser/ui/browser.h" #include "chrome/browser/ui/browser_commands.h" -#include "chrome/browser/ui/browser_finder.h" #include "chrome/browser/ui/browser_window.h" #include "chrome/browser/ui/browser_window/public/browser_window_interface.h" #include "chrome/browser/ui/browser_window/public/browser_window_interface_iterator.h"
diff --git a/chrome/browser/ui/ash/shelf/chrome_shelf_controller_unittest.cc b/chrome/browser/ui/ash/shelf/chrome_shelf_controller_unittest.cc index 4341ef2f..7d9b9b0 100644 --- a/chrome/browser/ui/ash/shelf/chrome_shelf_controller_unittest.cc +++ b/chrome/browser/ui/ash/shelf/chrome_shelf_controller_unittest.cc
@@ -119,7 +119,6 @@ #include "chrome/browser/ui/ash/wallpaper/wallpaper_controller_client_impl.h" #include "chrome/browser/ui/browser.h" #include "chrome/browser/ui/browser_commands.h" -#include "chrome/browser/ui/browser_finder.h" #include "chrome/browser/ui/browser_tabstrip.h" #include "chrome/browser/ui/browser_window/public/global_browser_collection.h" #include "chrome/browser/ui/tabs/tab_strip_model.h"
diff --git a/chrome/browser/ui/ash/test/DEPS b/chrome/browser/ui/ash/test/DEPS index 2a9a09538..974540f 100644 --- a/chrome/browser/ui/ash/test/DEPS +++ b/chrome/browser/ui/ash/test/DEPS
@@ -14,7 +14,6 @@ "+chrome/browser/ui/accelerator_table.h", "+chrome/browser/ui/ash/login", "+chrome/browser/ui/browser_commands.h", - "+chrome/browser/ui/browser_finder.h", "+chrome/browser/ui/browser.h", "+chrome/browser/ui/browser_tabstrip.h", "+chrome/browser/ui/browser_window.h",
diff --git a/chrome/browser/ui/ash/test/accelerator_commands_browsertest.cc b/chrome/browser/ui/ash/test/accelerator_commands_browsertest.cc index a761b5e7..4ad026d 100644 --- a/chrome/browser/ui/ash/test/accelerator_commands_browsertest.cc +++ b/chrome/browser/ui/ash/test/accelerator_commands_browsertest.cc
@@ -11,7 +11,6 @@ #include "chrome/browser/profiles/profile.h" #include "chrome/browser/ui/browser.h" #include "chrome/browser/ui/browser_commands.h" -#include "chrome/browser/ui/browser_finder.h" #include "chrome/browser/ui/browser_window.h" #include "chrome/browser/ui/views/frame/browser_view.h" #include "chrome/browser/ui/views/frame/browser_widget.h"
diff --git a/chrome/browser/ui/ash/wm/DEPS b/chrome/browser/ui/ash/wm/DEPS index c7da827..84edd96 100644 --- a/chrome/browser/ui/ash/wm/DEPS +++ b/chrome/browser/ui/ash/wm/DEPS
@@ -24,7 +24,6 @@ specific_include_rules = { ".*_browsertest\\.cc": [ - "+chrome/browser/ui/browser_finder.h", "+chrome/browser/ui/browser_tabstrip.h", "+chrome/browser/ui/browser_window.h", "+chrome/browser/ui/browser_window/public",
diff --git a/chrome/browser/ui/ash/wm/coral_browsertest.cc b/chrome/browser/ui/ash/wm/coral_browsertest.cc index d10a8de..952f1eb1 100644 --- a/chrome/browser/ui/ash/wm/coral_browsertest.cc +++ b/chrome/browser/ui/ash/wm/coral_browsertest.cc
@@ -31,7 +31,6 @@ #include "chrome/browser/ui/ash/birch/birch_test_util.h" #include "chrome/browser/ui/ash/system_web_apps/system_web_app_ui_utils.h" #include "chrome/browser/ui/browser.h" -#include "chrome/browser/ui/browser_finder.h" #include "chrome/browser/ui/browser_window/public/browser_window_interface.h" #include "chrome/browser/ui/browser_window/public/browser_window_interface_iterator.h" #include "chrome/browser/ui/browser_window/public/global_browser_collection.h"
diff --git a/chrome/browser/ui/ash/wm/snap_group_browsertest.cc b/chrome/browser/ui/ash/wm/snap_group_browsertest.cc index 93d7d4e..4a9f92b0 100644 --- a/chrome/browser/ui/ash/wm/snap_group_browsertest.cc +++ b/chrome/browser/ui/ash/wm/snap_group_browsertest.cc
@@ -29,7 +29,6 @@ #include "chrome/browser/ui/ash/new_window/chrome_new_window_client.h" #include "chrome/browser/ui/ash/system_web_apps/system_web_app_ui_utils.h" #include "chrome/browser/ui/browser.h" -#include "chrome/browser/ui/browser_finder.h" #include "chrome/browser/ui/browser_tabstrip.h" #include "chrome/browser/ui/browser_window.h" #include "chrome/browser/ui/browser_window/public/browser_window_interface.h"
diff --git a/chrome/browser/ui/autofill/BUILD.gn b/chrome/browser/ui/autofill/BUILD.gn index 081cc9a3..0f32db5 100644 --- a/chrome/browser/ui/autofill/BUILD.gn +++ b/chrome/browser/ui/autofill/BUILD.gn
@@ -251,6 +251,7 @@ "//chrome/browser/autofill/android:jni_headers", "//chrome/browser/keyboard_accessory/android", "//chrome/browser/keyboard_accessory/android:impl", + "//chrome/browser/ui/android/autofill", "//components/android_autofill/browser:android", "//components/messages/android:feature_flags", "//components/password_manager/core/browser/features:password_features", @@ -386,6 +387,7 @@ "//chrome/browser/autofill/android:jni_test_headers", "//chrome/browser/keyboard_accessory/android:impl", "//chrome/browser/preferences:android", + "//chrome/browser/ui/android/autofill", "//components/messages/android:test_support", ] } else {
diff --git a/chrome/browser/ui/autofill/address_bubbles_controller.cc b/chrome/browser/ui/autofill/address_bubbles_controller.cc index 38ab8a7..8468ac8c 100644 --- a/chrome/browser/ui/autofill/address_bubbles_controller.cc +++ b/chrome/browser/ui/autofill/address_bubbles_controller.cc
@@ -28,7 +28,6 @@ #include "chrome/browser/ui/autofill/save_address_bubble_controller.h" #include "chrome/browser/ui/autofill/update_address_bubble_controller.h" #include "chrome/browser/ui/browser.h" -#include "chrome/browser/ui/browser_finder.h" #include "chrome/browser/ui/browser_window.h" #include "chrome/browser/ui/desktop_to_mobile_promos/ios_promos_utils.h" #include "chrome/browser/ui/views/frame/browser_view.h"
diff --git a/chrome/browser/ui/autofill/payments/BUILD.gn b/chrome/browser/ui/autofill/payments/BUILD.gn index 1e8ec614..31be6ea 100644 --- a/chrome/browser/ui/autofill/payments/BUILD.gn +++ b/chrome/browser/ui/autofill/payments/BUILD.gn
@@ -43,6 +43,7 @@ ] public_deps += [ "//chrome/browser/touch_to_fill/autofill/android:public", + "//chrome/browser/ui/android/autofill/payments", "//components/autofill/android:autofill_core_browser_java_enums", "//components/messages/android", ] @@ -131,6 +132,8 @@ "//chrome/browser/profiles:profile", "//chrome/browser/touch_to_fill/autofill/android", "//chrome/browser/touch_to_fill/autofill/android:impl", + "//chrome/browser/ui/android/autofill", + "//chrome/browser/ui/android/autofill/payments", "//chrome/browser/ui/android/tab_model", "//chrome/browser/ui/browser_window", "//components/infobars/core", @@ -281,6 +284,7 @@ "//chrome/browser/autofill:test_support", "//chrome/browser/keyboard_accessory/test_utils/android", "//chrome/browser/touch_to_fill/autofill/android:test_support", + "//chrome/browser/ui/android/autofill", "//chrome/browser/ui/android/tab_model:test_support", "//components/autofill/android:autofill_core_browser_java_enums", "//components/autofill/content/browser:test_support", @@ -327,6 +331,7 @@ if (is_android) { sources += [ "android_payments_window_manager_test_api.h" ] + public_deps += [ "//chrome/browser/ui/android/autofill" ] } else { sources += [ "desktop_payments_window_manager_test_api.h" ] }
diff --git a/chrome/browser/ui/blocked_content/popup_blocker_browsertest.cc b/chrome/browser/ui/blocked_content/popup_blocker_browsertest.cc index bda09423..736d09c 100644 --- a/chrome/browser/ui/blocked_content/popup_blocker_browsertest.cc +++ b/chrome/browser/ui/blocked_content/popup_blocker_browsertest.cc
@@ -16,7 +16,6 @@ #include "chrome/browser/search_engines/template_url_service_factory.h" #include "chrome/browser/ui/browser.h" #include "chrome/browser/ui/browser_commands.h" -#include "chrome/browser/ui/browser_finder.h" #include "chrome/browser/ui/browser_window.h" #include "chrome/browser/ui/browser_window/public/browser_window_interface.h" #include "chrome/browser/ui/browser_window/public/browser_window_interface_iterator.h"
diff --git a/chrome/browser/ui/bookmarks/BUILD.gn b/chrome/browser/ui/bookmarks/BUILD.gn index 47b4d69..7ccfb115 100644 --- a/chrome/browser/ui/bookmarks/BUILD.gn +++ b/chrome/browser/ui/bookmarks/BUILD.gn
@@ -72,6 +72,7 @@ "//chrome/browser/renderer_host", "//chrome/browser/search", "//chrome/browser/tab_group_sync:factories", + "//chrome/browser/ui:simple_message_box_headers", "//chrome/browser/ui:ui_features", "//chrome/browser/ui/browser_window", "//chrome/browser/ui/dialogs",
diff --git a/chrome/browser/ui/browser.cc b/chrome/browser/ui/browser.cc index e7e0bd4..8ad020f 100644 --- a/chrome/browser/ui/browser.cc +++ b/chrome/browser/ui/browser.cc
@@ -2660,17 +2660,17 @@ permissions::PermissionRequestManager* permission_request_manager = permissions::PermissionRequestManager::FromWebContents(web_contents); if (permission_request_manager) { - // At this point, there will be UI presented, and running a dialog causes an - // exit to webpage-initiated fullscreen. http://crbug.com/41322524 - base::ScopedClosureRunner fullscreen_block = - web_contents->ForSecurityDropFullscreen( - /*display_id=*/display::kInvalidDisplayId); + auto blocker = web_contents->ForSecurityDropFullscreen( + /*display_id=*/display::kInvalidDisplayId); + if (!blocker) { + return; + } permission_request_manager->AddRequest( requesting_frame, std::make_unique< custom_handlers::RegisterProtocolHandlerPermissionRequest>( - registry, handler, url, std::move(fullscreen_block))); + registry, handler, url, std::move(*blocker))); } }
diff --git a/chrome/browser/ui/browser_element_identifiers.cc b/chrome/browser/ui/browser_element_identifiers.cc index f5bcc4d..674f31e 100644 --- a/chrome/browser/ui/browser_element_identifiers.cc +++ b/chrome/browser/ui/browser_element_identifiers.cc
@@ -201,6 +201,7 @@ DEFINE_ELEMENT_IDENTIFIER_VALUE(kToolbarAvatarButtonElementId); DEFINE_ELEMENT_IDENTIFIER_VALUE(kToolbarBackButtonElementId); DEFINE_ELEMENT_IDENTIFIER_VALUE(kToolbarBackButtonMenuElementId); +DEFINE_ELEMENT_IDENTIFIER_VALUE(kToolbarBatterySaverBubbleElementId); DEFINE_ELEMENT_IDENTIFIER_VALUE(kToolbarBatterySaverButtonElementId); DEFINE_ELEMENT_IDENTIFIER_VALUE(kToolbarChromeLabsBubbleElementId); DEFINE_ELEMENT_IDENTIFIER_VALUE(kToolbarChromeLabsButtonElementId);
diff --git a/chrome/browser/ui/browser_element_identifiers.h b/chrome/browser/ui/browser_element_identifiers.h index 728d916..0c947d9 100644 --- a/chrome/browser/ui/browser_element_identifiers.h +++ b/chrome/browser/ui/browser_element_identifiers.h
@@ -244,6 +244,7 @@ DECLARE_ELEMENT_IDENTIFIER_VALUE(kToolbarAvatarButtonElementId); DECLARE_ELEMENT_IDENTIFIER_VALUE(kToolbarBackButtonElementId); DECLARE_ELEMENT_IDENTIFIER_VALUE(kToolbarBackButtonMenuElementId); +DECLARE_ELEMENT_IDENTIFIER_VALUE(kToolbarBatterySaverBubbleElementId); DECLARE_ELEMENT_IDENTIFIER_VALUE(kToolbarBatterySaverButtonElementId); DECLARE_ELEMENT_IDENTIFIER_VALUE(kToolbarChromeLabsBubbleElementId); DECLARE_ELEMENT_IDENTIFIER_VALUE(kToolbarChromeLabsButtonElementId);
diff --git a/chrome/browser/ui/browser_finder_unittest.cc b/chrome/browser/ui/browser_finder_unittest.cc index 338ba2d..cd1ee78 100644 --- a/chrome/browser/ui/browser_finder_unittest.cc +++ b/chrome/browser/ui/browser_finder_unittest.cc
@@ -2,7 +2,6 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "chrome/browser/ui/browser_finder.h" #include "chrome/browser/ui/browser.h" #include "chrome/browser/ui/browser_window/public/browser_window_interface.h"
diff --git a/chrome/browser/ui/browser_instant_controller.cc b/chrome/browser/ui/browser_instant_controller.cc index 8595a7bf..31e6a91 100644 --- a/chrome/browser/ui/browser_instant_controller.cc +++ b/chrome/browser/ui/browser_instant_controller.cc
@@ -19,6 +19,7 @@ #include "content/public/browser/navigation_controller.h" #include "content/public/browser/render_frame_host.h" #include "content/public/browser/render_process_host.h" +#include "content/public/browser/security_principal.h" #include "content/public/browser/web_contents.h" #include "content/public/common/referrer.h" #include "ui/base/page_transition_types.h" @@ -55,8 +56,10 @@ continue; } - GURL site_url = - contents->GetPrimaryMainFrame()->GetSiteInstance()->GetSiteURL(); + GURL site_url = contents->GetPrimaryMainFrame() + ->GetSiteInstance() + ->GetSecurityPrincipal() + .GetDeprecatedSiteURL(); bool is_ntp = site_url == chrome::ChromeUINewTabPageURLAsGURL() || site_url == GURL(chrome::kChromeUINewTabPageThirdPartyURL);
diff --git a/chrome/browser/ui/browser_window/internal/BUILD.gn b/chrome/browser/ui/browser_window/internal/BUILD.gn index a85a4a7..5b7825c8 100644 --- a/chrome/browser/ui/browser_window/internal/BUILD.gn +++ b/chrome/browser/ui/browser_window/internal/BUILD.gn
@@ -40,6 +40,7 @@ "//chrome/browser/devtools", "//chrome/browser/prefs", "//chrome/browser/profiles:profile_util", + "//chrome/browser/ui/android", "//chrome/browser/ui/blocked_content", "//chrome/browser/ui/navigator", "//components/blocked_content",
diff --git a/chrome/browser/ui/browser_window/internal/android/create_browser_window_android_browsertest.cc b/chrome/browser/ui/browser_window/internal/android/create_browser_window_android_browsertest.cc index d2db845..5c2cf09 100644 --- a/chrome/browser/ui/browser_window/internal/android/create_browser_window_android_browsertest.cc +++ b/chrome/browser/ui/browser_window/internal/android/create_browser_window_android_browsertest.cc
@@ -20,12 +20,30 @@ #include "components/feed/feed_feature_list.h" #include "components/policy/core/common/policy_pref_names.h" #include "components/prefs/pref_service.h" +#include "content/public/browser/navigation_handle.h" #include "content/public/browser/web_contents.h" +#include "content/public/browser/web_contents_observer.h" #include "content/public/test/browser_test.h" #include "testing/gtest/include/gtest/gtest.h" #include "ui/base/mojom/window_show_state.mojom.h" namespace { +class NavigationStartedObserver : public content::WebContentsObserver { + public: + explicit NavigationStartedObserver(content::WebContents* web_contents) + : content::WebContentsObserver(web_contents) {} + + void DidStartNavigation( + content::NavigationHandle* navigation_handle) override { + navigation_started_ = true; + } + + bool navigation_started() const { return navigation_started_; } + + private: + bool navigation_started_ = false; +}; + BrowserWindowInterface* CreateBrowserWindowSync( BrowserWindowInterface::Type type, Profile* profile, @@ -321,6 +339,8 @@ content::WebContents::Create(content::WebContents::CreateParams(profile)); content::WebContents* expected_web_contents = web_contents.get(); + NavigationStartedObserver observer(expected_web_contents); + BrowserWindowCreateParams create_params(type, *profile, false); create_params.web_contents = std::move(web_contents); @@ -336,4 +356,5 @@ auto* tab = tab_list_interface->GetActiveTab(); ASSERT_NE(tab, nullptr); EXPECT_EQ(tab->GetContents(), expected_web_contents); + EXPECT_FALSE(observer.navigation_started()); }
diff --git a/chrome/browser/ui/browser_window/internal/android/java/src/org/chromium/chrome/browser/ui/browser_window/ChromeAndroidTaskIntegrationTest.java b/chrome/browser/ui/browser_window/internal/android/java/src/org/chromium/chrome/browser/ui/browser_window/ChromeAndroidTaskIntegrationTest.java index 2aa45b5e..c436ff2 100644 --- a/chrome/browser/ui/browser_window/internal/android/java/src/org/chromium/chrome/browser/ui/browser_window/ChromeAndroidTaskIntegrationTest.java +++ b/chrome/browser/ui/browser_window/internal/android/java/src/org/chromium/chrome/browser/ui/browser_window/ChromeAndroidTaskIntegrationTest.java
@@ -4,6 +4,8 @@ package org.chromium.chrome.browser.ui.browser_window; +import org.chromium.base.test.util.DisableIf; + import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotEquals; @@ -82,6 +84,7 @@ "Tests will be flaky if batched as they create/close windows and change window" + " states in quick succession") @NullMarked +@DisableIf.Device(DeviceFormFactor.DESKTOP_FREEFORM) // crbug.com/444482498 public class ChromeAndroidTaskIntegrationTest { @Rule
diff --git a/chrome/browser/ui/browser_window/internal/android/java/src/org/chromium/chrome/browser/ui/browser_window/ChromeAndroidTaskTrackerImpl.java b/chrome/browser/ui/browser_window/internal/android/java/src/org/chromium/chrome/browser/ui/browser_window/ChromeAndroidTaskTrackerImpl.java index 5a508dc00..a854d00 100644 --- a/chrome/browser/ui/browser_window/internal/android/java/src/org/chromium/chrome/browser/ui/browser_window/ChromeAndroidTaskTrackerImpl.java +++ b/chrome/browser/ui/browser_window/internal/android/java/src/org/chromium/chrome/browser/ui/browser_window/ChromeAndroidTaskTrackerImpl.java
@@ -236,11 +236,9 @@ return getAllTasks().size(); } - /** Returns all PENDING and ALIVE Tasks. */ + /** Returns all ALIVE Tasks. */ /*package*/ List<ChromeAndroidTask> getAllTasks() { - List<ChromeAndroidTask> tasks = new ArrayList<>(mTasks.values()); - tasks.addAll(mPendingTasks.values()); - return tasks; + return new ArrayList<>(mTasks.values()); } /** @@ -252,7 +250,7 @@ * <p>This method must be called on the UI thread. */ void removeAllForTesting() { - List<ChromeAndroidTask> tasks = new ArrayList<>(mTasks.values()); + List<ChromeAndroidTask> tasks = getAllTasks(); mTasks.clear(); for (var task : tasks) { task.destroy();
diff --git a/chrome/browser/ui/browser_window/internal/android/java/src/org/chromium/chrome/browser/ui/browser_window/ChromeAndroidTaskTrackerImplUnitTest.java b/chrome/browser/ui/browser_window/internal/android/java/src/org/chromium/chrome/browser/ui/browser_window/ChromeAndroidTaskTrackerImplUnitTest.java index 70625b7..8a2eb17 100644 --- a/chrome/browser/ui/browser_window/internal/android/java/src/org/chromium/chrome/browser/ui/browser_window/ChromeAndroidTaskTrackerImplUnitTest.java +++ b/chrome/browser/ui/browser_window/internal/android/java/src/org/chromium/chrome/browser/ui/browser_window/ChromeAndroidTaskTrackerImplUnitTest.java
@@ -125,10 +125,9 @@ assertNotNull( mChromeAndroidTaskTracker.getPendingTaskForTesting(pendingTaskInfo.mPendingTaskId)); assertEquals( - // Creating a pending Task of "NORMAL" type requires an existing ChromeAndroidTask. - // Therefore, getAllNativeBrowserWindowPtrs().length should be 2: - // one pointer is the existing Task and the other is the pending Task. - 2, mChromeAndroidTaskTracker.getAllNativeBrowserWindowPtrs().length); + // Creating a pending Task requires an existing ChromeAndroidTask. + // The pending task will not appear in this list. + 1, mChromeAndroidTaskTracker.getAllNativeBrowserWindowPtrs().length); assertNull(task.getId()); assertEquals(mockParams.getWindowType(), pendingTaskInfo.mCreateParams.getWindowType()); } @@ -148,10 +147,9 @@ assertNotNull( mChromeAndroidTaskTracker.getPendingTaskForTesting(pendingTaskInfo.mPendingTaskId)); assertEquals( - // Creating a pending Task of "POPUP" type requires an existing ChromeAndroidTask. - // Therefore, getAllNativeBrowserWindowPtrs().length should be 2: - // one pointer is the existing Task and the other is the pending Task. - 2, mChromeAndroidTaskTracker.getAllNativeBrowserWindowPtrs().length); + // Creating a pending Task requires an existing ChromeAndroidTask. + // The pending task will not appear in this list. + 1, mChromeAndroidTaskTracker.getAllNativeBrowserWindowPtrs().length); assertNull(task.getId()); assertEquals(mockParams.getWindowType(), pendingTaskInfo.mCreateParams.getWindowType()); @@ -376,10 +374,9 @@ assertNonNull(createPendingTaskWithExistingTask(mockParams)); int pendingId = assertNonNull(pendingTask.getPendingTaskInfo()).mPendingTaskId; assertEquals( - // Creating a pending Task of "NORMAL" type requires an existing ChromeAndroidTask. - // Therefore, getAllNativeBrowserWindowPtrs().length should be 2: - // one pointer is the existing Task and the other is the pending Task. - 2, mChromeAndroidTaskTracker.getAllNativeBrowserWindowPtrs().length); + // Creating a pending Task requires an existing ChromeAndroidTask. + // The pending task will not appear in this list. + 1, mChromeAndroidTaskTracker.getAllNativeBrowserWindowPtrs().length); int taskId = IdSequencer.next(); var newActivityScopedObjects = @@ -396,8 +393,7 @@ // Assert. assertNull(mChromeAndroidTaskTracker.getPendingTaskForTesting(pendingId)); assertEquals( - // As above, only two native browser window pointers are expected despite the new - // ActivityScopedObjects. + // Now that the task is no longer pending, there will be 2 pointers. 2, mChromeAndroidTaskTracker.getAllNativeBrowserWindowPtrs().length); assertEquals( newActivityScopedObjects.mActivityWindowAndroid, @@ -839,10 +835,7 @@ long[] ptrs = mChromeAndroidTaskTracker.getNativeBrowserWindowPtrsOrderedByActivation(); // Assert. - assertEquals( - "The pointer array should contain both the alive and the pending task.", - 2, - ptrs.length); + assertEquals("The pointer array should only contain the alive task.", 1, ptrs.length); } @Test
diff --git a/chrome/browser/ui/browser_window/internal/browser_window_features.cc b/chrome/browser/ui/browser_window/internal/browser_window_features.cc index 8c48a15..a6ea9356 100644 --- a/chrome/browser/ui/browser_window/internal/browser_window_features.cc +++ b/chrome/browser/ui/browser_window/internal/browser_window_features.cc
@@ -69,6 +69,7 @@ #include "chrome/browser/ui/find_bar/find_bar.h" #include "chrome/browser/ui/find_bar/find_bar_controller.h" #include "chrome/browser/ui/focus/browser_focus_controller.h" +#include "chrome/browser/ui/focus/browser_focus_controller_delegate_views.h" #include "chrome/browser/ui/fullscreen/browser_window_fullscreen_controller.h" #include "chrome/browser/ui/lens/lens_overlay_entry_point_controller.h" #include "chrome/browser/ui/omnibox/ai_mode_page_action_controller.h" @@ -419,9 +420,6 @@ GetUserDataFactory().CreateInstance<TranslateBubbleController>( *browser, browser, browser_actions_->root_action_item()); - browser_focus_controller_ = - std::make_unique<BrowserFocusController>(*browser); - cookie_controls_controller_ = std::make_unique<content_settings::CookieControlsController>( CookieSettingsFactory::GetForProfile(profile), @@ -571,12 +569,26 @@ } void BrowserWindowFeatures::InitPostWindowConstruction(Browser* browser) { + Profile* const profile = browser_->GetProfile(); + BrowserView* const browser_view = + BrowserView::GetBrowserViewForBrowser(browser); + desktop_browser_window_capabilities_ = GetUserDataFactory().CreateInstance<DesktopBrowserWindowCapabilities>( *browser, browser_window_modal_dialog_delegate_.get(), unload_controller_.get(), browser->window(), browser->GetUnownedUserDataHost()); + browser_focus_controller_ = + GetUserDataFactory().CreateInstance<BrowserFocusController>( + *browser, browser->GetWindow(), browser->GetUnownedUserDataHost()); + if (browser_view) { + browser_focus_controller_->SetDelegate( + std::make_unique<BrowserFocusControllerDelegateViews>( + profile, browser_elements_.get(), + ToolbarButtonProvider::From(browser))); + } + if (WebUIBrowserWindow* webui_browser_window = WebUIBrowserWindow::FromBrowser(browser)) { webui_browser_exclusive_access_context_ = @@ -596,9 +608,6 @@ } #endif - Profile* const profile = browser_->GetProfile(); - BrowserView* const browser_view = - BrowserView::GetBrowserViewForBrowser(browser); if (browser_view) { // Initialize fullscreen control host after exclusive access manager is // ready. @@ -1117,6 +1126,7 @@ data_protection_ui_controller_.reset(); + browser_focus_controller_.reset(); desktop_browser_window_capabilities_.reset(); signin_view_controller_->TearDownPreBrowserWindowDestruction();
diff --git a/chrome/browser/ui/browser_window/test/fake_global_browser_collection.h b/chrome/browser/ui/browser_window/test/fake_global_browser_collection.h index e2547f4..5013739 100644 --- a/chrome/browser/ui/browser_window/test/fake_global_browser_collection.h +++ b/chrome/browser/ui/browser_window/test/fake_global_browser_collection.h
@@ -20,9 +20,6 @@ void SimulateBrowserClosed(BrowserWindowInterface* browser) { GlobalBrowserCollection::OnBrowserClosed(browser); } - void SimulateBrowserActivated(BrowserWindowInterface* browser) { - GlobalBrowserCollection::OnBrowserActivated(browser); - } }; #endif // CHROME_BROWSER_UI_BROWSER_WINDOW_TEST_FAKE_GLOBAL_BROWSER_COLLECTION_H_
diff --git a/chrome/browser/ui/cocoa/BUILD.gn b/chrome/browser/ui/cocoa/BUILD.gn index 83038c9..4ad3d604 100644 --- a/chrome/browser/ui/cocoa/BUILD.gn +++ b/chrome/browser/ui/cocoa/BUILD.gn
@@ -152,6 +152,8 @@ "//chrome/browser/search_engines", "//chrome/browser/status_icons", "//chrome/browser/themes", + "//chrome/browser/ui:accelerator_utils_headers", + "//chrome/browser/ui:simple_message_box_headers", "//chrome/browser/ui:ui_features", "//chrome/browser/ui/autofill", "//chrome/browser/ui/bookmarks",
diff --git a/chrome/browser/ui/cocoa/applescript/browsercrapplication+applescript.mm b/chrome/browser/ui/cocoa/applescript/browsercrapplication+applescript.mm index 94f69bf..a3b0c6a 100644 --- a/chrome/browser/ui/cocoa/applescript/browsercrapplication+applescript.mm +++ b/chrome/browser/ui/cocoa/applescript/browsercrapplication+applescript.mm
@@ -12,7 +12,6 @@ #include "chrome/browser/bookmarks/bookmark_model_factory.h" #include "chrome/browser/profiles/profile.h" #include "chrome/browser/ui/browser.h" -#include "chrome/browser/ui/browser_finder.h" #include "chrome/browser/ui/browser_window.h" #include "chrome/browser/ui/browser_window/public/browser_window_interface.h" #include "chrome/browser/ui/browser_window/public/browser_window_interface_iterator.h"
diff --git a/chrome/browser/ui/cocoa/browser_window_command_handler.mm b/chrome/browser/ui/cocoa/browser_window_command_handler.mm index 52c9fa1..c55aa48 100644 --- a/chrome/browser/ui/cocoa/browser_window_command_handler.mm +++ b/chrome/browser/ui/cocoa/browser_window_command_handler.mm
@@ -12,7 +12,6 @@ #import "chrome/browser/app_controller_mac.h" #include "chrome/browser/profiles/profile.h" #include "chrome/browser/ui/browser_commands.h" -#include "chrome/browser/ui/browser_finder.h" #include "chrome/browser/ui/browser_window.h" #include "components/remote_cocoa/app_shim/native_widget_ns_window_bridge.h" #include "components/remote_cocoa/common/native_widget_ns_window_host.mojom.h"
diff --git a/chrome/browser/ui/cocoa/keystone_infobar_delegate.cc b/chrome/browser/ui/cocoa/keystone_infobar_delegate.cc index fca1c7cb..86d85f15 100644 --- a/chrome/browser/ui/cocoa/keystone_infobar_delegate.cc +++ b/chrome/browser/ui/cocoa/keystone_infobar_delegate.cc
@@ -12,7 +12,6 @@ #include "chrome/browser/infobars/confirm_infobar_creator.h" #include "chrome/browser/profiles/profile.h" #include "chrome/browser/ui/browser.h" -#include "chrome/browser/ui/browser_finder.h" #include "chrome/browser/ui/browser_window/public/browser_window_interface.h" #include "chrome/browser/ui/browser_window/public/global_browser_collection.h" #include "chrome/browser/updater/updater.h"
diff --git a/chrome/browser/ui/color/omnibox_color_mixer.cc b/chrome/browser/ui/color/omnibox_color_mixer.cc index d0658f00..a89bacc 100644 --- a/chrome/browser/ui/color/omnibox_color_mixer.cc +++ b/chrome/browser/ui/color/omnibox_color_mixer.cc
@@ -213,7 +213,7 @@ constexpr SkColor kComposeboxSubmitButtonBackgroundDark = SkColorSetRGB(0xA8, 0xC7, 0xFA); constexpr SkColor kComposeboxSubmitButtonBackgroundLight = - SkColorSetRGB(0x34, 0x6B, 0xF1); + SkColorSetRGB(0x33, 0x6E, 0xF3); constexpr SkColor kComposeboxSubmitButtonIconDark = SkColorSetRGB(0x06, 0x2E, 0x6F); constexpr SkColor kComposeboxSubmitButtonEnergy =
diff --git a/chrome/browser/ui/exclusive_access/fullscreen_controller_interactive_browsertest.cc b/chrome/browser/ui/exclusive_access/fullscreen_controller_interactive_browsertest.cc index cb319e26..9262a8f 100644 --- a/chrome/browser/ui/exclusive_access/fullscreen_controller_interactive_browsertest.cc +++ b/chrome/browser/ui/exclusive_access/fullscreen_controller_interactive_browsertest.cc
@@ -65,6 +65,7 @@ #endif // BUILDFLAG(IS_LINUX) && BUILDFLAG(IS_OZONE) #if BUILDFLAG(IS_MAC) +#include "base/mac/mac_util.h" #include "ui/base/cocoa/nswindow_test_util.h" #endif // BUILDFLAG(IS_MAC) @@ -1545,6 +1546,11 @@ SetUpWindowManagementTab(); #if !BUILDFLAG(IS_MAC) const gfx::Rect original_bounds = browser()->window()->GetBounds(); +#else + // TODO(crbug.com/510801992): Re-enable on macOS 26 once test is deflaked + if (base::mac::MacOSMajorVersion() == 26) { + GTEST_SKIP() << "Disabled on macOS Tahoe."; + } #endif const display::Display original_display = GetCurrentDisplay(browser()); @@ -1630,6 +1636,11 @@ SetUpWindowManagementTab(); #if !BUILDFLAG(IS_MAC) const gfx::Rect original_bounds = browser()->window()->GetBounds(); +#else + // TODO(crbug.com/510801992): Re-enable on macOS 26 once test is deflaked + if (base::mac::MacOSMajorVersion() == 26) { + GTEST_SKIP() << "Disabled on macOS Tahoe."; + } #endif const display::Display original_display = GetCurrentDisplay(browser());
diff --git a/chrome/browser/ui/extensions/BUILD.gn b/chrome/browser/ui/extensions/BUILD.gn index 984451f..cbaf3c07 100644 --- a/chrome/browser/ui/extensions/BUILD.gn +++ b/chrome/browser/ui/extensions/BUILD.gn
@@ -166,6 +166,7 @@ "//chrome/browser/signin", "//chrome/browser/supervised_user", "//chrome/browser/tab_list", + "//chrome/browser/ui:simple_message_box_headers", "//chrome/browser/ui:ui_features", "//chrome/browser/ui/browser_window", "//chrome/browser/ui/color:color_headers",
diff --git a/chrome/browser/ui/extensions/extension_install_ui_android.cc b/chrome/browser/ui/extensions/extension_install_ui_android.cc index f050f27..34b27c6 100644 --- a/chrome/browser/ui/extensions/extension_install_ui_android.cc +++ b/chrome/browser/ui/extensions/extension_install_ui_android.cc
@@ -13,7 +13,6 @@ #include "chrome/browser/profiles/profile.h" #include "chrome/browser/ui/android/tab_model/tab_model.h" #include "chrome/browser/ui/android/tab_model/tab_model_list.h" -#include "chrome/browser/ui/browser_finder.h" #include "chrome/browser/ui/browser_window/public/browser_window_interface.h" #include "chrome/browser/ui/browser_window/public/browser_window_interface_iterator.h" #include "chrome/browser/ui/extensions/extension_dialog_utils.h"
diff --git a/chrome/browser/ui/extensions/hosted_app_browsertest.cc b/chrome/browser/ui/extensions/hosted_app_browsertest.cc index ddc3527..b7d387e 100644 --- a/chrome/browser/ui/extensions/hosted_app_browsertest.cc +++ b/chrome/browser/ui/extensions/hosted_app_browsertest.cc
@@ -32,7 +32,6 @@ #include "chrome/browser/sessions/tab_restore_service_factory.h" #include "chrome/browser/ui/browser.h" #include "chrome/browser/ui/browser_commands.h" -#include "chrome/browser/ui/browser_finder.h" #include "chrome/browser/ui/browser_window.h" #include "chrome/browser/ui/browser_window/public/browser_window_interface.h" #include "chrome/browser/ui/browser_window/public/global_browser_collection.h" @@ -1111,7 +1110,8 @@ GURL GetSiteForURL(content::BrowserContext* browser_context, const GURL& url) { return content::SiteInstance::CreateForURL(browser_context, url) - ->GetSiteURL(); + ->GetSecurityPrincipal() + .GetDeprecatedSiteURL(); } protected: @@ -1537,10 +1537,17 @@ // URL with a valid host that corresponds to the app's ID. EXPECT_TRUE( process_map_->Contains(main_frame->GetProcess()->GetDeprecatedID())); - EXPECT_FALSE(main_frame->GetSiteInstance()->GetSiteURL().is_empty()); + EXPECT_FALSE(main_frame->GetSiteInstance() + ->GetSecurityPrincipal() + .GetDeprecatedSiteURL() + .is_empty()); EXPECT_TRUE(main_frame->GetSiteInstance()->GetSecurityPrincipal().SchemeIs( extensions::kExtensionScheme)); - EXPECT_EQ(main_frame->GetSiteInstance()->GetSiteURL().GetHost(), app_id_); + EXPECT_EQ(main_frame->GetSiteInstance() + ->GetSecurityPrincipal() + .GetDeprecatedSiteURL() + .GetHost(), + app_id_); } class HostedAppProcessModelFencedFrameTest : public HostedAppProcessModelTest { @@ -1603,8 +1610,10 @@ // Check that the app loaded properly. RenderFrameHost* app = web_contents->GetPrimaryMainFrame(); - EXPECT_EQ(extensions::kExtensionScheme, - app->GetSiteInstance()->GetSiteURL().GetScheme()); + EXPECT_EQ(extensions::kExtensionScheme, app->GetSiteInstance() + ->GetSecurityPrincipal() + .GetDeprecatedSiteURL() + .GetScheme()); GURL app_site = GetSiteForURL(app_browser_->profile(), app->GetLastCommittedURL()); EXPECT_EQ(extensions::kExtensionScheme, app_site.GetScheme()); @@ -1672,8 +1681,10 @@ // Check that the app loaded properly. Even though its URL is from an // isolated origin (isolated.com), it should go into an app process. RenderFrameHost* app = web_contents->GetPrimaryMainFrame(); - EXPECT_EQ(extensions::kExtensionScheme, - app->GetSiteInstance()->GetSiteURL().GetScheme()); + EXPECT_EQ(extensions::kExtensionScheme, app->GetSiteInstance() + ->GetSecurityPrincipal() + .GetDeprecatedSiteURL() + .GetScheme()); GURL app_site = GetSiteForURL(app_browser_->profile(), app->GetLastCommittedURL()); EXPECT_EQ(extensions::kExtensionScheme, app_site.GetScheme()); @@ -1754,8 +1765,10 @@ // The app URL should have loaded in an app process. RenderFrameHost* app = web_contents->GetPrimaryMainFrame(); EXPECT_TRUE(process_map_->Contains(app->GetProcess()->GetDeprecatedID())); - EXPECT_EQ(extensions::kExtensionScheme, - app->GetSiteInstance()->GetSiteURL().GetScheme()); + EXPECT_EQ(extensions::kExtensionScheme, app->GetSiteInstance() + ->GetSecurityPrincipal() + .GetDeprecatedSiteURL() + .GetScheme()); int first_app_process_id = app->GetProcess()->GetDeprecatedID(); // Creating a subframe on unisolated.com should not be allowed to share the
diff --git a/chrome/browser/ui/focus/BUILD.gn b/chrome/browser/ui/focus/BUILD.gn index 2fb46e7c..9e78cf2 100644 --- a/chrome/browser/ui/focus/BUILD.gn +++ b/chrome/browser/ui/focus/BUILD.gn
@@ -7,16 +7,23 @@ assert(!is_android) source_set("focus") { - public = [ "browser_focus_controller.h" ] + public = [ + "browser_focus_controller.h", + "browser_focus_controller_delegate_views.h", + ] public_deps = [ "//base", + "//chrome/browser/ui:browser_element_identifiers", "//ui/base/unowned_user_data", ] } source_set("impl") { - sources = [ "browser_focus_controller.cc" ] + sources = [ + "browser_focus_controller.cc", + "browser_focus_controller_delegate_views.cc", + ] public_deps = [ ":focus",
diff --git a/chrome/browser/ui/focus/browser_focus_controller.cc b/chrome/browser/ui/focus/browser_focus_controller.cc index ad34751..7e343ea 100644 --- a/chrome/browser/ui/focus/browser_focus_controller.cc +++ b/chrome/browser/ui/focus/browser_focus_controller.cc
@@ -4,60 +4,20 @@ #include "chrome/browser/ui/focus/browser_focus_controller.h" -#include "chrome/browser/feature_engagement/tracker_factory.h" -#include "chrome/browser/profiles/profile.h" -#include "chrome/browser/ui/browser.h" -#include "chrome/browser/ui/browser_element_identifiers.h" +#include <utility> + +#include "base/check_deref.h" #include "chrome/browser/ui/browser_window/public/browser_window_interface.h" -#include "chrome/browser/ui/interaction/browser_elements.h" -#include "chrome/browser/ui/views/frame/contents_web_view.h" -#include "chrome/browser/ui/views/frame/multi_contents_view.h" -#include "chrome/browser/ui/views/frame/toolbar_button_provider.h" -#include "chrome/browser/ui/views/frame/top_container_view.h" -#include "chrome/browser/ui/views/infobars/infobar_container_view.h" -#include "chrome/browser/ui/views/location_bar/location_bar_view.h" -#include "chrome/browser/ui/views/toolbar/toolbar_view.h" -#include "chrome/browser/user_education/user_education_service.h" -#include "chrome/browser/user_education/user_education_service_factory.h" -#include "components/feature_engagement/public/event_constants.h" -#include "components/feature_engagement/public/tracker.h" -#include "components/user_education/common/help_bubble/help_bubble_factory_registry.h" -#include "components/user_education/views/help_bubble_view.h" #include "ui/base/base_window.h" -#include "ui/views/accessible_pane_view.h" #include "ui/views/focus/focus_manager.h" -#include "ui/views/interaction/element_tracker_views.h" -#include "ui/views/view.h" -#include "ui/views/view_class_properties.h" -#include "ui/views/view_utils.h" #include "ui/views/widget/widget.h" DEFINE_USER_DATA(BrowserFocusController); -namespace { - -views::View* GetViewForId(BrowserWindowInterface* browser, - ui::ElementIdentifier element_id) { - auto* browser_elements = BrowserElements::From(browser); - if (!browser_elements) { - return nullptr; - } - - ui::TrackedElement* element = browser_elements->GetElement(element_id); - if (!element) { - return nullptr; - } - - views::TrackedElementViews* element_views = - element->AsA<views::TrackedElementViews>(); - return element_views ? element_views->view() : nullptr; -} - -} // namespace - -BrowserFocusController::BrowserFocusController(BrowserWindowInterface& browser) - : browser_(browser), - scoped_data_holder_(browser.GetUnownedUserDataHost(), *this) {} +BrowserFocusController::BrowserFocusController(ui::BaseWindow* base_window, + ui::UnownedUserDataHost& host) + : base_window_(CHECK_DEREF(base_window)), + scoped_data_holder_(host, *this) {} BrowserFocusController::~BrowserFocusController() = default; @@ -77,10 +37,13 @@ browser->GetUnownedUserDataHost()); } +void BrowserFocusController::SetDelegate(std::unique_ptr<Delegate> delegate) { + delegate_ = std::move(delegate); +} + void BrowserFocusController::RotatePaneFocus(bool forwards) { - gfx::NativeWindow native_window = browser_->GetWindow()->GetNativeWindow(); views::Widget* widget = - views::Widget::GetWidgetForNativeWindow(native_window); + views::Widget::GetWidgetForNativeWindow(base_window_->GetNativeWindow()); if (!widget) { return; } @@ -91,97 +54,20 @@ } void BrowserFocusController::FocusWebContentsPane() { - auto* multi_contents_view = views::AsViewClass<MultiContentsView>( - GetViewForId(&*browser_, kMultiContentsViewElementId)); - if (multi_contents_view) { - multi_contents_view->GetActiveContentsView()->RequestFocus(); + if (delegate_) { + delegate_->FocusWebContentsPane(); } } void BrowserFocusController::FocusInactivePopupForAccessibility() { - if (ActivateFirstInactiveBubbleForAccessibility()) { - return; - } - - auto* infobar_container = views::AsViewClass<views::AccessiblePaneView>( - GetViewForId(&*browser_, kInfoBarContainerElementId)); - if (infobar_container && !infobar_container->children().empty()) { - infobar_container->SetPaneFocusAndFocusDefault(); + if (delegate_) { + delegate_->FocusInactivePopupForAccessibility(); } } bool BrowserFocusController::ActivateFirstInactiveBubbleForAccessibility() { - Profile* profile = browser_->GetProfile(); - auto* const user_education = - UserEducationServiceFactory::GetForBrowserContext(profile); - if (user_education && - user_education->help_bubble_factory_registry() - .ToggleFocusForAccessibility( - BrowserElements::From(&*browser_)->GetContext())) { - feature_engagement::TrackerFactory::GetForBrowserContext(profile) - ->NotifyEvent( - feature_engagement::events::kFocusHelpBubbleAcceleratorPressed); - return true; + if (delegate_) { + return delegate_->ActivateFirstInactiveBubbleForAccessibility(); } - - auto* toolbar_button_provider = ToolbarButtonProvider::From(&*browser_); - if (!toolbar_button_provider) { - return false; - } - - // TODO: this fixes https://crbug.com/40668249 and https://crbug.com/40674460, - // but a more general solution should be desirable to find any bubbles - // anchored in the views hierarchy. - views::DialogDelegate* bubble = nullptr; - if (auto* control = toolbar_button_provider->GetAppMenuControl()) { - auto* dialog = control->GetDialogDelegate(); - if (dialog && !user_education::HelpBubbleView::IsHelpBubble(dialog)) { - bubble = dialog; - } - } - - if (!bubble) { - if (auto* avatar = - toolbar_button_provider->GetAvatarToolbarButtonInterface()) { - auto* dialog = avatar->GetDialogDelegate(); - if (dialog && !user_education::HelpBubbleView::IsHelpBubble(dialog)) { - bubble = dialog; - } - } - for (auto* view : std::initializer_list<views::View*>{ - GetViewForId(&*browser_, kLocationBarElementId), - toolbar_button_provider->GetDownloadButton(), - GetViewForId(&*browser_, kTopContainerElementId)}) { - if (view) { - if (auto* dialog = view->GetProperty(views::kAnchoredDialogKey); - dialog && !user_education::HelpBubbleView::IsHelpBubble(dialog)) { - bubble = dialog; - break; - } - } - } - } - - if (bubble) { - CHECK(!user_education::HelpBubbleView::IsHelpBubble(bubble)); - views::View* focusable = bubble->GetInitiallyFocusedView(); - - if (!focusable) { - focusable = bubble->GetCancelButton(); - } - - if (focusable) { - focusable->RequestFocus(); -#if BUILDFLAG(IS_MAC) - views::Widget* const widget = bubble->GetWidget(); - if (widget && widget->IsVisible() && !widget->IsActive()) { - DCHECK(browser_->IsActive()); - widget->Activate(); - } -#endif - return true; - } - } - return false; }
diff --git a/chrome/browser/ui/focus/browser_focus_controller.h b/chrome/browser/ui/focus/browser_focus_controller.h index 7950d1d3..6d6e44b 100644 --- a/chrome/browser/ui/focus/browser_focus_controller.h +++ b/chrome/browser/ui/focus/browser_focus_controller.h
@@ -5,18 +5,40 @@ #ifndef CHROME_BROWSER_UI_FOCUS_BROWSER_FOCUS_CONTROLLER_H_ #define CHROME_BROWSER_UI_FOCUS_BROWSER_FOCUS_CONTROLLER_H_ +#include <memory> + #include "base/memory/raw_ref.h" #include "ui/base/unowned_user_data/scoped_unowned_user_data.h" class BrowserWindowInterface; +namespace ui { +class BaseWindow; +} + // Manages pane focus rotation and accessibility focus for its associated // Browser window. class BrowserFocusController { public: + class Delegate { + public: + virtual ~Delegate() = default; + + // Focuses the active web contents pane. + virtual void FocusWebContentsPane() = 0; + + // Focuses the first inactive popup or infobar for accessibility. + virtual void FocusInactivePopupForAccessibility() = 0; + + // Helper to activate the first inactive bubble for accessibility. + // Returns true if a bubble was activated. + virtual bool ActivateFirstInactiveBubbleForAccessibility() = 0; + }; + DECLARE_USER_DATA(BrowserFocusController); - explicit BrowserFocusController(BrowserWindowInterface& browser); + BrowserFocusController(ui::BaseWindow* base_window, + ui::UnownedUserDataHost& host); ~BrowserFocusController(); BrowserFocusController(const BrowserFocusController&) = delete; @@ -26,6 +48,8 @@ static const BrowserFocusController* From( const BrowserWindowInterface* browser); + void SetDelegate(std::unique_ptr<Delegate> delegate); + // Rotates pane focus forwards or backwards. void RotatePaneFocus(bool forwards); @@ -40,7 +64,9 @@ bool ActivateFirstInactiveBubbleForAccessibility(); private: - const raw_ref<BrowserWindowInterface> browser_; + const raw_ref<ui::BaseWindow> base_window_; + + std::unique_ptr<Delegate> delegate_; ui::ScopedUnownedUserData<BrowserFocusController> scoped_data_holder_; };
diff --git a/chrome/browser/ui/focus/browser_focus_controller_delegate_views.cc b/chrome/browser/ui/focus/browser_focus_controller_delegate_views.cc new file mode 100644 index 0000000..1dbfbe5 --- /dev/null +++ b/chrome/browser/ui/focus/browser_focus_controller_delegate_views.cc
@@ -0,0 +1,143 @@ +// Copyright 2026 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chrome/browser/ui/focus/browser_focus_controller_delegate_views.h" + +#include "base/check_deref.h" +#include "chrome/browser/feature_engagement/tracker_factory.h" +#include "chrome/browser/profiles/profile.h" +#include "chrome/browser/ui/browser_element_identifiers.h" +#include "chrome/browser/ui/browser_window/public/browser_window_interface.h" +#include "chrome/browser/ui/interaction/browser_elements.h" +#include "chrome/browser/ui/views/frame/contents_web_view.h" +#include "chrome/browser/ui/views/frame/multi_contents_view.h" +#include "chrome/browser/ui/views/frame/toolbar_button_provider.h" +#include "chrome/browser/ui/views/frame/top_container_view.h" +#include "chrome/browser/ui/views/infobars/infobar_container_view.h" +#include "chrome/browser/ui/views/location_bar/location_bar_view.h" +#include "chrome/browser/ui/views/toolbar/toolbar_view.h" +#include "chrome/browser/user_education/user_education_service.h" +#include "chrome/browser/user_education/user_education_service_factory.h" +#include "components/feature_engagement/public/event_constants.h" +#include "components/feature_engagement/public/tracker.h" +#include "components/user_education/common/help_bubble/help_bubble_factory_registry.h" +#include "components/user_education/views/help_bubble_view.h" +#include "ui/views/accessible_pane_view.h" +#include "ui/views/interaction/element_tracker_views.h" +#include "ui/views/view.h" +#include "ui/views/view_class_properties.h" +#include "ui/views/view_utils.h" +#include "ui/views/widget/widget.h" + +BrowserFocusControllerDelegateViews::BrowserFocusControllerDelegateViews( + Profile* profile, + BrowserElements* browser_elements, + ToolbarButtonProvider* toolbar_button_provider) + : profile_(CHECK_DEREF(profile)), + browser_elements_(CHECK_DEREF(browser_elements)), + toolbar_button_provider_(CHECK_DEREF(toolbar_button_provider)) {} + +BrowserFocusControllerDelegateViews::~BrowserFocusControllerDelegateViews() = + default; + +void BrowserFocusControllerDelegateViews::FocusWebContentsPane() { + auto* multi_contents_view = views::AsViewClass<MultiContentsView>( + GetViewForId(kMultiContentsViewElementId)); + if (multi_contents_view) { + multi_contents_view->GetActiveContentsView()->RequestFocus(); + } +} + +void BrowserFocusControllerDelegateViews::FocusInactivePopupForAccessibility() { + if (ActivateFirstInactiveBubbleForAccessibility()) { + return; + } + + auto* infobar_container = views::AsViewClass<views::AccessiblePaneView>( + GetViewForId(kInfoBarContainerElementId)); + if (infobar_container && !infobar_container->children().empty()) { + infobar_container->SetPaneFocusAndFocusDefault(); + } +} + +bool BrowserFocusControllerDelegateViews:: + ActivateFirstInactiveBubbleForAccessibility() { + auto* const user_education = + UserEducationServiceFactory::GetForBrowserContext(&*profile_); + if (user_education && + user_education->help_bubble_factory_registry() + .ToggleFocusForAccessibility(browser_elements_->GetContext())) { + feature_engagement::TrackerFactory::GetForBrowserContext(&*profile_) + ->NotifyEvent( + feature_engagement::events::kFocusHelpBubbleAcceleratorPressed); + return true; + } + + // TODO: this fixes https://crbug.com/40668249 and https://crbug.com/40674460, + // but a more general solution should be desirable to find any bubbles + // anchored in the views hierarchy. + views::DialogDelegate* bubble = nullptr; + if (auto* control = toolbar_button_provider_->GetAppMenuControl()) { + auto* dialog = control->GetDialogDelegate(); + if (dialog && !user_education::HelpBubbleView::IsHelpBubble(dialog)) { + bubble = dialog; + } + } + + if (!bubble) { + if (auto* avatar = + toolbar_button_provider_->GetAvatarToolbarButtonInterface()) { + auto* dialog = avatar->GetDialogDelegate(); + if (dialog && !user_education::HelpBubbleView::IsHelpBubble(dialog)) { + bubble = dialog; + } + } + for (auto* view : std::initializer_list<views::View*>{ + GetViewForId(kLocationBarElementId), + toolbar_button_provider_->GetDownloadButton(), + GetViewForId(kTopContainerElementId)}) { + if (view) { + if (auto* dialog = view->GetProperty(views::kAnchoredDialogKey); + dialog && !user_education::HelpBubbleView::IsHelpBubble(dialog)) { + bubble = dialog; + break; + } + } + } + } + + if (bubble) { + CHECK(!user_education::HelpBubbleView::IsHelpBubble(bubble)); + views::View* focusable = bubble->GetInitiallyFocusedView(); + + if (!focusable) { + focusable = bubble->GetCancelButton(); + } + + if (focusable) { + focusable->RequestFocus(); +#if BUILDFLAG(IS_MAC) + views::Widget* const widget = bubble->GetWidget(); + if (widget && widget->IsVisible() && !widget->IsActive()) { + widget->Activate(); + } +#endif + return true; + } + } + + return false; +} + +views::View* BrowserFocusControllerDelegateViews::GetViewForId( + ui::ElementIdentifier element_id) { + ui::TrackedElement* element = browser_elements_->GetElement(element_id); + if (!element) { + return nullptr; + } + + views::TrackedElementViews* element_views = + element->AsA<views::TrackedElementViews>(); + return element_views ? element_views->view() : nullptr; +}
diff --git a/chrome/browser/ui/focus/browser_focus_controller_delegate_views.h b/chrome/browser/ui/focus/browser_focus_controller_delegate_views.h new file mode 100644 index 0000000..82a3a87e --- /dev/null +++ b/chrome/browser/ui/focus/browser_focus_controller_delegate_views.h
@@ -0,0 +1,47 @@ +// Copyright 2026 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CHROME_BROWSER_UI_FOCUS_BROWSER_FOCUS_CONTROLLER_DELEGATE_VIEWS_H_ +#define CHROME_BROWSER_UI_FOCUS_BROWSER_FOCUS_CONTROLLER_DELEGATE_VIEWS_H_ + +#include "base/memory/raw_ref.h" +#include "chrome/browser/ui/focus/browser_focus_controller.h" +#include "ui/base/interaction/element_identifier.h" + +class BrowserElements; +class Profile; +class ToolbarButtonProvider; + +namespace views { +class View; +} + +class BrowserFocusControllerDelegateViews + : public BrowserFocusController::Delegate { + public: + BrowserFocusControllerDelegateViews( + Profile* profile, + BrowserElements* browser_elements, + ToolbarButtonProvider* toolbar_button_provider); + ~BrowserFocusControllerDelegateViews() override; + + BrowserFocusControllerDelegateViews( + const BrowserFocusControllerDelegateViews&) = delete; + BrowserFocusControllerDelegateViews& operator=( + const BrowserFocusControllerDelegateViews&) = delete; + + // BrowserFocusController::Delegate: + void FocusWebContentsPane() override; + void FocusInactivePopupForAccessibility() override; + bool ActivateFirstInactiveBubbleForAccessibility() override; + + private: + views::View* GetViewForId(ui::ElementIdentifier element_id); + + const raw_ref<Profile> profile_; + const raw_ref<BrowserElements> browser_elements_; + const raw_ref<ToolbarButtonProvider> toolbar_button_provider_; +}; + +#endif // CHROME_BROWSER_UI_FOCUS_BROWSER_FOCUS_CONTROLLER_DELEGATE_VIEWS_H_
diff --git a/chrome/browser/ui/layout_constants.cc b/chrome/browser/ui/layout_constants.cc index ea298561..063b8fe 100644 --- a/chrome/browser/ui/layout_constants.cc +++ b/chrome/browser/ui/layout_constants.cc
@@ -72,8 +72,10 @@ return touch_ui ? 3 : 8; case LayoutConstant::kLocationBarHeight: return touch_ui ? 36 : 34; + // LINT.IfChange(LocationBarIconSize) case LayoutConstant::kLocationBarIconSize: return touch_ui ? 20 : 16; + // LINT.ThenChange(//chrome/browser/ui/omnibox/omnibox_edit_model.cc:SuperGIconSize) case LayoutConstant::kLocationBarLeadingIconSize: return GetLayoutConstant(LayoutConstant::kLocationBarIconSize); case LayoutConstant::kLocationBarTrailingIconSize:
diff --git a/chrome/browser/ui/lens/BUILD.gn b/chrome/browser/ui/lens/BUILD.gn index a6fee36..fcb4086f 100644 --- a/chrome/browser/ui/lens/BUILD.gn +++ b/chrome/browser/ui/lens/BUILD.gn
@@ -405,6 +405,7 @@ "lens_overlay_side_panel_navigation_throttle_unittest.cc", "lens_overlay_url_builder_unittest.cc", "lens_query_flow_router_unittest.cc", + "lens_searchbox_controller_unittest.cc", "lens_url_matcher_unittest.cc", ] deps = [ @@ -423,6 +424,7 @@ "//chrome/browser/ui/contextual_search", "//chrome/browser/ui/tabs:tabs_public", "//chrome/browser/ui/webui/new_tab_page/composebox/variations", + "//chrome/browser/ui/webui/searchbox:searchbox_test_utils", "//chrome/test:test_support", "//components/base32", "//components/contextual_search:test_support",
diff --git a/chrome/browser/ui/lens/lens_composebox_handler.cc b/chrome/browser/ui/lens/lens_composebox_handler.cc index e4c21e5..079598d 100644 --- a/chrome/browser/ui/lens/lens_composebox_handler.cc +++ b/chrome/browser/ui/lens/lens_composebox_handler.cc
@@ -190,6 +190,10 @@ std::move(callback).Run(false); } +void LensComposeboxHandler::NotifyComposeboxQuerySubmittedWithContext() { + // No-op for Lens composebox. +} + void LensComposeboxHandler::DeleteAutocompleteMatch(uint8_t line, const GURL& url) { mojo::ReportBadMessage("Delete autocomplete match not implemented in lens");
diff --git a/chrome/browser/ui/lens/lens_composebox_handler.h b/chrome/browser/ui/lens/lens_composebox_handler.h index 3b9dda2..628ccf2 100644 --- a/chrome/browser/ui/lens/lens_composebox_handler.h +++ b/chrome/browser/ui/lens/lens_composebox_handler.h
@@ -55,6 +55,7 @@ void SetSmartTabSharingActive(bool active) override; void GetSmartTabSharingActive( GetSmartTabSharingActiveCallback callback) override; + void NotifyComposeboxQuerySubmittedWithContext() override; // searchbox::mojom::PageHandler: void DeleteAutocompleteMatch(uint8_t line, const GURL& url) override; void ExecuteAction(uint8_t line,
diff --git a/chrome/browser/ui/lens/lens_searchbox_controller.cc b/chrome/browser/ui/lens/lens_searchbox_controller.cc index ff36955..50df8f5 100644 --- a/chrome/browser/ui/lens/lens_searchbox_controller.cc +++ b/chrome/browser/ui/lens/lens_searchbox_controller.cc
@@ -58,11 +58,13 @@ void LensSearchboxController::SetSidePanelSearchboxHandler( std::unique_ptr<LensSearchboxHandler> handler) { side_panel_searchbox_handler_ = std::move(handler); + OnPageBound(); } void LensSearchboxController::SetContextualSearchboxHandler( std::unique_ptr<LensSearchboxHandler> handler) { overlay_searchbox_handler_ = std::move(handler); + OnPageBound(); } void LensSearchboxController::ResetOverlaySearchboxHandler() {
diff --git a/chrome/browser/ui/lens/lens_searchbox_controller_unittest.cc b/chrome/browser/ui/lens/lens_searchbox_controller_unittest.cc new file mode 100644 index 0000000..b252cbb --- /dev/null +++ b/chrome/browser/ui/lens/lens_searchbox_controller_unittest.cc
@@ -0,0 +1,102 @@ +// Copyright 2026 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chrome/browser/ui/lens/lens_searchbox_controller.h" + +#include "chrome/browser/ui/webui/searchbox/lens_searchbox_handler.h" +#include "chrome/browser/ui/webui/searchbox/searchbox_test_utils.h" +#include "chrome/test/base/testing_profile.h" +#include "content/public/test/browser_task_environment.h" +#include "content/public/test/test_renderer_host.h" +#include "content/public/test/web_contents_tester.h" +#include "testing/gmock/include/gmock/gmock.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "third_party/metrics_proto/omnibox_event.pb.h" + +using ::testing::_; + +namespace lens { + +// Minimalist wrapper to override parent-dependent methods and isolate +// the specific lifecycle logic under investigation. +class TestLensSearchboxController : public LensSearchboxController { + public: + TestLensSearchboxController() : LensSearchboxController(nullptr) {} + ~TestLensSearchboxController() override = default; + + metrics::OmniboxEventProto::PageClassification GetPageClassification() + const override { + // Bypasses navigating the uninitialized parent pointers to calculate + // classification. + return metrics::OmniboxEventProto::CONTEXTUAL_SEARCHBOX; + } +}; + +class LensSearchboxControllerTest : public testing::Test { + public: + LensSearchboxControllerTest() = default; + ~LensSearchboxControllerTest() override = default; + + void SetUp() override { + profile_ = std::make_unique<TestingProfile>(); + web_contents_ = content::WebContentsTester::CreateTestWebContents( + profile_.get(), content::SiteInstance::Create(profile_.get())); + controller_ = std::make_unique<TestLensSearchboxController>(); + } + + protected: + content::BrowserTaskEnvironment task_environment_; + content::RenderViewHostTestEnabler rvh_test_enabler_; + std::unique_ptr<TestingProfile> profile_; + std::unique_ptr<content::WebContents> web_contents_; + std::unique_ptr<TestLensSearchboxController> controller_; +}; + +TEST_F(LensSearchboxControllerTest, FlushesPendingTextQueryOnHandlerSet) { + controller_->OnSessionStart(/*suppress_contextualization=*/false); + + // Setup data that should be pushed once bound. + const std::string expected_text = "test search query"; + controller_->SetSearchboxInputText(expected_text); + + // Setup mock page to listen for instructions. + testing::NiceMock<MockSearchboxPage> mock_page; + EXPECT_CALL(mock_page, SetInputText(expected_text)).Times(1); + + // Create a real handler but point it to our mock page via mojo. + auto handler = std::make_unique<LensSearchboxHandler>( + mojo::PendingReceiver<searchbox::mojom::PageHandler>(), + mock_page.BindAndGetRemote(), profile_.get(), web_contents_.get(), + controller_.get()); + + // Act: This injection should now properly route to OnPageBound and fire. + controller_->SetSidePanelSearchboxHandler(std::move(handler)); + + // Verify execution over async boundary. + mock_page.FlushForTesting(); +} + +TEST_F(LensSearchboxControllerTest, FlushesPendingThumbnailOnHandlerSet) { + controller_->OnSessionStart(/*suppress_contextualization=*/false); + + // Setup data that should be pushed once bound. + const std::string expected_uri = "data:image/jpeg;base64,abc"; + controller_->SetSearchboxThumbnail(expected_uri); + + // Setup mock page to listen for instructions. + testing::NiceMock<MockSearchboxPage> mock_page; + EXPECT_CALL(mock_page, SetThumbnail(expected_uri, false)).Times(1); + + auto handler = std::make_unique<LensSearchboxHandler>( + mojo::PendingReceiver<searchbox::mojom::PageHandler>(), + mock_page.BindAndGetRemote(), profile_.get(), web_contents_.get(), + controller_.get()); + + // Act: Injection must trigger OnPageBound successfully. + controller_->SetContextualSearchboxHandler(std::move(handler)); + + mock_page.FlushForTesting(); +} + +} // namespace lens
diff --git a/chrome/browser/ui/navigator/browser_navigator_browsertest_chromeos.cc b/chrome/browser/ui/navigator/browser_navigator_browsertest_chromeos.cc index 0e1cf8d..9bd785f3 100644 --- a/chrome/browser/ui/navigator/browser_navigator_browsertest_chromeos.cc +++ b/chrome/browser/ui/navigator/browser_navigator_browsertest_chromeos.cc
@@ -19,7 +19,6 @@ #include "chrome/browser/ui/ash/multi_user/multi_user_util.h" #include "chrome/browser/ui/ash/session/session_controller_client_impl.h" #include "chrome/browser/ui/browser.h" -#include "chrome/browser/ui/browser_finder.h" #include "chrome/browser/ui/browser_window.h" #include "chrome/browser/ui/browser_window/public/browser_window_interface.h" #include "chrome/browser/ui/browser_window/public/global_browser_collection.h"
diff --git a/chrome/browser/ui/omnibox/omnibox_edit_model.cc b/chrome/browser/ui/omnibox/omnibox_edit_model.cc index 81e9aba..4506d7eb 100644 --- a/chrome/browser/ui/omnibox/omnibox_edit_model.cc +++ b/chrome/browser/ui/omnibox/omnibox_edit_model.cc
@@ -9,6 +9,7 @@ #include <algorithm> #include <iterator> #include <memory> +#include <optional> #include <string> #include <string_view> #include <utility> @@ -48,6 +49,7 @@ #include "chrome/browser/ui/views/omnibox/omnibox_popup_closer.h" #include "chrome/common/chrome_features.h" #include "chrome/common/webui_url_constants.h" +#include "chrome/grit/theme_resources.h" #include "components/bookmarks/browser/bookmark_model.h" #include "components/contextual_search/contextual_search_metrics_recorder.h" #include "components/contextual_search/contextual_search_service.h" @@ -518,12 +520,47 @@ return ui::ImageModel::FromVectorIcon( vector_icons::kGoogleGLogoMonochromeIcon, ui::kColorRefPrimary100, image_size); - } else { - // The icon color does not matter in this case since this icon has colors - // hardcoded into it. + } + + // TODO(crbug.com/507061157): Remove this once + // `location_bar::GetSecurityChipIconEnum` and + // `location_bar::IsSecurityChipInteractive` support non-vector icons. + if (base::FeatureList::IsEnabled(features::kWebUILocationBar)) { return ui::ImageModel::FromVectorIcon(vector_icons::kGoogleSuperGIcon, gfx::kPlaceholderColor, image_size); } + + std::optional<int> resource_id; + // Note: The gradient "Super G" logo requires gradients and clip paths, + // which are not supported by Chromium vector icons (see + // `ui/gfx/vector_icon_types.h`). Therefore, we must use multi-size PNGs. + // LINT.IfChange(SuperGIconSize) + switch (image_size) { + case 16: + resource_id = IDR_GOOGLE_G_GRADIENT_16_ALT; + break; + case 20: + resource_id = IDR_GOOGLE_G_GRADIENT_20; + break; + } + // LINT.ThenChange(//chrome/browser/ui/layout_constants.cc:LocationBarIconSize) + + if (resource_id) { + const gfx::ImageSkia* image = + ui::ResourceBundle::GetSharedInstance().GetImageSkiaNamed(*resource_id); + if (image->width() == image_size) { + return ui::ImageModel::FromImageSkia(*image); + } + } + + // Fallback if somehow a new size bypasses the lint or if the loaded resource + // size was incorrect. + DCHECK(false) << "Unexpected icon size: " << image_size + << ". Please add a matching IDR_GOOGLE_G_GRADIENT resource."; + return ui::ImageModel::FromImage(controller_->client()->GetSizedIcon( + ui::ResourceBundle::GetSharedInstance() + .GetImageNamed(IDR_GOOGLE_G_GRADIENT_16_ALT) + .ToSkBitmap())); #else return ui::ImageModel(); #endif
diff --git a/chrome/browser/ui/passwords/BUILD.gn b/chrome/browser/ui/passwords/BUILD.gn index 247601b0..b436322d4 100644 --- a/chrome/browser/ui/passwords/BUILD.gn +++ b/chrome/browser/ui/passwords/BUILD.gn
@@ -81,6 +81,7 @@ "//chrome/browser/profiles:profile", "//chrome/browser/sync", "//chrome/browser/sync:factories", + "//chrome/browser/ui:simple_message_box_headers", "//chrome/browser/ui/browser_window", "//chrome/browser/ui/navigator", "//chrome/common",
diff --git a/chrome/browser/ui/passwords/password_change_ui_controller.cc b/chrome/browser/ui/passwords/password_change_ui_controller.cc index d44824c..0b6cdc2 100644 --- a/chrome/browser/ui/passwords/password_change_ui_controller.cc +++ b/chrome/browser/ui/passwords/password_change_ui_controller.cc
@@ -13,7 +13,6 @@ #include "chrome/browser/profiles/profile.h" #include "chrome/browser/signin/identity_manager_factory.h" #include "chrome/browser/sync/sync_service_factory.h" -#include "chrome/browser/ui/browser_finder.h" #include "chrome/browser/ui/browser_window/public/browser_window_interface.h" #include "chrome/browser/ui/passwords/ui_utils.h" #include "chrome/browser/ui/singleton_tabs.h"
diff --git a/chrome/browser/ui/profiles/BUILD.gn b/chrome/browser/ui/profiles/BUILD.gn index 1955596..e670f01 100644 --- a/chrome/browser/ui/profiles/BUILD.gn +++ b/chrome/browser/ui/profiles/BUILD.gn
@@ -62,6 +62,7 @@ deps = [ ":profiles", "//chrome/browser/feedback", + "//chrome/browser/ui:simple_message_box_headers", ] public_deps = []
diff --git a/chrome/browser/ui/read_anything/BUILD.gn b/chrome/browser/ui/read_anything/BUILD.gn index 7a0a069..8afc209 100644 --- a/chrome/browser/ui/read_anything/BUILD.gn +++ b/chrome/browser/ui/read_anything/BUILD.gn
@@ -76,6 +76,7 @@ "//chrome/browser/extensions", "//chrome/browser/language", "//chrome/browser/profiles", + "//chrome/browser/ui:accelerator_utils_headers", "//chrome/browser/ui:ui_features", "//chrome/browser/ui/exclusive_access", "//chrome/browser/ui/find_bar", @@ -111,6 +112,7 @@ "//base/test:test_support", "//chrome/browser", "//chrome/browser/pdf:pdf_extension_test_utils", + "//chrome/browser/search_engines", "//chrome/browser/ui", "//chrome/browser/ui:ui_features", "//chrome/browser/ui/find_bar", @@ -121,6 +123,7 @@ "//chrome/test:test_support_ui", "//components/optimization_guide/core", "//components/page_load_metrics/browser:test_support", + "//components/search_engines", "//content/test:test_support", "//testing/gtest", "//ui/views",
diff --git a/chrome/browser/ui/read_anything/read_anything_controller.cc b/chrome/browser/ui/read_anything/read_anything_controller.cc index 9a64ab36..2879f29 100644 --- a/chrome/browser/ui/read_anything/read_anything_controller.cc +++ b/chrome/browser/ui/read_anything/read_anything_controller.cc
@@ -194,7 +194,8 @@ void ReadAnythingController::OnEntryShown( std::optional<ReadAnythingOpenTrigger> trigger) { - observers_.Notify(&Observer::Activate, true, trigger); + observers_.Notify(&Observer::Activate, /*active=*/true, trigger, + /*completed_session_duration=*/std::nullopt); active_service_ = ReadAnythingService::Get(tab_->GetBrowserWindowInterface()->GetProfile()); // At the moment, services are created for normal, guest, and incognito @@ -224,21 +225,25 @@ } void ReadAnythingController::OnEntryHidden() { - observers_.Notify(&Observer::Activate, false, - std::optional<ReadAnythingOpenTrigger>()); + std::optional<base::TimeDelta> completed_session_duration = + RecordEntryHiddenMetrics(); + + observers_.Notify(&Observer::Activate, /*active=*/false, + /*trigger=*/std::optional<ReadAnythingOpenTrigger>(), + completed_session_duration); if (active_service_) { active_service_->OnReadAnythingHidden(); active_service_ = nullptr; } - RecordEntryHiddenMetrics(); } -void ReadAnythingController::RecordEntryHiddenMetrics() { +std::optional<base::TimeDelta> +ReadAnythingController::RecordEntryHiddenMetrics() { // If we are transitioning between UI modes (e.g., Side Panel to Immersive), // don't record OnEntryHidden metrics. if (is_presentation_transitioning_) { - return; + return std::nullopt; } // Record whether the UI was closed before it was successfully shown. @@ -247,20 +252,25 @@ hidden_before_shown); if (hidden_before_shown) { - return; + return std::nullopt; } + // Calculate the duration that Reading Mode was visible in this tab session. + base::TimeDelta completed_session_duration = + base::TimeTicks::Now() - entry_shown_timestamp_; + // Only record if RM was successfully shown. base::UmaHistogramCustomTimes( "Accessibility.ReadAnything.ShownDurationMax1Day", - base::TimeTicks::Now() - entry_shown_timestamp_, /*min=*/base::Seconds(1), + completed_session_duration, /*min=*/base::Seconds(1), /*max=*/base::Hours(24), /*buckets=*/100); // Reset the timestamp to null to avoid polluting data if the next RM show // request is closed before it's fully shown. entry_shown_timestamp_ = base::TimeTicks(); -} + return completed_session_duration; +} // Returns the SidePanelUI for the active tab if the tab is active and has a // browser window interface. Returns nullptr otherwise.
diff --git a/chrome/browser/ui/read_anything/read_anything_controller.h b/chrome/browser/ui/read_anything/read_anything_controller.h index da752a36..a515791 100644 --- a/chrome/browser/ui/read_anything/read_anything_controller.h +++ b/chrome/browser/ui/read_anything/read_anything_controller.h
@@ -224,8 +224,11 @@ // (e.g. due to being unresponsive). void OnRendererCrashed(); - // Helper function to record OnEntryHidden metrics. - void RecordEntryHiddenMetrics(); + // Records OnEntryHidden metrics and returns the calculated session duration + // if Reading Mode was successfully shown. Returns std::nullopt if the UI is + // closed before being successfully shown or hidden due to an in-progress + // presentation mode transition. + std::optional<base::TimeDelta> RecordEntryHiddenMetrics(); // Returns the SidePanelUI for the active tab if it can be shown. // Otherwise, returns nullptr.
diff --git a/chrome/browser/ui/read_anything/read_anything_controller_browsertest.cc b/chrome/browser/ui/read_anything/read_anything_controller_browsertest.cc index 2181ed4..5b1df3b 100644 --- a/chrome/browser/ui/read_anything/read_anything_controller_browsertest.cc +++ b/chrome/browser/ui/read_anything/read_anything_controller_browsertest.cc
@@ -9,6 +9,7 @@ #include "base/test/scoped_feature_list.h" #include "chrome/app/chrome_command_ids.h" #include "chrome/browser/renderer_context_menu/render_view_context_menu_test_util.h" +#include "chrome/browser/search_engines/template_url_service_factory.h" #include "chrome/browser/ui/browser.h" #include "chrome/browser/ui/browser_commands.h" #include "chrome/browser/ui/browser_tabstrip.h" @@ -40,6 +41,8 @@ #include "components/keyed_service/core/keyed_service.h" #include "components/page_load_metrics/browser/page_load_metrics_test_waiter.h" #include "components/prefs/pref_service.h" +#include "components/search_engines/template_url.h" +#include "components/search_engines/template_url_service.h" #include "content/public/test/browser_test.h" #include "content/public/test/browser_test_utils.h" #include "content/public/test/hit_test_region_observer.h" @@ -56,7 +59,9 @@ public: MOCK_METHOD(void, Activate, - (bool active, std::optional<ReadAnythingOpenTrigger>), + (bool active, + std::optional<ReadAnythingOpenTrigger>, + std::optional<base::TimeDelta>), (override)); MOCK_METHOD(void, OnDestroyed, (), (override)); MOCK_METHOD(void, OnReadingModePresenterChanged, (), (override)); @@ -205,9 +210,8 @@ controller->AddObserver(&observer); base::RunLoop run_loop; - EXPECT_CALL(observer, Activate(true, testing::_)).WillOnce([&run_loop]() { - run_loop.Quit(); - }); + EXPECT_CALL(observer, Activate(true, testing::_, testing::_)) + .WillOnce([&run_loop]() { run_loop.Quit(); }); controller->ShowImmersiveUI(ReadAnythingOpenTrigger::kOmniboxChip); run_loop.Run(); @@ -227,25 +231,22 @@ // 1. Show Immersive UI (first time) base::RunLoop run_loop_1; - EXPECT_CALL(observer, Activate(true, testing::_)).WillOnce([&run_loop_1]() { - run_loop_1.Quit(); - }); + EXPECT_CALL(observer, Activate(true, testing::_, testing::_)) + .WillOnce([&run_loop_1]() { run_loop_1.Quit(); }); controller->ShowImmersiveUI(ReadAnythingOpenTrigger::kOmniboxChip); run_loop_1.Run(); // 2. Close Immersive UI base::RunLoop run_loop_2; - EXPECT_CALL(observer, Activate(false, testing::_)).WillOnce([&run_loop_2]() { - run_loop_2.Quit(); - }); + EXPECT_CALL(observer, Activate(false, testing::_, testing::_)) + .WillOnce([&run_loop_2]() { run_loop_2.Quit(); }); controller->CloseImmersiveUI(ReadAnythingCloseReason::kClosedByUser); run_loop_2.Run(); // 3. Show Immersive UI (second time - reuse WebUI) base::RunLoop run_loop_3; - EXPECT_CALL(observer, Activate(true, testing::_)).WillOnce([&run_loop_3]() { - run_loop_3.Quit(); - }); + EXPECT_CALL(observer, Activate(true, testing::_, testing::_)) + .WillOnce([&run_loop_3]() { run_loop_3.Quit(); }); controller->ShowImmersiveUI(ReadAnythingOpenTrigger::kOmniboxChip); run_loop_3.Run(); @@ -268,9 +269,42 @@ // Close it base::RunLoop run_loop; - EXPECT_CALL(observer, Activate(false, testing::_)).WillOnce([&run_loop]() { - run_loop.Quit(); - }); + EXPECT_CALL(observer, Activate(false, testing::_, testing::_)) + .WillOnce([&run_loop]() { run_loop.Quit(); }); + controller->CloseImmersiveUI(ReadAnythingCloseReason::kClosedByUser); + run_loop.Run(); + + // Cleanup + controller->RemoveObserver(&observer); +} + +IN_PROC_BROWSER_TEST_F(ReadAnythingControllerBrowserTest, + CloseImmersiveUI_NotifiesObserversWithDuration) { + tabs::TabInterface* tab = browser()->tab_strip_model()->GetActiveTab(); + ASSERT_TRUE(tab); + auto* controller = ReadAnythingController::From(tab); + ASSERT_TRUE(controller); + + MockReadAnythingLifecycleObserver observer; + controller->AddObserver(&observer); + + base::RunLoop show_loop; + EXPECT_CALL(observer, Activate(true, testing::_, testing::_)) + .WillOnce([&show_loop]() { show_loop.Quit(); }); + + // Show it first + controller->ShowImmersiveUI(ReadAnythingOpenTrigger::kOmniboxChip); + show_loop.Run(); + + // Close it + base::RunLoop run_loop; + EXPECT_CALL(observer, Activate(false, testing::_, testing::_)) + .WillOnce([&run_loop]( + bool active, std::optional<ReadAnythingOpenTrigger> trigger, + std::optional<base::TimeDelta> completed_session_duration) { + EXPECT_TRUE(completed_session_duration.has_value()); + run_loop.Quit(); + }); controller->CloseImmersiveUI(ReadAnythingCloseReason::kClosedByUser); run_loop.Run(); @@ -1845,6 +1879,71 @@ ReadAnythingOpenTrigger::kOmniboxChip, 1); } +IN_PROC_BROWSER_TEST_F(ReadAnythingControllerBrowserTest, + OpenURLFromTab_InImmersiveMode_OpensNewTab) { + // Navigate to a page to have content to select. + GURL url(embedded_test_server()->GetURL("/simple.html")); + ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), url)); + + // Get controller. + tabs::TabInterface* tab = browser()->tab_strip_model()->GetActiveTab(); + ASSERT_TRUE(tab); + auto* controller = ReadAnythingController::From(tab); + ASSERT_TRUE(controller); + + // Show immersive reading mode. + controller->ShowImmersiveUI(ReadAnythingOpenTrigger::kOmniboxChip); + AwaitAndAssertOverlayVisibility(/*visible=*/true); + + // Get the immersive web contents. + content::WebContents* immersive_web_contents = GetImmersiveWebContents(); + ASSERT_TRUE(immersive_web_contents); + + // Wait for the web contents to be ready. + EXPECT_TRUE(content::WaitForLoadStop(immersive_web_contents)); + + // Simulate selecting text. + ASSERT_TRUE(content::ExecJs(immersive_web_contents, + "const p = document.createElement('p');" + "p.textContent = 'test';" + "document.body.appendChild(p);" + "const range = document.createRange();" + "range.selectNodeContents(p);" + "const selection = window.getSelection();" + "selection.removeAllRanges();" + "selection.addRange(range);")); + + // Simulate "Search Google for" action. + content::ContextMenuParams params; + params.page_url = GURL("http://example.com"); + params.selection_text = u"test"; + + TestRenderViewContextMenu menu(*immersive_web_contents->GetPrimaryMainFrame(), + params); + menu.Init(); + + // Add a waiter for the new tab. + ui_test_utils::TabAddedWaiter tab_waiter(browser()); + menu.ExecuteCommand(IDC_CONTENT_CONTEXT_SEARCHWEBFOR, 0); + tab_waiter.Wait(); + + // Verify the new tab was opened with the correct search args. + EXPECT_EQ(2, browser()->tab_strip_model()->count()); + content::WebContents* new_tab = + browser()->tab_strip_model()->GetWebContentsAt(1); + + TemplateURLService* template_url_service = + TemplateURLServiceFactory::GetForProfile(browser()->profile()); + const TemplateURL* default_provider = + template_url_service->GetDefaultSearchProvider(); + ASSERT_TRUE(default_provider); + TemplateURLRef::SearchTermsArgs search_args(u"test"); + GURL expected_url(default_provider->url_ref().ReplaceSearchTerms( + search_args, template_url_service->search_terms_data())); + + EXPECT_EQ(expected_url, new_tab->GetURL()); +} + IN_PROC_BROWSER_TEST_F( ReadAnythingControllerBrowserTest, HandleKeyboardEvent_WhenFullscreenInImmersiveMode_EscapeClosesFullscreen) {
diff --git a/chrome/browser/ui/read_anything/read_anything_immersive_web_view.cc b/chrome/browser/ui/read_anything/read_anything_immersive_web_view.cc index afb7db6..76bc71b 100644 --- a/chrome/browser/ui/read_anything/read_anything_immersive_web_view.cc +++ b/chrome/browser/ui/read_anything/read_anything_immersive_web_view.cc
@@ -53,6 +53,25 @@ return false; } +// content::WebContentsDelegate: +// This is called when the WebContents wants to open a link in a new tab, +// such as with the "Search Google for" context menu option. We forward the +// request to the main browser window to handle the navigation. +content::WebContents* ReadAnythingImmersiveWebView::OpenURLFromTab( + content::WebContents* source, + const content::OpenURLParams& params, + base::OnceCallback<void(content::NavigationHandle&)> + navigation_handle_callback) { + auto* controller = + ReadAnythingControllerGlue::FromWebContents(web_contents())->controller(); + if (controller && controller->tab() && + controller->tab()->GetBrowserWindowInterface()) { + return controller->tab()->GetBrowserWindowInterface()->OpenURL( + params, std::move(navigation_handle_callback)); + } + return nullptr; +} + bool ReadAnythingImmersiveWebView::HandleKeyboardEvent( content::WebContents* source, const input::NativeWebKeyboardEvent& event) {
diff --git a/chrome/browser/ui/read_anything/read_anything_immersive_web_view.h b/chrome/browser/ui/read_anything/read_anything_immersive_web_view.h index 8ac4703..a775093 100644 --- a/chrome/browser/ui/read_anything/read_anything_immersive_web_view.h +++ b/chrome/browser/ui/read_anything/read_anything_immersive_web_view.h
@@ -48,6 +48,11 @@ const content::ContextMenuParams& params) override; bool HandleKeyboardEvent(content::WebContents* source, const input::NativeWebKeyboardEvent& event) override; + content::WebContents* OpenURLFromTab( + content::WebContents* source, + const content::OpenURLParams& params, + base::OnceCallback<void(content::NavigationHandle&)> + navigation_handle_callback) override; void FindReply(content::WebContents* web_contents, int request_id, int number_of_matches,
diff --git a/chrome/browser/ui/read_anything/read_anything_lifecycle_observer.h b/chrome/browser/ui/read_anything/read_anything_lifecycle_observer.h index 9f9bbb6..5f206326 100644 --- a/chrome/browser/ui/read_anything/read_anything_lifecycle_observer.h +++ b/chrome/browser/ui/read_anything/read_anything_lifecycle_observer.h
@@ -5,18 +5,31 @@ #ifndef CHROME_BROWSER_UI_READ_ANYTHING_READ_ANYTHING_LIFECYCLE_OBSERVER_H_ #define CHROME_BROWSER_UI_READ_ANYTHING_READ_ANYTHING_LIFECYCLE_OBSERVER_H_ -#include "base/observer_list_types.h" -#include "chrome/browser/ui/read_anything/read_anything_enums.h" - #include <optional> +#include "base/observer_list_types.h" +#include "base/time/time.h" +#include "chrome/browser/ui/read_anything/read_anything_enums.h" + // Class of events tracking the lifecycle of the Reading Mode application. // Events that track the browser/tab (e.g. TabWillDetach) do not belong in this // class. class ReadAnythingLifecycleObserver : public base::CheckedObserver { public: - virtual void Activate(bool active, - std::optional<ReadAnythingOpenTrigger> trigger) {} + // Invoked when the active state of the Reading Mode application changes. + // `active`: True if the UI is becoming active/shown, false if it is hidden. + // `trigger`: The entry point trigger that initiated the activation (only + // populated when `active` is true). + // `completed_session_duration`: The total duration that Reading Mode was + // visible during the completed session (only + // populated when `active` is false, and not + // during in-progress presentation mode + // transitions or if closed before being + // successfully shown). + virtual void Activate( + bool active, + std::optional<ReadAnythingOpenTrigger> trigger, + std::optional<base::TimeDelta> completed_session_duration) {} virtual void OnDestroyed() = 0; virtual void OnReadingModePresenterChanged() {} virtual void OnWillClose(ReadAnythingCloseReason reason) {}
diff --git a/chrome/browser/ui/read_anything/read_anything_omnibox_controller.cc b/chrome/browser/ui/read_anything/read_anything_omnibox_controller.cc index efa659e0..9fc044b 100644 --- a/chrome/browser/ui/read_anything/read_anything_omnibox_controller.cc +++ b/chrome/browser/ui/read_anything/read_anything_omnibox_controller.cc
@@ -75,7 +75,8 @@ void ReadAnythingOmniboxController::Activate( bool active, - std::optional<ReadAnythingOpenTrigger> open_trigger) { + std::optional<ReadAnythingOpenTrigger> open_trigger, + std::optional<base::TimeDelta> completed_session_duration) { if (active) { if (iph_response_timer_ && iph_response_timer_->IsRunning()) { iph_response_timer_->Stop();
diff --git a/chrome/browser/ui/read_anything/read_anything_omnibox_controller.h b/chrome/browser/ui/read_anything/read_anything_omnibox_controller.h index 78d61ae..b3ff368 100644 --- a/chrome/browser/ui/read_anything/read_anything_omnibox_controller.h +++ b/chrome/browser/ui/read_anything/read_anything_omnibox_controller.h
@@ -33,8 +33,10 @@ void DidStopLoading() override; // ReadAnythingLifecycleObserver: - void Activate(bool active, - std::optional<ReadAnythingOpenTrigger> open_trigger) override; + void Activate( + bool active, + std::optional<ReadAnythingOpenTrigger> open_trigger, + std::optional<base::TimeDelta> completed_session_duration) override; void OnDestroyed() override; void OnReadingModePresenterChanged() override; void OnWillClose(ReadAnythingCloseReason reason) override;
diff --git a/chrome/browser/ui/read_anything/read_anything_side_panel_controller.cc b/chrome/browser/ui/read_anything/read_anything_side_panel_controller.cc index d6f3c3f..103e08b 100644 --- a/chrome/browser/ui/read_anything/read_anything_side_panel_controller.cc +++ b/chrome/browser/ui/read_anything/read_anything_side_panel_controller.cc
@@ -17,7 +17,6 @@ #include "chrome/browser/language/language_model_manager_factory.h" #include "chrome/browser/profiles/profile.h" #include "chrome/browser/ui/actions/chrome_action_id.h" -#include "chrome/browser/ui/browser_finder.h" #include "chrome/browser/ui/browser_window/public/browser_window_features.h" #include "chrome/browser/ui/page_action/page_action_observer.h" #include "chrome/browser/ui/read_anything/read_anything_controller.h" @@ -191,7 +190,9 @@ CHECK(controller); controller->OnEntryShown(read_anything_trigger); } else { - observers_.Notify(&Observer::Activate, true, read_anything_trigger); + observers_.Notify(&Observer::Activate, /*active=*/true, + read_anything_trigger, + /*completed_session_duration=*/std::nullopt); } } @@ -227,8 +228,9 @@ CHECK(controller); controller->OnEntryHidden(); } else { - observers_.Notify(&Observer::Activate, false, - std::optional<ReadAnythingOpenTrigger>()); + observers_.Notify(&Observer::Activate, /*active=*/false, + /*trigger=*/std::optional<ReadAnythingOpenTrigger>(), + /*completed_session_duration=*/std::nullopt); } // When the reading mode side panel is replaced with another side panel,
diff --git a/chrome/browser/ui/read_anything/read_anything_side_panel_controller_interactive_uitest.cc b/chrome/browser/ui/read_anything/read_anything_side_panel_controller_interactive_uitest.cc index ba3f606d..2fbd93f 100644 --- a/chrome/browser/ui/read_anything/read_anything_side_panel_controller_interactive_uitest.cc +++ b/chrome/browser/ui/read_anything/read_anything_side_panel_controller_interactive_uitest.cc
@@ -47,7 +47,9 @@ public: MOCK_METHOD(void, Activate, - (bool active, std::optional<ReadAnythingOpenTrigger>), + (bool active, + std::optional<ReadAnythingOpenTrigger>, + std::optional<base::TimeDelta>), (override)); MOCK_METHOD(void, OnDestroyed, (), (override)); }; @@ -144,8 +146,10 @@ entry->set_last_open_trigger(SidePanelOpenTrigger::kReadAnythingOmniboxChip); EXPECT_CALL(read_anything_observer_, - Activate(true, std::optional<ReadAnythingOpenTrigger>( - ReadAnythingOpenTrigger::kOmniboxChip))) + Activate(true, + std::optional<ReadAnythingOpenTrigger>( + ReadAnythingOpenTrigger::kOmniboxChip), + testing::_)) .Times(1); OnEntryShown(entry); } @@ -158,7 +162,8 @@ ->GetEntryForKey( SidePanelEntry::Key(SidePanelEntry::Id::kReadAnything)); - EXPECT_CALL(read_anything_observer_, Activate(false, empty_trigger())) + EXPECT_CALL(read_anything_observer_, + Activate(false, empty_trigger(), testing::_)) .Times(1); OnEntryHidden(entry); }
diff --git a/chrome/browser/ui/read_anything/read_anything_side_panel_navigation_throttle.cc b/chrome/browser/ui/read_anything/read_anything_side_panel_navigation_throttle.cc index 861deaab..6480224 100644 --- a/chrome/browser/ui/read_anything/read_anything_side_panel_navigation_throttle.cc +++ b/chrome/browser/ui/read_anything/read_anything_side_panel_navigation_throttle.cc
@@ -8,7 +8,6 @@ #include "base/memory/ptr_util.h" #include "chrome/browser/ui/browser.h" -#include "chrome/browser/ui/browser_finder.h" #include "chrome/browser/ui/read_anything/read_anything_entry_point_controller.h" #include "chrome/browser/ui/read_anything/read_anything_side_panel_controller_utils.h" #include "chrome/browser/ui/side_panel/side_panel_enums.h"
diff --git a/chrome/browser/ui/search/third_party_ntp_browsertest.cc b/chrome/browser/ui/search/third_party_ntp_browsertest.cc index b131d68..9f0e6eed 100644 --- a/chrome/browser/ui/search/third_party_ntp_browsertest.cc +++ b/chrome/browser/ui/search/third_party_ntp_browsertest.cc
@@ -16,6 +16,7 @@ #include "chrome/test/base/ui_test_utils.h" #include "content/public/browser/render_frame_host.h" #include "content/public/browser/render_process_host.h" +#include "content/public/browser/security_principal.h" #include "content/public/browser/spare_render_process_host_manager.h" #include "content/public/browser/web_contents.h" #include "content/public/test/browser_test.h" @@ -361,9 +362,11 @@ EXPECT_EQ(ntp_url, content::EvalJs(web_contents, "window.location.href")); // Verify that NTP committed in remote NTP SiteInstance. - EXPECT_EQ( - GURL("chrome-search://remote-ntp/"), - web_contents->GetPrimaryMainFrame()->GetSiteInstance()->GetSiteURL()); + EXPECT_EQ(GURL("chrome-search://remote-ntp/"), + web_contents->GetPrimaryMainFrame() + ->GetSiteInstance() + ->GetSecurityPrincipal() + .GetDeprecatedSiteURL()); } // Verify that a third-party NTP can use the spare renderer when we enable
diff --git a/chrome/browser/ui/send_tab_to_self/send_tab_to_self_context_menu_delegate.cc b/chrome/browser/ui/send_tab_to_self/send_tab_to_self_context_menu_delegate.cc index 2e185b1..9c33499 100644 --- a/chrome/browser/ui/send_tab_to_self/send_tab_to_self_context_menu_delegate.cc +++ b/chrome/browser/ui/send_tab_to_self/send_tab_to_self_context_menu_delegate.cc
@@ -14,7 +14,6 @@ #include "chrome/browser/send_tab_to_self/send_tab_to_self_page_handler.h" #include "chrome/browser/sync/send_tab_to_self_sync_service_factory.h" #include "chrome/browser/ui/browser.h" -#include "chrome/browser/ui/browser_finder.h" #include "chrome/browser/ui/navigator/browser_navigator.h" #include "chrome/browser/ui/navigator/browser_navigator_params.h" #include "chrome/browser/ui/send_tab_to_self/send_tab_to_self_util.h"
diff --git a/chrome/browser/ui/settings_window_manager_browsertest_chromeos.cc b/chrome/browser/ui/settings_window_manager_browsertest_chromeos.cc index 710c245..b6d089eb 100644 --- a/chrome/browser/ui/settings_window_manager_browsertest_chromeos.cc +++ b/chrome/browser/ui/settings_window_manager_browsertest_chromeos.cc
@@ -20,7 +20,6 @@ #include "chrome/browser/ash/system_web_apps/system_web_app_manager.h" #include "chrome/browser/ui/ash/system_web_apps/system_web_app_ui_utils.h" #include "chrome/browser/ui/browser.h" -#include "chrome/browser/ui/browser_finder.h" #include "chrome/browser/ui/browser_window.h" #include "chrome/browser/ui/browser_window/public/browser_window_interface.h" #include "chrome/browser/ui/browser_window/public/browser_window_interface_iterator.h"
diff --git a/chrome/browser/ui/side_panel_container/internal/BUILD.gn b/chrome/browser/ui/side_panel_container/internal/BUILD.gn index 98df37c..a2ce76c 100644 --- a/chrome/browser/ui/side_panel_container/internal/BUILD.gn +++ b/chrome/browser/ui/side_panel_container/internal/BUILD.gn
@@ -78,8 +78,11 @@ android_resources("java_resources") { sources = [ "android/java/res/drawable/side_panel_container_bg.xml", + "android/java/res/drawable/side_panel_container_stroke.xml", "android/java/res/layout/side_panel_container.xml", "android/java/res/layout/side_panel_dev_feature.xml", + "android/java/res/values/colors.xml", + "android/java/res/values/dimens.xml", ] }
diff --git a/chrome/browser/ui/side_panel_container/internal/android/java/res/drawable/side_panel_container_bg.xml b/chrome/browser/ui/side_panel_container/internal/android/java/res/drawable/side_panel_container_bg.xml index 3007b596..cc9bf52 100644 --- a/chrome/browser/ui/side_panel_container/internal/android/java/res/drawable/side_panel_container_bg.xml +++ b/chrome/browser/ui/side_panel_container/internal/android/java/res/drawable/side_panel_container_bg.xml
@@ -5,8 +5,19 @@ found in the LICENSE file. --> -<shape +<inset xmlns:android="http://schemas.android.com/apk/res/android" - android:shape="rectangle"> - <solid android:color="@macro/default_bg_color" /> -</shape> + android:insetLeft="@dimen/side_ui_container_padding" + android:insetTop="@dimen/side_ui_container_padding" + android:insetRight="@dimen/side_ui_container_padding" + android:insetBottom="@dimen/side_ui_container_padding"> + <shape + android:shape="rectangle"> + <solid android:color="@macro/default_bg_color" /> + <corners android:radius="@dimen/side_panel_corner_radius" /> + <padding android:left="@dimen/side_panel_hairline_width" + android:top="@dimen/side_panel_hairline_width" + android:right="@dimen/side_panel_hairline_width" + android:bottom="@dimen/side_panel_hairline_width"/> + </shape> +</inset>
diff --git a/chrome/browser/ui/side_panel_container/internal/android/java/res/drawable/side_panel_container_stroke.xml b/chrome/browser/ui/side_panel_container/internal/android/java/res/drawable/side_panel_container_stroke.xml new file mode 100644 index 0000000..4e69d114b --- /dev/null +++ b/chrome/browser/ui/side_panel_container/internal/android/java/res/drawable/side_panel_container_stroke.xml
@@ -0,0 +1,20 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +Copyright 2026 The Chromium Authors +Use of this source code is governed by a BSD-style license that can be +found in the LICENSE file. +--> + +<inset xmlns:android="http://schemas.android.com/apk/res/android" + android:insetLeft="@dimen/side_ui_container_padding" + android:insetTop="@dimen/side_ui_container_padding" + android:insetRight="@dimen/side_ui_container_padding" + android:insetBottom="@dimen/side_ui_container_padding"> + <shape + android:shape="rectangle"> + <corners android:radius="@dimen/side_panel_corner_radius" /> + <stroke + android:width="@dimen/side_panel_hairline_width" + android:color="@color/side_panel_hairline_color" /> + </shape> +</inset>
diff --git a/chrome/browser/ui/side_panel_container/internal/android/java/res/layout/side_panel_container.xml b/chrome/browser/ui/side_panel_container/internal/android/java/res/layout/side_panel_container.xml index 9ce5641..01962fd 100644 --- a/chrome/browser/ui/side_panel_container/internal/android/java/res/layout/side_panel_container.xml +++ b/chrome/browser/ui/side_panel_container/internal/android/java/res/layout/side_panel_container.xml
@@ -18,4 +18,6 @@ android:id="@+id/side_panel_container" android:layout_width="0dp" android:layout_height="match_parent" - android:background="@drawable/side_panel_container_bg" /> + android:background="@drawable/side_panel_container_bg" + android:foreground="@drawable/side_panel_container_stroke" + android:clipToOutline="true" />
diff --git a/chrome/browser/ui/side_panel_container/internal/android/java/res/values/colors.xml b/chrome/browser/ui/side_panel_container/internal/android/java/res/values/colors.xml new file mode 100644 index 0000000..5bddc648 --- /dev/null +++ b/chrome/browser/ui/side_panel_container/internal/android/java/res/values/colors.xml
@@ -0,0 +1,10 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +Copyright 2026 The Chromium Authors +Use of this source code is governed by a BSD-style license that can be +found in the LICENSE file. +--> + +<resources> + <color name="side_panel_hairline_color">@color/divider_color</color> +</resources>
diff --git a/chrome/browser/ui/side_panel_container/internal/android/java/res/values/dimens.xml b/chrome/browser/ui/side_panel_container/internal/android/java/res/values/dimens.xml new file mode 100644 index 0000000..aa7dfb4 --- /dev/null +++ b/chrome/browser/ui/side_panel_container/internal/android/java/res/values/dimens.xml
@@ -0,0 +1,12 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +Copyright 2026 The Chromium Authors +Use of this source code is governed by a BSD-style license that can be +found in the LICENSE file. +--> + +<resources> + <dimen name="side_panel_corner_radius">12dp</dimen> + <dimen name="side_panel_hairline_width">1dp</dimen> + <dimen name="side_ui_container_padding">6dp</dimen> +</resources>
diff --git a/chrome/browser/ui/side_ui/internal/BUILD.gn b/chrome/browser/ui/side_ui/internal/BUILD.gn index 75846da6..0273424 100644 --- a/chrome/browser/ui/side_ui/internal/BUILD.gn +++ b/chrome/browser/ui/side_ui/internal/BUILD.gn
@@ -46,5 +46,6 @@ "//third_party/androidx:androidx_annotation_annotation_java", "//third_party/junit", "//third_party/mockito:mockito_java", + "//ui/android:ui_java_test_support", ] }
diff --git a/chrome/browser/ui/side_ui/internal/android/java/src/org/chromium/chrome/browser/ui/side_ui/SideUiCoordinatorImplTest.java b/chrome/browser/ui/side_ui/internal/android/java/src/org/chromium/chrome/browser/ui/side_ui/SideUiCoordinatorImplTest.java index 0eb1e463..ffcddb6 100644 --- a/chrome/browser/ui/side_ui/internal/android/java/src/org/chromium/chrome/browser/ui/side_ui/SideUiCoordinatorImplTest.java +++ b/chrome/browser/ui/side_ui/internal/android/java/src/org/chromium/chrome/browser/ui/side_ui/SideUiCoordinatorImplTest.java
@@ -40,6 +40,7 @@ import org.chromium.chrome.browser.ui.side_ui.SideUiCoordinator.AnchorSide; import org.chromium.chrome.browser.ui.side_ui.SideUiCoordinator.SideUiContainerProperties; import org.chromium.chrome.browser.ui.side_ui.SideUiCoordinator.SideUiSpecs; +import org.chromium.ui.base.TestActivity; /** Unit tests for {@link SideUiCoordinatorImpl}. */ @RunWith(BaseRobolectricTestRunner.class) @@ -66,7 +67,7 @@ @Before public void setUp() { - Activity activity = Robolectric.buildActivity(Activity.class).setup().get(); + Activity activity = Robolectric.buildActivity(TestActivity.class).setup().get(); // Set up the parent View of side UI anchor containers. mAnchorContainerParent = new FrameLayout(activity);
diff --git a/chrome/browser/ui/side_ui/public/android/java/res/layout/side_ui_anchor_container.xml b/chrome/browser/ui/side_ui/public/android/java/res/layout/side_ui_anchor_container.xml index db7d586..0be8b87 100644 --- a/chrome/browser/ui/side_ui/public/android/java/res/layout/side_ui_anchor_container.xml +++ b/chrome/browser/ui/side_ui/public/android/java/res/layout/side_ui_anchor_container.xml
@@ -9,4 +9,5 @@ xmlns:android="http://schemas.android.com/apk/res/android" android:layout_height="match_parent" android:layout_width="wrap_content" - android:visibility="gone" /> + android:visibility="gone" + android:background="@macro/default_bg_color"/>
diff --git a/chrome/browser/ui/startup/BUILD.gn b/chrome/browser/ui/startup/BUILD.gn index d2f0587..cb92f8c 100644 --- a/chrome/browser/ui/startup/BUILD.gn +++ b/chrome/browser/ui/startup/BUILD.gn
@@ -144,6 +144,7 @@ "//chrome/browser/signin", "//chrome/browser/sync", "//chrome/browser/sync:factories", + "//chrome/browser/ui:simple_message_box_headers", "//chrome/browser/ui/dialogs", "//chrome/browser/ui/profiles", "//chrome/browser/webauthn",
diff --git a/chrome/browser/ui/startup/startup_browser_creator_browsertest.cc b/chrome/browser/ui/startup/startup_browser_creator_browsertest.cc index 2d945acc..3888a9c 100644 --- a/chrome/browser/ui/startup/startup_browser_creator_browsertest.cc +++ b/chrome/browser/ui/startup/startup_browser_creator_browsertest.cc
@@ -60,7 +60,6 @@ #include "chrome/browser/signin/signin_promo.h" #include "chrome/browser/signin/signin_util.h" #include "chrome/browser/ui/browser_commands.h" -#include "chrome/browser/ui/browser_finder.h" #include "chrome/browser/ui/browser_window.h" #include "chrome/browser/ui/browser_window/public/browser_collection_observer.h" #include "chrome/browser/ui/browser_window/public/browser_window_features.h"
diff --git a/chrome/browser/ui/startup/startup_browser_creator_interactive_uitest.cc b/chrome/browser/ui/startup/startup_browser_creator_interactive_uitest.cc index 8bb214b..8201a11b 100644 --- a/chrome/browser/ui/startup/startup_browser_creator_interactive_uitest.cc +++ b/chrome/browser/ui/startup/startup_browser_creator_interactive_uitest.cc
@@ -18,7 +18,6 @@ #include "chrome/browser/profiles/profile_test_util.h" #include "chrome/browser/sessions/session_restore.h" #include "chrome/browser/ui/browser.h" -#include "chrome/browser/ui/browser_finder.h" #include "chrome/browser/ui/browser_window.h" #include "chrome/browser/ui/browser_window/public/browser_window_interface.h" #include "chrome/browser/ui/browser_window/public/profile_browser_collection.h"
diff --git a/chrome/browser/ui/tab_contents/BUILD.gn b/chrome/browser/ui/tab_contents/BUILD.gn index 7d61c9f..f90e7c12 100644 --- a/chrome/browser/ui/tab_contents/BUILD.gn +++ b/chrome/browser/ui/tab_contents/BUILD.gn
@@ -77,7 +77,9 @@ "//ui/base/clipboard:file_info", "//ui/gfx/codec", ] - if (!is_android) { + if (is_android) { + deps += [ "//chrome/browser/ui/android/tab_contents" ] + } else { deps += [ "//chrome/browser/glic", "//chrome/browser/ui/views/side_panel",
diff --git a/chrome/browser/ui/tabs/features.cc b/chrome/browser/ui/tabs/features.cc index bd72be0..a7cd0e9a 100644 --- a/chrome/browser/ui/tabs/features.cc +++ b/chrome/browser/ui/tabs/features.cc
@@ -16,6 +16,13 @@ BASE_FEATURE(kSessionRestoreShowThrobberOnVisible, base::FEATURE_DISABLED_BY_DEFAULT); +BASE_FEATURE(kSplitViewHorizontal, base::FEATURE_DISABLED_BY_DEFAULT); +BASE_FEATURE_PARAM(bool, + kSplitViewHorizontalDirectAccess, + &kSplitViewHorizontal, + "split_view_horizontal_direct_access", + false); + BASE_FEATURE(kSplitViewTabRestore, base::FEATURE_DISABLED_BY_DEFAULT); BASE_FEATURE(kVerticalTabs, base::FEATURE_DISABLED_BY_DEFAULT);
diff --git a/chrome/browser/ui/tabs/features.h b/chrome/browser/ui/tabs/features.h index 214d878..2603e02 100644 --- a/chrome/browser/ui/tabs/features.h +++ b/chrome/browser/ui/tabs/features.h
@@ -21,6 +21,12 @@ // to be broken crbug.com/413080225#comment8). BASE_DECLARE_FEATURE(kSessionRestoreShowThrobberOnVisible); +// Allows split tabs to be arranged top/bottom. +BASE_DECLARE_FEATURE(kSplitViewHorizontal); +// When enabled, creating a new split tab through the context menu will open a +// submenu to select the split's orientation. +BASE_DECLARE_FEATURE_PARAM(bool, kSplitViewHorizontalDirectAccess); + // Whether or not a split view should restore together. BASE_DECLARE_FEATURE(kSplitViewTabRestore);
diff --git a/chrome/browser/ui/tabs/tab_list_bridge.cc b/chrome/browser/ui/tabs/tab_list_bridge.cc index 4869e104..4521509 100644 --- a/chrome/browser/ui/tabs/tab_list_bridge.cc +++ b/chrome/browser/ui/tabs/tab_list_bridge.cc
@@ -503,7 +503,7 @@ destination_index, std::move(detached_tab), AddTabTypes::ADD_NONE); } -void TabListBridge::MoveTabGroupToWindow(tab_groups::TabGroupId group_id, +bool TabListBridge::MoveTabGroupToWindow(tab_groups::TabGroupId group_id, SessionID destination_window_id, int destination_index) { BrowserWindowInterface* target_window = @@ -517,13 +517,13 @@ static_cast<TabListBridge*>(target_list_interface); auto target_tab_strip = target_bridge->tab_strip_; if (!target_tab_strip->SupportsTabGroups()) { - return; + return false; } // Handle the case where `destination_window_id` points to the same window. if (this == target_bridge) { MoveGroupTo(group_id, destination_index); - return; + return true; } TabGroup* tab_group = tab_strip_->group_model()->GetTabGroup(group_id); @@ -562,6 +562,7 @@ tab_strip_->DetachTabGroupForInsertion(group_id); target_tab_strip->InsertDetachedTabGroupAt(std::move(detached_group), target_index); + return true; } void TabListBridge::OnTabStripModelChanged(
diff --git a/chrome/browser/ui/tabs/tab_list_bridge.h b/chrome/browser/ui/tabs/tab_list_bridge.h index 86c1094..93e915e2 100644 --- a/chrome/browser/ui/tabs/tab_list_bridge.h +++ b/chrome/browser/ui/tabs/tab_list_bridge.h
@@ -89,7 +89,7 @@ void MoveTabToWindow(tabs::TabHandle tab, SessionID destination_window_id, int destination_index) override; - void MoveTabGroupToWindow(tab_groups::TabGroupId group_id, + bool MoveTabGroupToWindow(tab_groups::TabGroupId group_id, SessionID destination_window_id, int destination_index) override; bool IsThisTabListEditable() override;
diff --git a/chrome/browser/ui/tabs/tab_list_bridge_browsertest.cc b/chrome/browser/ui/tabs/tab_list_bridge_browsertest.cc index 099c0032..329a674a 100644 --- a/chrome/browser/ui/tabs/tab_list_bridge_browsertest.cc +++ b/chrome/browser/ui/tabs/tab_list_bridge_browsertest.cc
@@ -1110,8 +1110,8 @@ EXPECT_EQ("0g0 1g0 2", GetTabStripStateString(source_model, /*annotate_groups=*/true)); - source_list_interface->MoveTabGroupToWindow(*group_id, - second_browser->session_id(), 1); + EXPECT_TRUE(source_list_interface->MoveTabGroupToWindow( + *group_id, second_browser->session_id(), 1)); // Verify that the group has been moved to the destination window. EXPECT_EQ("2", @@ -1159,8 +1159,8 @@ // Now move the group to the second window. The group should be moved to the // end since the closest valid index that isn't in the middle of another tab // group is 3. - source_list_interface->MoveTabGroupToWindow(*group_id, - second_browser->session_id(), 2); + EXPECT_TRUE(source_list_interface->MoveTabGroupToWindow( + *group_id, second_browser->session_id(), 2)); EXPECT_EQ("2", GetTabStripStateString(source_model, /*annotate_groups=*/true));
diff --git a/chrome/browser/ui/tabs/tab_menu_model.cc b/chrome/browser/ui/tabs/tab_menu_model.cc index bfd6cfa..a20e456 100644 --- a/chrome/browser/ui/tabs/tab_menu_model.cc +++ b/chrome/browser/ui/tabs/tab_menu_model.cc
@@ -74,6 +74,7 @@ DEFINE_CLASS_ELEMENT_IDENTIFIER_VALUE(TabMenuModel, kArrangeSplitTabsMenuItem); DEFINE_CLASS_ELEMENT_IDENTIFIER_VALUE(TabMenuModel, kSwapSplitTabsMenuItem); DEFINE_CLASS_ELEMENT_IDENTIFIER_VALUE(TabMenuModel, kAddNewTabAdjacentMenuItem); +DEFINE_CLASS_ELEMENT_IDENTIFIER_VALUE(TabMenuModel, kDuplicateMenuItem); TabMenuModel::TabMenuModel(ui::SimpleMenuModel::Delegate* delegate, TabMenuModelDelegate* tab_menu_model_delegate, @@ -275,6 +276,7 @@ AddItemWithStringId(TabStripModel::CommandDuplicate, IDS_TAB_CXMENU_DUPLICATE); + SetElementIdentifierAt(GetItemCount() - 1, kDuplicateMenuItem); bool will_pin = tab_strip_->WillContextMenuPin(index); AddItemWithStringId(
diff --git a/chrome/browser/ui/tabs/tab_menu_model.h b/chrome/browser/ui/tabs/tab_menu_model.h index ef54948..5375e2b 100644 --- a/chrome/browser/ui/tabs/tab_menu_model.h +++ b/chrome/browser/ui/tabs/tab_menu_model.h
@@ -50,6 +50,7 @@ DECLARE_CLASS_ELEMENT_IDENTIFIER_VALUE(kSwapSplitTabsMenuItem); DECLARE_CLASS_ELEMENT_IDENTIFIER_VALUE(kAddNewTabAdjacentMenuItem); DECLARE_CLASS_ELEMENT_IDENTIFIER_VALUE(kAddToNewGroupItemIdentifier); + DECLARE_CLASS_ELEMENT_IDENTIFIER_VALUE(kDuplicateMenuItem); TabMenuModel(ui::SimpleMenuModel::Delegate* delegate, TabMenuModelDelegate* tab_menu_model_delegate,
diff --git a/chrome/browser/ui/tabs/tab_strip_api/android_tab_model_impl/android_browser_adapter_impl.cc b/chrome/browser/ui/tabs/tab_strip_api/android_tab_model_impl/android_browser_adapter_impl.cc index ce9e3b8..bcfca1a 100644 --- a/chrome/browser/ui/tabs/tab_strip_api/android_tab_model_impl/android_browser_adapter_impl.cc +++ b/chrome/browser/ui/tabs/tab_strip_api/android_tab_model_impl/android_browser_adapter_impl.cc
@@ -22,14 +22,21 @@ std::optional<int> index, std::optional<tab_groups::TabGroupId> group, bool pinned) { - if (group.has_value() || pinned || !index.has_value()) { - // TODO(crbug.com/494284032): to implement - NOTREACHED() << "not implemented yet"; + int target_index = index.value_or(model_->GetTabCount()); + + auto* result = model_->OpenTab(url, target_index); + CHECK(result); + tabs::TabHandle handle = result->GetHandle(); + + if (pinned) { + model_->PinTab(handle); } - auto* result = model_->OpenTab(url, index.value()); - CHECK(result); - return result->GetHandle(); + if (group.has_value()) { + model_->AddTabsToGroup(group.value(), {handle}); + } + + return handle; } } // namespace tabs_api
diff --git a/chrome/browser/ui/tabs/tab_strip_api/android_tab_model_impl/android_tab_strip_api_browsertest.cc b/chrome/browser/ui/tabs/tab_strip_api/android_tab_model_impl/android_tab_strip_api_browsertest.cc index efc8e29..b29aa4c 100644 --- a/chrome/browser/ui/tabs/tab_strip_api/android_tab_model_impl/android_tab_strip_api_browsertest.cc +++ b/chrome/browser/ui/tabs/tab_strip_api/android_tab_model_impl/android_tab_strip_api_browsertest.cc
@@ -3,6 +3,7 @@ // found in the LICENSE file. #include "base/strings/string_number_conversions.h" +#include "base/test/gtest_util.h" #include "chrome/browser/ui/android/tab_model/tab_model.h" #include "chrome/browser/ui/tabs/tab_strip_api/android_tab_model_impl/android_tab_strip_api_injector.h" #include "chrome/browser/ui/tabs/tab_strip_api/android_tab_model_impl/android_tab_strip_model_adapter.h" @@ -357,4 +358,101 @@ ASSERT_EQ(group_id, handle1.Get()->GetGroup()); } +IN_PROC_BROWSER_TEST_F(AndroidTabStripApiBrowserTest, CreateAtPosition) { + model_->DuplicateTab(model_->GetTab(0)->GetHandle()); + ASSERT_EQ(2, model_->GetTabCount()); + + auto handle0 = model_->GetTab(0)->GetHandle(); + auto handle1 = model_->GetTab(1)->GetHandle(); + + auto* tab_strip_collection = model_->GetTabStripCollection(GetPassKey()); + tabs_api::Path path( + {NodeId::FromWindowId(base::NumberToString(model_->GetSessionId().id())), + NodeId::FromTabCollectionHandle(tab_strip_collection->GetHandle()), + NodeId::FromTabCollectionHandle( + tab_strip_collection->unpinned_collection()->GetHandle())}); + tabs_api::Position pos(1, path); + + auto result = service_->CreateTabAt(pos, GURL("http://there.where")); + ASSERT_TRUE(result.has_value()); + ASSERT_EQ(3, model_->GetTabCount()); + + ASSERT_EQ(handle0, model_->GetTab(0)->GetHandle()); + ASSERT_EQ(handle1, model_->GetTab(2)->GetHandle()); + + auto new_tab_handle = model_->GetTab(1)->GetHandle(); + ASSERT_NE(handle0, new_tab_handle); + ASSERT_NE(handle1, new_tab_handle); +} + +IN_PROC_BROWSER_TEST_F(AndroidTabStripApiBrowserTest, CreateInGroup) { + model_->DuplicateTab(model_->GetTab(0)->GetHandle()); + ASSERT_EQ(2, model_->GetTabCount()); + + auto handle0 = model_->GetTab(0)->GetHandle(); + + auto group_id = model_->CreateTabGroup({handle0}).value(); + auto* group_collection = model_->GetTabStripCollection(GetPassKey()) + ->GetTabGroupCollection(group_id); + + tabs_api::Path path( + {NodeId::FromWindowId(base::NumberToString(model_->GetSessionId().id())), + NodeId::FromTabCollectionHandle( + model_->GetTabStripCollection(GetPassKey())->GetHandle()), + NodeId::FromTabCollectionHandle(group_collection->GetHandle())}); + tabs_api::Position pos(1, path); + + auto result = service_->CreateTabAt(pos, GURL("http://there.where")); + ASSERT_TRUE(result.has_value()); + ASSERT_EQ(3, model_->GetTabCount()); + + auto new_tab_handle = model_->GetTab(1)->GetHandle(); + ASSERT_EQ(group_id, new_tab_handle.Get()->GetGroup()); +} + +IN_PROC_BROWSER_TEST_F(AndroidTabStripApiBrowserTest, CreatePinned) { + auto* tab_strip_collection = model_->GetTabStripCollection(GetPassKey()); + auto* pinned_collection = tab_strip_collection->pinned_collection(); + + tabs_api::Path path( + {NodeId::FromWindowId(base::NumberToString(model_->GetSessionId().id())), + NodeId::FromTabCollectionHandle(tab_strip_collection->GetHandle()), + NodeId::FromTabCollectionHandle(pinned_collection->GetHandle())}); + tabs_api::Position pos(0, path); + + auto result = service_->CreateTabAt(pos, GURL("http://there.where")); + ASSERT_TRUE(result.has_value()); + + ASSERT_EQ(2, model_->GetTabCount()); + ASSERT_TRUE(model_->GetTab(0)->IsPinned()); +} + +IN_PROC_BROWSER_TEST_F(AndroidTabStripApiBrowserTest, CreateWithoutPosition) { + auto* tab_strip_collection = model_->GetTabStripCollection(GetPassKey()); + auto* unpinned_collection = tab_strip_collection->unpinned_collection(); + + tabs_api::Position pos(0, tabs_api::Path()); + + auto result = service_->CreateTabAt(pos, GURL("http://there.where")); + ASSERT_TRUE(result.has_value()); + + ASSERT_EQ(2, model_->GetTabCount()); + ASSERT_EQ(2ul, unpinned_collection->ChildCount()); +} + +IN_PROC_BROWSER_TEST_F(AndroidTabStripApiBrowserTest, CreateUnsupported) { + auto* tab_strip_collection = model_->GetTabStripCollection(GetPassKey()); + + tabs_api::Path bad_path_with_tabstrip( + {NodeId::FromWindowId(base::NumberToString(model_->GetSessionId().id())), + NodeId::FromTabCollectionHandle(tab_strip_collection->GetHandle())}); + EXPECT_DEATH_IF_SUPPORTED( + { + std::ignore = + service_->CreateTabAt(tabs_api::Position(0, bad_path_with_tabstrip), + GURL("http://there.where")); + }, + "Unsupported collection type for insertion"); +} + } // namespace tabs_api
diff --git a/chrome/browser/ui/tabs/tab_strip_api/android_tab_model_impl/android_tab_strip_model_adapter.cc b/chrome/browser/ui/tabs/tab_strip_api/android_tab_model_impl/android_tab_strip_model_adapter.cc index 22db626f..2738d1c 100644 --- a/chrome/browser/ui/tabs/tab_strip_api/android_tab_model_impl/android_tab_strip_model_adapter.cc +++ b/chrome/browser/ui/tabs/tab_strip_api/android_tab_model_impl/android_tab_strip_model_adapter.cc
@@ -325,10 +325,52 @@ InsertionParams AndroidTabStripModelAdapter::CalculateInsertionParams( const std::optional<tabs_api::Position>& pos) const { - // TODO(crbug.com/494284032): hardcoded to always insert at the end for now. - return {.index = model_->GetTabCount(), - .group_id = std::nullopt, - .pinned = false}; + if (!pos.has_value()) { + return {.index = model_->GetTabCount(), + .group_id = std::nullopt, + .pinned = false}; + } + + const auto& position = pos.value(); + NodeId parent_id; + if (position.path().components().empty()) { + parent_id = NodeId::FromTabCollectionHandle( + root_->unpinned_collection()->GetHandle()); + } else { + parent_id = position.path().components().back(); + } + + std::optional<tabs::TabCollectionHandle> collection_handle = + parent_id.ToTabCollectionHandle(); + CHECK(collection_handle.has_value()); + const tabs::TabCollection* collection = collection_handle.value().Get(); + + // Assuming that pinned tabs are always at the start of tab lists. + int index = 0; + std::optional<tab_groups::TabGroupId> group_id; + + switch (collection->type()) { + case tabs::TabCollection::Type::PINNED: + index = position.index(); + break; + case tabs::TabCollection::Type::UNPINNED: + index = root_->IndexOfFirstNonPinnedTab() + position.index(); + break; + + case tabs::TabCollection::Type::GROUP: + group_id = FindGroupIdFor(collection_handle.value()); + CHECK(group_id.has_value()); + index = model_->GetTabGroupTabIndices(group_id.value()).start() + + position.index(); + break; + + case tabs::TabCollection::Type::TABSTRIP: + case tabs::TabCollection::Type::SPLIT: + NOTREACHED() << "Unsupported collection type for insertion"; + } + + bool pinned = (collection->type() == tabs::TabCollection::Type::PINNED); + return {.index = index, .group_id = group_id, .pinned = pinned}; } base::expected<void, mojo_base::mojom::ErrorPtr>
diff --git a/chrome/browser/ui/tabs/tab_strip_api/tab_strip_model_impl/tab_context_menu_adapter_impl.cc b/chrome/browser/ui/tabs/tab_strip_api/tab_strip_model_impl/tab_context_menu_adapter_impl.cc index c6bb32c5..6beb258 100644 --- a/chrome/browser/ui/tabs/tab_strip_api/tab_strip_model_impl/tab_context_menu_adapter_impl.cc +++ b/chrome/browser/ui/tabs/tab_strip_api/tab_strip_model_impl/tab_context_menu_adapter_impl.cc
@@ -39,7 +39,7 @@ } context_menu_controller_ = - std::make_unique<TabContextMenuController>(tab_index, this); + std::make_unique<TabContextMenuController>(handle, this); auto menu_model = std::make_unique<TabMenuModel>( context_menu_controller_.get(), @@ -61,9 +61,10 @@ } bool TabContextMenuAdapterImpl::IsContextMenuCommandEnabled( - int index, + tabs::TabInterface* tab, TabStripModel::ContextMenuCommand command_id) { - return tab_strip_model_->IsContextMenuCommandEnabled(index, command_id); + return tab_strip_model_->IsContextMenuCommandEnabled( + tab_strip_model_->GetIndexOfTab(tab), command_id); } bool TabContextMenuAdapterImpl::IsContextMenuCommandAlerted( @@ -72,10 +73,11 @@ } void TabContextMenuAdapterImpl::ExecuteContextMenuCommand( - int index, + tabs::TabInterface* tab, TabStripModel::ContextMenuCommand command_id, int event_flags) { - tab_strip_model_->ExecuteContextMenuCommand(index, command_id); + tab_strip_model_->ExecuteContextMenuCommand( + tab_strip_model_->GetIndexOfTab(tab), command_id); } bool TabContextMenuAdapterImpl::GetContextMenuAccelerator(
diff --git a/chrome/browser/ui/tabs/tab_strip_api/tab_strip_model_impl/tab_context_menu_adapter_impl.h b/chrome/browser/ui/tabs/tab_strip_api/tab_strip_model_impl/tab_context_menu_adapter_impl.h index 8d0a3138..c9be7bb 100644 --- a/chrome/browser/ui/tabs/tab_strip_api/tab_strip_model_impl/tab_context_menu_adapter_impl.h +++ b/chrome/browser/ui/tabs/tab_strip_api/tab_strip_model_impl/tab_context_menu_adapter_impl.h
@@ -36,11 +36,11 @@ bool IsContextMenuCommandChecked( TabStripModel::ContextMenuCommand command_id) override; bool IsContextMenuCommandEnabled( - int index, + tabs::TabInterface* tab, TabStripModel::ContextMenuCommand command_id) override; bool IsContextMenuCommandAlerted( TabStripModel::ContextMenuCommand command_id) override; - void ExecuteContextMenuCommand(int index, + void ExecuteContextMenuCommand(tabs::TabInterface* tab, TabStripModel::ContextMenuCommand command_id, int event_flags) override; bool GetContextMenuAccelerator(int command_id,
diff --git a/chrome/browser/ui/tabs/tab_strip_model.h b/chrome/browser/ui/tabs/tab_strip_model.h index f1a0f0e..b503cc9 100644 --- a/chrome/browser/ui/tabs/tab_strip_model.h +++ b/chrome/browser/ui/tabs/tab_strip_model.h
@@ -29,7 +29,6 @@ #include "chrome/browser/ui/tabs/tab_strip_model_selection_state.h" #include "chrome/browser/ui/tabs/tab_strip_scrubbing_metrics.h" #include "chrome/browser/ui/tabs/tab_strip_user_gesture_details.h" -#include "chrome/common/buildflags.h" #include "components/sessions/core/session_id.h" #include "components/tab_groups/tab_group_id.h" #include "components/tab_groups/tab_group_visual_data.h"
diff --git a/chrome/browser/ui/test/BUILD.gn b/chrome/browser/ui/test/BUILD.gn index 5c2b61b2..346d4f2 100644 --- a/chrome/browser/ui/test/BUILD.gn +++ b/chrome/browser/ui/test/BUILD.gn
@@ -18,6 +18,8 @@ "test_browser_ui.h", ] + public_deps = [ "//chrome/browser:browser_public_dependencies" ] + deps = [ "//base", "//base/test:test_support",
diff --git a/chrome/browser/ui/ui_features.cc b/chrome/browser/ui/ui_features.cc index 4a84d2f..24e12a6 100644 --- a/chrome/browser/ui/ui_features.cc +++ b/chrome/browser/ui/ui_features.cc
@@ -471,4 +471,14 @@ "tab_groups_focusing_default_to_focused", false); +BASE_FEATURE(kVerticalTabsGrabHandleRemoval, base::FEATURE_DISABLED_BY_DEFAULT); + +// If false, then the grab handle will only be removed when the vertical tab +// strip is expanded. +BASE_FEATURE_PARAM(bool, + kVerticalTabsGrabHandleRemovalAlways, + &kVerticalTabsGrabHandleRemoval, + "vertical_tab_grab_handle_remove_always", + true); + } // namespace features
diff --git a/chrome/browser/ui/ui_features.h b/chrome/browser/ui/ui_features.h index 776fd9d..3243f96 100644 --- a/chrome/browser/ui/ui_features.h +++ b/chrome/browser/ui/ui_features.h
@@ -355,6 +355,9 @@ BASE_DECLARE_FEATURE_PARAM(bool, kTabGroupsFocusingAutoClose); BASE_DECLARE_FEATURE_PARAM(bool, kTabGroupsFocusingDefaultToFocused); +BASE_DECLARE_FEATURE(kVerticalTabsGrabHandleRemoval); +BASE_DECLARE_FEATURE_PARAM(bool, kVerticalTabsGrabHandleRemovalAlways); + } // namespace features #endif // CHROME_BROWSER_UI_UI_FEATURES_H_
diff --git a/chrome/browser/ui/uma_browsing_activity_observer.cc b/chrome/browser/ui/uma_browsing_activity_observer.cc index 99061663..5f7d396 100644 --- a/chrome/browser/ui/uma_browsing_activity_observer.cc +++ b/chrome/browser/ui/uma_browsing_activity_observer.cc
@@ -15,7 +15,6 @@ #include "chrome/browser/profiles/profile.h" #include "chrome/browser/search_engines/template_url_service_factory.h" #include "chrome/browser/ui/browser.h" -#include "chrome/browser/ui/browser_finder.h" #include "chrome/browser/ui/browser_window.h" #include "chrome/browser/ui/browser_window/public/browser_window_interface_iterator.h" #include "chrome/browser/ui/tabs/tab_group_model.h"
diff --git a/chrome/browser/ui/views/autofill/at_memory_promo_bubble_view.cc b/chrome/browser/ui/views/autofill/at_memory_promo_bubble_view.cc index 49caa60..60d41b3 100644 --- a/chrome/browser/ui/views/autofill/at_memory_promo_bubble_view.cc +++ b/chrome/browser/ui/views/autofill/at_memory_promo_bubble_view.cc
@@ -9,7 +9,6 @@ #include "base/task/single_thread_task_runner.h" #include "chrome/browser/profiles/profile.h" #include "chrome/browser/ui/actions/chrome_action_id.h" -#include "chrome/browser/ui/browser_finder.h" #include "chrome/browser/ui/navigator/browser_navigator.h" #include "chrome/browser/ui/navigator/browser_navigator_params.h" #include "chrome/browser/ui/views/chrome_layout_provider.h"
diff --git a/chrome/browser/ui/views/autofill/payments/save_card_bubble_views_browsertest.cc b/chrome/browser/ui/views/autofill/payments/save_card_bubble_views_browsertest.cc index c8df021d..d071486 100644 --- a/chrome/browser/ui/views/autofill/payments/save_card_bubble_views_browsertest.cc +++ b/chrome/browser/ui/views/autofill/payments/save_card_bubble_views_browsertest.cc
@@ -1457,8 +1457,6 @@ IN_PROC_BROWSER_TEST_P( SaveCardBubbleViewsFullFormBrowserTestWithAutofillUpstream, Upload_RequestedCardholderNameTextfieldIsPrefilledWithFocusName) { - base::HistogramTester histogram_tester; - // Start sync. ASSERT_TRUE(SetupSyncAndHideAccountNameEmailProfile()); // Set the user's full name. @@ -1478,8 +1476,6 @@ views::Textfield* cardholder_name_textfield = static_cast<views::Textfield*>( FindViewInBubbleById(DialogViewId::CARDHOLDER_NAME_TEXTFIELD)); EXPECT_EQ(cardholder_name_textfield->GetText(), u"John Smith"); - histogram_tester.ExpectUniqueSample( - "Autofill.SaveCardCardholderNamePrefilled", true, 1); EXPECT_TRUE(FindViewInBubbleById(DialogViewId::CARDHOLDER_NAME_TOOLTIP)); } @@ -1489,8 +1485,6 @@ IN_PROC_BROWSER_TEST_P( SaveCardBubbleViewsFullFormBrowserTestWithAutofillUpstream, Upload_RequestedCardholderNameTextfieldIsNotPrefilledWithFocusNameIfMissing) { - base::HistogramTester histogram_tester; - // Start sync. SetupSyncAndHideAccountNameEmailProfile() usually seeds account // information, including the full name, so a workaround for that is to sign // in first. @@ -1511,8 +1505,6 @@ views::Textfield* cardholder_name_textfield = static_cast<views::Textfield*>( FindViewInBubbleById(DialogViewId::CARDHOLDER_NAME_TEXTFIELD)); EXPECT_TRUE(cardholder_name_textfield->GetText().empty()); - histogram_tester.ExpectUniqueSample( - "Autofill.SaveCardCardholderNamePrefilled", false, 1); EXPECT_FALSE(FindViewInBubbleById(DialogViewId::CARDHOLDER_NAME_TOOLTIP)); }
diff --git a/chrome/browser/ui/views/autofill/payments/save_card_offer_bubble_views.cc b/chrome/browser/ui/views/autofill/payments/save_card_offer_bubble_views.cc index 4251c0ce..8b1fac11 100644 --- a/chrome/browser/ui/views/autofill/payments/save_card_offer_bubble_views.cc +++ b/chrome/browser/ui/views/autofill/payments/save_card_offer_bubble_views.cc
@@ -271,8 +271,6 @@ cardholder_name_textfield_->SetTextInputType( ui::TextInputType::TEXT_INPUT_TYPE_TEXT); cardholder_name_textfield_->SetText(prefilled_name); - autofill_metrics::LogSaveCardCardholderNamePrefilled( - !prefilled_name.empty()); // Add cardholder name elements to a single view, then to the final dialog. std::unique_ptr<views::View> cardholder_name_view =
diff --git a/chrome/browser/ui/views/autofill/popup/popup_base_view.cc b/chrome/browser/ui/views/autofill/popup/popup_base_view.cc index 3fbe32a..269b903 100644 --- a/chrome/browser/ui/views/autofill/popup/popup_base_view.cc +++ b/chrome/browser/ui/views/autofill/popup/popup_base_view.cc
@@ -378,7 +378,8 @@ {"PopupSuggestionView", "PopupPasswordSuggestionView", "PopupFooterView", "PopupSeparatorView", "PopupWarningView", "PopupBaseView", "PasswordGenerationPopupViewViews::GeneratedPasswordBox", "PopupRowView", - "PopupRowWithButtonView", "PopupRowContentView", "MdTextButton"}); + "PopupRowWithButtonView", "PopupRowContentView", "MdTextButton", + "PopupBnplFootnoteView"}); DCHECK(kDerivedClasses.contains(selected_view.GetClassName())) << "If you add a new derived class from AutofillPopupRowView, add it " "here and to onSelection(evt) in "
diff --git a/chrome/browser/ui/views/autofill/popup/popup_bnpl_footnote_view.cc b/chrome/browser/ui/views/autofill/popup/popup_bnpl_footnote_view.cc index f86c49b..4f29d09 100644 --- a/chrome/browser/ui/views/autofill/popup/popup_bnpl_footnote_view.cc +++ b/chrome/browser/ui/views/autofill/popup/popup_bnpl_footnote_view.cc
@@ -26,31 +26,25 @@ #include "ui/views/accessibility/view_accessibility.h" #include "ui/views/background.h" #include "ui/views/border.h" +#include "ui/views/controls/link.h" #include "ui/views/controls/styled_label.h" #include "ui/views/layout/box_layout.h" +#include "ui/views/view_utils.h" namespace autofill { namespace { - -void OnSettingsLinkClicked(base::WeakPtr<AutofillPopupController> controller) { - if (!controller || !controller->GetWebContents()) { - return; - } - - Profile* profile = Profile::FromBrowserContext( - controller->GetWebContents()->GetBrowserContext()); - if (!profile) { - return; - } - - chrome::ShowSettingsSubPageForProfile(profile, chrome::kPaymentsSubPage); +constexpr int kSelectionBorderThickness = 1; } -} // namespace - PopupBnplFootnoteView::PopupBnplFootnoteView( - base::WeakPtr<AutofillPopupController> controller) { + base::WeakPtr<AutofillPopupController> controller, + PopupRowView::AccessibilitySelectionDelegate& a11y_selection_delegate, + base::RepeatingCallback<void(const std::u16string&, bool)> + announce_callback) + : a11y_selection_delegate_(a11y_selection_delegate) { + controller_ = controller; + announce_callback_ = std::move(announce_callback); // TODO(crbug.com/477689220): Investigate better approaches to manage the // layout that doesn't rely on setting custom margins, and instead // reuses underlying code. @@ -61,6 +55,8 @@ SetBackground(views::CreateSolidBackground(ui::kColorBubbleFooterBackground)); + GetViewAccessibility().SetRole(ax::mojom::Role::kGroup); + if (!controller || !controller->GetWebContents()) { return; } @@ -92,15 +88,71 @@ views::StyledLabel::RangeStyleInfo link_style = views::StyledLabel::RangeStyleInfo::CreateForLink( - base::BindRepeating(&OnSettingsLinkClicked, controller)); + base::BindRepeating(&PopupBnplFootnoteView::ActivateSettingsLink, + weak_ptr_factory_.GetWeakPtr())); link_style.text_style = views::style::STYLE_LINK_5; styled_label->AddStyleRange(text_with_link.offset, link_style); AddChildView(std::move(styled_label)); + + if (auto* link = GetSettingsLink()) { + link->SetBorder(views::CreateEmptyBorder(kSelectionBorderThickness)); + } } PopupBnplFootnoteView::~PopupBnplFootnoteView() = default; +views::Link* PopupBnplFootnoteView::GetSettingsLink() const { + for (views::View* child : children()) { + if (auto* styled_label = views::AsViewClass<views::StyledLabel>(child)) { + for (views::View* label_child : styled_label->children()) { + if (auto* link = views::AsViewClass<views::Link>(label_child)) { + return link; + } + } + } + } + return nullptr; +} + +void PopupBnplFootnoteView::FocusSettingsLink() { + if (auto* link = GetSettingsLink()) { + is_settings_link_selected_ = true; + link->SetBorder(views::CreateSolidBorder(kSelectionBorderThickness, + ui::kColorFocusableBorderFocused)); + a11y_selection_delegate_->NotifyAXSelection(*this); + this->NotifyAccessibilityEventDeprecated(ax::mojom::Event::kFocus, true); + this->GetViewAccessibility().SetIsSelected(true); + announce_callback_.Run(std::u16string(link->GetText()), /*polite=*/false); + } +} + +bool PopupBnplFootnoteView::IsSettingsLinkFocused() const { + return is_settings_link_selected_; +} + +void PopupBnplFootnoteView::ActivateSettingsLink() { + if (!controller_ || !controller_->GetWebContents()) { + return; + } + + Profile* profile = Profile::FromBrowserContext( + controller_->GetWebContents()->GetBrowserContext()); + if (!profile) { + return; + } + + chrome::ShowSettingsSubPageForProfile(profile, chrome::kPaymentsSubPage); +} + +void PopupBnplFootnoteView::UnfocusSettingsLink() { + is_settings_link_selected_ = false; + if (auto* link = GetSettingsLink()) { + link->SetBorder(views::CreateEmptyBorder(kSelectionBorderThickness)); + } + this->GetViewAccessibility().SetIsSelected(false); +} + BEGIN_METADATA(PopupBnplFootnoteView) END_METADATA
diff --git a/chrome/browser/ui/views/autofill/popup/popup_bnpl_footnote_view.h b/chrome/browser/ui/views/autofill/popup/popup_bnpl_footnote_view.h index 88716f9..810e472a7 100644 --- a/chrome/browser/ui/views/autofill/popup/popup_bnpl_footnote_view.h +++ b/chrome/browser/ui/views/autofill/popup/popup_bnpl_footnote_view.h
@@ -5,10 +5,17 @@ #ifndef CHROME_BROWSER_UI_VIEWS_AUTOFILL_POPUP_POPUP_BNPL_FOOTNOTE_VIEW_H_ #define CHROME_BROWSER_UI_VIEWS_AUTOFILL_POPUP_POPUP_BNPL_FOOTNOTE_VIEW_H_ +#include "base/functional/callback.h" +#include "base/memory/raw_ref.h" #include "base/memory/weak_ptr.h" +#include "chrome/browser/ui/views/autofill/popup/popup_row_view.h" #include "ui/base/metadata/metadata_header_macros.h" #include "ui/views/view.h" +namespace views { +class Link; +} + namespace autofill { class AutofillPopupController; @@ -19,16 +26,41 @@ METADATA_HEADER(PopupBnplFootnoteView, views::View) public: - explicit PopupBnplFootnoteView( - base::WeakPtr<AutofillPopupController> controller); + PopupBnplFootnoteView( + base::WeakPtr<AutofillPopupController> controller, + PopupRowView::AccessibilitySelectionDelegate& a11y_selection_delegate, + base::RepeatingCallback<void(const std::u16string&, bool)> + announce_callback); PopupBnplFootnoteView(const PopupBnplFootnoteView&) = delete; PopupBnplFootnoteView& operator=(const PopupBnplFootnoteView&) = delete; ~PopupBnplFootnoteView() override; + // Styles the settings link with a focused border to visually indicate + // selection and announces the link text. + void FocusSettingsLink(); + + // Returns whether the settings link is currently visually focused/selected. + bool IsSettingsLinkFocused() const; + + // Opens the Chrome payments settings subpage in a separate tab. + void ActivateSettingsLink(); + + // Resets the settings link border to an empty border to visually remove + // focus. + void UnfocusSettingsLink(); + const std::u16string& GetFullText() const { return full_text_; } private: + views::Link* GetSettingsLink() const; + std::u16string full_text_; + bool is_settings_link_selected_ = false; + base::WeakPtr<AutofillPopupController> controller_; + base::RepeatingCallback<void(const std::u16string&, bool)> announce_callback_; + const raw_ref<PopupRowView::AccessibilitySelectionDelegate> + a11y_selection_delegate_; + base::WeakPtrFactory<PopupBnplFootnoteView> weak_ptr_factory_{this}; }; } // namespace autofill
diff --git a/chrome/browser/ui/views/autofill/popup/popup_bnpl_footnote_view_unittest.cc b/chrome/browser/ui/views/autofill/popup/popup_bnpl_footnote_view_unittest.cc index e273b56..ddc85c3 100644 --- a/chrome/browser/ui/views/autofill/popup/popup_bnpl_footnote_view_unittest.cc +++ b/chrome/browser/ui/views/autofill/popup/popup_bnpl_footnote_view_unittest.cc
@@ -6,8 +6,10 @@ #include <memory> +#include "base/functional/bind.h" #include "base/memory/raw_ptr.h" #include "chrome/browser/ui/autofill/mock_autofill_popup_controller.h" +#include "chrome/browser/ui/views/autofill/popup/mock_accessibility_selection_delegate.h" #include "chrome/test/views/chrome_views_test_base.h" #include "testing/gtest/include/gtest/gtest.h" #include "ui/accessibility/ax_enums.mojom.h" @@ -25,8 +27,9 @@ } void ShowView() { - view_ = widget_->SetContentsView( - std::make_unique<PopupBnplFootnoteView>(controller_.GetWeakPtr())); + view_ = widget_->SetContentsView(std::make_unique<PopupBnplFootnoteView>( + controller_.GetWeakPtr(), a11y_selection_delegate_, + base::BindRepeating([](const std::u16string&, bool) {}))); widget_->Show(); } @@ -41,22 +44,21 @@ views::Widget& widget() { return *widget_; } testing::NiceMock<MockAutofillPopupController> controller_; + testing::NiceMock<MockAccessibilitySelectionDelegate> + a11y_selection_delegate_; private: std::unique_ptr<views::Widget> widget_; raw_ptr<PopupBnplFootnoteView> view_ = nullptr; }; -TEST_F(PopupBnplFootnoteViewTest, AccessibilityWrapperRole) { +TEST_F(PopupBnplFootnoteViewTest, AccessibleProperties) { ShowView(); ui::AXNodeData node_data; view().GetViewAccessibility().GetAccessibleNodeData(&node_data); - // Check that PopupBnplFootnoteView is a layout wrapper, ensuring that the - // child `views::StyledLabel` handles the text and link accessibility - // natively. - EXPECT_EQ(ax::mojom::Role::kUnknown, node_data.role); + EXPECT_EQ(ax::mojom::Role::kGroup, node_data.role); } } // namespace autofill
diff --git a/chrome/browser/ui/views/autofill/popup/popup_view_views.cc b/chrome/browser/ui/views/autofill/popup/popup_view_views.cc index c932e27..3deaf6fa 100644 --- a/chrome/browser/ui/views/autofill/popup/popup_view_views.cc +++ b/chrome/browser/ui/views/autofill/popup/popup_view_views.cc
@@ -73,6 +73,7 @@ #include "components/strings/grit/components_strings.h" #include "components/sync/service/sync_service.h" #include "components/user_education/common/feature_promo/feature_promo_controller.h" +#include "content/public/browser/browser_accessibility_state.h" #include "content/public/browser/web_contents.h" #include "third_party/blink/public/common/input/web_input_event.h" #include "ui/accessibility/ax_node_data.h" @@ -451,10 +452,32 @@ // We do not want to handle Mod+TAB for other modifiers because this may // have other purposes (e.g., change the tab). if (!kHasNonShiftModifier) { + // If there is a BNPL footnote settings link, we want to focus it to + // make it accessible for keyboard and screenreader users, since the + // BNPL footnote suggestion itself is not selectable. + PopupBnplFootnoteView* bnpl_footnote = GetBnplFootnoteView(); + if (bnpl_footnote) { + if (bnpl_footnote->IsSettingsLinkFocused()) { + return false; + } + bnpl_footnote->FocusSettingsLink(); + SetSelectedCell(std::nullopt, PopupCellSelectionSource::kKeyboard); + return true; + } + AcceptSelectedContentOrCreditCardCell( AutofillMetrics::SuggestionAcceptedMethod::kKeyboard); } return false; + case ui::VKEY_RETURN: + if (!GetSelectedCell()) { + PopupBnplFootnoteView* bnpl_footnote = GetBnplFootnoteView(); + if (bnpl_footnote && bnpl_footnote->IsSettingsLinkFocused()) { + bnpl_footnote->ActivateSettingsLink(); + return true; + } + } + return false; default: return false; } @@ -895,6 +918,12 @@ open_sub_popup_timer_.Stop(); if (cell_index && HasSelectablePopupRowViewAt(cell_index->first)) { + if (auto* footnote = GetBnplFootnoteView()) { + // Since cell selection is based on virtual focus and not real focus, + // we need to manually unfocus the settings link when updating virtual + // focus. + footnote->UnfocusSettingsLink(); + } has_keyboard_focus_ = true; // The sub-popup hiding is canceled because the newly selected cell will // rule the sub-pupop visibility from now. @@ -1005,11 +1034,8 @@ } std::u16string footnote_text; - for (const RowPointer& row : rows_) { - if (const auto* footnote = std::get_if<PopupBnplFootnoteView*>(&row)) { - footnote_text = (*footnote)->GetFullText(); - break; - } + if (PopupBnplFootnoteView* bnpl_footnote = GetBnplFootnoteView()) { + footnote_text = bnpl_footnote->GetFullText(); } if (!footnote_text.empty()) { @@ -1043,6 +1069,15 @@ GetPopupRowViewAt(index).IsSelectable(); } +PopupBnplFootnoteView* PopupViewViews::GetBnplFootnoteView() const { + for (const RowPointer& row : rows_) { + if (const auto* footnote = std::get_if<PopupBnplFootnoteView*>(&row)) { + return *footnote; + } + } + return nullptr; +} + void PopupViewViews::InitViews() { a11y_announcer_ = base::BindRepeating(&DefaultA11yAnnouncer); @@ -1298,7 +1333,9 @@ } else if (suggestions[current_line_number].type == SuggestionType::kBnplFootnote) { rows_.push_back(footer_container_->AddChildView( - std::make_unique<PopupBnplFootnoteView>(controller()))); + std::make_unique<PopupBnplFootnoteView>( + controller(), /*a11y_selection_delegate=*/*this, + base::BindRepeating(&DefaultA11yAnnouncer)))); } else { rows_.push_back(footer_container_->AddChildView(CreatePopupRowView( controller(), /*a11y_selection_delegate=*/*this,
diff --git a/chrome/browser/ui/views/autofill/popup/popup_view_views.h b/chrome/browser/ui/views/autofill/popup/popup_view_views.h index b122bfe..b274cbf 100644 --- a/chrome/browser/ui/views/autofill/popup/popup_view_views.h +++ b/chrome/browser/ui/views/autofill/popup/popup_view_views.h
@@ -223,6 +223,8 @@ // selectable. bool HasSelectablePopupRowViewAt(size_t index) const; + PopupBnplFootnoteView* GetBnplFootnoteView() const; + // Instantiates the content of the popup. void InitViews();
diff --git a/chrome/browser/ui/views/autofill/popup/popup_view_views_test_api.h b/chrome/browser/ui/views/autofill/popup/popup_view_views_test_api.h index ec0618f2..ddecc55 100644 --- a/chrome/browser/ui/views/autofill/popup/popup_view_views_test_api.h +++ b/chrome/browser/ui/views/autofill/popup/popup_view_views_test_api.h
@@ -50,6 +50,10 @@ return view_->rows_; } + PopupBnplFootnoteView* GetBnplFootnoteView() const&& { + return view_->GetBnplFootnoteView(); + } + void SetSearchQuery(const std::u16string& query) { view_->search_bar_->SetInputTextForTesting(query); }
diff --git a/chrome/browser/ui/views/autofill/popup/popup_view_views_unittest.cc b/chrome/browser/ui/views/autofill/popup/popup_view_views_unittest.cc index 911f2fb..3588fe7 100644 --- a/chrome/browser/ui/views/autofill/popup/popup_view_views_unittest.cc +++ b/chrome/browser/ui/views/autofill/popup/popup_view_views_unittest.cc
@@ -1481,6 +1481,70 @@ /*non_shift_modifier_pressed=*/true); } +TEST_F(PopupViewViewsTestKeyboard, TabFocusesFootnoteLink) { + controller().set_suggestions( + {Suggestion(u"BNPL Footnote", SuggestionType::kBnplFootnote)}); + CreateAndShowView(); + + PopupBnplFootnoteView* bnpl_footnote = test_api(view()).GetBnplFootnoteView(); + ASSERT_TRUE(bnpl_footnote); + + ASSERT_FALSE(bnpl_footnote->IsSettingsLinkFocused()); + + SimulateKeyPress(ui::VKEY_TAB); + + EXPECT_TRUE(bnpl_footnote->IsSettingsLinkFocused()); + + ui::AXNodeData node_data; + bnpl_footnote->GetViewAccessibility().GetAccessibleNodeData(&node_data); + EXPECT_TRUE(node_data.GetBoolAttribute(ax::mojom::BoolAttribute::kSelected)); +} + +TEST_F(PopupViewViewsTestKeyboard, EnterHandledByFootnoteWhenLinkIsFocused) { + controller().set_suggestions( + {Suggestion(u"BNPL Footnote", SuggestionType::kBnplFootnote)}); + CreateAndShowView(); + + PopupBnplFootnoteView* bnpl_footnote = test_api(view()).GetBnplFootnoteView(); + ASSERT_TRUE(bnpl_footnote); + + ASSERT_FALSE(SimulateKeyPress(ui::VKEY_RETURN)); + + SimulateKeyPress(ui::VKEY_TAB); + ASSERT_TRUE(bnpl_footnote->IsSettingsLinkFocused()); + + // Forces `PopupBnplFootnoteView::ActivateSettingsLink` to return early, since + // `chrome::ShowSettingsSubPageForProfile` crashes in this test environment. + ON_CALL(controller(), GetWebContents()) + .WillByDefault(testing::Return(nullptr)); + + EXPECT_TRUE(SimulateKeyPress(ui::VKEY_RETURN)); +} + +TEST_F(PopupViewViewsTestKeyboard, UnfocusFootnoteLinkOnSuggestionSelection) { + controller().set_suggestions( + {Suggestion(u"Credit Card", SuggestionType::kCreditCardEntry), + Suggestion(u"BNPL Footnote", SuggestionType::kBnplFootnote)}); + CreateAndShowView(); + + PopupBnplFootnoteView* bnpl_footnote = test_api(view()).GetBnplFootnoteView(); + ASSERT_TRUE(bnpl_footnote); + + SimulateKeyPress(ui::VKEY_TAB); + + ui::AXNodeData node_data; + bnpl_footnote->GetViewAccessibility().GetAccessibleNodeData(&node_data); + ASSERT_TRUE(node_data.GetBoolAttribute(ax::mojom::BoolAttribute::kSelected)); + ASSERT_TRUE(bnpl_footnote->IsSettingsLinkFocused()); + + view().SetSelectedCell(CellIndex{0u, CellType::kContent}, + PopupCellSelectionSource::kKeyboard); + + bnpl_footnote->GetViewAccessibility().GetAccessibleNodeData(&node_data); + EXPECT_FALSE(node_data.GetBoolAttribute(ax::mojom::BoolAttribute::kSelected)); + EXPECT_FALSE(bnpl_footnote->IsSettingsLinkFocused()); +} + // Verify that pressing the tab key while the "Manage addresses..." entry is // selected does not trigger "accepting" the entry (which would mean opening // a tab with the autofill settings).
diff --git a/chrome/browser/ui/views/bookmarks/bookmark_drag_drop_views.cc b/chrome/browser/ui/views/bookmarks/bookmark_drag_drop_views.cc index ecb1b55..8c91f01 100644 --- a/chrome/browser/ui/views/bookmarks/bookmark_drag_drop_views.cc +++ b/chrome/browser/ui/views/bookmarks/bookmark_drag_drop_views.cc
@@ -19,7 +19,6 @@ #include "chrome/browser/profiles/profile.h" #include "chrome/browser/ui/bookmarks/bookmark_drag_drop.h" #include "chrome/browser/ui/bookmarks/bookmark_utils.h" -#include "chrome/browser/ui/browser_finder.h" #include "chrome/browser/ui/color/chrome_color_id.h" #include "chrome/browser/ui/views/frame/browser_view.h" #include "chrome/grit/platform_locale_settings.h"
diff --git a/chrome/browser/ui/views/drive_picker_host/drive_picker_host_controller.cc b/chrome/browser/ui/views/drive_picker_host/drive_picker_host_controller.cc index dec6a22..d6774e4a 100644 --- a/chrome/browser/ui/views/drive_picker_host/drive_picker_host_controller.cc +++ b/chrome/browser/ui/views/drive_picker_host/drive_picker_host_controller.cc
@@ -8,11 +8,16 @@ #include <utility> #include "base/functional/bind.h" +#include "base/logging.h" #include "chrome/browser/profiles/profile.h" #include "chrome/browser/ui/browser_window/public/browser_window_interface.h" #include "chrome/browser/ui/views/drive_picker_host/drive_picker_host_view.h" #include "components/constrained_window/constrained_window_views.h" +#include "content/public/browser/web_contents.h" #include "ui/base/base_window.h" +#include "ui/compositor/layer.h" +#include "ui/gfx/geometry/rect.h" +#include "ui/views/controls/webview/webview.h" #include "ui/views/view_utils.h" #include "ui/views/widget/widget.h" #include "ui/views/window/dialog_delegate.h" @@ -27,20 +32,32 @@ mojo::PendingRemote<drive_picker_host::mojom::DrivePickerResultHandler> result_handler) { // Ensure that we only have one Drive Picker Host view at a time. - CHECK(!widget_); + if (widget_) { + return; + } + + ui::BaseWindow* window = browser_window_interface_->GetWindow(); + if (!window) { + return; + } auto view = std::make_unique<DrivePickerHostView>( browser_window_interface_->GetProfile()); + // Ensure the view has a non-zero preferred size before creating the widget. + // Mac does not support zero-sized windows, and this also ensures the dialog + // starts with the correct dimensions (covering the entire browser window). + view->SetPreferredSize(window->GetBounds().size()); - gfx::NativeWindow native_window = - browser_window_interface_->GetWindow()->GetNativeWindow(); + gfx::NativeWindow native_window = window->GetNativeWindow(); if (!native_window) { return; } - delegate_ = std::make_unique<views::DialogDelegate>(); + auto delegate = std::make_unique<views::DialogDelegate>(); + delegate->set_use_custom_frame(true); + delegate->SetCanResize(false); // Ensure the widget is owned by the controller (via unique_ptr). - delegate_->SetOwnershipOfNewWidget( + delegate->SetOwnershipOfNewWidget( views::Widget::InitParams::Ownership::CLIENT_OWNS_WIDGET); // The Drive Picker Host is designed to be a window-modal overlay. This means @@ -60,22 +77,25 @@ // - Lifecycle Integrity: Blocking interaction with the rest of the window // prevents edge cases where the browser window's state might change // (e.g., navigating away in a tab) while the picker is still active. - delegate_->SetModalType(ui::mojom::ModalType::kWindow); - delegate_->SetButtons(static_cast<int>(ui::mojom::DialogButton::kNone)); - delegate_->SetShowTitle(false); - delegate_->SetShowCloseButton(false); - DrivePickerHostView* view_ptr = delegate_->SetContentsView(std::move(view)); + delegate->SetModalType(ui::mojom::ModalType::kWindow); + delegate->SetButtons(static_cast<int>(ui::mojom::DialogButton::kNone)); + delegate->SetShowTitle(false); + delegate->SetShowCloseButton(false); + DrivePickerHostView* view_ptr = delegate->SetContentsView(std::move(view)); // Use standard Chrome constrained window utility to create and show the modal // picker overlay. This utility manages the lifecycle and positioning of the - // dialog relative to the browser window, ensuring it correctly handles window - // resizes and modality. Despite CreateBrowserModalDialogViews being - // deprecated, it is the only option for creating a resizable *browser-modal* - // dialog that supports a WebContents view. + // dialog relative to the browser window, ensuring it correctly handles + // window resizes and modality. Despite CreateBrowserModalDialogViews being + // deprecated, it is the only option for creating a resizable + // *browser-modal* dialog that supports a WebContents view. views::Widget* widget = constrained_window::CreateBrowserModalDialogViews( - delegate_.get(), native_window); + std::move(delegate), native_window); widget_.reset(widget); + + widget_->SetBounds(window->GetBounds()); + widget_->MakeCloseSynchronous( base::BindOnce(&DrivePickerHostController::ResetControllerState, weak_ptr_factory_.GetWeakPtr())); @@ -93,13 +113,13 @@ pending_picker_result_handler_ = std::move(result_handler); } + widget_->Activate(); widget_->Show(); } void DrivePickerHostController::ResetControllerState( views::Widget::ClosedReason reason) { widget_.reset(); - delegate_.reset(); is_picker_document_loaded_ = false; pending_picker_result_handler_.reset(); Observe(nullptr); @@ -111,8 +131,10 @@ // We use `pending_picker_result_handler_` to check if there was a request to // trigger the picker UI before the document finished loading. This controller // only manages a single active picker session at a time. - if (pending_picker_result_handler_ && widget_ && delegate_) { - views::AsViewClass<DrivePickerHostView>(delegate_->GetContentsView()) + if (pending_picker_result_handler_ && widget_ && + widget_->widget_delegate()->GetContentsView()) { + views::AsViewClass<DrivePickerHostView>( + widget_->widget_delegate()->GetContentsView()) ->TriggerDrivePickerHostUi(std::move(pending_picker_result_handler_)); } }
diff --git a/chrome/browser/ui/views/drive_picker_host/drive_picker_host_controller.h b/chrome/browser/ui/views/drive_picker_host/drive_picker_host_controller.h index 9aaa6d4..6968779 100644 --- a/chrome/browser/ui/views/drive_picker_host/drive_picker_host_controller.h +++ b/chrome/browser/ui/views/drive_picker_host/drive_picker_host_controller.h
@@ -54,7 +54,7 @@ // Shows the Drive Picker Host (either a consent dialog or the picker // UI), and relays results to the provided result handler. - void ShowDrivePickerHost( + virtual void ShowDrivePickerHost( mojo::PendingRemote<drive_picker_host::mojom::DrivePickerResultHandler> result_handler); @@ -75,7 +75,6 @@ bool is_picker_document_loaded_ = false; raw_ptr<BrowserWindowInterface> browser_window_interface_; - std::unique_ptr<views::DialogDelegate> delegate_; std::unique_ptr<views::Widget> widget_; // Stores the result handler if the picker document is not yet loaded when
diff --git a/chrome/browser/ui/views/drive_picker_host/drive_picker_host_controller_unittest.cc b/chrome/browser/ui/views/drive_picker_host/drive_picker_host_controller_unittest.cc index b62dbda..8d02944 100644 --- a/chrome/browser/ui/views/drive_picker_host/drive_picker_host_controller_unittest.cc +++ b/chrome/browser/ui/views/drive_picker_host/drive_picker_host_controller_unittest.cc
@@ -49,9 +49,8 @@ base::RunLoop().RunUntilIdle(); ASSERT_TRUE(controller_->widget_); - ASSERT_TRUE(controller_->delegate_); DrivePickerHostView* view = views::AsViewClass<DrivePickerHostView>( - controller_->delegate_->GetContentsView()); + controller_->widget_->widget_delegate()->GetContentsView()); ASSERT_TRUE(view); EXPECT_EQ(controller_->web_contents(), view->GetWebContents()); }
diff --git a/chrome/browser/ui/views/drive_picker_host/drive_picker_host_view.cc b/chrome/browser/ui/views/drive_picker_host/drive_picker_host_view.cc index f2613a90..a186a52 100644 --- a/chrome/browser/ui/views/drive_picker_host/drive_picker_host_view.cc +++ b/chrome/browser/ui/views/drive_picker_host/drive_picker_host_view.cc
@@ -10,10 +10,10 @@ #include "content/public/browser/web_contents.h" #include "third_party/skia/include/core/SkColor.h" #include "ui/base/metadata/metadata_impl_macros.h" +#include "ui/compositor/layer.h" #include "ui/views/controls/webview/webview.h" #include "ui/views/layout/fill_layout.h" #include "ui/views/view_utils.h" -#include "ui/views/widget/widget.h" namespace { constexpr int kDefaultWidth = 800; @@ -26,10 +26,18 @@ // before the WebUI content determines the final layout. SetPreferredSize(gfx::Size(kDefaultWidth, kDefaultHeight)); + // Set the view to paint to a layer so that the view can be transparent over + // the web contents. This allows the web contents to appear like a floating + // dialog over the browser window. + SetPaintToLayer(); + layer()->SetFillsBoundsOpaquely(false); + SetBackground(nullptr); + SetBorder(nullptr); SetLayoutManager(std::make_unique<views::FillLayout>()); views::WebView* web_view = AddChildView(std::make_unique<views::WebView>(profile)); view_tracker_.SetView(web_view); + web_view->SetBackground(nullptr); // Since the WebView was just created with a valid profile, GetWebContents() // is guaranteed to return a valid pointer. We use a CHECK here to assert
diff --git a/chrome/browser/ui/views/extensions/BUILD.gn b/chrome/browser/ui/views/extensions/BUILD.gn index f0cb7bc..8964196f 100644 --- a/chrome/browser/ui/views/extensions/BUILD.gn +++ b/chrome/browser/ui/views/extensions/BUILD.gn
@@ -4,6 +4,14 @@ import("//build/config/ui.gni") +source_set("extensions_menu_handler") { + sources = [ "extensions_menu_handler.h" ] + deps = [ + "//extensions/browser", + "//extensions/common", + ] +} + if (toolkit_views) { source_set("extension_post_install_dialog_view_utils") { sources = [
diff --git a/chrome/browser/ui/views/frame/browser_native_widget_ash.cc b/chrome/browser/ui/views/frame/browser_native_widget_ash.cc index d69c80fcd..a2f8175 100644 --- a/chrome/browser/ui/views/frame/browser_native_widget_ash.cc +++ b/chrome/browser/ui/views/frame/browser_native_widget_ash.cc
@@ -17,7 +17,6 @@ #include "build/build_config.h" #include "chrome/browser/lifetime/browser_shutdown.h" #include "chrome/browser/ui/browser_commands.h" -#include "chrome/browser/ui/browser_finder.h" #include "chrome/browser/ui/views/frame/browser_view.h" #include "chrome/browser/ui/views/frame/browser_widget.h" #include "chrome/browser/ui/web_applications/app_browser_controller.h"
diff --git a/chrome/browser/ui/views/frame/browser_view.cc b/chrome/browser/ui/views/frame/browser_view.cc index afc267ac..c8adeee 100644 --- a/chrome/browser/ui/views/frame/browser_view.cc +++ b/chrome/browser/ui/views/frame/browser_view.cc
@@ -1965,18 +1965,19 @@ } if (change_tab_contents) { - // When the location bar or other UI focus will be restored, first focus the - // root view so that screen readers announce the current page title. The - // kFocusContext event will delay the subsequent focus event so that screen - // readers register them as distinct events. + // When the location bar or other UI focus will be restored, first send a + // transient root focus so that screen readers announce the current page + // title before the restored UI focus. if (will_restore_focus) { ChromeWebContentsViewFocusHelper* focus_helper = ChromeWebContentsViewFocusHelper::FromWebContents(new_contents); if (focus_helper && focus_helper->GetStoredFocus() != active_contents_view) { GetWidget()->UpdateAccessibleNameForRootView(); - GetWidget()->GetRootView()->NotifyAccessibilityEventDeprecated( - ax::mojom::Event::kFocusContext, true); + GetWidget() + ->GetRootView() + ->GetViewAccessibility() + .NotifyTransientFocus(); } }
diff --git a/chrome/browser/ui/views/frame/layout/browser_view_tabbed_layout_impl.cc b/chrome/browser/ui/views/frame/layout/browser_view_tabbed_layout_impl.cc index 4df2ac37..8df3fc4 100644 --- a/chrome/browser/ui/views/frame/layout/browser_view_tabbed_layout_impl.cc +++ b/chrome/browser/ui/views/frame/layout/browser_view_tabbed_layout_impl.cc
@@ -300,8 +300,11 @@ preferred_side_panel_width = std::max(min_side_panel_width, preferred_side_panel_width); + // Add additional padding except when it's not needed around a split view. layout.side_panel_padding = - GetLayoutConstant(LayoutConstant::kSidePanelInset); + layout.force_top_container_to_top && delegate().IsActiveTabSplit() + ? 0 + : GetLayoutConstant(LayoutConstant::kSidePanelInset); // See if the toolbar-height side panel can fit next to the toolbar. If not, // it is forced into content height. @@ -310,6 +313,11 @@ (toolbar_minimum_width + layout.side_panel_padding); layout.force_top_container_to_top = remainder < min_side_panel_width; + // Update the padding if necessary. + if (layout.force_top_container_to_top && delegate().IsActiveTabSplit()) { + layout.side_panel_padding = 0; + } + // If still allowing toolbar height, clamp the side panel based on what // the toolbar actually supports. if (!layout.force_top_container_to_top) { @@ -396,6 +404,14 @@ } int BrowserViewTabbedLayoutImpl::GetMinimumGrabHandlePadding() const { + if (base::FeatureList::IsEnabled(features::kVerticalTabsGrabHandleRemoval)) { + if (features::kVerticalTabsGrabHandleRemovalAlways.Get() || + GetVerticalTabStripCollapsedState() == + VerticalTabStripCollapsedState::kExpanded) { + return 0; + } + } + return kVerticalTabsGrabHandleSize - GetLayoutInsets(LayoutInset::TOOLBAR_INTERIOR_MARGIN).right(); } @@ -526,7 +542,7 @@ kMainBrowserContentsMinimumWidth}); // Maybe adjust for additional padding when the side panel is visible. - if (side_panel_size.width() > 0) { + if (side_panel_size.width() > 0 && !delegate().IsActiveTabSplit()) { const auto padding = GetLayoutConstant(LayoutConstant::kSidePanelInset); min_height += 2 * padding; min_width += padding; @@ -864,7 +880,15 @@ } } - const bool show_shadow_overlay = ShadowOverlayVisible(); + // The split view has outlines and padding around the web contents, so there's + // no need for an additional shadow box or separator around the split view. + // We still draw the shadow box in toolbar-height side panel mode, because it + // surrounds other elements like the toolbar. + const bool is_split_outline_replacing_shadow_or_separator = + delegate().IsActiveTabSplit() && + horizontal_layout.force_top_container_to_top; + const bool show_shadow_overlay = + ShadowOverlayVisible() && !is_split_outline_replacing_shadow_or_separator; gfx::Insets shadow_overlay_insets; if (show_shadow_overlay) { // As the side panel animates in, the main panel shrinks and moves over to @@ -935,8 +959,9 @@ // the required top padding. const auto top_separator_type = GetTopSeparatorType(); views().multi_contents_view->SetShouldShowTopSeparator( - !suppress_top_separator && - top_separator_type == TopSeparatorType::kMultiContents); + is_split_outline_replacing_shadow_or_separator || + (!suppress_top_separator && + top_separator_type == TopSeparatorType::kMultiContents)); // Update the multi-contents view about if we will be animating content // bounds. This is to make optimizations during animations e.g. avoid @@ -986,7 +1011,8 @@ // If the top separator is suppressed now, it won't be at the extent of // the animation. if (top_separator_type == TopSeparatorType::kMultiContents && - suppress_top_separator && side_panel_is_animating) { + suppress_top_separator && side_panel_is_animating && + !is_split_outline_replacing_shadow_or_separator) { unclipped_contents_region.Inset( gfx::Insets::TLBR(views::Separator::kThickness, 0, 0, 0)); }
diff --git a/chrome/browser/ui/views/frame/multi_contents_view_interactive_uitest.cc b/chrome/browser/ui/views/frame/multi_contents_view_interactive_uitest.cc index 8082bea..0c917c4 100644 --- a/chrome/browser/ui/views/frame/multi_contents_view_interactive_uitest.cc +++ b/chrome/browser/ui/views/frame/multi_contents_view_interactive_uitest.cc
@@ -52,6 +52,10 @@ #include "ui/events/keycodes/keyboard_codes.h" #include "ui/views/test/views_test_utils.h" +#if BUILDFLAG(IS_MAC) +#include "base/mac/mac_util.h" +#endif + namespace { class ViewBoundsChangedObserver : public views::ViewObserver, public ui::test::StateObserver<int> { @@ -512,7 +516,6 @@ kMultiContentsViewLayoutSnapResizeObserver)); } - // TODO(crbug.com/399212996): Flaky on all platforms. IN_PROC_BROWSER_TEST_F(MultiContentsViewUiTest, DISABLED_ResizesToMinWidthPercentage) { @@ -1169,6 +1172,12 @@ IN_PROC_BROWSER_TEST_F(MultiContentsViewDragEntrypointsUiTest, DoesNotShowDropTargetOnChromePageLinkDragged) { +#if BUILDFLAG(IS_MAC) + // TODO(crbug.com/510801992): Re-enable on macOS 26 once test is deflaked + if (base::mac::MacOSMajorVersion() == 26) { + GTEST_SKIP() << "Disabled on macOS Tahoe."; + } +#endif RunTestSequence( AddInstrumentedTab(kNewTab, GURL(chrome::kChromeUIChromeURLsURL), 0), WaitForActiveTabChange(0),
diff --git a/chrome/browser/ui/views/location_bar/cookie_controls/cookie_controls_icon_view.cc b/chrome/browser/ui/views/location_bar/cookie_controls/cookie_controls_icon_view.cc index e7a1049..0fdf183 100644 --- a/chrome/browser/ui/views/location_bar/cookie_controls/cookie_controls_icon_view.cc +++ b/chrome/browser/ui/views/location_bar/cookie_controls/cookie_controls_icon_view.cc
@@ -16,7 +16,6 @@ #include "chrome/browser/profiles/profile.h" #include "chrome/browser/ui/browser.h" #include "chrome/browser/ui/browser_element_identifiers.h" -#include "chrome/browser/ui/browser_finder.h" #include "chrome/browser/ui/browser_window.h" #include "chrome/browser/ui/browser_window/public/browser_window_features.h" #include "chrome/browser/ui/user_education/browser_user_education_interface.h"
diff --git a/chrome/browser/ui/views/location_bar/custom_tab_bar_view.cc b/chrome/browser/ui/views/location_bar/custom_tab_bar_view.cc index b5b2a52..8c06884 100644 --- a/chrome/browser/ui/views/location_bar/custom_tab_bar_view.cc +++ b/chrome/browser/ui/views/location_bar/custom_tab_bar_view.cc
@@ -13,7 +13,6 @@ #include "chrome/browser/themes/theme_properties.h" #include "chrome/browser/themes/theme_service.h" #include "chrome/browser/ui/browser_commands.h" -#include "chrome/browser/ui/browser_finder.h" #include "chrome/browser/ui/bubble_anchor_util.h" #include "chrome/browser/ui/color/chrome_color_id.h" #include "chrome/browser/ui/layout_constants.h"
diff --git a/chrome/browser/ui/views/location_bar/location_bar_view.cc b/chrome/browser/ui/views/location_bar/location_bar_view.cc index 3389c0e..8073d06 100644 --- a/chrome/browser/ui/views/location_bar/location_bar_view.cc +++ b/chrome/browser/ui/views/location_bar/location_bar_view.cc
@@ -45,7 +45,6 @@ #include "chrome/browser/ui/browser.h" #include "chrome/browser/ui/browser_actions.h" #include "chrome/browser/ui/browser_element_identifiers.h" -#include "chrome/browser/ui/browser_finder.h" #include "chrome/browser/ui/browser_window.h" #include "chrome/browser/ui/browser_window/public/browser_window_features.h" #include "chrome/browser/ui/color/chrome_color_id.h"
diff --git a/chrome/browser/ui/views/location_bar/location_icon_state_helper.cc b/chrome/browser/ui/views/location_bar/location_icon_state_helper.cc index d291ee89..bf6926e 100644 --- a/chrome/browser/ui/views/location_bar/location_icon_state_helper.cc +++ b/chrome/browser/ui/views/location_bar/location_icon_state_helper.cc
@@ -17,10 +17,14 @@ #include "extensions/common/constants.h" #include "ui/accessibility/ax_enums.mojom.h" #include "ui/base/l10n/l10n_util.h" +#include "ui/base/models/image_model.h" +#include "ui/gfx/image/image_skia.h" #include "ui/gfx/vector_icon_types.h" #if BUILDFLAG(GOOGLE_CHROME_BRANDING) // nocheck +#include "chrome/grit/theme_resources.h" #include "components/vector_icons/vector_icons.h" // nogncheck +#include "ui/base/resource/resource_bundle.h" #endif namespace location_bar { @@ -82,6 +86,25 @@ return !model->GetSecureDisplayText().empty(); } +bool IsGradientGoogleSuperGIcon(const ui::ImageModel& icon) { +#if BUILDFLAG(GOOGLE_CHROME_BRANDING) + if (!icon.IsImage()) { + return false; + } + gfx::ImageSkia image = icon.GetImage().AsImageSkia(); + gfx::ImageSkia target_16 = + *ui::ResourceBundle::GetSharedInstance().GetImageSkiaNamed( + IDR_GOOGLE_G_GRADIENT_16_ALT); + gfx::ImageSkia target_20 = + *ui::ResourceBundle::GetSharedInstance().GetImageSkiaNamed( + IDR_GOOGLE_G_GRADIENT_20); + return image.BackedBySameObjectAs(target_16) || + image.BackedBySameObjectAs(target_20); +#else + return false; +#endif +} + SecurityChipIcon GetSecurityChipIconEnum(const LocationBarModel* model, bool is_add_context_button_shown) { if (is_add_context_button_shown) { @@ -91,6 +114,8 @@ #if BUILDFLAG(GOOGLE_CHROME_BRANDING) // nocheck const gfx::VectorIcon& icon = model->GetVectorIcon(); const char* icon_name = icon.name; + // TODO(b/507061157): Use gradient icon here instead of + // `vector_icons::kGoogleSuperGIcon`. if (icon_name == vector_icons::kGoogleSuperGIcon.name) { return SecurityChipIcon::kGoogleSuperG; }
diff --git a/chrome/browser/ui/views/location_bar/location_icon_state_helper.h b/chrome/browser/ui/views/location_bar/location_icon_state_helper.h index 877b76ed..8add212 100644 --- a/chrome/browser/ui/views/location_bar/location_icon_state_helper.h +++ b/chrome/browser/ui/views/location_bar/location_icon_state_helper.h
@@ -9,6 +9,9 @@ #include "components/security_state/core/security_state.h" #include "ui/accessibility/ax_enums.mojom-forward.h" +namespace ui { +class ImageModel; +} class LocationBarModel; @@ -51,6 +54,9 @@ bool IsSecurityChipInteractive(bool is_editing_or_empty, SecurityChipIcon icon); +// Returns true if the icon is the Google Super G gradient icon. +bool IsGradientGoogleSuperGIcon(const ui::ImageModel& icon); + // Accessibility & Tooltip SecurityChipAccessibilityState GetSecurityChipAccessibilityState( const LocationBarModel* model,
diff --git a/chrome/browser/ui/views/location_bar/location_icon_state_helper_unittest.cc b/chrome/browser/ui/views/location_bar/location_icon_state_helper_unittest.cc index ba691ab0..46fb5e1 100644 --- a/chrome/browser/ui/views/location_bar/location_icon_state_helper_unittest.cc +++ b/chrome/browser/ui/views/location_bar/location_icon_state_helper_unittest.cc
@@ -14,10 +14,13 @@ #include "testing/gtest/include/gtest/gtest.h" #include "ui/accessibility/ax_enums.mojom.h" #include "ui/base/l10n/l10n_util.h" +#include "ui/gfx/image/image_skia.h" #include "ui/gfx/vector_icon_types.h" #if BUILDFLAG(GOOGLE_CHROME_BRANDING) // nocheck +#include "chrome/grit/theme_resources.h" #include "components/vector_icons/vector_icons.h" +#include "ui/base/resource/resource_bundle.h" #endif using location_bar::SecurityChipIcon; @@ -169,3 +172,26 @@ GetSecurityChipTooltipText( /*is_editing_or_empty=*/false)); } + +#if BUILDFLAG(GOOGLE_CHROME_BRANDING) // nocheck +TEST_F(SecurityChipStateHelperTest, IsGradientGoogleSuperGIcon) { + ui::ImageModel empty_icon = ui::ImageModel(); + EXPECT_FALSE(location_bar::IsGradientGoogleSuperGIcon(empty_icon)); + + ui::ImageModel vector_icon = + ui::ImageModel::FromVectorIcon(omnibox::kHttpIcon); + EXPECT_FALSE(location_bar::IsGradientGoogleSuperGIcon(vector_icon)); + + gfx::ImageSkia target_16 = + *ui::ResourceBundle::GetSharedInstance().GetImageSkiaNamed( + IDR_GOOGLE_G_GRADIENT_16_ALT); + ui::ImageModel gradient_icon = ui::ImageModel::FromImageSkia(target_16); + EXPECT_TRUE(location_bar::IsGradientGoogleSuperGIcon(gradient_icon)); + + gfx::ImageSkia target_20 = + *ui::ResourceBundle::GetSharedInstance().GetImageSkiaNamed( + IDR_GOOGLE_G_GRADIENT_20); + ui::ImageModel gradient_icon_20 = ui::ImageModel::FromImageSkia(target_20); + EXPECT_TRUE(location_bar::IsGradientGoogleSuperGIcon(gradient_icon_20)); +} +#endif
diff --git a/chrome/browser/ui/views/location_bar/location_icon_view.cc b/chrome/browser/ui/views/location_bar/location_icon_view.cc index e69ddb1..f428a01 100644 --- a/chrome/browser/ui/views/location_bar/location_icon_view.cc +++ b/chrome/browser/ui/views/location_bar/location_icon_view.cc
@@ -21,6 +21,7 @@ #include "chrome/browser/ui/views/page_info/page_info_bubble_view.h" #include "chrome/grit/branded_strings.h" #include "chrome/grit/generated_resources.h" +#include "chrome/grit/theme_resources.h" #include "components/contextual_tasks/public/features.h" #include "components/dom_distiller/core/url_constants.h" #include "components/omnibox/browser/location_bar_model.h" @@ -39,6 +40,7 @@ #include "ui/color/color_provider_utils.h" #include "ui/gfx/color_palette.h" #include "ui/gfx/geometry/insets.h" +#include "ui/gfx/image/image_skia.h" #include "ui/gfx/vector_icon_types.h" #include "ui/views/accessibility/view_accessibility.h" #include "ui/views/animation/flood_fill_ink_drop_ripple.h" @@ -256,32 +258,41 @@ base::BindOnce(&LocationIconView::OnIconFetched, icon_fetch_weak_ptr_factory_.GetWeakPtr())); + if (icon.IsEmpty()) { + return; + } + #if BUILDFLAG(GOOGLE_CHROME_BRANDING) - const bool is_vector_icon = !icon.IsEmpty() && icon.IsVectorIcon() && - icon.GetVectorIcon().vector_icon(); - if (is_vector_icon) { - const char* const icon_name = icon.GetVectorIcon().vector_icon()->name; - if (icon_name == vector_icons::kGoogleSuperGIcon.name || - icon_name == vector_icons::kGoogleGLogoMonochromeIcon.name) { - // Remove the inkdrop around the Google G logo since we cannot interact - // with it. - views::InkDrop::Get(this)->SetMode(views::InkDropHost::InkDropMode::OFF); - } else { - views::InkDrop::Get(this)->SetMode(views::InkDropHost::InkDropMode::ON); - } + bool is_super_g = location_bar::IsGradientGoogleSuperGIcon(icon); + bool is_monochrome_g = false; - bool has_custom_theme = - this->GetWidget() && this->GetWidget()->GetCustomTheme(); - - if (has_custom_theme && icon_name == vector_icons::kGoogleSuperGIcon.name) { - SetBackgroundColor(SK_ColorWHITE); + if (icon.IsVectorIcon() && icon.GetVectorIcon().vector_icon()) { + const char* icon_name = icon.GetVectorIcon().vector_icon()->name; + // TODO(crbug.com/507061157): Remove `kGoogleSuperGIcon` conditional once + // `location_bar::GetSecurityChipIconEnum` and + // `location_bar::IsSecurityChipInteractive` support non-vector icons. + if (icon_name == vector_icons::kGoogleSuperGIcon.name) { + is_super_g = true; + } else if (icon_name == vector_icons::kGoogleGLogoMonochromeIcon.name) { + is_monochrome_g = true; } } + + // Remove the inkdrop around the Google G logos since we cannot interact with + // them. + if (is_super_g || is_monochrome_g) { + views::InkDrop::Get(this)->SetMode(views::InkDropHost::InkDropMode::OFF); + } else { + views::InkDrop::Get(this)->SetMode(views::InkDropHost::InkDropMode::ON); + } + + // Handle custom theme backgrounds specifically for the Super G icon. + if (is_super_g && GetWidget() && GetWidget()->GetCustomTheme()) { + SetBackgroundColor(SK_ColorWHITE); + } #endif - if (!icon.IsEmpty()) { - SetImageModel(icon); - } + SetImageModel(icon); } void LocationIconView::UpdateBackground() {
diff --git a/chrome/browser/ui/views/location_bar/record_replay_page_action_controller.cc b/chrome/browser/ui/views/location_bar/record_replay_page_action_controller.cc index 9338c8c..2d92579 100644 --- a/chrome/browser/ui/views/location_bar/record_replay_page_action_controller.cc +++ b/chrome/browser/ui/views/location_bar/record_replay_page_action_controller.cc
@@ -8,7 +8,6 @@ #include "base/strings/utf_string_conversions.h" #include "chrome/browser/ui/actions/chrome_action_id.h" #include "chrome/browser/ui/browser.h" -#include "chrome/browser/ui/browser_finder.h" #include "chrome/browser/ui/browser_window/public/browser_window_interface.h" #include "chrome/browser/ui/color/chrome_color_id.h" #include "chrome/browser/ui/page_action/page_action_controller.h"
diff --git a/chrome/browser/ui/views/location_bar/webui_content_setting_image_control.cc b/chrome/browser/ui/views/location_bar/webui_content_setting_image_control.cc index 8397ffb0..a018548 100644 --- a/chrome/browser/ui/views/location_bar/webui_content_setting_image_control.cc +++ b/chrome/browser/ui/views/location_bar/webui_content_setting_image_control.cc
@@ -21,7 +21,6 @@ #include "chrome/browser/ui/webui/webui_embedding_context.h" #include "components/browser_apis/ui_controllers/toolbar/toolbar_ui_api.mojom.h" #include "components/browser_apis/ui_controllers/toolbar/toolbar_ui_api_data_model.mojom.h" -#include "components/content_settings/core/common/features.h" #include "content/public/browser/web_contents.h" #include "mojo/public/mojom/base/error.mojom.h" #include "ui/base/l10n/l10n_util.h" @@ -89,13 +88,6 @@ } for (auto& model : models_) { - // The activity indicators (camera, mic) are drawn on the left side of the - // location bar, managed by the Permissions Dashboard, so we don't include - // them in the right hand side content setting images here. - if (model->image_type() == ImageType::kMediaStream) { - continue; - } - auto image_state = GetImageStateForModel(model.get(), web_contents); if (image_state) { state.push_back(std::move(image_state)); @@ -119,13 +111,6 @@ return state; } -ContentSettingImageModel* WebUIContentSettingImageControl::GetModel( - ImageType type) const { - auto it = - std::ranges::find(models_, type, &ContentSettingImageModel::image_type); - return it != models_.end() ? it->get() : nullptr; -} - void WebUIContentSettingImageControl::ShowContentSettingsBubble( ImageType type, toolbar_ui_api::mojom::ToolbarUIService::ShowContentSettingsBubbleCallback @@ -141,7 +126,13 @@ return std::monostate(); } - ContentSettingImageModel* model = GetModel(type); + ContentSettingImageModel* model = nullptr; + for (auto& m : models_) { + if (m->image_type() == type) { + model = m.get(); + break; + } + } if (!model) { return base::unexpected(Error::New(
diff --git a/chrome/browser/ui/views/location_bar/webui_content_setting_image_control.h b/chrome/browser/ui/views/location_bar/webui_content_setting_image_control.h index 95c2fce..5308856 100644 --- a/chrome/browser/ui/views/location_bar/webui_content_setting_image_control.h +++ b/chrome/browser/ui/views/location_bar/webui_content_setting_image_control.h
@@ -48,9 +48,6 @@ std::vector<toolbar_ui_api::mojom::ContentSettingImageStatePtr> ProcessContentSettingState(content::WebContents* web_contents); - ContentSettingImageModel* GetModel( - toolbar_ui_api::mojom::ContentSettingImageType type) const; - // Creates and shows a bubble for the given `type`. void ShowContentSettingsBubble( toolbar_ui_api::mojom::ContentSettingImageType type,
diff --git a/chrome/browser/ui/views/location_bar/webui_location_bar.cc b/chrome/browser/ui/views/location_bar/webui_location_bar.cc index 5a2ac14b..95a0215 100644 --- a/chrome/browser/ui/views/location_bar/webui_location_bar.cc +++ b/chrome/browser/ui/views/location_bar/webui_location_bar.cc
@@ -206,28 +206,11 @@ if (!web_contents) { return; } - - bool permission_dashboard_changed = false; - if (base::FeatureList::IsEnabled( - content_settings::features::kLeftHandSideActivityIndicators)) { - ContentSettingImageModel* media_stream_model = - content_setting_image_control_.GetModel( - ContentSettingImageModel::ImageType::kMediaStream); - if (media_stream_model) { - permission_dashboard_changed |= - permission_dashboard_controller_->Update(media_stream_model); - } - } - if (!toolbar_delegate_) { return; } toolbar_delegate_->OnContentSettingChanged( content_setting_image_control_.ProcessContentSettingState(web_contents)); - - if (permission_dashboard_changed) { - UpdateLhsChipsState(); - } } void WebUILocationBar::SaveStateToContents(content::WebContents* contents) {
diff --git a/chrome/browser/ui/views/location_bar/webui_location_bar.h b/chrome/browser/ui/views/location_bar/webui_location_bar.h index ce3cf2b..dd803b2 100644 --- a/chrome/browser/ui/views/location_bar/webui_location_bar.h +++ b/chrome/browser/ui/views/location_bar/webui_location_bar.h
@@ -146,11 +146,6 @@ ui::ElementTracker::Subscription moved_subscription_; ui::ElementTracker::Subscription shown_subscription_; - // Must be declared before `permission_dashboard_controller_` because the - // `permission_dashboard_controller_` depends on models owned by - // `content_setting_image_control_` during teardown. - WebUIContentSettingImageControl content_setting_image_control_; - std::unique_ptr<WebUIPermissionDashboard> permission_dashboard_; std::unique_ptr<PermissionDashboardController> permission_dashboard_controller_; @@ -159,6 +154,8 @@ std::unique_ptr<WebUIReadOnlyOmnibox> omnibox_view_; std::unique_ptr<OmniboxPopupViewWebUI> omnibox_popup_view_; + WebUIContentSettingImageControl content_setting_image_control_; + bool is_initialized_ = false; security_state::SecurityLevel last_update_security_level_ =
diff --git a/chrome/browser/ui/views/location_bar/webui_location_bar_unittest.cc b/chrome/browser/ui/views/location_bar/webui_location_bar_unittest.cc index 056661dc..021e1894 100644 --- a/chrome/browser/ui/views/location_bar/webui_location_bar_unittest.cc +++ b/chrome/browser/ui/views/location_bar/webui_location_bar_unittest.cc
@@ -9,7 +9,6 @@ #include <string_view> #include "base/memory/raw_ptr.h" -#include "base/test/bind.h" #include "chrome/app/vector_icons/vector_icons.h" #include "chrome/browser/ui/browser_window/test/mock_browser_window_interface.h" #include "chrome/browser/ui/views/permissions/chip/webui_permission_chip.h" @@ -227,55 +226,3 @@ /*is_mouse_interaction=*/true); EXPECT_FALSE(GetSuppressLhsChipClicked()); } - -class MockPermissionChipObserver : public PermissionChipInterface::Observer { - public: - MOCK_METHOD(void, OnMousePressed, (), (override)); -}; - -TEST_F(WebUILocationBarTest, PermissionChipMouseEvents) { - // Ensure the permission dashboard is initialized. - ASSERT_TRUE(permission_dashboard()); - - bool request_chip_clicked = false; - bool indicator_chip_clicked = false; - - permission_dashboard()->request_chip()->SetPressedCallback( - base::BindLambdaForTesting([&]() { request_chip_clicked = true; })); - permission_dashboard()->indicator_chip()->SetPressedCallback( - base::BindLambdaForTesting([&]() { indicator_chip_clicked = true; })); - - testing::StrictMock<MockPermissionChipObserver> request_observer; - testing::StrictMock<MockPermissionChipObserver> indicator_observer; - permission_dashboard()->request_chip()->AddObserver(&request_observer); - permission_dashboard()->indicator_chip()->AddObserver(&indicator_observer); - - // Test Mouse Pressed events are forwarded. - EXPECT_CALL(request_observer, OnMousePressed()); - location_bar_->OnLhsChipMousePressed( - toolbar_ui_api::mojom::LhsChipIdentifier::kPermissionRequest); - testing::Mock::VerifyAndClearExpectations(&request_observer); - - EXPECT_CALL(indicator_observer, OnMousePressed()); - location_bar_->OnLhsChipMousePressed( - toolbar_ui_api::mojom::LhsChipIdentifier::kPermissionIndicator); - testing::Mock::VerifyAndClearExpectations(&indicator_observer); - - // Test Click events are forwarded. - location_bar_->OnLhsChipClicked( - toolbar_ui_api::mojom::LhsChipIdentifier::kPermissionRequest, - /*is_mouse_interaction=*/true); - EXPECT_TRUE(request_chip_clicked); - EXPECT_FALSE(indicator_chip_clicked); - - request_chip_clicked = false; - - location_bar_->OnLhsChipClicked( - toolbar_ui_api::mojom::LhsChipIdentifier::kPermissionIndicator, - /*is_mouse_interaction=*/false); - EXPECT_TRUE(indicator_chip_clicked); - EXPECT_FALSE(request_chip_clicked); - - permission_dashboard()->request_chip()->RemoveObserver(&request_observer); - permission_dashboard()->indicator_chip()->RemoveObserver(&indicator_observer); -}
diff --git a/chrome/browser/ui/views/media_router/media_router_dialog_controller_views.cc b/chrome/browser/ui/views/media_router/media_router_dialog_controller_views.cc index f7aa2cc..e6a5839c 100644 --- a/chrome/browser/ui/views/media_router/media_router_dialog_controller_views.cc +++ b/chrome/browser/ui/views/media_router/media_router_dialog_controller_views.cc
@@ -78,8 +78,12 @@ FullscreenController* fullscreen_controller = exclusive_access_manager->fullscreen_controller(); if (fullscreen_controller->IsTabFullscreen()) { - fullscreen_blocker_ = + auto blocker = initiator()->ForSecurityDropFullscreen(display::kInvalidDisplayId); + if (!blocker) { + return; + } + fullscreen_blocker_ = std::move(*blocker); } }
diff --git a/chrome/browser/ui/views/occlusion_visibility_interactive_uitest.cc b/chrome/browser/ui/views/occlusion_visibility_interactive_uitest.cc index db797d2..8a87d4b5a 100644 --- a/chrome/browser/ui/views/occlusion_visibility_interactive_uitest.cc +++ b/chrome/browser/ui/views/occlusion_visibility_interactive_uitest.cc
@@ -2,7 +2,6 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "chrome/browser/ui/browser_finder.h" #include "chrome/test/base/interactive_test_utils.h" #include "chrome/test/base/ui_test_utils.h" #include "chrome/test/interaction/interactive_browser_test.h"
diff --git a/chrome/browser/ui/views/omnibox/omnibox_popup_presenter.cc b/chrome/browser/ui/views/omnibox/omnibox_popup_presenter.cc index 0fe1595..3eec441 100644 --- a/chrome/browser/ui/views/omnibox/omnibox_popup_presenter.cc +++ b/chrome/browser/ui/views/omnibox/omnibox_popup_presenter.cc
@@ -29,13 +29,31 @@ this, this->location_bar(), controller, /*include_location_bar_cutout=*/!full_popup, /*wants_focus=*/full_popup)); + + // By initializing `content_height_` to 1, we ensure the widget starts 1px + // taller than the location bar on first show. This creates a tiny visible + // area that forces the renderer to run layout and submit a frame (carrying + // size metadata) instead of skipping it. This ensures auto-resizes are + // triggered reliably. + // Only needed if `kOmniboxWebUIDeferShowUntilVisualStateReady` is disabled, + // as waiting for the visual state callback fixes the issue. + if (!base::FeatureList::IsEnabled( + omnibox::kOmniboxWebUIDeferShowUntilVisualStateReady)) { + content_height_ = 1; + } } OmniboxPopupPresenter::~OmniboxPopupPresenter() = default; void OmniboxPopupPresenter::Hide() { OmniboxPopupPresenterBase::Hide(); - content_height_ = 1; + if (!base::FeatureList::IsEnabled( + omnibox::kOmniboxWebUIDeferShowUntilVisualStateReady)) { + // Reset the cached height to force a layout update when the popup is + // reshown. This prevents the popup from temporarily using a stale size + // from its previous state. + content_height_ = 1; + } } std::string_view OmniboxPopupPresenter::GetPopupMetricPrefix() const {
diff --git a/chrome/browser/ui/views/page_info/page_info_bubble_view_browsertest.cc b/chrome/browser/ui/views/page_info/page_info_bubble_view_browsertest.cc index ae5d1993..ddaf1a7 100644 --- a/chrome/browser/ui/views/page_info/page_info_bubble_view_browsertest.cc +++ b/chrome/browser/ui/views/page_info/page_info_bubble_view_browsertest.cc
@@ -29,7 +29,6 @@ #include "chrome/browser/safe_browsing/chrome_password_protection_service.h" #include "chrome/browser/ssl/https_upgrades_interceptor.h" #include "chrome/browser/ui/browser_commands.h" -#include "chrome/browser/ui/browser_finder.h" #include "chrome/browser/ui/file_system_access/file_system_access_ui_helpers.h" #include "chrome/browser/ui/hats/mock_trust_safety_sentiment_service.h" #include "chrome/browser/ui/hats/trust_safety_sentiment_service_factory.h"
diff --git a/chrome/browser/ui/views/passwords/password_change/successful_password_change_view_unittest.cc b/chrome/browser/ui/views/passwords/password_change/successful_password_change_view_unittest.cc index 8eaf4e3a..b284593 100644 --- a/chrome/browser/ui/views/passwords/password_change/successful_password_change_view_unittest.cc +++ b/chrome/browser/ui/views/passwords/password_change/successful_password_change_view_unittest.cc
@@ -9,7 +9,6 @@ #include "base/strings/utf_string_conversions.h" #include "base/test/gmock_callback_support.h" -#include "chrome/browser/ui/browser_finder.h" #include "chrome/browser/ui/passwords/bubble_controllers/password_change/successful_password_change_bubble_controller.h" #include "chrome/browser/ui/views/passwords/manage_passwords_view_ids.h" #include "chrome/browser/ui/views/passwords/password_bubble_view_test_base.h"
diff --git a/chrome/browser/ui/views/passwords/password_save_update_view.cc b/chrome/browser/ui/views/passwords/password_save_update_view.cc index 83cb433..5baac64 100644 --- a/chrome/browser/ui/views/passwords/password_save_update_view.cc +++ b/chrome/browser/ui/views/passwords/password_save_update_view.cc
@@ -23,7 +23,6 @@ #include "chrome/browser/signin/signin_ui_util.h" #include "chrome/browser/ui/browser.h" #include "chrome/browser/ui/browser_element_identifiers.h" -#include "chrome/browser/ui/browser_finder.h" #include "chrome/browser/ui/hats/hats_service.h" #include "chrome/browser/ui/hats/hats_service_factory.h" #include "chrome/browser/ui/passwords/password_dialog_prompts.h"
diff --git a/chrome/browser/ui/views/performance_controls/battery_saver_bubble_view.cc b/chrome/browser/ui/views/performance_controls/battery_saver_bubble_view.cc index 5857f42b..5b3c444 100644 --- a/chrome/browser/ui/views/performance_controls/battery_saver_bubble_view.cc +++ b/chrome/browser/ui/views/performance_controls/battery_saver_bubble_view.cc
@@ -6,6 +6,7 @@ #include "base/functional/bind.h" #include "chrome/browser/ui/browser.h" +#include "chrome/browser/ui/browser_element_identifiers.h" #include "chrome/browser/ui/performance_controls/battery_saver_bubble_delegate.h" #include "chrome/browser/ui/performance_controls/battery_saver_bubble_observer.h" #include "chrome/grit/generated_resources.h" @@ -60,6 +61,9 @@ views::BubbleDialogDelegate::CreateBubbleDeprecated( std::move(bubble_unique), views::Widget::InitParams::NATIVE_WIDGET_OWNS_WIDGET); + + widget->GetContentsView()->SetProperty(views::kElementIdentifierKey, + kToolbarBatterySaverBubbleElementId); widget->Show(); observer->OnBubbleShown();
diff --git a/chrome/browser/ui/views/permissions/chip/permission_dashboard_controller.cc b/chrome/browser/ui/views/permissions/chip/permission_dashboard_controller.cc index ebec9d1..dc12d4b 100644 --- a/chrome/browser/ui/views/permissions/chip/permission_dashboard_controller.cc +++ b/chrome/browser/ui/views/permissions/chip/permission_dashboard_controller.cc
@@ -31,7 +31,6 @@ #include "ui/gfx/animation/animation.h" #include "ui/views/accessibility/view_accessibility.h" #include "ui/views/bubble/bubble_dialog_delegate_view.h" -#include "ui/views/mouse_constants.h" namespace { @@ -345,17 +344,7 @@ } void PermissionDashboardController::OnMousePressed() { - // TODO(crbug.com/495419742): This synchronous check works for Native Views - // but suffers from a race condition in WebUI. In WebUI, the bubble may close - // on the OS side before the async `OnMousePressed` IPC arrives, causing this - // view check to fail and the bubble to reopen. The timestamp-based fallback - // currently used in `WebUILocationBar::OnLhsChipMousePressed` for the - // location icon should be extracted into a shared helper class so that it - // can be used here for permission chips as well. - should_suppress_reopening_page_info_ = - !!page_info_bubble_tracker_.view() || - (base::TimeTicks::Now() - last_page_info_bubble_close_time_ < - views::kMinimumTimeBetweenButtonClicks); + should_suppress_reopening_page_info_ = !!page_info_bubble_tracker_.view(); } bool PermissionDashboardController::SuppressVerboseIndicator() { @@ -499,16 +488,13 @@ views::BubbleDialogDelegateView* const bubble = PageInfoBubbleView::CreatePageInfoBubble(std::move(specification)); - bubble->SetHighlightedElement(PermissionChipView::kIndicatorChipElementId); bubble->GetWidget()->Show(); page_info_bubble_tracker_.SetView(bubble); } void PermissionDashboardController::OnPageInfoBubbleClosed( views::Widget::ClosedReason closed_reason, - bool reload_prompt) { - last_page_info_bubble_close_time_ = base::TimeTicks::Now(); -} + bool reload_prompt) {} void PermissionDashboardController::OnIndicatorsChipButtonPressed() { content::WebContents* contents = location_bar_->GetWebContents();
diff --git a/chrome/browser/ui/views/permissions/chip/permission_dashboard_controller.h b/chrome/browser/ui/views/permissions/chip/permission_dashboard_controller.h index b814a80..0a3a079 100644 --- a/chrome/browser/ui/views/permissions/chip/permission_dashboard_controller.h +++ b/chrome/browser/ui/views/permissions/chip/permission_dashboard_controller.h
@@ -9,7 +9,6 @@ #include "base/memory/raw_ptr.h" #include "base/scoped_observation.h" -#include "base/time/time.h" #include "base/timer/timer.h" #include "chrome/browser/ui/views/location_bar/content_setting_image_view.h" #include "chrome/browser/ui/views/permissions/chip/permission_chip_interface.h" @@ -116,8 +115,6 @@ // button handles the mouse release event. bool should_suppress_reopening_page_info_ = false; - base::TimeTicks last_page_info_bubble_close_time_; - base::ScopedObservation<PermissionChipInterface, PermissionChipInterface::Observer> observation_{this};
diff --git a/chrome/browser/ui/views/permissions/chip/webui_permission_chip.h b/chrome/browser/ui/views/permissions/chip/webui_permission_chip.h index 79bda8f..d48931b 100644 --- a/chrome/browser/ui/views/permissions/chip/webui_permission_chip.h +++ b/chrome/browser/ui/views/permissions/chip/webui_permission_chip.h
@@ -86,10 +86,10 @@ PermissionPromptStyle prompt_style_ = PermissionPromptStyle::kChip; // True only when the chip has fully finished its collapse animation. - bool is_fully_collapsed_ = true; + bool is_fully_collapsed_ = false; // Collapse request sent over Mojo to the WebUI, which instantly triggers CSS // animations on the frontend. - bool should_collapse_ = true; + bool should_collapse_ = false; bool is_animating_ = false; std::u16string accessibility_name_; @@ -98,7 +98,6 @@ raw_ptr<BubbleOwnerDelegate> bubble_owner_ = nullptr; base::RepeatingClosure pressed_callback_; - base::ObserverList<Observer> observers_; base::RepeatingClosureList visibility_callbacks_; };
diff --git a/chrome/browser/ui/views/permissions/chip/webui_permission_dashboard.cc b/chrome/browser/ui/views/permissions/chip/webui_permission_dashboard.cc index 608a0a6..faa5242 100644 --- a/chrome/browser/ui/views/permissions/chip/webui_permission_dashboard.cc +++ b/chrome/browser/ui/views/permissions/chip/webui_permission_dashboard.cc
@@ -35,12 +35,12 @@ } views::BubbleAnchor WebUIPermissionDashboard::GetAnchor() { - // Note: Native Views anchors the bubble to the PermissionDashboardView - // (which tightly bounds the chips). However, when manually tested, the bubble - // seems to always align with the left side of the location bar anyway. - // For WebUI, we just anchor to the location bar for simplicity. We can - // revisit this as a follow-up if tighter anchoring to the individual WebUI - // chips is found to be necessary. + // The WebUI element tracker registration happens asynchronously over Mojo. + // If a permission is requested during browser startup, we might attempt to + // anchor the bubble before the WebUI has finished registering the tracked + // element, causing GetAnchorOrNull() to return nullptr. We fallback to the + // main window contents view to prevent a crash during this sub-millisecond + // race condition. if (ui::TrackedElement* element = location_bar_->GetAnchorOrNull()) { return views::BubbleAnchor(element); }
diff --git a/chrome/browser/ui/views/permissions/chooser_bubble_ui.cc b/chrome/browser/ui/views/permissions/chooser_bubble_ui.cc index 06bebdf..30a9f227 100644 --- a/chrome/browser/ui/views/permissions/chooser_bubble_ui.cc +++ b/chrome/browser/ui/views/permissions/chooser_bubble_ui.cc
@@ -158,8 +158,11 @@ // Drop fullscreen mode for the current webcontent so that the user sees the // URL. if (fullscreen_controller->IsTabFullscreen()) { - fullscreen_blocker_ = + auto blocker = contents->ForSecurityDropFullscreen(display::kInvalidDisplayId); + if (blocker) { + fullscreen_blocker_ = std::move(*blocker); + } } }
diff --git a/chrome/browser/ui/views/permissions/permission_prompt_bubble.cc b/chrome/browser/ui/views/permissions/permission_prompt_bubble.cc index bf1ddf2..f176aab 100644 --- a/chrome/browser/ui/views/permissions/permission_prompt_bubble.cc +++ b/chrome/browser/ui/views/permissions/permission_prompt_bubble.cc
@@ -46,8 +46,12 @@ ->fullscreen_controller(); CHECK(fullscreen_controller); if (fullscreen_controller->IsTabFullscreen()) { - fullscreen_blocker_ = + auto blocker = web_contents()->ForSecurityDropFullscreen(display::kInvalidDisplayId); + if (!blocker) { + return; + } + fullscreen_blocker_ = std::move(*blocker); } raw_ptr<PermissionPromptBubbleBaseView> prompt_bubble =
diff --git a/chrome/browser/ui/views/profiles/profile_management_flow_controller.cc b/chrome/browser/ui/views/profiles/profile_management_flow_controller.cc index 4df11b8b..1a20077 100644 --- a/chrome/browser/ui/views/profiles/profile_management_flow_controller.cc +++ b/chrome/browser/ui/views/profiles/profile_management_flow_controller.cc
@@ -15,7 +15,6 @@ #include "chrome/browser/profiles/profile.h" #include "chrome/browser/profiles/profile_window.h" #include "chrome/browser/ui/browser.h" -#include "chrome/browser/ui/browser_finder.h" #include "chrome/browser/ui/browser_window/public/browser_window_interface.h" #include "chrome/browser/ui/views/profiles/profile_management_step_controller.h" #include "chrome/browser/ui/views/profiles/profile_management_types.h"
diff --git a/chrome/browser/ui/views/profiles/profile_menu_view.cc b/chrome/browser/ui/views/profiles/profile_menu_view.cc index 8f3977d..6f588e9 100644 --- a/chrome/browser/ui/views/profiles/profile_menu_view.cc +++ b/chrome/browser/ui/views/profiles/profile_menu_view.cc
@@ -47,7 +47,6 @@ #include "chrome/browser/sync/sync_service_factory.h" #include "chrome/browser/ui/browser.h" #include "chrome/browser/ui/browser_commands.h" -#include "chrome/browser/ui/browser_finder.h" #include "chrome/browser/ui/browser_window/public/browser_window_features.h" #include "chrome/browser/ui/color/chrome_color_id.h" #include "chrome/browser/ui/dialogs/browser_dialogs.h"
diff --git a/chrome/browser/ui/views/profiles/profile_picker_flow_controller.cc b/chrome/browser/ui/views/profiles/profile_picker_flow_controller.cc index e43a8ed0e..d3642b9d 100644 --- a/chrome/browser/ui/views/profiles/profile_picker_flow_controller.cc +++ b/chrome/browser/ui/views/profiles/profile_picker_flow_controller.cc
@@ -349,6 +349,7 @@ metrics::StartupProfilingFinishReason finish_reason) override; void RecordNavigationFinished(base::TimeTicks navigation_start) override; void RecordFirstNonEmptyPaint() override; + void RecordFirstNonEmptyPaintForOsLaunch() override; bool WasStartupInterrupted() override; private: @@ -387,6 +388,15 @@ TRACE_EVENT_END("startup", perfetto::Track::FromPointer(this), paint_time); } +void FirstWebContentsProfilerForProfilePicker:: + RecordFirstNonEmptyPaintForOsLaunch() { + base::TimeTicks paint_time = base::TimeTicks::Now(); + base::UmaHistogramLongTimes100( + "ProfilePicker.FirstProfileTime.FirstWebContentsNonEmptyPaint." + "AutoLaunchByOs", + paint_time - pick_time_); +} + bool FirstWebContentsProfilerForProfilePicker::WasStartupInterrupted() { // We're assuming that no interruptions block opening an existing profile // from the profile picker. We would detect this by observing really high
diff --git a/chrome/browser/ui/views/renderer_context_menu/render_view_context_menu_views.cc b/chrome/browser/ui/views/renderer_context_menu/render_view_context_menu_views.cc index e3c0a319ce..5348c50 100644 --- a/chrome/browser/ui/views/renderer_context_menu/render_view_context_menu_views.cc +++ b/chrome/browser/ui/views/renderer_context_menu/render_view_context_menu_views.cc
@@ -16,7 +16,6 @@ #include "base/task/current_thread.h" #include "build/build_config.h" #include "chrome/app/chrome_command_ids.h" -#include "chrome/browser/ui/browser_finder.h" #include "chrome/browser/ui/views/frame/browser_view.h" #include "chrome/browser/ui/views/side_panel/side_panel_coordinator.h" #include "chrome/common/chrome_switches.h"
diff --git a/chrome/browser/ui/views/sad_tab_split_view_browsertest.cc b/chrome/browser/ui/views/sad_tab_split_view_browsertest.cc index 685d368..8e59558 100644 --- a/chrome/browser/ui/views/sad_tab_split_view_browsertest.cc +++ b/chrome/browser/ui/views/sad_tab_split_view_browsertest.cc
@@ -4,7 +4,6 @@ #include "chrome/browser/ui/browser.h" #include "chrome/browser/ui/browser_commands.h" -#include "chrome/browser/ui/browser_finder.h" #include "chrome/browser/ui/sad_tab.h" #include "chrome/browser/ui/sad_tab_helper.h" #include "chrome/browser/ui/tabs/split_tab_metrics.h"
diff --git a/chrome/browser/ui/views/sad_tab_view.cc b/chrome/browser/ui/views/sad_tab_view.cc index 7c48036..ba3245d 100644 --- a/chrome/browser/ui/views/sad_tab_view.cc +++ b/chrome/browser/ui/views/sad_tab_view.cc
@@ -10,7 +10,6 @@ #include "base/strings/utf_string_conversions.h" #include "build/build_config.h" #include "chrome/app/vector_icons/vector_icons.h" -#include "chrome/browser/ui/browser_finder.h" #include "chrome/browser/ui/sad_tab_controller.h" #include "chrome/browser/ui/views/chrome_layout_provider.h" #include "chrome/browser/ui/views/chrome_typography.h"
diff --git a/chrome/browser/ui/views/safe_browsing/tailored_security_desktop_dialog_manager.cc b/chrome/browser/ui/views/safe_browsing/tailored_security_desktop_dialog_manager.cc index 45626e0..6669bfa 100644 --- a/chrome/browser/ui/views/safe_browsing/tailored_security_desktop_dialog_manager.cc +++ b/chrome/browser/ui/views/safe_browsing/tailored_security_desktop_dialog_manager.cc
@@ -12,7 +12,6 @@ #include "base/metrics/user_metrics.h" #include "base/metrics/user_metrics_action.h" #include "chrome/browser/ui/browser.h" -#include "chrome/browser/ui/browser_finder.h" #include "chrome/browser/ui/browser_window.h" #include "chrome/browser/ui/chrome_pages.h" #include "chrome/grit/generated_resources.h"
diff --git a/chrome/browser/ui/views/search_engines/dse_reset_dialog.cc b/chrome/browser/ui/views/search_engines/dse_reset_dialog.cc index 0d9f93ca..06aefe13 100644 --- a/chrome/browser/ui/views/search_engines/dse_reset_dialog.cc +++ b/chrome/browser/ui/views/search_engines/dse_reset_dialog.cc
@@ -10,7 +10,6 @@ #include "base/metrics/histogram_functions.h" #include "chrome/browser/profiles/profile.h" #include "chrome/browser/search_engines/template_url_service_factory.h" -#include "chrome/browser/ui/browser_finder.h" #include "chrome/browser/ui/chrome_pages.h" #include "chrome/browser/ui/views/frame/app_menu_button.h" #include "chrome/browser/ui/views/frame/browser_view.h"
diff --git a/chrome/browser/ui/views/select_audio_output/select_audio_output_views.cc b/chrome/browser/ui/views/select_audio_output/select_audio_output_views.cc index 76fa0c9..f29f6fab 100644 --- a/chrome/browser/ui/views/select_audio_output/select_audio_output_views.cc +++ b/chrome/browser/ui/views/select_audio_output/select_audio_output_views.cc
@@ -5,7 +5,6 @@ #include "chrome/browser/ui/views/select_audio_output/select_audio_output_views.h" #include "base/functional/callback.h" -#include "chrome/browser/ui/browser_finder.h" #include "chrome/browser/ui/browser_window.h" #include "chrome/browser/ui/extensions/extensions_container.h" #include "chrome/browser/ui/views/media_picker_utils.h"
diff --git a/chrome/browser/ui/views/send_tab_to_self/send_tab_to_self_device_picker_bubble_view.cc b/chrome/browser/ui/views/send_tab_to_self/send_tab_to_self_device_picker_bubble_view.cc index 5f9b647f..0d40b15 100644 --- a/chrome/browser/ui/views/send_tab_to_self/send_tab_to_self_device_picker_bubble_view.cc +++ b/chrome/browser/ui/views/send_tab_to_self/send_tab_to_self_device_picker_bubble_view.cc
@@ -6,7 +6,6 @@ #include "base/functional/callback.h" #include "base/strings/utf_string_conversions.h" -#include "chrome/browser/ui/browser_finder.h" #include "chrome/browser/ui/views/chrome_typography.h" #include "chrome/browser/ui/views/frame/browser_view.h" #include "chrome/browser/ui/views/frame/toolbar_button_provider.h"
diff --git a/chrome/browser/ui/views/sharing/sharing_dialog_view.cc b/chrome/browser/ui/views/sharing/sharing_dialog_view.cc index e3fe92ea..b16c975 100644 --- a/chrome/browser/ui/views/sharing/sharing_dialog_view.cc +++ b/chrome/browser/ui/views/sharing/sharing_dialog_view.cc
@@ -10,7 +10,6 @@ #include "base/strings/utf_string_conversions.h" #include "build/build_config.h" #include "chrome/app/vector_icons/vector_icons.h" -#include "chrome/browser/ui/browser_finder.h" #include "chrome/browser/ui/views/accessibility/theme_tracking_non_accessible_image_view.h" #include "chrome/browser/ui/views/chrome_layout_provider.h" #include "chrome/browser/ui/views/chrome_typography.h"
diff --git a/chrome/browser/ui/views/side_panel/extensions/extension_side_panel_coordinator.cc b/chrome/browser/ui/views/side_panel/extensions/extension_side_panel_coordinator.cc index 2f02314..54dde3a 100644 --- a/chrome/browser/ui/views/side_panel/extensions/extension_side_panel_coordinator.cc +++ b/chrome/browser/ui/views/side_panel/extensions/extension_side_panel_coordinator.cc
@@ -11,7 +11,6 @@ #include "chrome/browser/ui/actions/chrome_actions.h" #include "chrome/browser/ui/browser.h" #include "chrome/browser/ui/browser_actions.h" -#include "chrome/browser/ui/browser_finder.h" #include "chrome/browser/ui/browser_window/public/browser_window_features.h" #include "chrome/browser/ui/side_panel/side_panel_entry.h" #include "chrome/browser/ui/side_panel/side_panel_registry.h"
diff --git a/chrome/browser/ui/views/side_panel/extensions/extension_side_panel_manager.cc b/chrome/browser/ui/views/side_panel/extensions/extension_side_panel_manager.cc index c2437bf..eb57e9d 100644 --- a/chrome/browser/ui/views/side_panel/extensions/extension_side_panel_manager.cc +++ b/chrome/browser/ui/views/side_panel/extensions/extension_side_panel_manager.cc
@@ -11,7 +11,6 @@ #include "chrome/browser/ui/actions/chrome_actions.h" #include "chrome/browser/ui/browser.h" #include "chrome/browser/ui/browser_actions.h" -#include "chrome/browser/ui/browser_finder.h" #include "chrome/browser/ui/browser_window/public/browser_window_features.h" #include "chrome/browser/ui/side_panel/side_panel_action_callback.h" #include "chrome/browser/ui/side_panel/side_panel_registry.h"
diff --git a/chrome/browser/ui/views/side_panel/history_clusters/history_clusters_side_panel_controller_utils.cc b/chrome/browser/ui/views/side_panel/history_clusters/history_clusters_side_panel_controller_utils.cc index 9ca3dcf1..8bdff9d1 100644 --- a/chrome/browser/ui/views/side_panel/history_clusters/history_clusters_side_panel_controller_utils.cc +++ b/chrome/browser/ui/views/side_panel/history_clusters/history_clusters_side_panel_controller_utils.cc
@@ -7,7 +7,6 @@ #include <memory> #include "chrome/browser/ui/browser.h" -#include "chrome/browser/ui/browser_finder.h" #include "chrome/browser/ui/views/side_panel/history_clusters/history_clusters_side_panel_controller.h" namespace side_panel::history_clusters {
diff --git a/chrome/browser/ui/views/simple_web_view_dialog_browsertest.cc b/chrome/browser/ui/views/simple_web_view_dialog_browsertest.cc index 0742fcf..ab4fadd 100644 --- a/chrome/browser/ui/views/simple_web_view_dialog_browsertest.cc +++ b/chrome/browser/ui/views/simple_web_view_dialog_browsertest.cc
@@ -12,7 +12,6 @@ #include "base/test/run_until.h" #include "chrome/browser/ssl/https_upgrades_interceptor.h" #include "chrome/browser/ui/browser.h" -#include "chrome/browser/ui/browser_finder.h" #include "chrome/browser/ui/login/login_handler.h" #include "chrome/test/base/in_process_browser_test.h" #include "chrome/test/views/chrome_test_widget.h"
diff --git a/chrome/browser/ui/views/sync/inline_login_ui_browsertest.cc b/chrome/browser/ui/views/sync/inline_login_ui_browsertest.cc index 8156bb0..b544420b 100644 --- a/chrome/browser/ui/views/sync/inline_login_ui_browsertest.cc +++ b/chrome/browser/ui/views/sync/inline_login_ui_browsertest.cc
@@ -22,7 +22,6 @@ #include "chrome/browser/signin/signin_promo.h" #include "chrome/browser/signin/signin_util.h" #include "chrome/browser/ui/browser.h" -#include "chrome/browser/ui/browser_finder.h" #include "chrome/browser/ui/tabs/tab_strip_model.h" #include "chrome/browser/ui/test/test_browser_dialog.h" #include "chrome/browser/ui/webui/signin/inline_login_handler_impl.h"
diff --git a/chrome/browser/ui/views/tab_sharing/tab_sharing_ui_views_browsertest.cc b/chrome/browser/ui/views/tab_sharing/tab_sharing_ui_views_browsertest.cc index 24865e1b..ab54aed 100644 --- a/chrome/browser/ui/views/tab_sharing/tab_sharing_ui_views_browsertest.cc +++ b/chrome/browser/ui/views/tab_sharing/tab_sharing_ui_views_browsertest.cc
@@ -17,7 +17,6 @@ #include "chrome/browser/browser_features.h" #include "chrome/browser/media/webrtc/media_capture_devices_dispatcher.h" #include "chrome/browser/ui/browser.h" -#include "chrome/browser/ui/browser_finder.h" #include "chrome/browser/ui/tabs/tab_enums.h" #include "chrome/browser/ui/tabs/tab_strip_model.h" #include "chrome/browser/ui/tabs/tab_strip_user_gesture_details.h"
diff --git a/chrome/browser/ui/views/tabs/browser_tab_strip_controller.cc b/chrome/browser/ui/views/tabs/browser_tab_strip_controller.cc index 84c55ca..8d1cd40 100644 --- a/chrome/browser/ui/views/tabs/browser_tab_strip_controller.cc +++ b/chrome/browser/ui/views/tabs/browser_tab_strip_controller.cc
@@ -517,8 +517,8 @@ return; } - context_menu_controller_ = - std::make_unique<TabContextMenuController>(tab_index.value(), this); + context_menu_controller_ = std::make_unique<TabContextMenuController>( + model_->GetTabAtIndex(tab_index.value())->GetHandle(), this); auto model = menu_model_factory_->Create( context_menu_controller_.get(), @@ -909,9 +909,10 @@ } bool BrowserTabStripController::IsContextMenuCommandEnabled( - int index, + tabs::TabInterface* tab, TabStripModel::ContextMenuCommand command_id) { - return model_->IsContextMenuCommandEnabled(index, command_id); + return model_->IsContextMenuCommandEnabled(model_->GetIndexOfTab(tab), + command_id); } bool BrowserTabStripController::IsContextMenuCommandAlerted( @@ -920,10 +921,10 @@ } void BrowserTabStripController::ExecuteContextMenuCommand( - int index, + tabs::TabInterface* tab, TabStripModel::ContextMenuCommand command_id, int event_flags) { - model_->ExecuteContextMenuCommand(index, command_id); + model_->ExecuteContextMenuCommand(model_->GetIndexOfTab(tab), command_id); } bool BrowserTabStripController::GetContextMenuAccelerator(
diff --git a/chrome/browser/ui/views/tabs/browser_tab_strip_controller.h b/chrome/browser/ui/views/tabs/browser_tab_strip_controller.h index 8f9574b..4a5bb3a 100644 --- a/chrome/browser/ui/views/tabs/browser_tab_strip_controller.h +++ b/chrome/browser/ui/views/tabs/browser_tab_strip_controller.h
@@ -150,11 +150,11 @@ bool IsContextMenuCommandChecked( TabStripModel::ContextMenuCommand command_id) override; bool IsContextMenuCommandEnabled( - int index, + tabs::TabInterface* tab, TabStripModel::ContextMenuCommand command_id) override; bool IsContextMenuCommandAlerted( TabStripModel::ContextMenuCommand command_id) override; - void ExecuteContextMenuCommand(int index, + void ExecuteContextMenuCommand(tabs::TabInterface* tab, TabStripModel::ContextMenuCommand command_id, int event_flags) override; bool GetContextMenuAccelerator(int command_id,
diff --git a/chrome/browser/ui/views/tabs/dragging/tab_drag_controller_interactive_uitest.cc b/chrome/browser/ui/views/tabs/dragging/tab_drag_controller_interactive_uitest.cc index 3540b7ba..11b58b7 100644 --- a/chrome/browser/ui/views/tabs/dragging/tab_drag_controller_interactive_uitest.cc +++ b/chrome/browser/ui/views/tabs/dragging/tab_drag_controller_interactive_uitest.cc
@@ -103,6 +103,10 @@ #include "ui/views/widget/widget.h" #include "ui/views/window/dialog_delegate.h" +#if BUILDFLAG(IS_MAC) +#include "base/mac/mac_util.h" +#endif + #if defined(USE_AURA) #include "ui/aura/client/aura_constants.h" #include "ui/aura/test/test_window_delegate.h" @@ -3270,6 +3274,12 @@ #define MAYBE_DragAll DragAll #endif IN_PROC_BROWSER_TEST_P(DetachToBrowserTabDragControllerTest, MAYBE_DragAll) { +#if BUILDFLAG(IS_MAC) + // TODO(crbug.com/510801992): Re-enable on macOS 26 once test is deflaked + if (base::mac::MacOSMajorVersion() == 26) { + GTEST_SKIP() << "Disabled on macOS Tahoe."; + } +#endif AddTabsAndResetBrowser(browser(), 1); TabStrip* tab_strip = GetTabStripForBrowser(browser()); browser()->tab_strip_model()->SelectTabAt(0); @@ -6219,19 +6229,19 @@ run_loop.Run(); // Drag the tab 1 to left-snapping. - DragTabAndNotify( - tab_strip, base::BindLambdaForTesting([&]() { - const gfx::Rect display_bounds = - display::Screen::Get()->GetPrimaryDisplay().bounds(); - const gfx::Point target(display_bounds.x(), - display_bounds.CenterPoint().y()); - ASSERT_TRUE(DragInputToNotifyWhenDone( - target, - base::BindLambdaForTesting([&]() { ASSERT_TRUE(ReleaseInput()); }), - // The window hint isn't used on Ash. - gfx::NativeWindow())); - }), - 1); + DragTabAndNotify(tab_strip, base::BindLambdaForTesting([&]() { + const gfx::Rect display_bounds = + display::Screen::Get()->GetPrimaryDisplay().bounds(); + const gfx::Point target(display_bounds.x(), + display_bounds.CenterPoint().y()); + ASSERT_TRUE(DragInputToNotifyWhenDone( + target, base::BindLambdaForTesting([&]() { + ASSERT_TRUE(ReleaseInput()); + }), + // The window hint isn't used on Ash. + gfx::NativeWindow())); + }), + 1); ASSERT_FALSE(TabDragController::IsActive()); EXPECT_EQ(2u, GlobalBrowserCollection::GetInstance()->GetSize());
diff --git a/chrome/browser/ui/views/tabs/tab/tab_context_menu_controller.cc b/chrome/browser/ui/views/tabs/tab/tab_context_menu_controller.cc index 3d49f63..fa941910 100644 --- a/chrome/browser/ui/views/tabs/tab/tab_context_menu_controller.cc +++ b/chrome/browser/ui/views/tabs/tab/tab_context_menu_controller.cc
@@ -8,9 +8,9 @@ #include "ui/gfx/geometry/rect.h" #include "ui/views/controls/menu/menu_runner.h" -TabContextMenuController::TabContextMenuController(int index, +TabContextMenuController::TabContextMenuController(tabs::TabHandle tab_handle, Delegate* delegate) - : tab_index_(index), delegate_(delegate) {} + : tab_handle_(tab_handle), delegate_(delegate) {} TabContextMenuController::~TabContextMenuController() = default; @@ -45,8 +45,11 @@ } bool TabContextMenuController::IsCommandIdEnabled(int command_id) const { - return delegate_->IsContextMenuCommandEnabled( - tab_index_, static_cast<TabStripModel::ContextMenuCommand>(command_id)); + if (auto* tab = tab_handle_.Get()) { + return delegate_->IsContextMenuCommandEnabled( + tab, static_cast<TabStripModel::ContextMenuCommand>(command_id)); + } + return false; } bool TabContextMenuController::IsCommandIdAlerted(int command_id) const { @@ -55,9 +58,11 @@ } void TabContextMenuController::ExecuteCommand(int command_id, int event_flags) { - delegate_->ExecuteContextMenuCommand( - tab_index_, static_cast<TabStripModel::ContextMenuCommand>(command_id), - event_flags); + if (auto* tab = tab_handle_.Get()) { + delegate_->ExecuteContextMenuCommand( + tab, static_cast<TabStripModel::ContextMenuCommand>(command_id), + event_flags); + } } bool TabContextMenuController::GetAcceleratorForCommandId(
diff --git a/chrome/browser/ui/views/tabs/tab/tab_context_menu_controller.h b/chrome/browser/ui/views/tabs/tab/tab_context_menu_controller.h index 9754d6b2..b0b75db 100644 --- a/chrome/browser/ui/views/tabs/tab/tab_context_menu_controller.h +++ b/chrome/browser/ui/views/tabs/tab/tab_context_menu_controller.h
@@ -34,12 +34,12 @@ virtual bool IsContextMenuCommandChecked( TabStripModel::ContextMenuCommand command_id) = 0; virtual bool IsContextMenuCommandEnabled( - int index, + tabs::TabInterface* tab, TabStripModel::ContextMenuCommand command_id) = 0; virtual bool IsContextMenuCommandAlerted( TabStripModel::ContextMenuCommand command_id) = 0; virtual void ExecuteContextMenuCommand( - int index, + tabs::TabInterface* tab, TabStripModel::ContextMenuCommand command_id, int event_flags) = 0; virtual bool GetContextMenuAccelerator(int command_id, @@ -49,7 +49,8 @@ virtual ~Delegate() = default; }; - explicit TabContextMenuController(int index, Delegate* delegate); + explicit TabContextMenuController(tabs::TabHandle tab_handle, + Delegate* delegate); ~TabContextMenuController() override; @@ -79,7 +80,7 @@ private: std::unique_ptr<ui::SimpleMenuModel> model_; std::unique_ptr<views::MenuRunner> menu_runner_; - const int tab_index_; + tabs::TabHandle tab_handle_; raw_ptr<Delegate> delegate_; };
diff --git a/chrome/browser/ui/views/tabs/tab/tab_context_menu_controller_unittest.cc b/chrome/browser/ui/views/tabs/tab/tab_context_menu_controller_unittest.cc index 0ecdfb27..77641aca 100644 --- a/chrome/browser/ui/views/tabs/tab/tab_context_menu_controller_unittest.cc +++ b/chrome/browser/ui/views/tabs/tab/tab_context_menu_controller_unittest.cc
@@ -9,6 +9,7 @@ #include "base/functional/callback.h" #include "base/test/mock_callback.h" #include "chrome/browser/ui/tabs/tab_strip_model.h" +#include "components/tabs/public/mock_tab_interface.h" #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" #include "ui/base/accelerators/accelerator.h" @@ -33,7 +34,8 @@ (override)); MOCK_METHOD(bool, IsContextMenuCommandEnabled, - (int index, TabStripModel::ContextMenuCommand command_id), + (tabs::TabInterface * tab, + TabStripModel::ContextMenuCommand command_id), (override)); MOCK_METHOD(bool, IsContextMenuCommandAlerted, @@ -41,7 +43,7 @@ (override)); MOCK_METHOD(void, ExecuteContextMenuCommand, - (int index, + (tabs::TabInterface * tab, TabStripModel::ContextMenuCommand command_id, int event_flags), (override)); @@ -58,11 +60,13 @@ void SetUp() override { testing::Test::SetUp(); - controller_ = - std::make_unique<TabContextMenuController>(0, &mock_delegate_); + tab_ = std::make_unique<tabs::MockTabInterface>(); + controller_ = std::make_unique<TabContextMenuController>(tab_->GetHandle(), + &mock_delegate_); } protected: + std::unique_ptr<tabs::TabInterface> tab_; std::unique_ptr<TabContextMenuController> controller_; testing::StrictMock<MockTabContextMenuControllerDelegate> mock_delegate_; };
diff --git a/chrome/browser/ui/views/tabs/vertical/vertical_tab_strip_controller.cc b/chrome/browser/ui/views/tabs/vertical/vertical_tab_strip_controller.cc index d3214da0..3834751 100644 --- a/chrome/browser/ui/views/tabs/vertical/vertical_tab_strip_controller.cc +++ b/chrome/browser/ui/views/tabs/vertical/vertical_tab_strip_controller.cc
@@ -87,7 +87,7 @@ } context_menu_controller_ = - std::make_unique<TabContextMenuController>(tab_index.value(), this); + std::make_unique<TabContextMenuController>(tab->GetHandle(), this); auto model = menu_model_factory_->Create( context_menu_controller_.get(), @@ -403,9 +403,10 @@ } bool VerticalTabStripController::IsContextMenuCommandEnabled( - int index, + tabs::TabInterface* tab, TabStripModel::ContextMenuCommand command_id) { - return model_->IsContextMenuCommandEnabled(index, command_id); + return model_->IsContextMenuCommandEnabled(model_->GetIndexOfTab(tab), + command_id); } bool VerticalTabStripController::IsContextMenuCommandAlerted( @@ -414,10 +415,10 @@ } void VerticalTabStripController::ExecuteContextMenuCommand( - int index, + tabs::TabInterface* tab, TabStripModel::ContextMenuCommand command_id, int event_flags) { - model_->ExecuteContextMenuCommand(index, command_id); + model_->ExecuteContextMenuCommand(model_->GetIndexOfTab(tab), command_id); } bool VerticalTabStripController::GetContextMenuAccelerator(
diff --git a/chrome/browser/ui/views/tabs/vertical/vertical_tab_strip_controller.h b/chrome/browser/ui/views/tabs/vertical/vertical_tab_strip_controller.h index b20355f..7b65cfe1 100644 --- a/chrome/browser/ui/views/tabs/vertical/vertical_tab_strip_controller.h +++ b/chrome/browser/ui/views/tabs/vertical/vertical_tab_strip_controller.h
@@ -110,11 +110,11 @@ bool IsContextMenuCommandChecked( TabStripModel::ContextMenuCommand command_id) override; bool IsContextMenuCommandEnabled( - int index, + tabs::TabInterface* tab, TabStripModel::ContextMenuCommand command_id) override; bool IsContextMenuCommandAlerted( TabStripModel::ContextMenuCommand command_id) override; - void ExecuteContextMenuCommand(int index, + void ExecuteContextMenuCommand(tabs::TabInterface* tab, TabStripModel::ContextMenuCommand command_id, int event_flags) override; bool GetContextMenuAccelerator(int command_id,
diff --git a/chrome/browser/ui/views/tabs/vertical/vertical_tab_strip_controller_interactive_uitest.cc b/chrome/browser/ui/views/tabs/vertical/vertical_tab_strip_controller_interactive_uitest.cc index b863e7a..4578072 100644 --- a/chrome/browser/ui/views/tabs/vertical/vertical_tab_strip_controller_interactive_uitest.cc +++ b/chrome/browser/ui/views/tabs/vertical/vertical_tab_strip_controller_interactive_uitest.cc
@@ -16,6 +16,7 @@ #include "chrome/browser/ui/views/tabs/hovercard/tab_hover_card_bubble_view.h" #include "chrome/browser/ui/views/tabs/vertical/vertical_tab_view.h" #include "chrome/browser/ui/views/test/vertical_tabs_interactive_test_mixin.h" +#include "chrome/common/webui_url_constants.h" #include "chrome/grit/generated_resources.h" #include "chrome/test/base/in_process_browser_test.h" #include "chrome/test/interaction/interactive_browser_test.h" @@ -393,6 +394,57 @@ 2)); } +// TODO(crbug.com/505768540): Investigate why test fails to show the duplicate +// menu item on windows. +#if BUILDFLAG(IS_WIN) +#define MAYBE_TabOpenedWhileUsingTabContextMenu \ + DISABLED_TabOpenedWhileUsingTabContextMenu +#else +#define MAYBE_TabOpenedWhileUsingTabContextMenu \ + TabOpenedWhileUsingTabContextMenu +#endif +IN_PROC_BROWSER_TEST_F(VerticalTabStripControllerInteractiveUiTest, + MAYBE_TabOpenedWhileUsingTabContextMenu) { + DEFINE_LOCAL_ELEMENT_IDENTIFIER_VALUE(kSecondTabId); + DEFINE_LOCAL_ELEMENT_IDENTIFIER_VALUE(kThirdTabId); + RunTestSequence( + // Verify Vertical Tabs is showing. + WaitForShow(kVerticalTabStripBottomContainerElementId), + // Add a second tab and open its context menu. + AddInstrumentedTab(kSecondTabId, GURL("https://www.example.com/")), + NameDescendantViewByType<VerticalTabView>(kBrowserViewElementId, + kSecondTabName, 1), + MoveMouseTo(kSecondTabName), + MayInvolveNativeContextMenu(ClickMouse(ui_controls::RIGHT)), + // Wait for the menu to show before opening a third tab, otherwise the + // context menu could be opened on that third tab. + WaitForShow(TabMenuModel::kDuplicateMenuItem), + // Add a third tab at the index of the second tab, while the context menu + // is still open. + AddInstrumentedTab(kThirdTabId, chrome::ChromeUINewTabPageURLAsGURL(), 1), + // Select the duplicate tab menu item. + MayInvolveNativeContextMenu( + WaitForShow(TabMenuModel::kDuplicateMenuItem), + SelectMenuItem(TabMenuModel::kDuplicateMenuItem)), + // Verify that the original tab that the context menu was opened on is the + // one that was duplicated, not the tab inserted after the context menu + // was opened. + CheckResult([this]() { return browser()->tab_strip_model()->count(); }, + 4), + CheckResult( + [this]() { return browser()->tab_strip_model()->active_index(); }, 3), + CheckResult( + [this]() { + return browser() + ->tab_strip_model() + ->GetActiveTab() + ->GetContents() + ->GetLastCommittedURL() + .spec(); + }, + "https://www.example.com/")); +} + class VerticalTabStripControllerTabGroupFocusingInteractiveUiTest : public VerticalTabsInteractiveTestMixin<InteractiveBrowserTest> { public:
diff --git a/chrome/browser/ui/views/toolbar/BUILD.gn b/chrome/browser/ui/views/toolbar/BUILD.gn index 7de5072..3d8da43 100644 --- a/chrome/browser/ui/views/toolbar/BUILD.gn +++ b/chrome/browser/ui/views/toolbar/BUILD.gn
@@ -328,7 +328,6 @@ "//chrome/browser/ui/side_panel", "//chrome/browser/ui/side_panel:side_panel_views_dependent", "//chrome/browser/ui/views/frame", - "//chrome/browser/ui/views/page_info", "//chrome/browser/web_applications:web_applications_test_support", "//chrome/test:test_support", "//components/commerce/core:feature_list",
diff --git a/chrome/browser/ui/views/toolbar/toolbar_controller.cc b/chrome/browser/ui/views/toolbar/toolbar_controller.cc index f34c01c6..64ef3b7 100644 --- a/chrome/browser/ui/views/toolbar/toolbar_controller.cc +++ b/chrome/browser/ui/views/toolbar/toolbar_controller.cc
@@ -284,6 +284,13 @@ elements.insert( elements.end(), {ToolbarController::ResponsiveElementInfo( + ToolbarController::ElementIdInfo( + kToolbarBatterySaverButtonElementId, + IDS_OVERFLOW_MENU_ITEM_TEXT_ENERGY_SAVER, + &kBatterySaverRefreshIcon, kToolbarBatterySaverButtonElementId, + kToolbarBatterySaverBubbleElementId), + /*is_section_end=*/false), + ToolbarController::ResponsiveElementInfo( ToolbarController::ElementIdInfo(kToolbarChromeLabsButtonElementId, IDS_OVERFLOW_MENU_ITEM_TEXT_LABS, &kScienceIcon, @@ -319,8 +326,8 @@ return std::vector<ui::ElementIdentifier>( {kToolbarHomeButtonElementId, kToolbarChromeLabsButtonElementId, kToolbarMediaButtonElementId, kToolbarNewTabButtonElementId, - kToolbarForwardButtonElementId, kToolbarAvatarButtonElementId, - kToolbarSplitTabsToolbarButtonElementId, + kToolbarForwardButtonElementId, kToolbarBatterySaverButtonElementId, + kToolbarAvatarButtonElementId, kToolbarSplitTabsToolbarButtonElementId, ContextualTasksButton::kContextualTasksToolbarButton}); } @@ -332,6 +339,7 @@ std::variant<ui::ElementIdentifier, actions::ActionId>, std::string_view>> identifier_to_action_name_map( {{kToolbarAvatarButtonElementId, "AvatarButton"}, + {kToolbarBatterySaverButtonElementId, "BatterySaverButton"}, {kToolbarChromeLabsButtonElementId, "ChromeLabsButton"}, {kExtensionsMenuButtonElementId, "ExtensionsMenuButton"}, {kToolbarForwardButtonElementId, "ForwardButton"},
diff --git a/chrome/browser/ui/views/toolbar/webui_home_control.cc b/chrome/browser/ui/views/toolbar/webui_home_control.cc index 0767f34..0ae0f02 100644 --- a/chrome/browser/ui/views/toolbar/webui_home_control.cc +++ b/chrome/browser/ui/views/toolbar/webui_home_control.cc
@@ -8,7 +8,6 @@ #include "chrome/browser/ui/actions/chrome_action_id.h" #include "chrome/browser/ui/browser.h" #include "chrome/browser/ui/browser_element_identifiers.h" -#include "chrome/browser/ui/browser_finder.h" #include "chrome/browser/ui/browser_window.h" #include "chrome/browser/ui/browser_window/public/browser_window_interface.h" #include "chrome/browser/ui/interaction/browser_elements.h"
diff --git a/chrome/browser/ui/views/toolbar/webui_split_tabs_control.cc b/chrome/browser/ui/views/toolbar/webui_split_tabs_control.cc index 379965cd..67a403e6 100644 --- a/chrome/browser/ui/views/toolbar/webui_split_tabs_control.cc +++ b/chrome/browser/ui/views/toolbar/webui_split_tabs_control.cc
@@ -8,7 +8,6 @@ #include "chrome/browser/ui/actions/chrome_action_id.h" #include "chrome/browser/ui/browser.h" #include "chrome/browser/ui/browser_element_identifiers.h" -#include "chrome/browser/ui/browser_finder.h" #include "chrome/browser/ui/browser_window.h" #include "chrome/browser/ui/browser_window/public/browser_window_interface.h" #include "chrome/browser/ui/tabs/split_tab_menu_model.h"
diff --git a/chrome/browser/ui/views/toolbar/webui_toolbar_web_view_browsertest.cc b/chrome/browser/ui/views/toolbar/webui_toolbar_web_view_browsertest.cc index 7ac55f2c..172e346 100644 --- a/chrome/browser/ui/views/toolbar/webui_toolbar_web_view_browsertest.cc +++ b/chrome/browser/ui/views/toolbar/webui_toolbar_web_view_browsertest.cc
@@ -48,7 +48,6 @@ #include "chrome/browser/ui/views/frame/browser_view.h" #include "chrome/browser/ui/views/frame/toolbar_button_provider.h" #include "chrome/browser/ui/views/location_bar/webui_location_bar.h" -#include "chrome/browser/ui/views/page_info/page_info_bubble_view_base.h" #include "chrome/browser/ui/views/toolbar/home_button.h" #include "chrome/browser/ui/views/toolbar/reload_button.h" #include "chrome/browser/ui/views/toolbar/toolbar_view.h" @@ -62,14 +61,12 @@ #include "chrome/grit/generated_resources.h" #include "chrome/test/base/in_process_browser_test.h" #include "chrome/test/base/ui_test_utils.h" -#include "chrome/test/permissions/permission_request_manager_test_api.h" #include "components/browser_apis/browser_controls/browser_controls_api.mojom.h" #include "components/browser_apis/ui_controllers/toolbar/toolbar_ui_api_data_model.mojom.h" #include "components/collaboration/public/features.h" #include "components/contextual_tasks/public/features.h" #include "components/data_sharing/public/features.h" #include "components/metrics/content/subprocess_metrics_provider.h" -#include "components/permissions/test/permission_request_observer.h" #include "components/prefs/pref_service.h" #include "components/strings/grit/components_strings.h" #include "components/vector_icons/vector_icons.h" @@ -115,7 +112,6 @@ #include "ui/views/interaction/element_tracker_views.h" #include "ui/views/test/menu_runner_test_api.h" #include "ui/views/test/view_skia_gold_pixel_diff.h" -#include "ui/views/test/widget_test.h" #include "ui/views/view_utils.h" #include "ui/views/widget/any_widget_observer.h" #include "ui/views/widget/widget.h" @@ -3740,164 +3736,3 @@ "InitialWebUI.Toolbar.ProcessAlreadyExistsForTheSameProfileOnCreation", false, 1); } - -class WebUIToolbarWebViewPermissionBrowserTest - : public WebUIToolbarWebViewBrowserTest, - public testing::WithParamInterface<permissions::RequestType> { - protected: - WebUIToolbarWebViewPermissionBrowserTest() - : WebUIToolbarWebViewBrowserTest( - {features::kInitialWebUI, features::kWebUILocationBar}, - {}) {} -}; - -IN_PROC_BROWSER_TEST_P(WebUIToolbarWebViewPermissionBrowserTest, - PermissionChipE2E) { - WebUIToolbarWebView* webui_toolbar_view = GetWebUIToolbarWebView(browser()); - ASSERT_TRUE(webui_toolbar_view); - views::WebView* web_view = webui_toolbar_view->GetWebViewForTesting(); - content::WebContents* web_contents = web_view->GetWebContents(); - - ASSERT_TRUE(embedded_test_server()->Start()); - ASSERT_TRUE(ui_test_utils::NavigateToURL( - browser(), embedded_test_server()->GetURL("/empty.html"))); - - test::PermissionRequestManagerTestApi test_api(browser()); - permissions::PermissionRequestObserver observer( - browser()->tab_strip_model()->GetActiveWebContents()); - - EXPECT_NE(nullptr, test_api.manager()); - test_api.AddSimpleRequest(browser() - ->tab_strip_model() - ->GetActiveWebContents() - ->GetPrimaryMainFrame(), - GetParam()); - - observer.Wait(); - - std::string get_chip_js = - "document.querySelector('toolbar-app')?.shadowRoot?" - ".querySelector('location-bar')?.shadowRoot?" - ".querySelector('permission-dashboard')?.shadowRoot?" - ".querySelector('#request-chip')?.shadowRoot?" - ".querySelector('#chip')"; - - ASSERT_TRUE(base::test::RunUntil([&]() { - return content::EvalJs(web_contents, - base::StrCat({get_chip_js, " !== null && ", - get_chip_js, ".offsetHeight > 0"})) - .ExtractBool(); - })); - - views::NamedWidgetShownWaiter widget_waiter( - views::test::AnyWidgetTestPasskey{}, "PermissionPromptBubbleBaseView"); - - EXPECT_TRUE(content::ExecJs( - web_contents, - base::StringPrintf( - "%s?.dispatchEvent(new MouseEvent('click', " - "{bubbles: true, cancelable: true, view: window, button: 0}));", - get_chip_js.c_str()))); - - views::Widget* bubble_widget = widget_waiter.WaitIfNeededAndGet(); - EXPECT_TRUE(bubble_widget); - EXPECT_TRUE(bubble_widget->IsVisible()); - - // Test the suppression logic: simulating the OS closing the bubble due to - // focus loss, followed immediately by the async Mojo IPCs arriving from the - // WebUI click that caused the focus loss. - views::test::WidgetDestroyedWaiter destroyed_waiter(bubble_widget); - bubble_widget->CloseWithReason(views::Widget::ClosedReason::kLostFocus); - destroyed_waiter.Wait(); - - EXPECT_TRUE(content::ExecJs( - web_contents, - base::StringPrintf( - "%s?.dispatchEvent(new PointerEvent('pointerdown', " - "{bubbles: true, cancelable: true, view: window, button: 0}));" - "%s?.dispatchEvent(new PointerEvent('click', " - "{bubbles: true, cancelable: true, view: window, button: 0, " - "pointerType: 'mouse'}));", - get_chip_js.c_str(), get_chip_js.c_str()))); - - base::RunLoop run_loop; - base::SingleThreadTaskRunner::GetCurrentDefault()->PostDelayedTask( - FROM_HERE, run_loop.QuitClosure(), base::Milliseconds(50)); - run_loop.Run(); - - // The Permission Prompt Bubble should NOT have reopened! - EXPECT_FALSE(test_api.manager()->IsRequestInProgress()); -} - -IN_PROC_BROWSER_TEST_F(WebUIToolbarWebViewPermissionBrowserTest, - LocationIconSuppressionE2E) { - WebUIToolbarWebView* webui_toolbar_view = GetWebUIToolbarWebView(browser()); - ASSERT_TRUE(webui_toolbar_view); - views::WebView* web_view = webui_toolbar_view->GetWebViewForTesting(); - content::WebContents* web_contents = web_view->GetWebContents(); - - ASSERT_TRUE(embedded_test_server()->Start()); - ASSERT_TRUE(ui_test_utils::NavigateToURL( - browser(), embedded_test_server()->GetURL("/empty.html"))); - - std::string get_icon_js = - "document.querySelector('toolbar-app')?.shadowRoot?" - ".querySelector('location-bar')?.shadowRoot?" - ".querySelector('location-icon')?.shadowRoot?" - ".querySelector('#iconContainer')"; - - ASSERT_TRUE(base::test::RunUntil([&]() { - return content::EvalJs(web_contents, - base::StrCat({get_icon_js, " !== null && ", - get_icon_js, ".offsetHeight > 0"})) - .ExtractBool(); - })); - - views::NamedWidgetShownWaiter widget_waiter( - views::test::AnyWidgetTestPasskey{}, "PageInfoBubbleView"); - - // First click: opens the Page Info Bubble. - EXPECT_TRUE(content::ExecJs( - web_contents, - base::StringPrintf( - "%s?.dispatchEvent(new MouseEvent('click', " - "{bubbles: true, cancelable: true, view: window, button: 0}));", - get_icon_js.c_str()))); - - views::Widget* bubble_widget = widget_waiter.WaitIfNeededAndGet(); - EXPECT_TRUE(bubble_widget); - EXPECT_TRUE(bubble_widget->IsVisible()); - - // Second click (simulating clicking to close): the pointerdown should trigger - // OnLhsChipMousePressed which sets suppress_lhs_chip_clicked_, and then - // the bubble closes natively due to focus loss. Since JS events don't - // natively trigger blur, we explicitly close the bubble. - views::test::WidgetDestroyedWaiter destroyed_waiter(bubble_widget); - bubble_widget->CloseWithReason(views::Widget::ClosedReason::kLostFocus); - destroyed_waiter.Wait(); - - EXPECT_TRUE(content::ExecJs( - web_contents, - base::StringPrintf( - "%s?.dispatchEvent(new PointerEvent('pointerdown', " - "{bubbles: true, cancelable: true, view: window, button: 0}));" - "%s?.dispatchEvent(new PointerEvent('click', " - "{bubbles: true, cancelable: true, view: window, button: 0, " - "pointerType: 'mouse'}));", - get_icon_js.c_str(), get_icon_js.c_str()))); - - base::RunLoop run_loop; - base::SingleThreadTaskRunner::GetCurrentDefault()->PostDelayedTask( - FROM_HERE, run_loop.QuitClosure(), base::Milliseconds(50)); - run_loop.Run(); - - // The Page Info Bubble should NOT have reopened! - EXPECT_EQ(nullptr, PageInfoBubbleViewBase::GetPageInfoBubbleForTesting()); -} - -INSTANTIATE_TEST_SUITE_P( - All, - WebUIToolbarWebViewPermissionBrowserTest, - testing::Values(permissions::RequestType::kGeolocation, - permissions::RequestType::kCameraStream, - permissions::RequestType::kMicStream));
diff --git a/chrome/browser/ui/views/user_education/browser_user_education_service.cc b/chrome/browser/ui/views/user_education/browser_user_education_service.cc index d3166d4..f2bee31 100644 --- a/chrome/browser/ui/views/user_education/browser_user_education_service.cc +++ b/chrome/browser/ui/views/user_education/browser_user_education_service.cc
@@ -29,7 +29,6 @@ #include "chrome/browser/ui/browser_actions.h" #include "chrome/browser/ui/browser_commands.h" #include "chrome/browser/ui/browser_element_identifiers.h" -#include "chrome/browser/ui/browser_finder.h" #include "chrome/browser/ui/browser_window/public/browser_window_features.h" #include "chrome/browser/ui/chrome_pages.h" #include "chrome/browser/ui/feature_first_run/autofill_ai_first_run_dialog.h"
diff --git a/chrome/browser/ui/views/user_education/custom_webui_help_bubble.h b/chrome/browser/ui/views/user_education/custom_webui_help_bubble.h index bfa8635..f71d8b45 100644 --- a/chrome/browser/ui/views/user_education/custom_webui_help_bubble.h +++ b/chrome/browser/ui/views/user_education/custom_webui_help_bubble.h
@@ -9,7 +9,6 @@ #include "base/memory/ptr_util.h" #include "chrome/browser/profiles/profiles_state.h" #include "chrome/browser/ui/browser.h" -#include "chrome/browser/ui/browser_finder.h" #include "chrome/browser/ui/views/bubble/webui_bubble_dialog_view.h" #include "chrome/browser/ui/views/user_education/custom_webui_help_bubble_controller.h" #include "chrome/browser/ui/views/user_education/impl/browser_user_education_context.h"
diff --git a/chrome/browser/ui/views/user_education/help_bubble_view_interactive_uitest.cc b/chrome/browser/ui/views/user_education/help_bubble_view_interactive_uitest.cc index 3b8663c..1e671f4 100644 --- a/chrome/browser/ui/views/user_education/help_bubble_view_interactive_uitest.cc +++ b/chrome/browser/ui/views/user_education/help_bubble_view_interactive_uitest.cc
@@ -48,6 +48,10 @@ #include "ui/views/view_class_properties.h" #include "ui/views/view_utils.h" +#if BUILDFLAG(IS_MAC) +#include "base/mac/mac_util.h" +#endif + using user_education::HelpBubbleArrow; using user_education::HelpBubbleParams; using user_education::HelpBubbleView; @@ -349,6 +353,13 @@ GTEST_SKIP_(kLinuxWaylandErrorMessage); } +#if BUILDFLAG(IS_MAC) + // TODO(crbug.com/510801992): Re-enable on macOS 26 once test is deflaked + if (base::mac::MacOSMajorVersion() == 26) { + GTEST_SKIP() << "Disabled on macOS Tahoe."; + } +#endif + UNCALLED_MOCK_CALLBACK(base::OnceClosure, default_button_clicked); constexpr char16_t kButton1Text[] = u"button 1"; @@ -391,6 +402,13 @@ GTEST_SKIP_(kLinuxWaylandErrorMessage); } +#if BUILDFLAG(IS_MAC) + // TODO(crbug.com/510801992): Re-enable on macOS 26 once test is deflaked + if (base::mac::MacOSMajorVersion() == 26) { + GTEST_SKIP() << "Disabled on macOS Tahoe."; + } +#endif + UNCALLED_MOCK_CALLBACK(base::OnceClosure, button_clicked); constexpr char16_t kButtonText[] = u"button";
diff --git a/chrome/browser/ui/views/web_apps/frame_toolbar/web_app_frame_toolbar_browsertest.cc b/chrome/browser/ui/views/web_apps/frame_toolbar/web_app_frame_toolbar_browsertest.cc index 1974d12..99963c8 100644 --- a/chrome/browser/ui/views/web_apps/frame_toolbar/web_app_frame_toolbar_browsertest.cc +++ b/chrome/browser/ui/views/web_apps/frame_toolbar/web_app_frame_toolbar_browsertest.cc
@@ -44,7 +44,7 @@ #include "chrome/browser/themes/theme_properties.h" #include "chrome/browser/ui/browser_commands.h" #include "chrome/browser/ui/browser_element_identifiers.h" -#include "chrome/browser/ui/browser_finder.h" +#include "chrome/browser/ui/download/download_display.h" #include "chrome/browser/ui/page_action/page_action_icon_type.h" #include "chrome/browser/ui/page_action/page_action_properties_provider.h" #include "chrome/browser/ui/tabs/tab_strip_model.h"
diff --git a/chrome/browser/ui/views/web_apps/web_app_blocked_migration_infobar_delegate_browsertest.cc b/chrome/browser/ui/views/web_apps/web_app_blocked_migration_infobar_delegate_browsertest.cc index ee4daa86..fa8b9264 100644 --- a/chrome/browser/ui/views/web_apps/web_app_blocked_migration_infobar_delegate_browsertest.cc +++ b/chrome/browser/ui/views/web_apps/web_app_blocked_migration_infobar_delegate_browsertest.cc
@@ -9,7 +9,6 @@ #include "base/time/default_clock.h" #include "chrome/browser/ui/browser.h" #include "chrome/browser/ui/browser_commands.h" -#include "chrome/browser/ui/browser_finder.h" #include "chrome/browser/ui/browser_window/public/browser_window_interface.h" #include "chrome/browser/ui/test/test_browser_ui.h" #include "chrome/browser/ui/views/frame/browser_view.h"
diff --git a/chrome/browser/ui/views/web_apps/web_app_install_dialog_delegate.cc b/chrome/browser/ui/views/web_apps/web_app_install_dialog_delegate.cc index 7b11edf..e25a3be 100644 --- a/chrome/browser/ui/views/web_apps/web_app_install_dialog_delegate.cc +++ b/chrome/browser/ui/views/web_apps/web_app_install_dialog_delegate.cc
@@ -19,7 +19,6 @@ #include "chrome/browser/feature_engagement/tracker_factory.h" #include "chrome/browser/profiles/profile.h" #include "chrome/browser/ui/actions/chrome_action_id.h" -#include "chrome/browser/ui/browser_finder.h" #include "chrome/browser/ui/page_action/page_action_controller.h" #include "chrome/browser/ui/page_action/page_action_icon_type.h" #include "chrome/browser/ui/tabs/public/tab_features.h"
diff --git a/chrome/browser/ui/views/web_apps/web_app_install_flow_dialog_delegate.cc b/chrome/browser/ui/views/web_apps/web_app_install_flow_dialog_delegate.cc index 1542982..fd358b0 100644 --- a/chrome/browser/ui/views/web_apps/web_app_install_flow_dialog_delegate.cc +++ b/chrome/browser/ui/views/web_apps/web_app_install_flow_dialog_delegate.cc
@@ -14,19 +14,31 @@ #include <vector> #include "base/check.h" +#include "base/check_deref.h" #include "base/functional/bind.h" #include "base/functional/callback_helpers.h" #include "base/memory/weak_ptr.h" +#include "base/metrics/histogram_functions.h" +#include "base/metrics/user_metrics.h" +#include "base/metrics/user_metrics_action.h" +#include "base/task/sequenced_task_runner.h" +#include "build/build_config.h" #include "chrome/app/vector_icons/vector_icons.h" #include "chrome/browser/apps/app_service/app_icon/app_icon_factory.h" #include "chrome/browser/apps/icon_standardizer.h" #include "chrome/browser/feature_engagement/tracker_factory.h" #include "chrome/browser/profiles/profile.h" +#include "chrome/browser/ui/actions/chrome_action_id.h" #include "chrome/browser/ui/intent_picker_tab_helper.h" +#include "chrome/browser/ui/page_action/page_action_controller.h" +#include "chrome/browser/ui/page_action/page_action_icon_type.h" +#include "chrome/browser/ui/tabs/public/tab_features.h" #include "chrome/browser/ui/url_identity.h" #include "chrome/browser/ui/views/chrome_layout_provider.h" #include "chrome/browser/ui/views/controls/site_icon_text_and_origin_view.h" -#include "chrome/browser/ui/views/page_action/page_action_icon_view.h" +#include "chrome/browser/ui/views/frame/browser_view.h" +#include "chrome/browser/ui/views/frame/toolbar_button_provider.h" +#include "chrome/browser/ui/views/location_bar/icon_label_bubble_view.h" #include "chrome/browser/ui/views/web_apps/progress_delay.h" #include "chrome/browser/ui/views/web_apps/web_app_icon_name_and_origin_view.h" #include "chrome/browser/ui/views/web_apps/web_app_install_dialog_delegate.h" @@ -40,12 +52,15 @@ #include "chrome/browser/web_applications/web_app_constants.h" #include "chrome/browser/web_applications/web_app_helpers.h" #include "chrome/browser/web_applications/web_app_install_info.h" +#include "chrome/browser/web_applications/web_app_pref_guardrails.h" #include "chrome/browser/web_applications/web_app_screenshot_fetcher.h" #include "chrome/common/url_constants.h" #include "chrome/grit/browser_resources.h" #include "chrome/grit/generated_resources.h" #include "chrome/grit/theme_resources.h" #include "components/constrained_window/constrained_window_views.h" +#include "components/feature_engagement/public/event_constants.h" +#include "components/feature_engagement/public/tracker.h" #include "components/prefs/pref_service.h" #include "components/strings/grit/components_strings.h" #include "components/url_formatter/elide_url.h" @@ -72,6 +87,13 @@ #include "url/gurl.h" #include "url/origin.h" +#if BUILDFLAG(IS_CHROMEOS) +// TODO(crbug.com/40147906): Enable gn check once it learns about conditional +// includes. +#include "components/metrics/structured/structured_events.h" // nogncheck +#include "components/metrics/structured/structured_metrics_client.h" // nogncheck +#endif + namespace web_app { DEFINE_CLASS_ELEMENT_IDENTIFIER_VALUE(WebAppInstallFlowDialogDelegate, @@ -80,6 +102,8 @@ kLearnMoreButtonId); DEFINE_CLASS_ELEMENT_IDENTIFIER_VALUE(WebAppInstallFlowDialogDelegate, kCancelButtonId); +DEFINE_CLASS_ELEMENT_IDENTIFIER_VALUE(WebAppInstallFlowDialogDelegate, + kInstallButton); std::ostream& operator<<(std::ostream& os, InstallOsType type) { switch (type) { @@ -97,6 +121,14 @@ namespace { +#if BUILDFLAG(IS_CHROMEOS) +namespace cros_events = metrics::structured::events::v2::cr_os_events; + +int64_t ToLong(web_app::WebAppInstallStatus web_app_install_status) { + return static_cast<int64_t>(web_app_install_status); +} +#endif + // The defaulted <=> compares fields in declaration order: not needing // upscaling beats needing it, and within each group the smaller distance to // the target wins. @@ -136,6 +168,55 @@ return resized_image; } +// Creates a scoped highlight on the corresponding page action icon, if any. +// Returns nullopt if not found. +std::optional<std::variant<views::Button::ScopedAnchorHighlight, + page_actions::ScopedPageActionActivity>> +NewPageActionHighlight(content::WebContents& web_contents) { + tabs::TabInterface* tab = + tabs::TabInterface::MaybeGetFromContents(&web_contents); + if (!tab) { + return std::nullopt; + } + + if (IsPageActionMigrated(PageActionIconType::kPwaInstall)) { + tabs::TabFeatures* tab_features = tab->GetTabFeatures(); + CHECK(tab_features); + + return tab_features->page_action_controller()->AddActivity( + kActionInstallPwa); + } + + // TODO(crbug.com/425953501): We shouldn't be using this. Once + // `ToolbarButtonProvider` is migrated to `BrowserWindowInterface`, we can + // use that directly. + Browser* browser = + tab->GetBrowserWindowInterface()->GetBrowserForMigrationOnly(); + + BrowserView* browser_view = BrowserView::GetBrowserViewForBrowser(browser); + if (!browser_view) { + return std::nullopt; + } + + ToolbarButtonProvider* toolbar_button_provider = + browser_view->toolbar_button_provider(); + if (!toolbar_button_provider) { + return std::nullopt; + } + + views::Button* install_icon = + toolbar_button_provider->GetPageActionView(kActionInstallPwa); + + if (install_icon) { + // TODO(crbug.com/40841129): move this to dialog->SetHighlightedElement. + return install_icon->AddAnchorHighlight(); + } + + return std::nullopt; +} + +constexpr int kMinBoundsForInstallDialog = 50; + } // namespace WebAppInstallFlowDialogDelegate::WebAppInstallFlowDialogDelegate( @@ -149,19 +230,20 @@ InstallDialogType dialog_type, InstallOsType os_type, std::unique_ptr<ProgressDelay> progress_delay) - : WebAppInstallDialogDelegate( - web_contents, - std::move(install_info), - std::move(install_tracker), - base::BindOnce(&WebAppInstallFlowDialogDelegate::OnAcceptCallback, - base::Unretained(this)), - iph_state, - prefs, - tracker, - dialog_type), + : WebAppModalDialogDelegate(web_contents), os_type_(os_type), + install_info_(std::move(install_info)), + install_tracker_(std::move(install_tracker)), callback_(std::move(callback)), + iph_state_(std::move(iph_state)), + prefs_(prefs), + tracker_(tracker), + dialog_type_(dialog_type), + page_action_highlight_(NewPageActionHighlight(CHECK_DEREF(web_contents))), progress_delay_(std::move(progress_delay)) { + CHECK(install_info_); + CHECK(install_tracker_); + CHECK(prefs_); CHECK(progress_delay_); } @@ -189,6 +271,9 @@ break; case InstallDialogStep::kSuccessful: + base::UmaHistogramEnumeration( + "WebApp.InstallConfirmation.CloseReason", + views::Widget::ClosedReason::kAcceptButtonClicked); if (reparent_closure_) { std::move(reparent_closure_).Run(); } @@ -202,7 +287,7 @@ case InstallDialogStep::kInstallerOptions: { ui::DialogModel::Button* ok_button = - dialog_model()->GetButtonByUniqueId(kPwaInstallDialogInstallButton); + dialog_model()->GetButtonByUniqueId(kInstallButton); if (ok_button) { dialog_model()->SetButtonLabel(ok_button, l10n_util::GetStringUTF16(IDS_INSTALL)); @@ -221,13 +306,13 @@ // Hide buttons on progress step. dialog_model()->SetVisible(kLearnMoreButtonId, false); - dialog_model()->SetVisible(kPwaInstallDialogInstallButton, false); + dialog_model()->SetVisible(kInstallButton, false); dialog_model()->SetVisible(kCancelButtonId, false); break; case InstallDialogStep::kSuccessful: { ui::DialogModel::Button* ok_button = - dialog_model()->GetButtonByUniqueId(kPwaInstallDialogInstallButton); + dialog_model()->GetButtonByUniqueId(kInstallButton); if (ok_button) { dialog_model()->SetButtonLabel( ok_button, @@ -239,7 +324,7 @@ dialog_model()->SetButtonLabel(cancel_button, l10n_util::GetStringUTF16(IDS_CLOSE)); } - dialog_model()->SetVisible(kPwaInstallDialogInstallButton, true); + dialog_model()->SetVisible(kInstallButton, true); dialog_model()->SetVisible(kCancelButtonId, true); break; } @@ -274,7 +359,195 @@ install_info_->add_to_desktop = options_view_->IsAddDesktopShortcutChecked(); } - WebAppInstallDialogDelegate::OnAccept(); + + MeasureAcceptUserActionsForInstallDialog(); + if (iph_state_ == PwaInProductHelpState::kShown) { + webapps::AppId app_id = + GenerateAppIdFromManifestId(install_info_->manifest_id()); + WebAppPrefGuardrails::GetForDesktopInstallIph(prefs_).RecordAccept(app_id); + tracker_->NotifyEvent(feature_engagement::events::kDesktopPwaInstalled); + } + +#if BUILDFLAG(IS_CHROMEOS) + const webapps::AppId app_id = + web_app::GenerateAppIdFromManifestId(install_info_->manifest_id()); + metrics::structured::StructuredMetricsClient::Record( + cros_events::AppDiscovery_Browser_AppInstallDialogResult() + .SetWebAppInstallStatus( + ToLong(web_app::WebAppInstallStatus::kAccepted)) + .SetAppId(app_id)); +#endif // BUILDFLAG(IS_CHROMEOS) + + // DIY apps get their name from the DIY install dialog and are always set to + // open in a new window. + if (dialog_type_ == InstallDialogType::kDiy) { + CHECK(!text_field_contents_.empty()); + install_info_->title = text_field_contents_; + install_info_->user_display_mode = + web_app::mojom::UserDisplayMode::kStandalone; + } + + // The password manager PWA installation tutorial requires the + // `kInstalledPWAEventId` event to be fired from the detailed install dialog. + // See `kPasswordManagerTutorialMetricPrefix` in + // `MaybeRegisterChromeTutorials()` for more information. + if (dialog_type_ == InstallDialogType::kDetailed) { + auto* element_tracker = ui::ElementTracker::GetElementTracker(); + auto* element_framework = ui::ElementTracker::GetFrameworkDelegate(); + CHECK(element_tracker); + auto* ok_button = element_tracker->GetElementInAnyContext(kInstallButton); + if (ok_button && element_framework) { + element_framework->NotifyCustomEvent( + ok_button, WebAppInstallDialogDelegate::kInstalledPWAEventId); + } + } + + CHECK(callback_); + CHECK(install_tracker_); + install_tracker_->ReportResult(webapps::MlInstallUserResponse::kAccepted); + received_user_response_ = true; + + std::move(callback_).Run( + true, std::move(install_info_), + base::BindOnce(&WebAppInstallFlowDialogDelegate::OnInstallResult, + AsWeakPtr())); +} + +void WebAppInstallFlowDialogDelegate::OnCancel() { + CHECK(install_tracker_); + install_tracker_->ReportResult(webapps::MlInstallUserResponse::kCancelled); + received_user_response_ = true; + base::UmaHistogramEnumeration( + "WebApp.InstallConfirmation.CloseReason", + views::Widget::ClosedReason::kCancelButtonClicked); + MeasureIphOnDialogClose(); +} + +void WebAppInstallFlowDialogDelegate::OnClose() { + CHECK(install_tracker_); + install_tracker_->ReportResult(webapps::MlInstallUserResponse::kIgnored); + received_user_response_ = true; + base::UmaHistogramEnumeration( + "WebApp.InstallConfirmation.CloseReason", + views::Widget::ClosedReason::kCloseButtonClicked); + MeasureIphOnDialogClose(); +} + +void WebAppInstallFlowDialogDelegate::OnDestroyed() { + if (received_user_response_) { + return; + } + install_tracker_->ReportResult(webapps::MlInstallUserResponse::kIgnored); + base::UmaHistogramEnumeration("WebApp.InstallConfirmation.CloseReason", + views::Widget::ClosedReason::kUnspecified); + MeasureIphOnDialogClose(); +} + +void WebAppInstallFlowDialogDelegate::OnTextFieldChangedMaybeUpdateButton( + const std::u16string& text_field_contents) { + text_field_contents_ = text_field_contents; + if (!dialog_model() || !dialog_model()->host()) { + return; + } + + ui::DialogModel::Button* ok_button = nullptr; + if (dialog_model()->HasField(kInstallButton)) { + ok_button = dialog_model()->GetButtonByUniqueId(kInstallButton); + } + + if (ok_button) { + dialog_model()->SetButtonEnabled(ok_button, + /*enabled=*/!text_field_contents.empty()); + } +} + +bool WebAppInstallFlowDialogDelegate:: + IsWidgetCurrentSizeSmallerThanPreferredSize(views::Widget* widget) { + const gfx::Size& current_size = widget->GetSize(); + const gfx::Size& preferred_size = + widget->GetContentsView()->GetPreferredSize(); + int min_width = preferred_size.width() - kMinBoundsForInstallDialog; + int min_height = preferred_size.height() - kMinBoundsForInstallDialog; + return current_size.width() < min_width || current_size.height() < min_height; +} + +void WebAppInstallFlowDialogDelegate::OnWidgetBoundsChanged( + views::Widget* widget, + const gfx::Rect& new_bounds) { + if (IsWidgetCurrentSizeSmallerThanPreferredSize(widget)) { + base::SequencedTaskRunner::GetCurrentDefault()->PostTask( + FROM_HERE, + base::BindOnce(&WebAppInstallFlowDialogDelegate::CloseDialogAsIgnored, + weak_ptr_factory_.GetWeakPtr())); + } +} + +void WebAppInstallFlowDialogDelegate::CloseDialogAsIgnored() { + if (!dialog_model() || !dialog_model()->host()) { + return; + } + CHECK(install_tracker_); + install_tracker_->ReportResult(webapps::MlInstallUserResponse::kIgnored); + dialog_model()->host()->Close(); +} + +void WebAppInstallFlowDialogDelegate::MeasureIphOnDialogClose() { + MeasureCancelUserActionsForInstallDialog(); + if (iph_state_ == PwaInProductHelpState::kShown && install_info_) { + webapps::AppId app_id = + GenerateAppIdFromManifestId(install_info_->manifest_id()); + WebAppPrefGuardrails::GetForDesktopInstallIph(prefs_).RecordIgnore( + app_id, base::Time::Now()); + } + + if (install_info_ && callback_) { + // If |install_info_| is populated, then the dialog was not accepted. +#if BUILDFLAG(IS_CHROMEOS) + const webapps::AppId app_id = + web_app::GenerateAppIdFromManifestId(install_info_->manifest_id()); + metrics::structured::StructuredMetricsClient::Record( + cros_events::AppDiscovery_Browser_AppInstallDialogResult() + .SetWebAppInstallStatus( + ToLong(web_app::WebAppInstallStatus::kCancelled)) + .SetAppId(app_id)); +#endif // BUILDFLAG(IS_CHROMEOS) + std::move(callback_).Run(false, std::move(install_info_), + base::DoNothing()); + } +} + +void WebAppInstallFlowDialogDelegate:: + MeasureAcceptUserActionsForInstallDialog() { + const char* accept_dialog_metric_name = nullptr; + switch (dialog_type_) { + case InstallDialogType::kDetailed: + accept_dialog_metric_name = "WebAppDetailedInstallAccepted"; + break; + case InstallDialogType::kSimple: + accept_dialog_metric_name = "WebAppInstallAccepted"; + break; + case InstallDialogType::kDiy: + accept_dialog_metric_name = "WebAppDiyInstallAccepted"; + break; + } + base::RecordAction(base::UserMetricsAction(accept_dialog_metric_name)); +} + +void WebAppInstallFlowDialogDelegate:: + MeasureCancelUserActionsForInstallDialog() { + const char* cancel_dialog_metric_name = nullptr; + switch (dialog_type_) { + case InstallDialogType::kDetailed: + cancel_dialog_metric_name = "WebAppDetailedInstallCancelled"; + break; + case InstallDialogType::kSimple: + cancel_dialog_metric_name = "WebAppInstallCancelled"; + break; + case InstallDialogType::kDiy: + cancel_dialog_metric_name = "WebAppDiyInstallCancelled"; + break; + } + base::RecordAction(base::UserMetricsAction(cancel_dialog_metric_name)); } // Updates dialog title based on current step. @@ -343,17 +616,6 @@ UpdateProgressAndMaybeAdvance(); } -void WebAppInstallFlowDialogDelegate::OnAcceptCallback( - bool success, - std::unique_ptr<WebAppInstallInfo> web_app_info) { - if (callback_) { - std::move(callback_).Run( - success, std::move(web_app_info), - base::BindOnce(&WebAppInstallFlowDialogDelegate::OnInstallResult, - AsWeakPtr())); - } -} - void WebAppInstallFlowDialogDelegate::OnProgress( std::optional<double> percent) { timer_percentage_ = percent; @@ -407,9 +669,9 @@ WebAppInstallIntroView::Create( install_type, icon_image_32, title, start_url, dialog_image_info.is_maskable, description, screenshot_fetcher, - base::BindRepeating( - &WebAppInstallDialogDelegate::OnTextFieldChangedMaybeUpdateButton, - delegate_weak_ptr)); + base::BindRepeating(&WebAppInstallFlowDialogDelegate:: + OnTextFieldChangedMaybeUpdateButton, + delegate_weak_ptr)); // kInstallerOptions auto options_view = WebAppInstallOptionsView::Create( @@ -483,15 +745,15 @@ ? l10n_util::GetStringUTF16(IDS_INSTALL) : l10n_util::GetStringUTF16( IDS_WEB_APP_INSTALL_FLOW_NEXT)) - .SetId( - WebAppInstallDialogDelegate::kPwaInstallDialogInstallButton)) - .AddCancelButton(base::BindOnce(&WebAppInstallDialogDelegate::OnCancel, - delegate_weak_ptr), - ui::DialogModel::Button::Params().SetId(kCancelButtonId)) + .SetId(WebAppInstallFlowDialogDelegate::kInstallButton)) + .AddCancelButton( + base::BindOnce(&WebAppInstallFlowDialogDelegate::OnCancel, + delegate_weak_ptr), + ui::DialogModel::Button::Params().SetId(kCancelButtonId)) .SetCloseActionCallback(base::BindOnce( - &WebAppInstallDialogDelegate::OnClose, delegate_weak_ptr)) + &WebAppInstallFlowDialogDelegate::OnClose, delegate_weak_ptr)) .SetDialogDestroyingCallback(base::BindOnce( - &WebAppInstallDialogDelegate::OnDestroyed, delegate_weak_ptr)) + &WebAppInstallFlowDialogDelegate::OnDestroyed, delegate_weak_ptr)) .OverrideDefaultButton(ui::mojom::DialogButton::kCancel) .AddCustomField( std::make_unique<views::BubbleDialogModelHost::CustomView>(
diff --git a/chrome/browser/ui/views/web_apps/web_app_install_flow_dialog_delegate.h b/chrome/browser/ui/views/web_apps/web_app_install_flow_dialog_delegate.h index 7f073669..9620fd1 100644 --- a/chrome/browser/ui/views/web_apps/web_app_install_flow_dialog_delegate.h +++ b/chrome/browser/ui/views/web_apps/web_app_install_flow_dialog_delegate.h
@@ -8,15 +8,23 @@ #include <iosfwd> #include <memory> #include <optional> +#include <string> +#include <variant> +#include "base/memory/raw_ptr.h" #include "base/memory/weak_ptr.h" +#include "chrome/browser/ui/page_action/page_action_controller.h" #include "chrome/browser/ui/views/web_apps/web_app_install_dialog_delegate.h" +#include "chrome/browser/ui/views/web_apps/web_app_modal_dialog_delegate.h" #include "chrome/browser/ui/web_applications/web_app_dialogs.h" #include "chrome/browser/web_applications/web_app_install_params.h" #include "ui/base/identifier/unique_identifier.h" +#include "ui/views/controls/button/button.h" -namespace content { -class WebContents; +class PrefService; + +namespace feature_engagement { +class Tracker; } namespace webapps { @@ -43,11 +51,12 @@ inline constexpr int kLargeImageSize = 80; std::ostream& operator<<(std::ostream& os, InstallOsType type); -class WebAppInstallFlowDialogDelegate : public WebAppInstallDialogDelegate { +class WebAppInstallFlowDialogDelegate : public WebAppModalDialogDelegate { public: DECLARE_CLASS_ELEMENT_IDENTIFIER_VALUE(kInstallDialogFlowViewId); DECLARE_CLASS_ELEMENT_IDENTIFIER_VALUE(kLearnMoreButtonId); DECLARE_CLASS_ELEMENT_IDENTIFIER_VALUE(kCancelButtonId); + DECLARE_CLASS_ELEMENT_IDENTIFIER_VALUE(kInstallButton); WebAppInstallFlowDialogDelegate( content::WebContents* web_contents, @@ -85,13 +94,28 @@ bool AdvanceToNextStepOrClose(); - void OnAccept() override; + void OnAccept(); + void OnCancel(); + void OnClose(); + void OnDestroyed(); + + void OnTextFieldChangedMaybeUpdateButton( + const std::u16string& text_field_contents); + void OnProgress(std::optional<double> percent); base::WeakPtr<WebAppInstallFlowDialogDelegate> AsWeakPtr() { return weak_ptr_factory_.GetWeakPtr(); } + // views::WidgetObserver overrides: + void OnWidgetBoundsChanged(views::Widget* widget, + const gfx::Rect& new_bounds) override; + // WebAppModalDialogDelegate overrides: + void CloseDialogAsIgnored() override; + + InstallDialogType dialog_type() const { return dialog_type_; } + protected: InstallDialogStep current_step_ = InstallDialogStep::kInstallDialog; InstallOsType os_type_; @@ -104,10 +128,28 @@ void UpdateDialogTitleAndHeader(InstallDialogStep step); void UpdateProgressAndMaybeAdvance(); void OnInstallResult(bool success, base::OnceClosure reparent_closure); - void OnAcceptCallback(bool success, - std::unique_ptr<WebAppInstallInfo> web_app_info); + void MeasureIphOnDialogClose(); + void MeasureAcceptUserActionsForInstallDialog(); + void MeasureCancelUserActionsForInstallDialog(); + + static bool IsWidgetCurrentSizeSmallerThanPreferredSize( + views::Widget* widget); + + std::unique_ptr<WebAppInstallInfo> install_info_; + std::unique_ptr<webapps::MlInstallOperationTracker> install_tracker_; WebAppInstallationAcceptanceCallback callback_; + PwaInProductHelpState iph_state_; + raw_ptr<PrefService> prefs_; + raw_ptr<feature_engagement::Tracker> tracker_; + InstallDialogType dialog_type_; + std::u16string text_field_contents_; + bool received_user_response_ = false; + + const std::optional<std::variant<views::Button::ScopedAnchorHighlight, + page_actions::ScopedPageActionActivity>> + page_action_highlight_; + std::unique_ptr<ProgressDelay> progress_delay_; bool install_success_ = false; std::optional<double> timer_percentage_ = 0.0;
diff --git a/chrome/browser/ui/views/web_apps/web_app_integration_test_driver.cc b/chrome/browser/ui/views/web_apps/web_app_integration_test_driver.cc index 01c152f..f19c7de4 100644 --- a/chrome/browser/ui/views/web_apps/web_app_integration_test_driver.cc +++ b/chrome/browser/ui/views/web_apps/web_app_integration_test_driver.cc
@@ -1813,13 +1813,18 @@ target_contents = tab_added_waiter.Wait(); } ASSERT_TRUE(target_contents); - auto url_matcher = base::BindRepeating([](const GURL& url) { - return base::EndsWith(url.path(), "foo_handler.html") || - base::EndsWith(url.path(), "bar_handler.html"); - }); - test::WebAppPageWaiter page_waiter(target_contents); - page_waiter.ExpectUrlIf(url_matcher).ManifestOrLoadedNoManifest(); - ASSERT_TRUE(page_waiter.WaitAndFlushCommands()); + + base::flat_set<GURL> valid_urls = { + delegate_->EmbeddedTestServer()->GetURL( + "/webapps_integration/file_handler/bar_handler.html"), + delegate_->EmbeddedTestServer()->GetURL( + "/webapps_integration/file_handler/foo_handler.html") + + }; + ASSERT_TRUE(test::WebAppPageWaiter(target_contents) + .ExpectAnyUrl(valid_urls) + .ManifestOrLoadedNoManifest() + .WaitAndFlushCommands()); } AfterStateChangeAction(); @@ -1865,18 +1870,22 @@ bool is_denied = site_remember_deny_open_file_.contains(site); std::string expected_fallback_path = GetSiteConfiguration(site).relative_url; - auto url_matcher = base::BindRepeating( - [](bool is_denied, const std::string& fallback_path, const GURL& url) { - if (is_denied) { - return url.path() == fallback_path; - } - return base::EndsWith(url.path(), "foo_handler.html") || - base::EndsWith(url.path(), "bar_handler.html"); - }, - is_denied, expected_fallback_path); - test::WebAppPageWaiter waiter(target_contents); - waiter.ExpectUrlIf(url_matcher).ManifestOrLoadedNoManifest(); - ASSERT_TRUE(waiter.WaitAndFlushCommands()); + base::flat_set<GURL> valid_urls; + if (is_denied) { + valid_urls = { + delegate_->EmbeddedTestServer()->GetURL(expected_fallback_path)}; + } else { + valid_urls = {delegate_->EmbeddedTestServer()->GetURL( + "/webapps_integration/file_handler/bar_handler.html"), + delegate_->EmbeddedTestServer()->GetURL( + "/webapps_integration/file_handler/foo_handler.html") + + }; + } + ASSERT_TRUE(test::WebAppPageWaiter(target_contents) + .ExpectAnyUrl(valid_urls) + .ManifestOrLoadedNoManifest() + .WaitAndFlushCommands()); AfterStateChangeAction(); }
diff --git a/chrome/browser/ui/views/webid/account_selection_bubble_view.cc b/chrome/browser/ui/views/webid/account_selection_bubble_view.cc index b6a2858..37f33a5 100644 --- a/chrome/browser/ui/views/webid/account_selection_bubble_view.cc +++ b/chrome/browser/ui/views/webid/account_selection_bubble_view.cc
@@ -14,7 +14,6 @@ #include "base/strings/utf_string_conversions.h" #include "chrome/app/vector_icons/vector_icons.h" #include "chrome/browser/image_fetcher/image_decoder_impl.h" -#include "chrome/browser/ui/browser_finder.h" #include "chrome/browser/ui/passwords/ui_utils.h" #include "chrome/browser/ui/ui_features.h" #include "chrome/browser/ui/views/controls/hover_button.h"
diff --git a/chrome/browser/ui/web_applications/app_browser_controller_browsertest_chromeos.cc b/chrome/browser/ui/web_applications/app_browser_controller_browsertest_chromeos.cc index 3cec70d..99a44d2 100644 --- a/chrome/browser/ui/web_applications/app_browser_controller_browsertest_chromeos.cc +++ b/chrome/browser/ui/web_applications/app_browser_controller_browsertest_chromeos.cc
@@ -16,7 +16,6 @@ #include "chrome/browser/ui/ash/system_web_apps/system_web_app_ui_utils.h" #include "chrome/browser/ui/browser.h" #include "chrome/browser/ui/browser_commands.h" -#include "chrome/browser/ui/browser_finder.h" #include "chrome/browser/ui/browser_tabstrip.h" #include "chrome/browser/ui/browser_window.h" #include "chrome/browser/ui/browser_window/public/global_browser_collection.h"
diff --git a/chrome/browser/ui/web_applications/test/web_app_browsertest_util.cc b/chrome/browser/ui/web_applications/test/web_app_browsertest_util.cc index a1c46b1..dd9fb6a 100644 --- a/chrome/browser/ui/web_applications/test/web_app_browsertest_util.cc +++ b/chrome/browser/ui/web_applications/test/web_app_browsertest_util.cc
@@ -120,6 +120,15 @@ const GURL& app_url, InstallWebAppOptions options) { EXPECT_TRUE(ui_test_utils::NavigateToURL(browser, app_url)); + testing::AssertionResult waiter_result = + test::WebAppPageWaiter(browser->tab_strip_model()->GetActiveWebContents()) + .ExpectUrl(app_url) + .ManifestOrLoadedNoManifest() + .WaitAndFlushCommands(); + EXPECT_TRUE(waiter_result); + if (!waiter_result) { + return webapps::AppId(); + } webapps::AppId app_id; base::RunLoop run_loop; @@ -139,7 +148,9 @@ return webapps::AppId(); } - provider->command_manager().AwaitAllCommandsCompleteForTesting(); + if (options.launch_or_reparent_page_to_app) { + provider->command_manager().AwaitAllCommandsCompleteForTesting(); + } EXPECT_EQ(install_future.Get<webapps::InstallResultCode>(), webapps::InstallResultCode::kSuccessNewInstall); @@ -188,6 +199,15 @@ app_url); NavigateViaLinkClickToURLAndWait(browser, app_url); registration_waiter.AwaitRegistration(); + testing::AssertionResult waiter_result = + test::WebAppPageWaiter(browser->tab_strip_model()->GetActiveWebContents()) + .ExpectUrl(app_url) + .ExpectManifest() + .WaitAndFlushCommands(); + EXPECT_TRUE(waiter_result); + if (!waiter_result) { + return webapps::AppId(); + } webapps::AppId app_id; base::RunLoop run_loop; @@ -198,7 +218,7 @@ provider->scheduler().FetchManifestAndInstall( webapps::WebappInstallSource::MENU_BROWSER_TAB, browser->tab_strip_model()->GetActiveWebContents()->GetWeakPtr(), - base::BindOnce(&AutoAcceptDialogCallback, true), + base::BindOnce(&AutoAcceptDialogCallback, /*launch=*/false), base::BindLambdaForTesting( [&run_loop, &app_id](const webapps::AppId& installed_app_id, webapps::InstallResultCode code) { @@ -209,7 +229,6 @@ FallbackBehavior::kCraftedManifestOnly); run_loop.Run(); - provider->command_manager().AwaitAllCommandsCompleteForTesting(); return app_id; } @@ -244,7 +263,14 @@ // that case from waiting for loading to stop. bool will_url_finish_loading = start_url.GetPath() != "/hung"; if (will_url_finish_loading) { - content::WaitForLoadStop(web_contents); + testing::AssertionResult waiter_result = + test::WebAppPageWaiter(web_contents) + .ManifestOrLoadedNoManifest() + .WaitAndFlushCommands(); + EXPECT_TRUE(waiter_result); + if (!waiter_result) { + return nullptr; + } } WebAppTabHelper* tab_helper = WebAppTabHelper::FromWebContents(web_contents); @@ -271,16 +297,7 @@ Browser* LaunchWebAppBrowserAndWait(Profile* profile, const webapps::AppId& app_id, WindowOpenDisposition disposition) { - ui_test_utils::UrlLoadObserver url_observer( - WebAppProvider::GetForTest(profile)->registrar_unsafe().GetAppLaunchUrl( - app_id)); - Browser* const app_browser = - LaunchWebAppBrowser(profile, app_id, disposition); - if (app_browser) { - url_observer.Wait(); - content::WaitForLoadStop(url_observer.web_contents()); - } - return app_browser; + return LaunchWebAppBrowser(profile, app_id, disposition); } Browser* LaunchBrowserForWebAppInTab(Profile* profile,
diff --git a/chrome/browser/ui/web_applications/test/web_app_navigation_browsertest.cc b/chrome/browser/ui/web_applications/test/web_app_navigation_browsertest.cc index 32cf198..0a1d5d1 100644 --- a/chrome/browser/ui/web_applications/test/web_app_navigation_browsertest.cc +++ b/chrome/browser/ui/web_applications/test/web_app_navigation_browsertest.cc
@@ -16,7 +16,6 @@ #include "chrome/browser/apps/app_service/app_registry_cache_waiter.h" #include "chrome/browser/profiles/profile_io_data.h" #include "chrome/browser/ui/browser.h" -#include "chrome/browser/ui/browser_finder.h" #include "chrome/browser/ui/browser_window/public/global_browser_collection.h" #include "chrome/browser/ui/browser_window/public/profile_browser_collection.h" #include "chrome/browser/ui/tabs/tab_strip_model.h"
diff --git a/chrome/browser/ui/web_applications/web_app_browsertest.cc b/chrome/browser/ui/web_applications/web_app_browsertest.cc index 53e9dd8c..906ba44f 100644 --- a/chrome/browser/ui/web_applications/web_app_browsertest.cc +++ b/chrome/browser/ui/web_applications/web_app_browsertest.cc
@@ -43,7 +43,6 @@ #include "chrome/browser/themes/theme_service_factory.h" #include "chrome/browser/ui/browser.h" #include "chrome/browser/ui/browser_commands.h" -#include "chrome/browser/ui/browser_finder.h" #include "chrome/browser/ui/browser_tabstrip.h" #include "chrome/browser/ui/browser_window.h" #include "chrome/browser/ui/browser_window/public/browser_window_features.h" @@ -71,6 +70,7 @@ #include "chrome/browser/web_applications/os_integration/web_app_shortcut.h" #include "chrome/browser/web_applications/test/os_integration_test_override_impl.h" #include "chrome/browser/web_applications/test/web_app_install_test_utils.h" +#include "chrome/browser/web_applications/test/web_app_page_waiter.h" #include "chrome/browser/web_applications/test/web_app_test_observers.h" #include "chrome/browser/web_applications/test/web_app_test_utils.h" #include "chrome/browser/web_applications/web_app_command_manager.h" @@ -265,8 +265,10 @@ content::WebContents* const web_contents = app_browser->tab_strip_model()->GetActiveWebContents(); - EXPECT_TRUE(WaitForLoadStop(web_contents)); - EXPECT_EQ(app_url, web_contents->GetVisibleURL()); + EXPECT_TRUE(test::WebAppPageWaiter(web_contents) + .ExpectUrl(app_url) + .ManifestOrLoadedNoManifest() + .WaitAndFlushCommands()); const bool result = app_browser->app_controller()->HasMinimalUiButtons(); EXPECT_EQ(
diff --git a/chrome/browser/ui/web_applications/web_app_engagement_browsertest.cc b/chrome/browser/ui/web_applications/web_app_engagement_browsertest.cc index 128d2be8..4b29881 100644 --- a/chrome/browser/ui/web_applications/web_app_engagement_browsertest.cc +++ b/chrome/browser/ui/web_applications/web_app_engagement_browsertest.cc
@@ -246,7 +246,7 @@ base::HistogramTester tester; GURL example_url( - embedded_test_server()->GetURL("/banners/manifest_test_page.html")); + embedded_test_server()->GetURL("/banners/no_manifest_test_page.html")); auto web_app_info = WebAppInstallInfo::CreateWithStartUrlForTesting(example_url); web_app_info->scope = example_url; @@ -315,7 +315,7 @@ base::HistogramTester tester; GURL example_url( - embedded_test_server()->GetURL("/banners/manifest_test_page.html")); + embedded_test_server()->GetURL("/banners/no_manifest_test_page.html")); auto web_app_info = WebAppInstallInfo::CreateWithStartUrlForTesting(example_url);
diff --git a/chrome/browser/ui/web_applications/web_app_menu_model_browsertest.cc b/chrome/browser/ui/web_applications/web_app_menu_model_browsertest.cc index a216e03..5ccdbcb 100644 --- a/chrome/browser/ui/web_applications/web_app_menu_model_browsertest.cc +++ b/chrome/browser/ui/web_applications/web_app_menu_model_browsertest.cc
@@ -10,6 +10,7 @@ #include "ash/constants/web_app_id_constants.h" #include "chrome/browser/ui/browser.h" #include "chrome/browser/ui/browser_commands.h" +#include "chrome/browser/ui/web_applications/test/web_app_browsertest_util.h" #include "chrome/browser/ui/web_applications/web_app_browsertest_base.h" #include "chrome/browser/web_applications/model/pending_migration_info.h" #include "chrome/browser/web_applications/test/prevent_close_test_base.h" @@ -113,7 +114,7 @@ IN_PROC_BROWSER_TEST_F(WebAppMenuModelBrowserTest, HasPendingUpdate) { const GURL app_url = GetInstallableAppURL(); - const webapps::AppId app_id = InstallPWA(app_url); + const webapps::AppId app_id = InstallWebAppFromPage(browser(), app_url); Browser* const browser = LaunchWebAppBrowser(app_id); { @@ -177,7 +178,7 @@ IN_PROC_BROWSER_TEST_F(WebAppMenuModelMigrationBrowserTest, HasPendingMigration) { const GURL app_url = GetInstallableAppURL(); - const webapps::AppId app_id = InstallPWA(app_url); + const webapps::AppId app_id = InstallWebAppFromPage(browser(), app_url); Browser* const browser = LaunchWebAppBrowser(app_id); {
diff --git a/chrome/browser/ui/webui/accessibility_annotator/BUILD.gn b/chrome/browser/ui/webui/accessibility_annotator/BUILD.gn index 9023d85..3274fe2 100644 --- a/chrome/browser/ui/webui/accessibility_annotator/BUILD.gn +++ b/chrome/browser/ui/webui/accessibility_annotator/BUILD.gn
@@ -30,7 +30,6 @@ "//components/accessibility_annotator/core", "//components/accessibility_annotator/first_run", "//components/signin/public/identity_manager", - "//components/strings:components_variant_strings", "//content/public/browser", "//mojo/public/cpp/bindings", "//skia",
diff --git a/chrome/browser/ui/webui/accessibility_annotator/accessibility_annotator_info_ui.cc b/chrome/browser/ui/webui/accessibility_annotator/accessibility_annotator_info_ui.cc index 6531747..80735f76 100644 --- a/chrome/browser/ui/webui/accessibility_annotator/accessibility_annotator_info_ui.cc +++ b/chrome/browser/ui/webui/accessibility_annotator/accessibility_annotator_info_ui.cc
@@ -11,8 +11,6 @@ #include "chrome/grit/accessibility_annotator_info_resources.h" #include "chrome/grit/accessibility_annotator_info_resources_map.h" #include "chrome/grit/generated_resources.h" -#include "components/accessibility_annotator/core/url_constants.h" -#include "components/strings/grit/components_variant_strings.h" #include "content/public/browser/browser_context.h" #include "content/public/browser/web_contents.h" #include "content/public/browser/web_ui_data_source.h" @@ -45,25 +43,8 @@ webui::SetupWebUIDataSource( source, kAccessibilityAnnotatorInfoResources, IDR_ACCESSIBILITY_ANNOTATOR_INFO_ACCESSIBILITY_ANNOTATOR_INFO_HTML); - source->AddLocalizedString("accessibilityAnnotatorInfoTitle", - IDS_ACCESSIBILITY_ANNOTATOR_INFO_TITLE); - source->AddLocalizedString( - "accessibilityAnnotatorInfoDescription", - IDS_ACCESSIBILITY_ANNOTATOR_INFO_DESCRIPTION_DESKTOP); - source->AddLocalizedString("accessibilityAnnotatorInfoCard1", - IDS_ACCESSIBILITY_ANNOTATOR_INFO_CARD_1_DESKTOP); - source->AddLocalizedString("accessibilityAnnotatorInfoCard2", - IDS_ACCESSIBILITY_ANNOTATOR_INFO_CARD_2_DESKTOP); - source->AddLocalizedString( - "accessibilityAnnotatorInfoLearnMore", - IDS_ACCESSIBILITY_ANNOTATOR_INFO_LEARN_MORE_DESKTOP); - source->AddLocalizedString("accessibilityAnnotatorInfoPrimaryButton", - IDS_ACCESSIBILITY_ANNOTATOR_INFO_PRIMARY_BUTTON); - source->AddLocalizedString("accessibilityAnnotatorInfoSecondaryButton", - IDS_ACCESSIBILITY_ANNOTATOR_INFO_SECONDARY_BUTTON); - source->AddString( - "accessibilityAnnotatorTriggerText", - accessibility_annotator::kAccessibilityAnnotatorTriggerText); + // TODO(crbug.com/500663691): Update strings. + source->AddLocalizedString("privacyPageTitle", IDS_SETTINGS_PRIVACY); } AccessibilityAnnotatorInfoUI::~AccessibilityAnnotatorInfoUI() {
diff --git a/chrome/browser/ui/webui/ai_overlay_dialog/tools/tools.cc b/chrome/browser/ui/webui/ai_overlay_dialog/tools/tools.cc index bf26a59..e53e383c 100644 --- a/chrome/browser/ui/webui/ai_overlay_dialog/tools/tools.cc +++ b/chrome/browser/ui/webui/ai_overlay_dialog/tools/tools.cc
@@ -27,7 +27,6 @@ #include "chrome/browser/search_engines/template_url_service_factory.h" #include "chrome/browser/translate/chrome_translate_client.h" #include "chrome/browser/ui/browser.h" -#include "chrome/browser/ui/browser_finder.h" #include "chrome/browser/ui/browser_window.h" #include "chrome/browser/ui/browser_window/public/browser_window_interface.h" #include "chrome/browser/ui/navigator/browser_navigator.h"
diff --git a/chrome/browser/ui/webui/ash/cellular_setup/BUILD.gn b/chrome/browser/ui/webui/ash/cellular_setup/BUILD.gn index 70f58b5..f3f043e 100644 --- a/chrome/browser/ui/webui/ash/cellular_setup/BUILD.gn +++ b/chrome/browser/ui/webui/ash/cellular_setup/BUILD.gn
@@ -29,6 +29,7 @@ "//chrome/browser:browser_process", "//chrome/browser:resources", "//chrome/browser/profiles:profile", + "//chrome/browser/ui:simple_message_box_headers", "//chrome/common", "//chromeos/ash/components/network", "//components/login",
diff --git a/chrome/browser/ui/webui/chrome_web_ui_controller_factory.cc b/chrome/browser/ui/webui/chrome_web_ui_controller_factory.cc index ec24b0ff..c978f8f2 100644 --- a/chrome/browser/ui/webui/chrome_web_ui_controller_factory.cc +++ b/chrome/browser/ui/webui/chrome_web_ui_controller_factory.cc
@@ -352,7 +352,9 @@ // https://crbug.com/40583261 origin.host() == chrome::kChromeUIDownloadsHost || // https://crbug.com/376417346 - origin.host() == chrome::kChromeUIExtensionsHost; + origin.host() == chrome::kChromeUIExtensionsHost || + // https://crbug.com/509216218 + origin.host() == chrome::kChromeUIDrivePickerHostHost; } ChromeWebUIControllerFactory::ChromeWebUIControllerFactory() = default;
diff --git a/chrome/browser/ui/webui/cr_components/composebox/BUILD.gn b/chrome/browser/ui/webui/cr_components/composebox/BUILD.gn index e759a5ee..905d24f 100644 --- a/chrome/browser/ui/webui/cr_components/composebox/BUILD.gn +++ b/chrome/browser/ui/webui/cr_components/composebox/BUILD.gn
@@ -15,8 +15,10 @@ "//base", "//chrome/browser/ui/browser_window", "//chrome/browser/ui/omnibox", + "//chrome/browser/ui/user_education", "//chrome/browser/ui/webui:webui_util", "//chrome/browser/ui/webui/cr_components/searchbox", + "//chrome/browser/ui/webui/top_chrome", "//components/contextual_search:public", "//components/contextual_tasks/public:feature_list", "//components/lens",
diff --git a/chrome/browser/ui/webui/cr_components/composebox/composebox_handler.cc b/chrome/browser/ui/webui/cr_components/composebox/composebox_handler.cc index b9b6581..ae7e6739 100644 --- a/chrome/browser/ui/webui/cr_components/composebox/composebox_handler.cc +++ b/chrome/browser/ui/webui/cr_components/composebox/composebox_handler.cc
@@ -13,12 +13,14 @@ #include "base/time/time.h" #include "chrome/browser/ui/browser_window/public/browser_window_interface.h" #include "chrome/browser/ui/omnibox/omnibox_controller.h" +#include "chrome/browser/ui/user_education/browser_user_education_interface.h" #include "chrome/browser/ui/webui/cr_components/searchbox/contextual_searchbox_handler.h" #include "chrome/browser/ui/webui/cr_components/searchbox/searchbox_utils.h" #include "chrome/browser/ui/webui/webui_embedding_context.h" #include "components/contextual_search/contextual_search_types.h" #include "components/contextual_search/input_state_model.h" #include "components/contextual_tasks/public/features.h" +#include "components/feature_engagement/public/feature_constants.h" #include "components/lens/lens_url_utils.h" #include "components/metrics/metrics_provider.h" #include "components/omnibox/browser/autocomplete_match_type.h" @@ -194,6 +196,25 @@ } } +void ComposeboxHandler::NotifyComposeboxQuerySubmittedWithContext() { + if (!web_contents_) { + return; + } + auto* browser_window_interface = + webui::GetBrowserWindowInterface(web_contents_); + if (!browser_window_interface) { + return; + } + auto* user_education_interface = + BrowserUserEducationInterface::From(browser_window_interface); + if (!user_education_interface) { + return; + } + user_education_interface->NotifyFeaturePromoFeatureUsed( + feature_engagement::kIPHDesktopRealboxContextualSearchFeature, + FeaturePromoFeatureUsedAction::kClosePromoIfPresent); +} + void ComposeboxHandler::NavigateUrl(const GURL& url) { if (!url.is_valid()) { return;
diff --git a/chrome/browser/ui/webui/cr_components/composebox/composebox_handler.h b/chrome/browser/ui/webui/cr_components/composebox/composebox_handler.h index bb8e8421..c7a71f5 100644 --- a/chrome/browser/ui/webui/cr_components/composebox/composebox_handler.h +++ b/chrome/browser/ui/webui/cr_components/composebox/composebox_handler.h
@@ -52,6 +52,7 @@ void GetSmartTabSharingActive( GetSmartTabSharingActiveCallback callback) override; void OnContextMenuOpened() override; + void NotifyComposeboxQuerySubmittedWithContext() override; // searchbox::mojom::PageHandler: void ExecuteAction(uint8_t line,
diff --git a/chrome/browser/ui/webui/cr_components/searchbox/searchbox_handler.cc b/chrome/browser/ui/webui/cr_components/searchbox/searchbox_handler.cc index c9ad08b5..5b94198 100644 --- a/chrome/browser/ui/webui/cr_components/searchbox/searchbox_handler.cc +++ b/chrome/browser/ui/webui/cr_components/searchbox/searchbox_handler.cc
@@ -453,6 +453,7 @@ {"composeFileTypesAllowedError", IDS_NTP_COMPOSE_FILE_TYPE_NOT_ALLOWED_ERROR}, {"voiceClose", IDS_NEW_TAB_VOICE_CLOSE_TOOLTIP}, + {"voiceStop", IDS_FUSEBOX_VOICE_SEARCH_STOP_TITLE}, {"voiceDetails", IDS_NEW_TAB_VOICE_DETAILS}, {"voiceListening", IDS_NEW_TAB_VOICE_LISTENING}, {"voicePermissionError", IDS_NEW_TAB_VOICE_PERMISSION_ERROR},
diff --git a/chrome/browser/ui/webui/drive_picker_host/BUILD.gn b/chrome/browser/ui/webui/drive_picker_host/BUILD.gn index b8a86806..a0df68a 100644 --- a/chrome/browser/ui/webui/drive_picker_host/BUILD.gn +++ b/chrome/browser/ui/webui/drive_picker_host/BUILD.gn
@@ -28,6 +28,7 @@ deps = [ "//chrome/browser/profiles:profile", "//chrome/browser/resources/drive_picker_host:resources", + "//chrome/browser/signin", "//chrome/browser/ui/browser_window", "//chrome/common", "//components/omnibox/common",
diff --git a/chrome/browser/ui/webui/drive_picker_host/drive_picker_host_ui.cc b/chrome/browser/ui/webui/drive_picker_host/drive_picker_host_ui.cc index 9658525..90e646a 100644 --- a/chrome/browser/ui/webui/drive_picker_host/drive_picker_host_ui.cc +++ b/chrome/browser/ui/webui/drive_picker_host/drive_picker_host_ui.cc
@@ -5,18 +5,45 @@ #include "chrome/browser/ui/webui/drive_picker_host/drive_picker_host_ui.h" #include "chrome/browser/profiles/profile.h" +#include "chrome/browser/signin/identity_manager_factory.h" #include "chrome/browser/ui/webui/drive_picker_host/untrusted/drive_picker_host_untrusted_ui.h" #include "chrome/common/webui_url_constants.h" #include "chrome/grit/drive_picker_host_resources.h" #include "chrome/grit/drive_picker_host_resources_map.h" #include "components/omnibox/common/omnibox_features.h" +#include "components/signin/public/identity_manager/identity_manager.h" +#include "components/signin/public/identity_manager/primary_account_access_token_fetcher.h" #include "content/public/browser/navigation_handle.h" #include "content/public/browser/render_frame_host.h" #include "content/public/browser/web_contents.h" #include "content/public/browser/web_ui_data_source.h" +#include "google_apis/google_api_keys.h" #include "services/network/public/mojom/content_security_policy.mojom.h" #include "ui/webui/webui_util.h" +namespace { + +// Extracts the numeric Cloud Project Number from a full OAuth2 Client ID string +// (e.g., extracts "77185425430" from "77185425430.apps.googleusercontent.com"). +// +// This extraction is necessary because the Google Picker API's `setAppId` +// method specifically requires the numeric "Cloud project number" as its input, +// rather than the full OAuth2 Client ID string used by the identity service. +// +// Providing the correct numeric Project Number (App ID) is required for the +// Picker to perform a "PreOpen" request to the backend. This request +// authorizes the specific Cloud project to interact with the selected file, +// which is a prerequisite for using the limited `drive.readonly` OAuth scope. +std::string ExtractProjectNumber(const std::string& client_id) { + size_t dot_pos = client_id.find('.'); + if (dot_pos != std::string::npos) { + return client_id.substr(0, dot_pos); + } + return client_id; +} + +} // namespace + DrivePickerHostUIConfig::DrivePickerHostUIConfig() : DefaultTopChromeWebUIConfig(content::kChromeUIScheme, chrome::kChromeUIDrivePickerHostHost) {} @@ -35,6 +62,16 @@ webui::SetupWebUIDataSource(source, kDrivePickerHostResources, IDR_DRIVE_PICKER_HOST_DRIVE_PICKER_HOST_HTML); + + // Allow iframing the untrusted drive picker. + source->OverrideContentSecurityPolicy( + network::mojom::CSPDirectiveName::FrameSrc, + "frame-src chrome-untrusted://drive-picker-host/;"); + source->OverrideContentSecurityPolicy( + network::mojom::CSPDirectiveName::ChildSrc, + "child-src 'self' chrome-untrusted://drive-picker-host/;"); + + web_ui->AddRequestableScheme(content::kChromeUIUntrustedScheme); } DrivePickerHostUI::~DrivePickerHostUI() = default; @@ -76,11 +113,65 @@ result_handler) { if (untrusted_bridge_remote_.is_bound() && untrusted_bridge_remote_.is_connected()) { - untrusted_bridge_remote_->ShowDrivePicker(std::move(result_handler)); + FetchTokenAndShowPicker(std::move(result_handler)); } else { // Only the most recent request is kept if the bridge is not yet ready or // has been disconnected. - pending_request_ = std::move(result_handler); + pending_result_handler_ = std::move(result_handler); + } +} + +void DrivePickerHostUI::FetchTokenAndShowPicker( + mojo::PendingRemote<drive_picker_host::mojom::DrivePickerResultHandler> + result_handler) { + if (access_token_fetcher_) { + return; + } + + Profile* profile = Profile::FromWebUI(web_ui()); + signin::IdentityManager* identity_manager = + IdentityManagerFactory::GetForProfile(profile); + if (!identity_manager) { + return; + } + + // Drive is only available for users if they are (1) signed into Chrome and + // (2) the browser identity matches the AIM identity. We can only get to this + // point into the flow is these conditions are met, so we can assume that the + // OAuth token is available. + access_token_fetcher_ = + std::make_unique<signin::PrimaryAccountAccessTokenFetcher>( + signin::OAuthConsumerId::kDrivePickerHost, identity_manager, + base::BindOnce(&DrivePickerHostUI::OnAccessTokenFetched, + weak_ptr_factory_.GetWeakPtr(), + std::move(result_handler)), + signin::PrimaryAccountAccessTokenFetcher::Mode::kWaitUntilAvailable, + signin::ConsentLevel::kSignin); +} + +void DrivePickerHostUI::OnAccessTokenFetched( + mojo::PendingRemote<drive_picker_host::mojom::DrivePickerResultHandler> + result_handler, + GoogleServiceAuthError error, + signin::AccessTokenInfo access_token_info) { + access_token_fetcher_.reset(); + if (error.state() != GoogleServiceAuthError::NONE) { + return; + } + + if (untrusted_bridge_remote_.is_bound() && + untrusted_bridge_remote_.is_connected()) { + drive_picker_host_untrusted::mojom::DrivePickerKeysPtr keys = + drive_picker_host_untrusted::mojom::DrivePickerKeys::New(); + keys->oauth_token = access_token_info.token; + keys->api_key = google_apis::GetAPIKey(); + keys->app_id = ExtractProjectNumber( + google_apis::GetOAuth2ClientID(google_apis::OAuth2Client::CLIENT_MAIN)); + + untrusted_bridge_remote_->ShowDrivePicker(std::move(result_handler), + std::move(keys)); + } else { + pending_result_handler_ = std::move(result_handler); } } @@ -90,8 +181,8 @@ untrusted_bridge_remote_.reset(); untrusted_bridge_remote_.Bind(std::move(untrusted_bridge)); - if (pending_request_) { - untrusted_bridge_remote_->ShowDrivePicker(std::move(pending_request_)); + if (pending_result_handler_) { + FetchTokenAndShowPicker(std::move(pending_result_handler_)); } }
diff --git a/chrome/browser/ui/webui/drive_picker_host/drive_picker_host_ui.h b/chrome/browser/ui/webui/drive_picker_host/drive_picker_host_ui.h index c9e3d0f..d88d26d 100644 --- a/chrome/browser/ui/webui/drive_picker_host/drive_picker_host_ui.h +++ b/chrome/browser/ui/webui/drive_picker_host/drive_picker_host_ui.h
@@ -14,11 +14,17 @@ #include "chrome/browser/ui/webui/top_chrome/top_chrome_web_ui_controller.h" #include "chrome/browser/ui/webui/top_chrome/top_chrome_webui_config.h" #include "chrome/common/webui_url_constants.h" +#include "components/signin/public/identity_manager/access_token_info.h" #include "content/public/browser/web_contents_observer.h" +#include "google_apis/gaia/google_service_auth_error.h" #include "mojo/public/cpp/bindings/pending_receiver.h" #include "mojo/public/cpp/bindings/receiver.h" #include "mojo/public/cpp/bindings/remote.h" +namespace signin { +class PrimaryAccountAccessTokenFetcher; +} // namespace signin + class DrivePickerHostUI; class DrivePickerHostUIConfig @@ -64,6 +70,22 @@ receiver); private: + // Callback for the access token fetcher. + void OnAccessTokenFetched( + mojo::PendingRemote<drive_picker_host::mojom::DrivePickerResultHandler> + result_handler, + GoogleServiceAuthError error, + signin::AccessTokenInfo access_token_info); + + // Initiates the OAuth token fetch and subsequent picker display. + void FetchTokenAndShowPicker( + mojo::PendingRemote<drive_picker_host::mojom::DrivePickerResultHandler> + result_handler); + + // Both RenderFrameCreated and DidFinishNavigation are necessary to establish + // the bridge as early as possible. RenderFrameCreated is the earliest signal, + // but GetWebUI() may still be null. DidFinishNavigation serves as a reliable + // fallback where the WebUI is guaranteed to be associated. // content::WebContentsObserver: void RenderFrameCreated(content::RenderFrameHost* render_frame_host) override; void DidFinishNavigation( @@ -78,13 +100,18 @@ // Stores a single request that arrived before the untrusted bridge was bound. mojo::PendingRemote<drive_picker_host::mojom::DrivePickerResultHandler> - pending_request_; + pending_result_handler_; + + std::unique_ptr<signin::PrimaryAccountAccessTokenFetcher> + access_token_fetcher_; mojo::Remote<drive_picker_host_untrusted::mojom::DrivePickerBridge> untrusted_bridge_remote_; mojo::Receiver<drive_picker_host::mojom::DrivePickerHostHandler> receiver_{ this}; + base::WeakPtrFactory<DrivePickerHostUI> weak_ptr_factory_{this}; + WEB_UI_CONTROLLER_TYPE_DECL(); };
diff --git a/chrome/browser/ui/webui/drive_picker_host/drive_picker_host_ui_unittest.cc b/chrome/browser/ui/webui/drive_picker_host/drive_picker_host_ui_unittest.cc index e32c5e81..e20d44e 100644 --- a/chrome/browser/ui/webui/drive_picker_host/drive_picker_host_ui_unittest.cc +++ b/chrome/browser/ui/webui/drive_picker_host/drive_picker_host_ui_unittest.cc
@@ -5,10 +5,13 @@ #include "chrome/browser/ui/webui/drive_picker_host/drive_picker_host_ui.h" #include "base/test/scoped_feature_list.h" +#include "chrome/browser/signin/identity_manager_factory.h" +#include "chrome/browser/signin/identity_test_environment_profile_adaptor.h" #include "chrome/browser/ui/webui/drive_picker_host/untrusted/drive_picker_host_untrusted_ui.h" #include "chrome/common/webui_url_constants.h" #include "chrome/test/base/testing_profile.h" #include "components/omnibox/common/omnibox_features.h" +#include "components/signin/public/identity_manager/identity_test_utils.h" #include "content/public/browser/web_contents.h" #include "content/public/browser/web_ui.h" #include "content/public/test/browser_task_environment.h" @@ -21,6 +24,9 @@ namespace { +constexpr char kAccessToken[] = "access_token"; +constexpr char kEmail[] = "test@example.com"; + class MockDrivePickerBridge : public drive_picker_host_untrusted::mojom::DrivePickerBridge { public: @@ -35,7 +41,8 @@ MOCK_METHOD( void, ShowDrivePicker, - (mojo::PendingRemote<drive_picker_host::mojom::DrivePickerResultHandler>), + (mojo::PendingRemote<drive_picker_host::mojom::DrivePickerResultHandler>, + drive_picker_host_untrusted::mojom::DrivePickerKeysPtr), (override)); private: @@ -67,18 +74,29 @@ ~DrivePickerHostUITest() override = default; void SetUp() override { - profile_ = std::make_unique<TestingProfile>(); + TestingProfile::Builder builder; + builder.AddTestingFactories(IdentityTestEnvironmentProfileAdaptor:: + GetIdentityTestEnvironmentFactories()); + profile_ = builder.Build(); + identity_test_env_adaptor_ = + std::make_unique<IdentityTestEnvironmentProfileAdaptor>(profile_.get()); + web_contents_ = content::WebContentsTester::CreateTestWebContents( profile_.get(), nullptr); } TestingProfile* profile() { return profile_.get(); } content::WebContents* web_contents() { return web_contents_.get(); } + signin::IdentityTestEnvironment* identity_test_env() { + return identity_test_env_adaptor_->identity_test_env(); + } protected: content::BrowserTaskEnvironment task_environment_; content::RenderViewHostTestEnabler rvh_test_enabler_; std::unique_ptr<TestingProfile> profile_; + std::unique_ptr<IdentityTestEnvironmentProfileAdaptor> + identity_test_env_adaptor_; std::unique_ptr<content::WebContents> web_contents_; base::test::ScopedFeatureList feature_list_; }; @@ -101,6 +119,9 @@ feature_list_.InitAndEnableFeature( omnibox::kComposeboxDriveContextMenuOption); + identity_test_env()->MakePrimaryAccountAvailable( + kEmail, signin::ConsentLevel::kSignin); + content::TestWebUI test_web_ui; test_web_ui.set_web_contents(web_contents()); DrivePickerHostUI controller(&test_web_ui); @@ -109,9 +130,16 @@ controller.SetBridge(mock_bridge.BindAndGetRemote()); MockResultHandler result_handler; - EXPECT_CALL(mock_bridge, ShowDrivePicker(testing::_)); + EXPECT_CALL(mock_bridge, ShowDrivePicker(testing::_, testing::_)) + .WillOnce(testing::WithArg<1>( + [](drive_picker_host_untrusted::mojom::DrivePickerKeysPtr keys) { + EXPECT_EQ(keys->oauth_token, kAccessToken); + })); controller.TriggerDrivePickerHost(result_handler.BindAndGetRemote()); + identity_test_env()->WaitForAccessTokenRequestIfNecessaryAndRespondWithToken( + kAccessToken, base::Time::Now() + base::Hours(1)); + base::RunLoop().RunUntilIdle(); } @@ -119,6 +147,9 @@ feature_list_.InitAndEnableFeature( omnibox::kComposeboxDriveContextMenuOption); + identity_test_env()->MakePrimaryAccountAvailable( + kEmail, signin::ConsentLevel::kSignin); + content::TestWebUI test_web_ui; test_web_ui.set_web_contents(web_contents()); DrivePickerHostUI controller(&test_web_ui); @@ -128,9 +159,19 @@ controller.TriggerDrivePickerHost(result_handler.BindAndGetRemote()); MockDrivePickerBridge mock_bridge; - // Setting bridge should flush the pending request. - EXPECT_CALL(mock_bridge, ShowDrivePicker(testing::_)); + // Setting bridge should flush the pending request and initiate the token + // fetch. controller.SetBridge(mock_bridge.BindAndGetRemote()); + // The call happens AFTER the token is fetched. + EXPECT_CALL(mock_bridge, ShowDrivePicker(testing::_, testing::_)) + .WillOnce(testing::WithArg<1>( + [](drive_picker_host_untrusted::mojom::DrivePickerKeysPtr keys) { + EXPECT_EQ(keys->oauth_token, kAccessToken); + })); + + identity_test_env()->WaitForAccessTokenRequestIfNecessaryAndRespondWithToken( + kAccessToken, base::Time::Now() + base::Hours(1)); + base::RunLoop().RunUntilIdle(); }
diff --git a/chrome/browser/ui/webui/drive_picker_host/untrusted/drive_picker_host_untrusted.mojom b/chrome/browser/ui/webui/drive_picker_host/untrusted/drive_picker_host_untrusted.mojom index 3735166..019487c 100644 --- a/chrome/browser/ui/webui/drive_picker_host/untrusted/drive_picker_host_untrusted.mojom +++ b/chrome/browser/ui/webui/drive_picker_host/untrusted/drive_picker_host_untrusted.mojom
@@ -7,9 +7,7 @@ import "chrome/browser/ui/views/drive_picker_host/drive_picker_result_handler.mojom"; // Browser-side handler for the untrusted page. -interface DrivePickerUntrustedHostHandler { - // Binds the page interface to the handler. - BindPage(pending_remote<Page> page); +interface PageHandler { }; // WebUI-side page interface for the untrusted page. @@ -17,7 +15,22 @@ // Triggers the Drive Picker selection UI in the JS. ShowDrivePicker( pending_remote<drive_picker_host.mojom.DrivePickerResultHandler> - result_handler); + result_handler, + DrivePickerKeys keys); +}; + +// Factory for creating Page handlers. +interface PageHandlerFactory { + // Creates a Page handler for the given Page. + CreatePageHandler(pending_remote<Page> page, + pending_receiver<PageHandler> handler); +}; + +// Keys for the Drive Picker selection UI. +struct DrivePickerKeys { + string oauth_token; + string api_key; + string app_id; }; // Interface for communication from the trusted host to the @@ -26,5 +39,6 @@ // Triggers the Drive Picker selection UI in the untrusted page. ShowDrivePicker( pending_remote<drive_picker_host.mojom.DrivePickerResultHandler> - result_handler); + result_handler, + DrivePickerKeys keys); };
diff --git a/chrome/browser/ui/webui/drive_picker_host/untrusted/drive_picker_host_untrusted_ui.cc b/chrome/browser/ui/webui/drive_picker_host/untrusted/drive_picker_host_untrusted_ui.cc index d9addb35..07b2aab 100644 --- a/chrome/browser/ui/webui/drive_picker_host/untrusted/drive_picker_host_untrusted_ui.cc +++ b/chrome/browser/ui/webui/drive_picker_host/untrusted/drive_picker_host_untrusted_ui.cc
@@ -13,6 +13,7 @@ #include "content/public/browser/web_ui.h" #include "content/public/browser/web_ui_data_source.h" #include "ui/webui/webui_util.h" +#include "url/gurl.h" // DrivePickerUntrustedHostUIConfig DrivePickerUntrustedHostUIConfig::DrivePickerUntrustedHostUIConfig() @@ -30,6 +31,14 @@ // DrivePickerUntrustedHostUI WEB_UI_CONTROLLER_TYPE_IMPL(DrivePickerUntrustedHostUI) +DrivePickerUntrustedHostUI::PendingRequest::PendingRequest( + mojo::PendingRemote<drive_picker_host::mojom::DrivePickerResultHandler> + handler, + drive_picker_host_untrusted::mojom::DrivePickerKeysPtr keys) + : result_handler(std::move(handler)), keys(std::move(keys)) {} + +DrivePickerUntrustedHostUI::PendingRequest::~PendingRequest() = default; + DrivePickerUntrustedHostUI::DrivePickerUntrustedHostUI(content::WebUI* web_ui) : ui::UntrustedWebUIController(web_ui) { content::WebUIDataSource* source = content::WebUIDataSource::CreateAndAdd( @@ -39,16 +48,37 @@ webui::SetupWebUIDataSource( source, kDrivePickerHostUntrustedResources, IDR_DRIVE_PICKER_HOST_UNTRUSTED_DRIVE_PICKER_HOST_UNTRUSTED_HTML); + + source->AddFrameAncestor(GURL(chrome::kChromeUIDrivePickerHostURL)); + + source->OverrideContentSecurityPolicy( + network::mojom::CSPDirectiveName::ScriptSrc, + "script-src 'self' chrome-untrusted://resources/ " + "https://apis.google.com;"); + source->OverrideContentSecurityPolicy( + network::mojom::CSPDirectiveName::ConnectSrc, + "connect-src 'self' https://apis.google.com;"); + source->OverrideContentSecurityPolicy( + network::mojom::CSPDirectiveName::FrameSrc, + "frame-src 'self' https://docs.google.com;"); + source->OverrideContentSecurityPolicy( + network::mojom::CSPDirectiveName::ImgSrc, + "img-src 'self' chrome-untrusted://resources/ " + "https://*.googleusercontent.com https://*.google.com data:;"); + + // This is required to allow the Google Picker API to be loaded via GAPI. + // Otherwise, the picker will not be able to load due to CSP restrictions + // since it requires a TrustedTypePolicy to be set up. + source->DisableTrustedTypesCSP(); } DrivePickerUntrustedHostUI::~DrivePickerUntrustedHostUI() = default; void DrivePickerUntrustedHostUI::BindInterface( mojo::PendingReceiver< - drive_picker_host_untrusted::mojom::DrivePickerUntrustedHostHandler> - receiver) { - untrusted_host_receiver_.reset(); - untrusted_host_receiver_.Bind(std::move(receiver)); + drive_picker_host_untrusted::mojom::PageHandlerFactory> receiver) { + factory_receiver_.reset(); + factory_receiver_.Bind(std::move(receiver)); } void DrivePickerUntrustedHostUI::BindInterface( @@ -58,24 +88,33 @@ bridge_receiver_.Bind(std::move(receiver)); } -void DrivePickerUntrustedHostUI::BindPage( - mojo::PendingRemote<drive_picker_host_untrusted::mojom::Page> page) { +void DrivePickerUntrustedHostUI::CreatePageHandler( + mojo::PendingRemote<drive_picker_host_untrusted::mojom::Page> page, + mojo::PendingReceiver<drive_picker_host_untrusted::mojom::PageHandler> + handler) { page_.reset(); page_.Bind(std::move(page)); + page_handler_receiver_.reset(); + page_handler_receiver_.Bind(std::move(handler)); + if (pending_request_) { - page_->ShowDrivePicker(std::move(pending_request_)); + page_->ShowDrivePicker(std::move(pending_request_->result_handler), + std::move(pending_request_->keys)); + pending_request_.reset(); } } void DrivePickerUntrustedHostUI::ShowDrivePicker( mojo::PendingRemote<drive_picker_host::mojom::DrivePickerResultHandler> - result_handler) { + result_handler, + drive_picker_host_untrusted::mojom::DrivePickerKeysPtr keys) { if (page_.is_bound() && page_.is_connected()) { - page_->ShowDrivePicker(std::move(result_handler)); + page_->ShowDrivePicker(std::move(result_handler), std::move(keys)); } else { // Only the most recent request is kept if the page is not yet ready or // has been disconnected. - pending_request_ = std::move(result_handler); + pending_request_ = std::make_unique<PendingRequest>( + std::move(result_handler), std::move(keys)); } }
diff --git a/chrome/browser/ui/webui/drive_picker_host/untrusted/drive_picker_host_untrusted_ui.h b/chrome/browser/ui/webui/drive_picker_host/untrusted/drive_picker_host_untrusted_ui.h index f2292be..9403272 100644 --- a/chrome/browser/ui/webui/drive_picker_host/untrusted/drive_picker_host_untrusted_ui.h +++ b/chrome/browser/ui/webui/drive_picker_host/untrusted/drive_picker_host_untrusted_ui.h
@@ -30,12 +30,12 @@ // The WebUI controller for chrome-untrusted://drive-picker-host. // It implements DrivePickerBridge for communication from the Trusted side, -// and DrivePickerUntrustedHostHandler for communication from the Untrusted JS. +// and PageHandlerFactory for communication from the Untrusted JS. class DrivePickerUntrustedHostUI : public ui::UntrustedWebUIController, public drive_picker_host_untrusted::mojom::DrivePickerBridge, - public drive_picker_host_untrusted::mojom:: - DrivePickerUntrustedHostHandler { + public drive_picker_host_untrusted::mojom::PageHandlerFactory, + public drive_picker_host_untrusted::mojom::PageHandler { public: explicit DrivePickerUntrustedHostUI(content::WebUI* web_ui); ~DrivePickerUntrustedHostUI() override; @@ -46,37 +46,49 @@ void BindInterface( mojo::PendingReceiver< - drive_picker_host_untrusted::mojom::DrivePickerUntrustedHostHandler> - receiver); + drive_picker_host_untrusted::mojom::PageHandlerFactory> receiver); void BindInterface( mojo::PendingReceiver< drive_picker_host_untrusted::mojom::DrivePickerBridge> receiver); - // drive_picker_host_untrusted::mojom::DrivePickerUntrustedHostHandler: - void BindPage(mojo::PendingRemote<drive_picker_host_untrusted::mojom::Page> - page) override; + // drive_picker_host_untrusted::mojom::PageHandlerFactory: + void CreatePageHandler( + mojo::PendingRemote<drive_picker_host_untrusted::mojom::Page> page, + mojo::PendingReceiver<drive_picker_host_untrusted::mojom::PageHandler> + handler) override; // drive_picker_host_untrusted::mojom::DrivePickerBridge: void ShowDrivePicker( mojo::PendingRemote<drive_picker_host::mojom::DrivePickerResultHandler> - result_handler) override; + result_handler, + drive_picker_host_untrusted::mojom::DrivePickerKeysPtr keys) override; private: FRIEND_TEST_ALL_PREFIXES(DrivePickerUntrustedHostUITest, ShowDrivePickerQueuesOnDisconnect); - mojo::Receiver< - drive_picker_host_untrusted::mojom::DrivePickerUntrustedHostHandler> - untrusted_host_receiver_{this}; + mojo::Receiver<drive_picker_host_untrusted::mojom::PageHandlerFactory> + factory_receiver_{this}; + mojo::Receiver<drive_picker_host_untrusted::mojom::PageHandler> + page_handler_receiver_{this}; mojo::Receiver<drive_picker_host_untrusted::mojom::DrivePickerBridge> bridge_receiver_{this}; mojo::Remote<drive_picker_host_untrusted::mojom::Page> page_; - // Stores a single request that arrived before the page was bound. - mojo::PendingRemote<drive_picker_host::mojom::DrivePickerResultHandler> - pending_request_; + // Data for a single request that arrived before the page was bound. + struct PendingRequest { + PendingRequest( + mojo::PendingRemote<drive_picker_host::mojom::DrivePickerResultHandler> + handler, + drive_picker_host_untrusted::mojom::DrivePickerKeysPtr keys); + ~PendingRequest(); + mojo::PendingRemote<drive_picker_host::mojom::DrivePickerResultHandler> + result_handler; + drive_picker_host_untrusted::mojom::DrivePickerKeysPtr keys; + }; + std::unique_ptr<PendingRequest> pending_request_; WEB_UI_CONTROLLER_TYPE_DECL(); };
diff --git a/chrome/browser/ui/webui/drive_picker_host/untrusted/drive_picker_host_untrusted_ui_unittest.cc b/chrome/browser/ui/webui/drive_picker_host/untrusted/drive_picker_host_untrusted_ui_unittest.cc index 732abe0f..04cb30a 100644 --- a/chrome/browser/ui/webui/drive_picker_host/untrusted/drive_picker_host_untrusted_ui_unittest.cc +++ b/chrome/browser/ui/webui/drive_picker_host/untrusted/drive_picker_host_untrusted_ui_unittest.cc
@@ -20,6 +20,10 @@ namespace { +constexpr char kAccessToken[] = "token"; +constexpr char kApiKey[] = "key"; +constexpr char kAppId[] = "id"; + class MockPage : public drive_picker_host_untrusted::mojom::Page { public: MockPage() = default; @@ -33,7 +37,8 @@ MOCK_METHOD( void, ShowDrivePicker, - (mojo::PendingRemote<drive_picker_host::mojom::DrivePickerResultHandler>), + (mojo::PendingRemote<drive_picker_host::mojom::DrivePickerResultHandler>, + drive_picker_host_untrusted::mojom::DrivePickerKeysPtr), (override)); private: @@ -100,17 +105,30 @@ DrivePickerUntrustedHostUI controller(&test_web_ui); MockPage mock_page; - mojo::Remote< - drive_picker_host_untrusted::mojom::DrivePickerUntrustedHostHandler> - untrusted_host; - controller.BindInterface(untrusted_host.BindNewPipeAndPassReceiver()); + mojo::Remote<drive_picker_host_untrusted::mojom::PageHandlerFactory> + untrusted_factory; + controller.BindInterface(untrusted_factory.BindNewPipeAndPassReceiver()); - untrusted_host->BindPage(mock_page.BindAndGetRemote()); + mojo::Remote<drive_picker_host_untrusted::mojom::PageHandler> untrusted_host; + untrusted_factory->CreatePageHandler(mock_page.BindAndGetRemote(), + untrusted_host.BindNewPipeAndPassReceiver()); base::RunLoop().RunUntilIdle(); MockResultHandler result_handler; - EXPECT_CALL(mock_page, ShowDrivePicker(testing::_)); - controller.ShowDrivePicker(result_handler.BindAndGetRemote()); + EXPECT_CALL(mock_page, ShowDrivePicker(testing::_, testing::_)) + .WillOnce(testing::WithArg<1>( + [](drive_picker_host_untrusted::mojom::DrivePickerKeysPtr keys) { + EXPECT_EQ(keys->oauth_token, kAccessToken); + EXPECT_EQ(keys->api_key, kApiKey); + EXPECT_EQ(keys->app_id, kAppId); + })); + drive_picker_host_untrusted::mojom::DrivePickerKeysPtr keys = + drive_picker_host_untrusted::mojom::DrivePickerKeys::New(); + keys->oauth_token = kAccessToken; + keys->api_key = kApiKey; + keys->app_id = kAppId; + controller.ShowDrivePicker(result_handler.BindAndGetRemote(), + std::move(keys)); // Need to flush mojo calls. base::RunLoop().RunUntilIdle(); @@ -123,16 +141,30 @@ MockResultHandler result_handler; // Trigger before page is bound. - controller.ShowDrivePicker(result_handler.BindAndGetRemote()); + drive_picker_host_untrusted::mojom::DrivePickerKeysPtr keys = + drive_picker_host_untrusted::mojom::DrivePickerKeys::New(); + keys->oauth_token = kAccessToken; + keys->api_key = kApiKey; + keys->app_id = kAppId; + controller.ShowDrivePicker(result_handler.BindAndGetRemote(), + std::move(keys)); MockPage mock_page; - mojo::Remote< - drive_picker_host_untrusted::mojom::DrivePickerUntrustedHostHandler> - untrusted_host; - controller.BindInterface(untrusted_host.BindNewPipeAndPassReceiver()); + mojo::Remote<drive_picker_host_untrusted::mojom::PageHandlerFactory> + untrusted_factory; + controller.BindInterface(untrusted_factory.BindNewPipeAndPassReceiver()); - EXPECT_CALL(mock_page, ShowDrivePicker(testing::_)); - untrusted_host->BindPage(mock_page.BindAndGetRemote()); + // Binding page should flush the pending request. + EXPECT_CALL(mock_page, ShowDrivePicker(testing::_, testing::_)) + .WillOnce(testing::WithArg<1>( + [](drive_picker_host_untrusted::mojom::DrivePickerKeysPtr keys) { + EXPECT_EQ(keys->oauth_token, kAccessToken); + EXPECT_EQ(keys->api_key, kApiKey); + EXPECT_EQ(keys->app_id, kAppId); + })); + mojo::Remote<drive_picker_host_untrusted::mojom::PageHandler> untrusted_host; + untrusted_factory->CreatePageHandler(mock_page.BindAndGetRemote(), + untrusted_host.BindNewPipeAndPassReceiver()); base::RunLoop().RunUntilIdle(); } @@ -144,7 +176,9 @@ { MockPage mock_page; - controller.BindPage(mock_page.BindAndGetRemote()); + mojo::PendingRemote<drive_picker_host_untrusted::mojom::PageHandler> handler; + controller.CreatePageHandler(mock_page.BindAndGetRemote(), + handler.InitWithNewPipeAndPassReceiver()); EXPECT_TRUE(controller.page_.is_connected()); } @@ -154,12 +188,26 @@ MockResultHandler result_handler; // This should now be queued because it's not connected. - controller.ShowDrivePicker(result_handler.BindAndGetRemote()); - EXPECT_TRUE(controller.pending_request_.is_valid()); + drive_picker_host_untrusted::mojom::DrivePickerKeysPtr keys = + drive_picker_host_untrusted::mojom::DrivePickerKeys::New(); + keys->oauth_token = kAccessToken; + keys->api_key = kApiKey; + keys->app_id = kAppId; + controller.ShowDrivePicker(result_handler.BindAndGetRemote(), + std::move(keys)); + EXPECT_TRUE(controller.pending_request_); MockPage mock_page2; - EXPECT_CALL(mock_page2, ShowDrivePicker(testing::_)); - controller.BindPage(mock_page2.BindAndGetRemote()); + EXPECT_CALL(mock_page2, ShowDrivePicker(testing::_, testing::_)) + .WillOnce(testing::WithArg<1>( + [](drive_picker_host_untrusted::mojom::DrivePickerKeysPtr keys) { + EXPECT_EQ(keys->oauth_token, kAccessToken); + EXPECT_EQ(keys->api_key, kApiKey); + EXPECT_EQ(keys->app_id, kAppId); + })); + mojo::PendingRemote<drive_picker_host_untrusted::mojom::PageHandler> handler2; + controller.CreatePageHandler(mock_page2.BindAndGetRemote(), + handler2.InitWithNewPipeAndPassReceiver()); base::RunLoop().RunUntilIdle(); }
diff --git a/chrome/browser/ui/webui/extensions_zero_state_promo/zero_state_promo_page_handler.cc b/chrome/browser/ui/webui/extensions_zero_state_promo/zero_state_promo_page_handler.cc index 3d08e564..fad64a15 100644 --- a/chrome/browser/ui/webui/extensions_zero_state_promo/zero_state_promo_page_handler.cc +++ b/chrome/browser/ui/webui/extensions_zero_state_promo/zero_state_promo_page_handler.cc
@@ -9,7 +9,6 @@ #include "base/metrics/histogram_functions.h" #include "chrome/browser/ui/browser.h" #include "chrome/browser/ui/browser_commands.h" -#include "chrome/browser/ui/browser_finder.h" #include "chrome/browser/ui/browser_window.h" #include "chrome/browser/ui/navigator/browser_navigator.h" #include "components/feature_engagement/public/feature_constants.h"
diff --git a/chrome/browser/ui/webui/feed_internals/feedv2_internals_page_handler.cc b/chrome/browser/ui/webui/feed_internals/feedv2_internals_page_handler.cc index ab69bfb..110a2c8 100644 --- a/chrome/browser/ui/webui/feed_internals/feedv2_internals_page_handler.cc +++ b/chrome/browser/ui/webui/feed_internals/feedv2_internals_page_handler.cc
@@ -150,13 +150,15 @@ } bool FeedV2InternalsPageHandler::IsWebFeedFollowIntroDebugEnabled() { - return pref_service_->GetBoolean(feed::prefs::kEnableWebFeedFollowIntroDebug); + // TODO(crbug.com/407797637): remove the web feed this function and the + // related UI + return false; } void FeedV2InternalsPageHandler::SetWebFeedFollowIntroDebugEnabled( const bool enabled) { - pref_service_->SetBoolean(feed::prefs::kEnableWebFeedFollowIntroDebug, - enabled); + // TODO(crbug.com/407797637): remove the web feed this function and the + // related UI } bool FeedV2InternalsPageHandler::ShouldUseFeedQueryRequests() {
diff --git a/chrome/browser/ui/webui/history/history_login_handler.cc b/chrome/browser/ui/webui/history/history_login_handler.cc index 0790d82..c50a24d4 100644 --- a/chrome/browser/ui/webui/history/history_login_handler.cc +++ b/chrome/browser/ui/webui/history/history_login_handler.cc
@@ -13,7 +13,6 @@ #include "chrome/browser/signin/signin_ui_util.h" #include "chrome/browser/sync/sync_service_factory.h" #include "chrome/browser/ui/browser.h" -#include "chrome/browser/ui/browser_finder.h" #include "chrome/browser/ui/webui/history/history_identity_state_watcher.h" #include "components/signin/public/base/signin_metrics.h" #include "components/signin/public/base/signin_switches.h"
diff --git a/chrome/browser/ui/webui/log_web_ui_url.cc b/chrome/browser/ui/webui/log_web_ui_url.cc index 8e38aa8..c14e1087 100644 --- a/chrome/browser/ui/webui/log_web_ui_url.cc +++ b/chrome/browser/ui/webui/log_web_ui_url.cc
@@ -12,6 +12,7 @@ #include "base/metrics/histogram_functions.h" #include "build/build_config.h" #include "content/public/browser/render_frame_host.h" +#include "content/public/browser/security_principal.h" #include "content/public/browser/site_instance.h" #include "content/public/browser/web_ui.h" #include "content/public/common/url_constants.h" @@ -27,7 +28,10 @@ // This returns the actual WebUI url which can differ from the visible // URL (e.g. chrome://newtab can be rewrote to chrome://new-tab-page or // chrome://new-tab-page-third-party) - return web_ui->GetRenderFrameHost()->GetSiteInstance()->GetSiteURL(); + return web_ui->GetRenderFrameHost() + ->GetSiteInstance() + ->GetSecurityPrincipal() + .GetDeprecatedSiteURL(); } bool ShouldLogUrl(const GURL& web_ui_url) {
diff --git a/chrome/browser/ui/webui/new_tab_page/action_chips/action_chips.mojom b/chrome/browser/ui/webui/new_tab_page/action_chips/action_chips.mojom index b424a26..b7c84a9d1 100644 --- a/chrome/browser/ui/webui/new_tab_page/action_chips/action_chips.mojom +++ b/chrome/browser/ui/webui/new_tab_page/action_chips/action_chips.mojom
@@ -86,6 +86,9 @@ // Changes the visibility of the action chips feature. SetActionChipsVisibility(bool is_visible); + + // Notifies the feature engagement tracker that an action chip was clicked. + NotifyActionChipClicked(); }; // Interface for the page-side handler receiving information from the browser.
diff --git a/chrome/browser/ui/webui/new_tab_page/action_chips/action_chips_handler.cc b/chrome/browser/ui/webui/new_tab_page/action_chips/action_chips_handler.cc index d3131a29..6b28eb3 100644 --- a/chrome/browser/ui/webui/new_tab_page/action_chips/action_chips_handler.cc +++ b/chrome/browser/ui/webui/new_tab_page/action_chips/action_chips_handler.cc
@@ -15,6 +15,7 @@ #include "chrome/browser/contextual_search/contextual_search_web_contents_helper.h" #include "chrome/browser/ui/browser_window/public/browser_window_interface.h" #include "chrome/browser/ui/tabs/tab_strip_model.h" +#include "chrome/browser/ui/user_education/browser_user_education_interface.h" #include "chrome/browser/ui/webui/new_tab_page/action_chips/action_chips.mojom.h" #include "chrome/browser/ui/webui/new_tab_page/action_chips/action_chips_generator.h" #include "chrome/browser/ui/webui/new_tab_page/action_chips/action_chips_metrics.h" @@ -24,6 +25,7 @@ #include "components/contextual_search/contextual_search_metrics_recorder.h" #include "components/contextual_search/contextual_search_service.h" #include "components/contextual_search/contextual_search_session_handle.h" +#include "components/feature_engagement/public/feature_constants.h" #include "components/google/core/common/google_util.h" #include "components/search/ntp_features.h" #include "components/sessions/content/session_tab_helper.h" @@ -181,6 +183,21 @@ profile_->GetPrefs()->SetBoolean(prefs::kNtpToolChipsVisible, is_visible); } +void ActionChipsHandler::NotifyActionChipClicked() { + if (!web_ui_ || !web_ui_->GetWebContents()) { + return; + } + auto* user_education_interface = + BrowserUserEducationInterface::MaybeGetForWebUiContents( + web_ui_->GetWebContents()); + if (!user_education_interface) { + return; + } + user_education_interface->NotifyFeaturePromoFeatureUsed( + feature_engagement::kIPHDesktopRealboxContextualSearchFeature, + FeaturePromoFeatureUsedAction::kClosePromoIfPresent); +} + void ActionChipsHandler::SendActionChipsToUi(base::TimeTicks start_time, std::vector<ActionChipPtr> chips) { if (!page_.is_bound()) {
diff --git a/chrome/browser/ui/webui/new_tab_page/action_chips/action_chips_handler.h b/chrome/browser/ui/webui/new_tab_page/action_chips/action_chips_handler.h index f220d80..78e5ee44 100644 --- a/chrome/browser/ui/webui/new_tab_page/action_chips/action_chips_handler.h +++ b/chrome/browser/ui/webui/new_tab_page/action_chips/action_chips_handler.h
@@ -47,6 +47,7 @@ void StartActionChipsRetrieval() override; void ActivateMetricsFunnel(const std::string& funnel_name) override; void SetActionChipsVisibility(bool is_visible) override; + void NotifyActionChipClicked() override; void OnTabStripModelChanged( TabStripModel* tab_strip_model,
diff --git a/chrome/browser/ui/webui/new_tab_page/new_tab_page_handler.cc b/chrome/browser/ui/webui/new_tab_page/new_tab_page_handler.cc index cd398933..2349fb72 100644 --- a/chrome/browser/ui/webui/new_tab_page/new_tab_page_handler.cc +++ b/chrome/browser/ui/webui/new_tab_page/new_tab_page_handler.cc
@@ -111,7 +111,6 @@ #if !BUILDFLAG(IS_ANDROID) #include "chrome/browser/ui/browser.h" -#include "chrome/browser/ui/browser_finder.h" #include "chrome/browser/ui/browser_window.h" #include "chrome/browser/ui/browser_window/public/browser_window_features.h" #include "components/user_education/webui/help_bubble_handler.h"
diff --git a/chrome/browser/ui/webui/new_tab_page/new_tab_page_ui.cc b/chrome/browser/ui/webui/new_tab_page/new_tab_page_ui.cc index 619bb6d3..9128720c 100644 --- a/chrome/browser/ui/webui/new_tab_page/new_tab_page_ui.cc +++ b/chrome/browser/ui/webui/new_tab_page/new_tab_page_ui.cc
@@ -134,7 +134,6 @@ #include "url/url_util.h" #if !BUILDFLAG(IS_ANDROID) -#include "chrome/browser/ui/browser_finder.h" #include "chrome/browser/ui/browser_window.h" #include "chrome/browser/ui/tabs/public/tab_features.h" #include "chrome/browser/ui/webui/new_tab_page/action_chips/action_chips_generator.h"
diff --git a/chrome/browser/ui/webui/new_tab_page/webui_ntp_browsertest.cc b/chrome/browser/ui/webui/new_tab_page/webui_ntp_browsertest.cc index 2894170a..3bcec78 100644 --- a/chrome/browser/ui/webui/new_tab_page/webui_ntp_browsertest.cc +++ b/chrome/browser/ui/webui/new_tab_page/webui_ntp_browsertest.cc
@@ -25,6 +25,7 @@ #include "components/prefs/pref_service.h" #include "components/search/ntp_features.h" #include "content/public/browser/render_process_host.h" +#include "content/public/browser/security_principal.h" #include "content/public/browser/spare_render_process_host_manager.h" #include "content/public/browser/web_contents.h" #include "content/public/common/child_process_id.h" @@ -120,9 +121,10 @@ ASSERT_EQ(ntp_url, web_contents->GetLastCommittedURL()); const GURL& webui_ntp_url = chrome::ChromeUINewTabPageURLAsGURL(); - ASSERT_EQ( - webui_ntp_url, - web_contents->GetPrimaryMainFrame()->GetSiteInstance()->GetSiteURL()); + ASSERT_EQ(webui_ntp_url, web_contents->GetPrimaryMainFrame() + ->GetSiteInstance() + ->GetSecurityPrincipal() + .GetDeprecatedSiteURL()); } // Verify that the WebUI NTP uses process-per-site.
diff --git a/chrome/browser/ui/webui/omnibox_popup/omnibox_popup_ui.cc b/chrome/browser/ui/webui/omnibox_popup/omnibox_popup_ui.cc index 6f5ada9..ac10fe1d 100644 --- a/chrome/browser/ui/webui/omnibox_popup/omnibox_popup_ui.cc +++ b/chrome/browser/ui/webui/omnibox_popup/omnibox_popup_ui.cc
@@ -12,7 +12,6 @@ #include "chrome/browser/autocomplete/aim_eligibility_service_factory.h" #include "chrome/browser/contextual_search/contextual_search_service_factory.h" #include "chrome/browser/profiles/profile.h" -#include "chrome/browser/ui/browser_finder.h" #include "chrome/browser/ui/browser_window.h" #include "chrome/browser/ui/location_bar/location_bar.h" #include "chrome/browser/ui/omnibox/omnibox_next_features.h"
diff --git a/chrome/browser/ui/webui/print_preview/print_preview_handler.cc b/chrome/browser/ui/webui/print_preview/print_preview_handler.cc index 083e2ab..9d76666b4 100644 --- a/chrome/browser/ui/webui/print_preview/print_preview_handler.cc +++ b/chrome/browser/ui/webui/print_preview/print_preview_handler.cc
@@ -35,7 +35,6 @@ #include "chrome/browser/printing/print_view_manager.h" #include "chrome/browser/printing/printer_manager_dialog.h" #include "chrome/browser/profiles/profile.h" -#include "chrome/browser/ui/browser_finder.h" #include "chrome/browser/ui/browser_tabstrip.h" #include "chrome/browser/ui/chrome_pages.h" #include "chrome/browser/ui/ui_features.h"
diff --git a/chrome/browser/ui/webui/searchbox/lens_searchbox_handler.cc b/chrome/browser/ui/webui/searchbox/lens_searchbox_handler.cc index c914de3..02aca88 100644 --- a/chrome/browser/ui/webui/searchbox/lens_searchbox_handler.cc +++ b/chrome/browser/ui/webui/searchbox/lens_searchbox_handler.cc
@@ -121,10 +121,6 @@ lens_searchbox_client_(lens_searchbox_client) { autocomplete_controller_observation_.Observe(autocomplete_controller()); - // The client may have text waiting to be sent to the searchbox that it - // couldn't do earlier since the page binding was not set. So now we let the - // client know the binding is ready. - lens_searchbox_client_->OnPageBound(); } LensSearchboxHandler::~LensSearchboxHandler() = default;
diff --git a/chrome/browser/ui/webui/searchbox/realbox_handler.cc b/chrome/browser/ui/webui/searchbox/realbox_handler.cc index a0121d7..d9513db 100644 --- a/chrome/browser/ui/webui/searchbox/realbox_handler.cc +++ b/chrome/browser/ui/webui/searchbox/realbox_handler.cc
@@ -9,7 +9,6 @@ #include "build/build_config.h" #include "chrome/browser/profiles/profile.h" -#include "chrome/browser/ui/browser_finder.h" #include "chrome/browser/ui/omnibox/omnibox_controller.h" #include "chrome/browser/ui/omnibox/omnibox_edit_model.h" #include "chrome/browser/ui/omnibox/omnibox_pedal_implementations.h"
diff --git a/chrome/browser/ui/webui/searchbox/searchbox_handler_unittest.cc b/chrome/browser/ui/webui/searchbox/searchbox_handler_unittest.cc index a6b359b..1219c8f9 100644 --- a/chrome/browser/ui/webui/searchbox/searchbox_handler_unittest.cc +++ b/chrome/browser/ui/webui/searchbox/searchbox_handler_unittest.cc
@@ -578,6 +578,8 @@ std::make_unique<FakeOmniboxPopupView>(omnibox_controller_.get()); omnibox_controller_->edit_model()->set_popup_view(popup_view_.get()); + EXPECT_CALL(page_, AutocompleteResultChanged(testing::_)).Times(1); + handler_ = std::make_unique<WebuiOmniboxHandler>( mojo::PendingReceiver<searchbox::mojom::PageHandler>(), page_.BindAndGetRemote(),
diff --git a/chrome/browser/ui/webui/searchbox/webui_omnibox_handler.cc b/chrome/browser/ui/webui/searchbox/webui_omnibox_handler.cc index 6abc5792..bbf90475 100644 --- a/chrome/browser/ui/webui/searchbox/webui_omnibox_handler.cc +++ b/chrome/browser/ui/webui/searchbox/webui_omnibox_handler.cc
@@ -16,7 +16,6 @@ #include "chrome/browser/profiles/profile.h" #include "chrome/browser/ui/bookmarks/bookmark_stats.h" #include "chrome/browser/ui/browser.h" -#include "chrome/browser/ui/browser_finder.h" #include "chrome/browser/ui/browser_window.h" #include "chrome/browser/ui/browser_window/public/browser_window_features.h" #include "chrome/browser/ui/browser_window/public/browser_window_interface.h" @@ -146,6 +145,12 @@ OnAimPopupEligibilityChanged(); OnContentSharingPolicyChanged(); + + // Ensure the page receives the current autocomplete state on startup. + // This handles the case where results are generated before the remote is + // bound and the handler is created and starts observing the + // AutocompleteController. + OnResultChanged(controller_->autocomplete_controller(), false); } WebuiOmniboxHandler::~WebuiOmniboxHandler() = default;
diff --git a/chrome/browser/ui/webui/settings/font_handler.cc b/chrome/browser/ui/webui/settings/font_handler.cc index 99c9999..64a0c39 100644 --- a/chrome/browser/ui/webui/settings/font_handler.cc +++ b/chrome/browser/ui/webui/settings/font_handler.cc
@@ -17,7 +17,6 @@ #include "build/build_config.h" #include "chrome/browser/browser_process.h" #include "chrome/browser/profiles/profile.h" -#include "chrome/browser/ui/browser_finder.h" #include "chrome/common/pref_names.h" #include "components/prefs/pref_service.h" #include "content/public/browser/font_list_async.h"
diff --git a/chrome/browser/ui/webui/settings/privacy_sandbox_handler.cc b/chrome/browser/ui/webui/settings/privacy_sandbox_handler.cc index 78618cd..870bc2a4 100644 --- a/chrome/browser/ui/webui/settings/privacy_sandbox_handler.cc +++ b/chrome/browser/ui/webui/settings/privacy_sandbox_handler.cc
@@ -85,12 +85,6 @@ &PrivacySandboxHandler:: HandlePrivacySandboxPrivacyGuideShouldShowAdTopicsCard, base::Unretained(this))); - web_ui()->RegisterMessageCallback( - "shouldShowPrivacySandboxAdTopicsContentParity", - base::BindRepeating( - &PrivacySandboxHandler:: - HandleShouldShowPrivacySandboxAdTopicsContentParity, - base::Unretained(this))); } void PrivacySandboxHandler::HandleSetFledgeJoiningAllowed( @@ -214,14 +208,6 @@ ResolveJavascriptCallback(args[0], should_show_ad_topics_card); } -void PrivacySandboxHandler::HandleShouldShowPrivacySandboxAdTopicsContentParity( - const base::ListValue& args) { - AllowJavascript(); - ResolveJavascriptCallback( - args[0], base::FeatureList::IsEnabled( - privacy_sandbox::kPrivacySandboxAdTopicsContentParity)); -} - PrivacySandboxCountries* PrivacySandboxHandler::GetPrivacySandboxCountries() { return GetSingletonPrivacySandboxCountries(); }
diff --git a/chrome/browser/ui/webui/settings/privacy_sandbox_handler.h b/chrome/browser/ui/webui/settings/privacy_sandbox_handler.h index af37d424..d52aee7c 100644 --- a/chrome/browser/ui/webui/settings/privacy_sandbox_handler.h +++ b/chrome/browser/ui/webui/settings/privacy_sandbox_handler.h
@@ -47,9 +47,6 @@ // AND the user to be located in a Privacy Sandbox Consent Country. void HandlePrivacySandboxPrivacyGuideShouldShowAdTopicsCard( const base::ListValue& args); - // Determines if the Ad Topics Content Parity should be shown. - void HandleShouldShowPrivacySandboxAdTopicsContentParity( - const base::ListValue& args); virtual PrivacySandboxCountries* GetPrivacySandboxCountries(); virtual PrivacySandboxService* GetPrivacySandboxService();
diff --git a/chrome/browser/ui/webui/settings/privacy_sandbox_handler_unittest.cc b/chrome/browser/ui/webui/settings/privacy_sandbox_handler_unittest.cc index d6cf7a5..1b9f7bd 100644 --- a/chrome/browser/ui/webui/settings/privacy_sandbox_handler_unittest.cc +++ b/chrome/browser/ui/webui/settings/privacy_sandbox_handler_unittest.cc
@@ -284,6 +284,19 @@ raw_ptr<PrivacySandboxHandler> handler_; }; +TEST_F(PrivacySandboxMessageHandlerTest, TopicsToggleChanged) { + std::vector<bool> states = {true, false}; + for (bool state : states) { + EXPECT_CALL(*mock_privacy_sandbox_service(), TopicsToggleChanged(state)); + + base::ListValue args; + args.Append(state); + web_ui()->ProcessWebUIMessage(GURL(), "topicsToggleChanged", + std::move(args)); + testing::Mock::VerifyAndClearExpectations(mock_privacy_sandbox_service()); + } +} + // Params correspond to (ShouldShowAdTopicsCard, ExpectedResult). class PrivacySandboxPrivacyGuideAdTopicsShownTest : public PrivacySandboxMessageHandlerTest, @@ -326,75 +339,4 @@ testing::Values(std::pair(true, true), std::pair(false, false))); -class PrivacySandboxAdTopicsContentParityEnabledTest - : public PrivacySandboxMessageHandlerTest { - public: - void SetUp() override { - PrivacySandboxMessageHandlerTest::SetUp(); - feature_list_.InitAndEnableFeature( - privacy_sandbox::kPrivacySandboxAdTopicsContentParity); - } - - private: - base::test::ScopedFeatureList feature_list_; -}; - -TEST_F(PrivacySandboxAdTopicsContentParityEnabledTest, TopicsToggleChanged) { - std::vector<bool> states = {true, false}; - for (bool state : states) { - EXPECT_CALL(*mock_privacy_sandbox_service(), TopicsToggleChanged(state)); - - base::ListValue args; - args.Append(state); - web_ui()->ProcessWebUIMessage(GURL(), "topicsToggleChanged", - std::move(args)); - testing::Mock::VerifyAndClearExpectations(mock_privacy_sandbox_service()); - } -} - -TEST_F(PrivacySandboxAdTopicsContentParityEnabledTest, - shouldShowAdTopicsContentParity) { - base::ListValue args; - args.Append(kCallbackId1); - web_ui()->ProcessWebUIMessage( - GURL(), "shouldShowPrivacySandboxAdTopicsContentParity", std::move(args)); - - const content::TestWebUI::CallData& data = *web_ui()->call_data().back(); - EXPECT_EQ(data.arg1()->GetString(), kCallbackId1); - EXPECT_EQ(data.function_name(), "cr.webUIResponse"); - ASSERT_TRUE(data.arg2()->is_bool()); - EXPECT_TRUE(data.arg2()->GetBool()); - ASSERT_TRUE(data.arg3()->is_bool()); - EXPECT_TRUE(data.arg3()->GetBool()); -} - -class PrivacySandboxAdTopicsContentParityDisabledTest - : public PrivacySandboxMessageHandlerTest { - public: - void SetUp() override { - PrivacySandboxMessageHandlerTest::SetUp(); - feature_list_.InitAndDisableFeature( - privacy_sandbox::kPrivacySandboxAdTopicsContentParity); - } - - private: - base::test::ScopedFeatureList feature_list_; -}; - -TEST_F(PrivacySandboxAdTopicsContentParityDisabledTest, - shouldNotShowAdTopicsContentParity) { - base::ListValue args; - args.Append(kCallbackId1); - web_ui()->ProcessWebUIMessage( - GURL(), "shouldShowPrivacySandboxAdTopicsContentParity", std::move(args)); - - const content::TestWebUI::CallData& data = *web_ui()->call_data().back(); - EXPECT_EQ(data.arg1()->GetString(), kCallbackId1); - EXPECT_EQ(data.function_name(), "cr.webUIResponse"); - ASSERT_TRUE(data.arg2()->is_bool()); - EXPECT_TRUE(data.arg2()->GetBool()); - ASSERT_TRUE(data.arg3()->is_bool()); - EXPECT_FALSE(data.arg3()->GetBool()); -} - } // namespace settings
diff --git a/chrome/browser/ui/webui/settings/settings_clear_browsing_data_handler.cc b/chrome/browser/ui/webui/settings/settings_clear_browsing_data_handler.cc index 35453d4..e7d96e9 100644 --- a/chrome/browser/ui/webui/settings/settings_clear_browsing_data_handler.cc +++ b/chrome/browser/ui/webui/settings/settings_clear_browsing_data_handler.cc
@@ -25,7 +25,6 @@ #include "chrome/browser/sync/sync_service_factory.h" #include "chrome/browser/sync/sync_ui_util.h" #include "chrome/browser/ui/browser_element_identifiers.h" -#include "chrome/browser/ui/browser_finder.h" #include "chrome/browser/ui/browser_window/public/browser_window_features.h" #include "chrome/browser/ui/hats/trust_safety_sentiment_service.h" #include "chrome/browser/ui/hats/trust_safety_sentiment_service_factory.h"
diff --git a/chrome/browser/ui/webui/settings/settings_localized_strings_privacy_sandbox_provider.cc b/chrome/browser/ui/webui/settings/settings_localized_strings_privacy_sandbox_provider.cc index 409ae99..4ad7bf4 100644 --- a/chrome/browser/ui/webui/settings/settings_localized_strings_privacy_sandbox_provider.cc +++ b/chrome/browser/ui/webui/settings/settings_localized_strings_privacy_sandbox_provider.cc
@@ -84,8 +84,6 @@ IDS_SETTINGS_TOPICS_PAGE_TOGGLE_SUB_LABEL_V2}, {"topicsPageActiveTopicsHeading", IDS_SETTINGS_TOPICS_PAGE_ACTIVE_TOPICS_HEADING}, - {"topicsPageActiveTopicsDescription", - IDS_SETTINGS_TOPICS_PAGE_ACTIVE_TOPICS_DESCRIPTION}, {"topicsPageCurrentTopicsRegionA11yDescription", IDS_SETTINGS_TOPICS_PAGE_CURRENT_TOPICS_REGION_A11Y_DESCRIPTION}, {"topicsPageCurrentTopicsDescriptionDisabled", @@ -338,14 +336,6 @@ l10n_util::GetStringUTF16(IDS_SETTINGS_OPENS_IN_NEW_TAB))); // Ad Topics Page - Ads API UX Enhancements html_source->AddString( - "adTopicsPageDisclaimer", - l10n_util::GetStringFUTF16( - IDS_SETTINGS_AD_TOPICS_PAGE_DISCLAIMER, - base::ASCIIToUTF16(privacy_policy_url), - l10n_util::GetStringUTF16( - IDS_SETTINGS_SITE_SUGGESTED_ADS_PAGE_DISCLAIMER_LINK_ARIA_DESCRIPTION), - kPrivacyPolicyFunc, kPrivacyPolicyId)); - html_source->AddString( "adTopicsPageFooterV2Desktop", l10n_util::GetStringFUTF16( IDS_SETTINGS_AD_TOPICS_PAGE_FOOTER_V2_DESKTOP,
diff --git a/chrome/browser/ui/webui/settings/settings_localized_strings_provider.cc b/chrome/browser/ui/webui/settings/settings_localized_strings_provider.cc index 291984e..c6a51279 100644 --- a/chrome/browser/ui/webui/settings/settings_localized_strings_provider.cc +++ b/chrome/browser/ui/webui/settings/settings_localized_strings_provider.cc
@@ -2651,8 +2651,6 @@ IDS_SETTINGS_PRIVACY_GUIDE_COMPLETION_CARD_LEAVE_BUTTON}, {"privacyGuideCompletionCardPrivacySandboxLabel", IDS_SETTINGS_PRIVACY_GUIDE_COMPLETION_CARD_PRIVACY_SANDBOX_LABEL}, - {"privacyGuideCompletionCardPrivacySandboxSubLabel", - IDS_SETTINGS_PRIVACY_GUIDE_COMPLETION_CARD_PRIVACY_SANDBOX_SUB_LABEL}, {"privacyGuideCompletionCardPrivacySandboxSubLabelAdTopics", IDS_SETTINGS_PRIVACY_GUIDE_COMPLETION_CARD_PRIVACY_SANDBOX_SUB_LABEL_AD_TOPICS}, {"privacyGuideCompletionCardWaaLabel",
diff --git a/chrome/browser/ui/webui/settings/settings_manage_profile_handler.cc b/chrome/browser/ui/webui/settings/settings_manage_profile_handler.cc index 0108e63..fec7bf1 100644 --- a/chrome/browser/ui/webui/settings/settings_manage_profile_handler.cc +++ b/chrome/browser/ui/webui/settings/settings_manage_profile_handler.cc
@@ -23,7 +23,6 @@ #include "chrome/browser/profiles/profile_shortcut_manager.h" #include "chrome/browser/profiles/profile_window.h" #include "chrome/browser/profiles/profiles_state.h" -#include "chrome/browser/ui/browser_finder.h" #include "chrome/browser/ui/profiles/profile_colors_util.h" #include "chrome/browser/ui/webui/theme_source.h" #include "chrome/common/pref_names.h"
diff --git a/chrome/browser/ui/webui/settings/settings_ui.cc b/chrome/browser/ui/webui/settings/settings_ui.cc index 957b467..bd44e7a 100644 --- a/chrome/browser/ui/webui/settings/settings_ui.cc +++ b/chrome/browser/ui/webui/settings/settings_ui.cc
@@ -556,8 +556,10 @@ #if BUILDFLAG(IS_WIN) html_source->AddBoolean( "showProcessIsolationSetting", - base::FeatureList::IsEnabled(features::kProcessIsolationSettings) && - install_static::IsSystemInstall()); + install_static::IsSystemInstall() && + (base::FeatureList::IsEnabled(features::kProcessIsolationSettings) || + g_browser_process->local_state()->GetBoolean( + prefs::kProcessIsolationEnabled))); #endif // BUILDFLAG(IS_WIN) html_source->AddBoolean(
diff --git a/chrome/browser/ui/webui/side_panel/read_anything/read_anything_untrusted_page_handler.cc b/chrome/browser/ui/webui/side_panel/read_anything/read_anything_untrusted_page_handler.cc index 1859e46..3ad38ed 100644 --- a/chrome/browser/ui/webui/side_panel/read_anything/read_anything_untrusted_page_handler.cc +++ b/chrome/browser/ui/webui/side_panel/read_anything/read_anything_untrusted_page_handler.cc
@@ -1201,7 +1201,8 @@ void ReadAnythingUntrustedPageHandler::Activate( bool active, - std::optional<ReadAnythingOpenTrigger> open_trigger) { + std::optional<ReadAnythingOpenTrigger> open_trigger, + std::optional<base::TimeDelta> completed_session_duration) { active_ = active; if (active_) { last_open_trigger_ = open_trigger;
diff --git a/chrome/browser/ui/webui/side_panel/read_anything/read_anything_untrusted_page_handler.h b/chrome/browser/ui/webui/side_panel/read_anything/read_anything_untrusted_page_handler.h index 12b5b47..c7e0ba7 100644 --- a/chrome/browser/ui/webui/side_panel/read_anything/read_anything_untrusted_page_handler.h +++ b/chrome/browser/ui/webui/side_panel/read_anything/read_anything_untrusted_page_handler.h
@@ -237,8 +237,10 @@ // ReadAnythingLifecycleObserver: void OnDestroyed() override; - void Activate(bool active, - std::optional<ReadAnythingOpenTrigger> open_trigger) override; + void Activate( + bool active, + std::optional<ReadAnythingOpenTrigger> open_trigger, + std::optional<base::TimeDelta> completed_session_duration) override; void OnReadingModePresenterChanged() override; void OnTabWillDetach(tabs::TabInterface* tab,
diff --git a/chrome/browser/ui/webui/signin/profile_picker_handler.cc b/chrome/browser/ui/webui/signin/profile_picker_handler.cc index fb86aff..991bbd5 100644 --- a/chrome/browser/ui/webui/signin/profile_picker_handler.cc +++ b/chrome/browser/ui/webui/signin/profile_picker_handler.cc
@@ -39,7 +39,6 @@ #include "chrome/browser/profiles/profiles_state.h" #include "chrome/browser/signin/signin_util.h" #include "chrome/browser/sync/sync_service_factory.h" -#include "chrome/browser/ui/browser_finder.h" #include "chrome/browser/ui/browser_window.h" #include "chrome/browser/ui/browser_window/public/browser_window_interface.h" #include "chrome/browser/ui/color/chrome_color_id.h"
diff --git a/chrome/browser/ui/webui/top_chrome/webui_contents_preload_manager.cc b/chrome/browser/ui/webui/top_chrome/webui_contents_preload_manager.cc index 068bf94..7c31234 100644 --- a/chrome/browser/ui/webui/top_chrome/webui_contents_preload_manager.cc +++ b/chrome/browser/ui/webui/top_chrome/webui_contents_preload_manager.cc
@@ -469,7 +469,9 @@ preload_state->pending_request = false; // Non-preloaded WebUIs are logged by WebUIMainFrameObserver. if (preload_state->preloaded) { - webui::LogWebUIShown(web_contents_ret->GetSiteInstance()->GetSiteURL()); + webui::LogWebUIShown(web_contents_ret->GetSiteInstance() + ->GetSecurityPrincipal() + .GetDeprecatedSiteURL()); } RequestResult result; @@ -623,8 +625,10 @@ visible_url.Set(web_contents->GetVisibleURL().possibly_invalid_spec()); static crash_reporter::CrashKeyString<1024> site_instance_url( "webui-preload-site-instance-url"); - site_instance_url.Set( - web_contents->GetSiteInstance()->GetSiteURL().possibly_invalid_spec()); + site_instance_url.Set(web_contents->GetSiteInstance() + ->GetSecurityPrincipal() + .GetDeprecatedSiteURL() + .possibly_invalid_spec()); const bool should_auto_reisze_host = TopChromeWebUIConfig::From(web_contents->GetBrowserContext(),
diff --git a/chrome/browser/ui/webui/top_chrome/webui_contents_warmup_level_recorder.cc b/chrome/browser/ui/webui/top_chrome/webui_contents_warmup_level_recorder.cc index f2460d2..e5bc93db 100644 --- a/chrome/browser/ui/webui/top_chrome/webui_contents_warmup_level_recorder.cc +++ b/chrome/browser/ui/webui/top_chrome/webui_contents_warmup_level_recorder.cc
@@ -10,6 +10,7 @@ #include "chrome/browser/ui/webui/top_chrome/webui_contents_warmup_level.h" #include "chrome/common/webui_url_utils.h" #include "content/public/browser/render_process_host.h" +#include "content/public/browser/security_principal.h" #include "content/public/browser/site_instance.h" #include "content/public/browser/spare_render_process_host_manager.h" #include "content/public/browser/web_contents.h" @@ -30,9 +31,10 @@ size_t top_chrome_frames = 0; web_contents->GetPrimaryMainFrame()->GetProcess()->ForEachRenderFrameHost( [&top_chrome_frames](content::RenderFrameHost* rfh) { - top_chrome_frames += - rfh->GetOutermostMainFrame() == rfh && - IsTopChromeWebUIURL(rfh->GetSiteInstance()->GetSiteURL()); + top_chrome_frames += rfh->GetOutermostMainFrame() == rfh && + IsTopChromeWebUIURL(rfh->GetSiteInstance() + ->GetSecurityPrincipal() + .GetDeprecatedSiteURL()); }); const size_t has_preloaded_contents = WebUIContentsPreloadManager::GetInstance()->preloaded_web_contents() ? 1
diff --git a/chrome/browser/ui/webui/webui_toolbar/BUILD.gn b/chrome/browser/ui/webui/webui_toolbar/BUILD.gn index af0b5b9..3025e34 100644 --- a/chrome/browser/ui/webui/webui_toolbar/BUILD.gn +++ b/chrome/browser/ui/webui/webui_toolbar/BUILD.gn
@@ -58,7 +58,6 @@ "//chrome/browser/ui/extensions", "//chrome/browser/ui/interaction", "//chrome/browser/ui/toolbar", - "//chrome/browser/ui/views/permissions/chip", "//chrome/browser/ui/webui:webui_util", "//chrome/browser/ui/webui/util", "//components/user_education/webui",
diff --git a/chrome/browser/ui/webui/webui_toolbar/webui_toolbar_ui.cc b/chrome/browser/ui/webui/webui_toolbar/webui_toolbar_ui.cc index 3946ff1..3f15231 100644 --- a/chrome/browser/ui/webui/webui_toolbar/webui_toolbar_ui.cc +++ b/chrome/browser/ui/webui/webui_toolbar/webui_toolbar_ui.cc
@@ -12,12 +12,10 @@ #include "base/strings/strcat.h" #include "chrome/browser/profiles/profile.h" #include "chrome/browser/ui/browser_element_identifiers.h" -#include "chrome/browser/ui/browser_finder.h" #include "chrome/browser/ui/browser_window/public/browser_window_interface.h" #include "chrome/browser/ui/interaction/browser_elements.h" #include "chrome/browser/ui/ui_features.h" #include "chrome/browser/ui/views/frame/browser_widget.h" -#include "chrome/browser/ui/views/permissions/chip/permission_chip_view.h" #include "chrome/browser/ui/webui/metrics_handler.h" #include "chrome/browser/ui/webui/metrics_reporter/metrics_reporter_service.h" #include "chrome/browser/ui/webui/theme_colors_source_manager.h" @@ -269,9 +267,7 @@ kPinnedToolbarActionShowSidePanelLensOverlayResultsElementId, kPinnedToolbarActionShowSidePanelBookmarksElementId, kPinnedToolbarActionSendTabToSelfElementId, - kToolbarAvatarButtonElementId, - PermissionChipView::kPermissionRequestChipElementId, - PermissionChipView::kIndicatorChipElementId}); + kToolbarAvatarButtonElementId}); auto pinned_ids = webui_toolbar::GetPinnedToolbarActionElementIds(); pinned_ids.reserve(pinned_ids.size() + ids->size()); pinned_ids.insert(pinned_ids.end(), ids->begin(), ids->end());
diff --git a/chrome/browser/ui/webui/whats_new/whats_new_handler.cc b/chrome/browser/ui/webui/whats_new/whats_new_handler.cc index c6eac8b8d..96d49d2 100644 --- a/chrome/browser/ui/webui/whats_new/whats_new_handler.cc +++ b/chrome/browser/ui/webui/whats_new/whats_new_handler.cc
@@ -16,7 +16,6 @@ #include "chrome/browser/glic/public/glic_keyed_service_factory.h" #include "chrome/browser/profiles/profile.h" #include "chrome/browser/ui/browser.h" -#include "chrome/browser/ui/browser_finder.h" #include "chrome/browser/ui/browser_tabstrip.h" #include "chrome/browser/ui/hats/hats_service.h" #include "chrome/browser/ui/hats/hats_service_factory.h"
diff --git a/chrome/browser/ui/window_sizer/window_sizer_chromeos_uitest.cc b/chrome/browser/ui/window_sizer/window_sizer_chromeos_uitest.cc index 55a13c8..b27a77f6 100644 --- a/chrome/browser/ui/window_sizer/window_sizer_chromeos_uitest.cc +++ b/chrome/browser/ui/window_sizer/window_sizer_chromeos_uitest.cc
@@ -10,7 +10,6 @@ #include "base/command_line.h" #include "base/run_loop.h" #include "chrome/browser/ui/browser.h" -#include "chrome/browser/ui/browser_finder.h" #include "chrome/browser/ui/browser_tabstrip.h" #include "chrome/browser/ui/browser_window.h" #include "chrome/browser/ui/browser_window/public/browser_window_interface.h"
diff --git a/chrome/browser/updater/BUILD.gn b/chrome/browser/updater/BUILD.gn index 92415113..cf8be9f1 100644 --- a/chrome/browser/updater/BUILD.gn +++ b/chrome/browser/updater/BUILD.gn
@@ -19,6 +19,8 @@ sources = [ "browser_updater_client.cc", "browser_updater_client.h", + "browser_updater_client_testutils.cc", + "browser_updater_client_testutils.h", "browser_updater_client_util.cc", "browser_updater_client_util.h", "check_updater_health_task.cc",
diff --git a/chrome/browser/updates/update_metrics_provider.cc b/chrome/browser/updates/update_metrics_provider.cc index 2ef0035..b258a9b 100644 --- a/chrome/browser/updates/update_metrics_provider.cc +++ b/chrome/browser/updates/update_metrics_provider.cc
@@ -6,7 +6,6 @@ #include "base/metrics/histogram_functions.h" #include "chrome/browser/ui/browser.h" -#include "chrome/browser/ui/browser_finder.h" #include "chrome/browser/ui/browser_window/public/browser_window_interface.h" #include "chrome/browser/ui/browser_window/public/global_browser_collection.h" #include "chrome/browser/ui/tabs/tab_strip_model.h"
diff --git a/chrome/browser/usb/BUILD.gn b/chrome/browser/usb/BUILD.gn index d897fb7..55f5aa1 100644 --- a/chrome/browser/usb/BUILD.gn +++ b/chrome/browser/usb/BUILD.gn
@@ -108,7 +108,10 @@ "android/usb_bridge.cc", "android/web_usb_chooser_android.cc", ] - deps += [ "//chrome/browser/usb/android:jni_headers" ] + deps += [ + "//chrome/browser/ui/android/device_dialog", + "//chrome/browser/usb/android:jni_headers", + ] } else { sources += [ "usb_connection_tracker.cc",
diff --git a/chrome/browser/usb/chrome_usb_delegate.cc b/chrome/browser/usb/chrome_usb_delegate.cc index 4a975ff..caa1eb6 100644 --- a/chrome/browser/usb/chrome_usb_delegate.cc +++ b/chrome/browser/usb/chrome_usb_delegate.cc
@@ -12,7 +12,6 @@ #include "base/observer_list_types.h" #include "base/scoped_observation.h" #include "chrome/browser/profiles/profile.h" -#include "chrome/browser/ui/browser_finder.h" #include "chrome/browser/ui/dialogs/browser_dialogs.h" #include "chrome/browser/usb/usb_blocklist.h" #include "chrome/browser/usb/usb_chooser_context.h"
diff --git a/chrome/browser/web_applications/app_service/web_app_publisher_helper_browsertest.cc b/chrome/browser/web_applications/app_service/web_app_publisher_helper_browsertest.cc index 04ee7dde..433ff2a4 100644 --- a/chrome/browser/web_applications/app_service/web_app_publisher_helper_browsertest.cc +++ b/chrome/browser/web_applications/app_service/web_app_publisher_helper_browsertest.cc
@@ -17,6 +17,7 @@ #include "chrome/browser/ui/web_applications/test/web_app_browsertest_util.h" #include "chrome/browser/ui/web_applications/web_app_browsertest_base.h" #include "chrome/browser/ui/web_applications/web_app_menu_model.h" +#include "chrome/browser/web_applications/test/web_app_page_waiter.h" #include "chrome/browser/web_applications/web_app.h" #include "chrome/browser/web_applications/web_app_helpers.h" #include "chrome/browser/web_applications/web_app_provider.h" @@ -254,12 +255,14 @@ GURL to_url = embedded_test_server()->GetURL( "/web_apps/migration/migrate_to/suggest.html"); EXPECT_TRUE(ui_test_utils::NavigateToURL(browser(), to_url)); - web_app::test::WaitForLoadCompleteAndMaybeManifestSeen( - *browser()->tab_strip_model()->GetActiveWebContents()); + EXPECT_TRUE(web_app::test::WebAppPageWaiter( + browser()->tab_strip_model()->GetActiveWebContents()) + .ExpectUrl(to_url) + .ManifestOrLoadedNoManifest() + .WaitAndFlushCommands()); web_app::WebAppProvider* provider = web_app::WebAppProvider::GetForTest(browser()->profile()); - provider->command_manager().AwaitAllCommandsCompleteForTesting(); // 3. Accept migration dialog. views::NamedWidgetShownWaiter update_dialog_waiter(
diff --git a/chrome/browser/web_applications/commands/apply_manifest_migration_command_browsertest.cc b/chrome/browser/web_applications/commands/apply_manifest_migration_command_browsertest.cc index d7b1ad6..d0d4c488 100644 --- a/chrome/browser/web_applications/commands/apply_manifest_migration_command_browsertest.cc +++ b/chrome/browser/web_applications/commands/apply_manifest_migration_command_browsertest.cc
@@ -12,6 +12,7 @@ #include "chrome/browser/ui/web_applications/web_app_browsertest_base.h" #include "chrome/browser/ui/web_applications/web_app_menu_model.h" #include "chrome/browser/web_applications/scheduler/apply_manifest_migration_result.h" +#include "chrome/browser/web_applications/test/web_app_page_waiter.h" #include "chrome/browser/web_applications/web_app_command_manager.h" #include "chrome/browser/web_applications/web_app_filter.h" #include "chrome/browser/web_applications/web_app_helpers.h" @@ -84,12 +85,14 @@ browser(), embedded_https_test_server().GetURL(kMigrateFromInstallUrl)); // This should register the migration: - EXPECT_TRUE(ui_test_utils::NavigateToURL( - browser(), - embedded_https_test_server().GetURL(kMigrateToWithSuggestMigrationUrl))); - test::WaitForLoadCompleteAndMaybeManifestSeen( - *browser()->tab_strip_model()->GetActiveWebContents()); - provider().command_manager().AwaitAllCommandsCompleteForTesting(); + GURL migrate_to_url = + embedded_https_test_server().GetURL(kMigrateToWithSuggestMigrationUrl); + EXPECT_TRUE(ui_test_utils::NavigateToURL(browser(), migrate_to_url)); + EXPECT_TRUE(test::WebAppPageWaiter( + browser()->tab_strip_model()->GetActiveWebContents()) + .ExpectUrl(migrate_to_url) + .ManifestOrLoadedNoManifest() + .WaitAndFlushCommands()); // Approve the migration, waiting for the old browser to be closed and new one // to be created.
diff --git a/chrome/browser/web_applications/commands/fetch_manifest_and_install_command_browsertest.cc b/chrome/browser/web_applications/commands/fetch_manifest_and_install_command_browsertest.cc index 61e5ee0d..8e9876c4 100644 --- a/chrome/browser/web_applications/commands/fetch_manifest_and_install_command_browsertest.cc +++ b/chrome/browser/web_applications/commands/fetch_manifest_and_install_command_browsertest.cc
@@ -17,7 +17,6 @@ #include "base/test/test_future.h" #include "chrome/browser/ui/browser.h" #include "chrome/browser/ui/browser_commands.h" -#include "chrome/browser/ui/browser_finder.h" #include "chrome/browser/ui/browser_window/public/browser_window_interface.h" #include "chrome/browser/ui/browser_window/public/global_browser_collection.h" #include "chrome/browser/ui/tabs/tab_strip_model.h"
diff --git a/chrome/browser/web_applications/commands/launch_or_reparent_web_contents_into_app_command_browsertest.cc b/chrome/browser/web_applications/commands/launch_or_reparent_web_contents_into_app_command_browsertest.cc index 83a44b9ea..7930d8d 100644 --- a/chrome/browser/web_applications/commands/launch_or_reparent_web_contents_into_app_command_browsertest.cc +++ b/chrome/browser/web_applications/commands/launch_or_reparent_web_contents_into_app_command_browsertest.cc
@@ -6,7 +6,6 @@ #include "base/test/test_future.h" #include "chrome/browser/ui/browser.h" -#include "chrome/browser/ui/browser_finder.h" #include "chrome/browser/ui/browser_window/public/global_browser_collection.h" #include "chrome/browser/ui/web_applications/app_browser_controller.h" #include "chrome/browser/ui/web_applications/web_app_browsertest_base.h"
diff --git a/chrome/browser/web_applications/commands/launch_web_app_command_browsertest.cc b/chrome/browser/web_applications/commands/launch_web_app_command_browsertest.cc index d11c08e..1bc1886 100644 --- a/chrome/browser/web_applications/commands/launch_web_app_command_browsertest.cc +++ b/chrome/browser/web_applications/commands/launch_web_app_command_browsertest.cc
@@ -27,6 +27,7 @@ #include "chrome/browser/web_applications/model/migration_behavior.h" #include "chrome/browser/web_applications/proto/web_app_install_state.pb.h" #include "chrome/browser/web_applications/test/web_app_install_test_utils.h" +#include "chrome/browser/web_applications/test/web_app_page_waiter.h" #include "chrome/browser/web_applications/test/web_app_test_utils.h" #include "chrome/browser/web_applications/web_app_command_scheduler.h" #include "chrome/browser/web_applications/web_app_filter.h" @@ -364,11 +365,10 @@ DoLaunch(std::move(launch_params)); ASSERT_TRUE(web_contents); - content::WaitForLoadStop(web_contents.get()); - - // The launch process should intercept the out-of-scope/cross-origin URL - // and redirect it to the app's start URL. - EXPECT_EQ(kAppStartUrl, web_contents->GetLastCommittedURL()); + EXPECT_TRUE(test::WebAppPageWaiter(web_contents.get()) + .ExpectUrl(kAppStartUrl) + .ManifestOrLoadedNoManifest() + .WaitAndFlushCommands()); } } // namespace web_app
diff --git a/chrome/browser/web_applications/isolated_web_apps/window_management/isolated_web_apps_window_open_permission_service_browsertest.cc b/chrome/browser/web_applications/isolated_web_apps/window_management/isolated_web_apps_window_open_permission_service_browsertest.cc index d306e2d..d10b804a 100644 --- a/chrome/browser/web_applications/isolated_web_apps/window_management/isolated_web_apps_window_open_permission_service_browsertest.cc +++ b/chrome/browser/web_applications/isolated_web_apps/window_management/isolated_web_apps_window_open_permission_service_browsertest.cc
@@ -16,7 +16,6 @@ #include "chrome/browser/notifications/notification_handler.h" #include "chrome/browser/notifications/stub_notification_display_service.h" #include "chrome/browser/ui/browser.h" -#include "chrome/browser/ui/browser_finder.h" #include "chrome/browser/ui/browser_window/public/browser_window_interface.h" #include "chrome/browser/ui/browser_window/public/desktop_browser_window_capabilities.h" #include "chrome/browser/ui/web_applications/test/isolated_web_app_test_utils.h"
diff --git a/chrome/browser/web_applications/preinstalled_web_app_manager_browsertest.cc b/chrome/browser/web_applications/preinstalled_web_app_manager_browsertest.cc index c9be261c..727ab24f0 100644 --- a/chrome/browser/web_applications/preinstalled_web_app_manager_browsertest.cc +++ b/chrome/browser/web_applications/preinstalled_web_app_manager_browsertest.cc
@@ -48,6 +48,7 @@ #include "chrome/browser/web_applications/test/test_file_utils.h" #include "chrome/browser/web_applications/test/web_app_icon_test_utils.h" #include "chrome/browser/web_applications/test/web_app_install_test_utils.h" +#include "chrome/browser/web_applications/test/web_app_page_waiter.h" #include "chrome/browser/web_applications/test/web_app_test_utils.h" #include "chrome/browser/web_applications/user_uninstalled_preinstalled_web_app_prefs.h" #include "chrome/browser/web_applications/web_app.h" @@ -2054,7 +2055,10 @@ base::WeakPtr<content::WebContents> web_contents = launch.Get<base::WeakPtr<content::WebContents>>(); ASSERT_TRUE(web_contents); - content::WaitForLoadStop(web_contents.get()); + EXPECT_TRUE(test::WebAppPageWaiter(web_contents.get()) + .ExpectUrl(GetOutOfScopeUrl()) + .ManifestOrLoadedNoManifest() + .WaitAndFlushCommands()); provider().command_manager().AwaitAllCommandsCompleteForTesting(); EXPECT_EQ(provider().registrar_unsafe().GetAppShortName(GetAppId()), @@ -2082,7 +2086,10 @@ base::WeakPtr<content::WebContents> web_contents = launch.Get<base::WeakPtr<content::WebContents>>(); ASSERT_TRUE(web_contents); - content::WaitForLoadStop(web_contents.get()); + EXPECT_TRUE(test::WebAppPageWaiter(web_contents.get()) + .ExpectUrl(GetOutOfScopeUrl()) + .ManifestOrLoadedNoManifest() + .WaitAndFlushCommands()); provider().command_manager().AwaitAllCommandsCompleteForTesting(); histogram_tester.ExpectUniqueSample("WebApp.FetchManifestAndUpdate.Result", FetchManifestAndUpdateResult::kSuccess,
diff --git a/chrome/browser/web_applications/test/web_app_page_waiter.cc b/chrome/browser/web_applications/test/web_app_page_waiter.cc index 7d9dd64..5d66a3f 100644 --- a/chrome/browser/web_applications/test/web_app_page_waiter.cc +++ b/chrome/browser/web_applications/test/web_app_page_waiter.cc
@@ -4,16 +4,20 @@ #include "chrome/browser/web_applications/test/web_app_page_waiter.h" +#include <algorithm> + #include "base/functional/bind.h" #include "base/functional/callback_helpers.h" #include "base/logging.h" #include "base/notreached.h" +#include "base/test/run_until.h" #include "chrome/browser/profiles/profile.h" #include "chrome/browser/web_applications/web_app_command_manager.h" #include "chrome/browser/web_applications/web_app_provider.h" #include "chrome/browser/web_applications/web_app_tab_helper.h" #include "content/public/browser/navigation_controller.h" #include "content/public/browser/navigation_entry.h" +#include "content/public/browser/navigation_handle.h" #include "content/public/browser/page.h" #include "content/public/browser/page_manifest_manager.h" #include "content/public/browser/web_contents.h" @@ -22,6 +26,21 @@ namespace web_app::test { +namespace { +std::string ToString(WebAppPageWaiter::Expectation expectation) { + switch (expectation) { + case WebAppPageWaiter::Expectation::kUnset: + return "kUnset"; + case WebAppPageWaiter::Expectation::kManifest: + return "kManifest"; + case WebAppPageWaiter::Expectation::kNoManifest: + return "kNoManifest"; + case WebAppPageWaiter::Expectation::kManifestOrLoadedNoManifest: + return "kManifestOrLoadedNoManifest"; + } +} +} // namespace + WebAppPageWaiter::WebAppPageWaiter(content::WebContents* web_contents) : content::WebContentsObserver(web_contents) { CHECK(web_contents); @@ -29,17 +48,13 @@ WebAppPageWaiter::~WebAppPageWaiter() = default; -WebAppPageWaiter& WebAppPageWaiter::ExpectUrlIf( - base::RepeatingCallback<bool(const GURL&)> url_matcher) { - url_matcher_ = std::move(url_matcher); - return *this; +WebAppPageWaiter& WebAppPageWaiter::ExpectUrl(const GURL& url) { + return ExpectAnyUrl({url}); } -WebAppPageWaiter& WebAppPageWaiter::ExpectUrl(const GURL& url) { - return ExpectUrlIf( - base::BindRepeating([](const GURL& expected, - const GURL& actual) { return expected == actual; }, - url)); +WebAppPageWaiter& WebAppPageWaiter::ExpectAnyUrl(base::flat_set<GURL> urls) { + expected_urls_ = std::move(urls); + return *this; } WebAppPageWaiter& WebAppPageWaiter::ExpectManifest( @@ -64,6 +79,13 @@ testing::AssertionResult WebAppPageWaiter::WaitAndFlushCommands() { CHECK_NE(expectation_, Expectation::kUnset); + CHECK(!wait_called_) << "The WebAppPageWaiter can only be used once."; + wait_called_ = true; + + DVLOG(1) << "WaitAndFlushCommands started. Expectation: " + << ToString(expectation_) << ", expected_manifest_id: " + << (expected_manifest_id_ ? expected_manifest_id_->spec() : "none") + << ", Current URL: " << web_contents()->GetVisibleURL(); if (destroyed_) { return testing::AssertionFailure() @@ -72,26 +94,49 @@ // Wait for the expected URL to load first (if set), otherwise just wait for // current load to stop. - if (url_matcher_) { - while (!UrlMatches() || web_contents()->IsLoading()) { - // Nestable tasks ensure other testing base::RunLoops don't deadlock the - // test. - base::RunLoop url_loop(base::RunLoop::Type::kNestableTasksAllowed); - url_quit_closure_ = url_loop.QuitClosure(); - url_loop.Run(); - url_quit_closure_.Reset(); - if (destroyed_) { - return testing::AssertionFailure() - << "WebContents destroyed while waiting for URL"; + is_waiting_for_url_load_ = true; + if (!expected_urls_.empty()) { + DVLOG(1) << "Waiting for URL matching condition. Current URL: " + << web_contents()->GetVisibleURL(); + bool waitSuccess = base::test::RunUntil([&]() { + if (!web_contents()) { + return true; } + bool is_web_contents_considered_loaded = + !web_contents()->IsLoading() || + web_contents()->GetVisibleURL().path() == "/hung"; + bool url_matches_and_web_contents_loaded = + UrlMatches() && is_web_contents_considered_loaded; + return url_matches_and_web_contents_loaded || load_failed_; + }); + if (!waitSuccess) { + return testing::AssertionFailure() + << "Timed out waiting for URL. Current URL: " + << web_contents()->GetVisibleURL() + << ", Expected: " << base::ToString(expected_urls_); } + if (destroyed_) { + return testing::AssertionFailure() + << "WebContents destroyed while waiting for URL"; + } + if (load_failed_) { + return testing::AssertionFailure() << "URL load failed. Current URL: " + << web_contents()->GetVisibleURL(); + } + DVLOG(1) << "URL matched and loaded. Final URL: " + << web_contents()->GetVisibleURL(); } else { + DVLOG(1) << "No URL matcher. Waiting for load stop. Current URL: " + << web_contents()->GetVisibleURL(); content::WaitForLoadStop(web_contents()); + DVLOG(1) << "Load stopped. Current URL: " + << web_contents()->GetVisibleURL(); if (destroyed_) { return testing::AssertionFailure() << "WebContents destroyed during WaitForLoadStop"; } } + is_waiting_for_url_load_ = false; document_loaded_ = web_contents()->IsDocumentOnLoadCompletedInPrimaryMainFrame(); @@ -119,6 +164,9 @@ if (tab_helper && tab_helper->manifest_processed_for_current_page()) { processed_manifest_id_ = tab_helper->last_processed_manifest_id_for_current_page(); + DVLOG(1) << "Manifest already processed for current page: " + << (processed_manifest_id_ ? processed_manifest_id_->spec() + : "none"); if (!expected_manifest_id_.has_value() || processed_manifest_id_ == expected_manifest_id_) { matching_manifest_processed_ = true; @@ -126,6 +174,7 @@ } if (tab_helper) { + DVLOG(1) << "Subscribing to OnManifestProcessed."; manifest_subscription_ = tab_helper->AddOnManifestProcessedCallbackForTesting( base::BindRepeating(&WebAppPageWaiter::OnManifestProcessed, @@ -134,7 +183,9 @@ MaybeQuit(); + DVLOG(1) << "Starting main run loop."; run_loop_.Run(); + DVLOG(1) << "Main run loop finished. destroyed: " << destroyed_; if (destroyed_) { return testing::AssertionFailure() << "WebContents destroyed while waiting"; @@ -182,11 +233,16 @@ } break; } + Profile* profile = + Profile::FromBrowserContext(web_contents()->GetBrowserContext()); + + // Stop observing to prevent re-entry if the commands or test messes with this + // web contents too. (e.g. a command navigates it, or the test has this waiter + // still constructed / on the stack and continues to use the web contents. + Observe(nullptr); // Wait for all commands to complete in the command manager to ensure any // spawned tasks are done. - Profile* profile = - Profile::FromBrowserContext(web_contents()->GetBrowserContext()); WebAppProvider* provider = WebAppProvider::GetForTest(profile); if (provider) { provider->command_manager().AwaitAllCommandsCompleteForTesting(); @@ -196,20 +252,23 @@ } void WebAppPageWaiter::DidStopLoading() { - if (url_quit_closure_) { - url_quit_closure_.Run(); - return; - } + DVLOG(1) << "DidStopLoading. Current URL: " + << web_contents()->GetVisibleURL(); MaybeQuit(); } void WebAppPageWaiter::DocumentOnLoadCompletedInPrimaryMainFrame() { + DVLOG(1) << "DocumentOnLoadCompletedInPrimaryMainFrame. Current URL: " + << web_contents()->GetVisibleURL(); MaybeQuit(); } void WebAppPageWaiter::DidFailLoad(content::RenderFrameHost* render_frame_host, const GURL& validated_url, int error_code) { + DVLOG(1) << "DidFailLoad. URL: " << validated_url + << ", error_code: " << error_code << ", IsInPrimaryMainFrame: " + << (render_frame_host && render_frame_host->IsInPrimaryMainFrame()); if (render_frame_host && render_frame_host->IsInPrimaryMainFrame()) { load_failed_ = true; MaybeQuit(); @@ -217,16 +276,17 @@ } void WebAppPageWaiter::WebContentsDestroyed() { + DVLOG(1) << "WebContentsDestroyed."; destroyed_ = true; - if (url_quit_closure_) { - url_quit_closure_.Run(); - } Observe(nullptr); run_loop_.Quit(); } void WebAppPageWaiter::OnManifestProcessed( const webapps::ManifestId& manifest_id) { + DVLOG(1) << "OnManifestProcessed: " << manifest_id.spec() + << ", expected_manifest_id: " + << (expected_manifest_id_ ? expected_manifest_id_->spec() : "none"); processed_manifest_id_ = manifest_id; if (!expected_manifest_id_.has_value() || processed_manifest_id_ == expected_manifest_id_) { @@ -239,7 +299,13 @@ } void WebAppPageWaiter::MaybeQuit() { + if (is_waiting_for_url_load_) { + DVLOG(1) << "MaybeQuit: Skipping because still waiting for URL load."; + return; + } + if (destroyed_) { + DVLOG(1) << "MaybeQuit: Quitting because destroyed."; run_loop_.Quit(); return; } @@ -254,9 +320,18 @@ web_contents()->GetPrimaryPage().GetManifestUrl().has_value(); bool has_load_completed_enough_for_manifest_url_check = document_loaded_ || load_failed_; + + DVLOG(2) << "MaybeQuit. Expectation: " << ToString(expectation_) + << ", document_loaded: " << document_loaded_ + << ", load_failed: " << load_failed_ + << ", matching_manifest_processed: " << matching_manifest_processed_ + << ", has_manifest_url: " << has_manifest_url + << ", webapps_enabled: " << (!!manifest_subscription_); + switch (expectation_) { case Expectation::kManifestOrLoadedNoManifest: if (matching_manifest_processed_) { + DVLOG(1) << "MaybeQuit: Quitting because matching manifest processed."; run_loop_.Quit(); return; } @@ -264,9 +339,14 @@ bool webapps_enabled_for_web_contents = !!manifest_subscription_; if (webapps_enabled_for_web_contents && has_manifest_url && !matching_manifest_processed_) { + DVLOG(1) << "MaybeQuit: Keeping waiting because has manifest URL but " + "not processed yet."; // Keep waiting for manifest. return; } + DVLOG(1) + << "MaybeQuit: Quitting because load completed and no manifest " + "to wait for."; run_loop_.Quit(); } return; @@ -274,15 +354,24 @@ // If the manifest url doesn't exist and load has completed, assume that // there will not be a manifest set on the page, and fail early so we // don't time out. - if (matching_manifest_processed_ || - (has_load_completed_enough_for_manifest_url_check && - !has_manifest_url)) { + if (matching_manifest_processed_) { + DVLOG(1) << "MaybeQuit: Quitting because matching manifest processed " + "(kManifest)."; + run_loop_.Quit(); + } else if (has_load_completed_enough_for_manifest_url_check && + !has_manifest_url) { + DVLOG(1) + << "MaybeQuit: Quitting because load completed and no manifest " + "URL found."; run_loop_.Quit(); } return; case Expectation::kNoManifest: if (has_load_completed_enough_for_manifest_url_check || has_manifest_url) { + DVLOG(1) << "MaybeQuit: Quitting (kNoManifest). has_load_completed: " + << has_load_completed_enough_for_manifest_url_check + << ", has_manifest_url: " << has_manifest_url; run_loop_.Quit(); } break; @@ -292,12 +381,36 @@ } bool WebAppPageWaiter::UrlMatches() const { - if (!url_matcher_) { - return true; - } + CHECK(!expected_urls_.empty()); content::NavigationController& controller = web_contents()->GetController(); content::NavigationEntry* entry = controller.GetVisibleEntry(); - return entry && url_matcher_.Run(entry->GetVirtualURL()); + if (!entry) { + return false; + } + if (expected_urls_.contains(entry->GetURL())) { + return true; + } + // It is likely a mistake if the expected url was redirected, so print a + // warning. + if (expected_urls_.contains(entry->GetOriginalRequestURL())) { + DLOG(WARNING) + << "WebAppPageWaiter: Match found via OriginalRequestURL: " + << entry->GetOriginalRequestURL() << " (URL is " << entry->GetURL() + << "). Tests may want to explicitly expect this redirected-to url."; + return true; + } + + const std::vector<GURL>& chain = entry->GetRedirectChain(); + for (const GURL& redirect_chain_url : chain) { + if (expected_urls_.contains(redirect_chain_url)) { + DLOG(WARNING) + << "WebAppPageWaiter: Match found in redirect chain containing " + << redirect_chain_url << " (Current URL is " << entry->GetURL() + << "). Tests may want to explicitly expect this redirected-to url."; + return true; + } + } + return false; } } // namespace web_app::test
diff --git a/chrome/browser/web_applications/test/web_app_page_waiter.h b/chrome/browser/web_applications/test/web_app_page_waiter.h index 84c4f82..fd119c13 100644 --- a/chrome/browser/web_applications/test/web_app_page_waiter.h +++ b/chrome/browser/web_applications/test/web_app_page_waiter.h
@@ -8,6 +8,7 @@ #include <optional> #include "base/callback_list.h" +#include "base/containers/flat_set.h" #include "base/functional/callback_forward.h" #include "base/run_loop.h" #include "base/timer/timer.h" @@ -41,17 +42,26 @@ // error (like a network error). class WebAppPageWaiter : public content::WebContentsObserver { public: + enum class Expectation { + kUnset, + kManifest, + kNoManifest, + kManifestOrLoadedNoManifest, + }; + explicit WebAppPageWaiter(content::WebContents* web_contents); ~WebAppPageWaiter() override; // Configures the waiter to expect the WebContents to navigate to and finish // loading the specified URL. + // + // If the page redirects during navigation, and the redirect starts from + // the expected URL, the waiter will automatically update its expectation + // to the redirect destination and log a warning. WebAppPageWaiter& ExpectUrl(const GURL& url); - // Configures the waiter to expect a URL that satisfies the given matcher - // callback. - WebAppPageWaiter& ExpectUrlIf( - base::RepeatingCallback<bool(const GURL&)> url_matcher); + // Same as above, for any of the given urls. + WebAppPageWaiter& ExpectAnyUrl(base::flat_set<GURL> urls); // Configures the waiter to expect a valid manifest to be discovered and // processed. @@ -93,27 +103,23 @@ private: void OnManifestProcessed(const webapps::ManifestId& manifest_id); void MaybeQuit(); - bool UrlMatches() const; - enum class Expectation { - kUnset, - kManifest, - kNoManifest, - kManifestOrLoadedNoManifest, - }; + bool UrlMatches() const; Expectation expectation_ = Expectation::kUnset; std::optional<webapps::ManifestId> expected_manifest_id_; - base::RepeatingCallback<bool(const GURL&)> url_matcher_; + base::flat_set<GURL> expected_urls_; std::optional<webapps::ManifestId> processed_manifest_id_; + bool is_waiting_for_url_load_ = false; + bool document_loaded_ = false; bool load_failed_ = false; bool matching_manifest_processed_ = false; bool destroyed_ = false; + bool wait_called_ = false; base::CallbackListSubscription manifest_subscription_; - base::RepeatingClosure url_quit_closure_; base::RunLoop run_loop_; };
diff --git a/chrome/browser/web_applications/web_app_icon_manager_browsertest.cc b/chrome/browser/web_applications/web_app_icon_manager_browsertest.cc index 4b24a16..5d668f1 100644 --- a/chrome/browser/web_applications/web_app_icon_manager_browsertest.cc +++ b/chrome/browser/web_applications/web_app_icon_manager_browsertest.cc
@@ -10,7 +10,6 @@ #include "chrome/browser/apps/app_service/app_service_test.h" #include "chrome/browser/apps/app_service/browser_app_launcher.h" #include "chrome/browser/ui/browser.h" -#include "chrome/browser/ui/browser_finder.h" #include "chrome/browser/ui/web_applications/test/web_app_browsertest_util.h" #include "chrome/browser/ui/web_applications/web_app_browser_controller.h" #include "chrome/browser/ui/web_applications/web_app_browsertest_base.h" @@ -60,8 +59,8 @@ }; IN_PROC_BROWSER_TEST_F(WebAppIconManagerBrowserTest, SingleIcon) { - const GURL start_url = - embedded_https_test_server().GetURL("/banners/manifest_test_page.html"); + const GURL start_url = embedded_https_test_server().GetURL( + "/banners/no_manifest_test_page.html"); webapps::AppId app_id; {
diff --git a/chrome/browser/web_applications/web_app_navigation_capturing_intent_picker_browsertest.cc b/chrome/browser/web_applications/web_app_navigation_capturing_intent_picker_browsertest.cc index a492504..52fd171 100644 --- a/chrome/browser/web_applications/web_app_navigation_capturing_intent_picker_browsertest.cc +++ b/chrome/browser/web_applications/web_app_navigation_capturing_intent_picker_browsertest.cc
@@ -41,15 +41,23 @@ class WebAppNavigationCapturingIntentPickerBrowserTest : public WebAppNavigationCapturingBrowserTestBase { protected: - GURL GetAppUrl() { - return embedded_https_test_server().GetURL( + GURL GetAppUrl(std::string manifest = "") { + GURL url = embedded_https_test_server().GetURL( "/web_apps/intent_picker_nav_capture/index.html"); + if (!manifest.empty()) { + url = GURL(url.spec() + "?manifest=" + manifest); + } + return url; } - GURL GetAppUrlWithQuery() { - return embedded_https_test_server().GetURL( + GURL GetAppUrlWithQuery(std::string manifest = "") { + GURL url = embedded_https_test_server().GetURL( "/web_apps/intent_picker_nav_capture/" "index.html?q=fake_query_to_check_navigation"); + if (!manifest.empty()) { + url = GURL(url.spec() + "&manifest=" + manifest); + } + return url; } GURL GetAppUrlWithWCO() { @@ -58,17 +66,12 @@ } }; -// TODO(crbug.com/376641667): Flaky on Mac & Windows. -#if BUILDFLAG(IS_MAC) || BUILDFLAG(IS_WIN) -#define MAYBE_FocusExisting DISABLED_FocusExisting -#else -#define MAYBE_FocusExisting FocusExisting -#endif // BUILDFLAG(IS_MAC) IN_PROC_BROWSER_TEST_F(WebAppNavigationCapturingIntentPickerBrowserTest, - MAYBE_FocusExisting) { + FocusExisting) { webapps::AppId app_id = test::InstallWebApp( profile(), WebAppInstallInfo::CreateForTesting( - GetAppUrl(), blink::mojom::DisplayMode::kMinimalUi, + GetAppUrl("manifest_focus.json"), + blink::mojom::DisplayMode::kMinimalUi, mojom::UserDisplayMode::kStandalone, ManifestLaunchHandler_ClientMode::kFocusExisting)); @@ -77,8 +80,8 @@ content::WebContents* app_contents = app_browser->tab_strip_model()->GetWebContentsAt(0); - content::RenderFrameHost* host = - ui_test_utils::NavigateToURL(browser(), GetAppUrlWithQuery()); + content::RenderFrameHost* host = ui_test_utils::NavigateToURL( + browser(), GetAppUrlWithQuery("manifest_focus.json")); EXPECT_NE(nullptr, host); // Warning: A `tab_contents` pointer obtained from browser() will be invalid @@ -94,7 +97,7 @@ // Check the end state for the app. EXPECT_EQ(1, app_browser->tab_strip_model()->count()); EXPECT_EQ(app_contents->GetPrimaryMainFrame()->GetLastCommittedURL(), - GetAppUrl()); + GetAppUrl("manifest_focus.json")); std::vector<GURL> launch_params = apps::test::GetLaunchParamUrlsInContents( app_contents, "launchParamsTargetUrls"); @@ -102,20 +105,15 @@ // for when the existing app got focus (via the Intent Picker) and launch // params were enqueued. EXPECT_THAT(launch_params, - testing::ElementsAre(GetAppUrl(), GetAppUrlWithQuery())); + testing::ElementsAre(GetAppUrl("manifest_focus.json"), + GetAppUrlWithQuery("manifest_focus.json"))); } -// TODO(crbug.com/382315984): Fix this flake. -#if BUILDFLAG(IS_MAC) -#define MAYBE_NavigateExisting DISABLED_NavigateExisting -#else -#define MAYBE_NavigateExisting NavigateExisting -#endif - IN_PROC_BROWSER_TEST_F(WebAppNavigationCapturingIntentPickerBrowserTest, - MAYBE_NavigateExisting) { + NavigateExisting) { webapps::AppId app_id = InstallWebApp(WebAppInstallInfo::CreateForTesting( - GetAppUrl(), blink::mojom::DisplayMode::kMinimalUi, + GetAppUrl("manifest_navigate.json"), + blink::mojom::DisplayMode::kMinimalUi, mojom::UserDisplayMode::kStandalone, ManifestLaunchHandler_ClientMode::kNavigateExisting)); @@ -124,8 +122,8 @@ content::WebContents* app_contents = app_browser->tab_strip_model()->GetWebContentsAt(0); - content::RenderFrameHost* host = - ui_test_utils::NavigateToURL(browser(), GetAppUrlWithQuery()); + content::RenderFrameHost* host = ui_test_utils::NavigateToURL( + browser(), GetAppUrlWithQuery("manifest_navigate.json")); EXPECT_NE(nullptr, host); // Warning: A `tab_contents` pointer obtained from browser() will be invalid @@ -138,13 +136,14 @@ EXPECT_EQ(1, browser()->tab_strip_model()->count()); EXPECT_EQ(1, app_browser->tab_strip_model()->count()); EXPECT_EQ(app_contents->GetPrimaryMainFrame()->GetLastCommittedURL(), - GetAppUrlWithQuery()); + GetAppUrlWithQuery("manifest_navigate.json")); std::vector<GURL> launch_params = apps::test::GetLaunchParamUrlsInContents( app_contents, "launchParamsTargetUrls"); // There should be one launch param -- because the Intent Picker triggers a // new navigation in the app (and launch params are then enqueued). - EXPECT_THAT(launch_params, testing::ElementsAre(GetAppUrlWithQuery())); + EXPECT_THAT(launch_params, testing::ElementsAre( + GetAppUrlWithQuery("manifest_navigate.json"))); } // Test that the intent picker shows up for chrome://password-manager, since it
diff --git a/chrome/browser/web_applications/web_install_browsertest.cc b/chrome/browser/web_applications/web_install_browsertest.cc index 8516fdc..0c74d0cb9 100644 --- a/chrome/browser/web_applications/web_install_browsertest.cc +++ b/chrome/browser/web_applications/web_install_browsertest.cc
@@ -13,7 +13,6 @@ #include "chrome/browser/profiles/profile.h" #include "chrome/browser/ui/browser.h" #include "chrome/browser/ui/browser_commands.h" -#include "chrome/browser/ui/browser_finder.h" #include "chrome/browser/ui/tabs/tab_enums.h" #include "chrome/browser/ui/tabs/tab_strip_model.h" #include "chrome/browser/ui/views/intent_picker_bubble_view.h"
diff --git a/chrome/browser/webid/BUILD.gn b/chrome/browser/webid/BUILD.gn index 5507bac..72a1538b 100644 --- a/chrome/browser/webid/BUILD.gn +++ b/chrome/browser/webid/BUILD.gn
@@ -51,6 +51,7 @@ ":java", "$google_play_services_package:google_play_services_identity_credentials_java", "//base:base_junit_test_support", + "//base:promise_java", "//content/public/android:content_java", "//third_party/androidx:androidx_credentials_credentials_java", "//third_party/junit",
diff --git a/chrome/browser/webid/android/java/src/org/chromium/chrome/browser/webid/DigitalCredentialsPresentationDelegate.java b/chrome/browser/webid/android/java/src/org/chromium/chrome/browser/webid/DigitalCredentialsPresentationDelegate.java index e4b5b235..c62272f 100644 --- a/chrome/browser/webid/android/java/src/org/chromium/chrome/browser/webid/DigitalCredentialsPresentationDelegate.java +++ b/chrome/browser/webid/android/java/src/org/chromium/chrome/browser/webid/DigitalCredentialsPresentationDelegate.java
@@ -51,6 +51,15 @@ public static final String BUNDLE_KEY_PROVIDER_DATA = "androidx.identitycredentials.BUNDLE_KEY_PROVIDER_DATA"; + private static final String EXTRA_LARGE_PAYLOAD_RESULT_RECEIVER = + "androidx.credentials.provider.EXTRA_LARGE_PAYLOAD_RESULT_RECEIVER"; + + @VisibleForTesting + public static final String EXTRA_PASS_IT_BY_RESULT_RECEIVER = + "androidx.credentials.provider.EXTRA_PASS_IT_BY_RESULT_RECEIVER"; + + @VisibleForTesting public static final String RESULT_DATA = "RESULT_DATA"; + private static final String TYPE_USER_CANCELED = "android.credentials.GetCredentialException.TYPE_USER_CANCELED"; private static final String TYPE_NO_CREDENTIAL = @@ -58,6 +67,8 @@ private static final String TYPE_INTERRUPTED = "android.credentials.GetCredentialException.TYPE_INTERRUPTED"; + private Bundle mLargeResultData = Bundle.EMPTY; + @OptIn(markerClass = ExperimentalDigitalCredentialApi.class) public Promise<DigitalCredential> get( WindowAndroid windowAndroid, String origin, String request) { @@ -75,54 +86,38 @@ final Promise<DigitalCredential> result = new Promise<>(); + final ResultReceiver largePayloadResultReceiver = + new ResultReceiver(new Handler(Looper.getMainLooper())) { + @Override + protected void onReceiveResult(int resultCode, Bundle resultData) { + mLargeResultData = resultData; + } + }; + ResultReceiver resultReceiver = new ResultReceiver(new Handler(Looper.getMainLooper())) { @Override protected void onReceiveResult(int code, Bundle data) { - if (!result.isPending()) { - return; - } - Log.d(TAG, "Received a response"); try { - Intent providerData = - data == null - ? null - : IntentUtils.safeGetParcelable( - data, BUNDLE_KEY_PROVIDER_DATA); - if (code != Activity.RESULT_OK) { - androidx.credentials.exceptions.GetCredentialException exception = - providerData != null - ? PendingIntentHandler - .retrieveGetCredentialException( - providerData) - : null; - handleGetCredentialException(code, exception, result); - return; - } - var credential = extractDigitalCredentialFromIntent(providerData); - if (credential == null) { - result.reject( - new GetCredentialUnknownException( - "Response does not contain a credential")); - } else { - result.fulfill(credential); - } - } catch (androidx.credentials.exceptions.GetCredentialException e) { - handleGetCredentialException(code, e, result); - } catch (Exception e) { - Log.e(TAG, e.toString()); - result.reject(new GetCredentialUnknownException(e.getMessage())); + handleOnReceiveResult(code, data, result, mLargeResultData); + } finally { + mLargeResultData = Bundle.EMPTY; // release the reference } } }; GetDigitalCredentialOption option = new GetDigitalCredentialOption(request); + Bundle requestData = option.getRequestData(); + requestData.putParcelable( + EXTRA_LARGE_PAYLOAD_RESULT_RECEIVER, + toIpcFriendlyResultReceiver(largePayloadResultReceiver)); + client.getCredential( new GetCredentialRequest( Arrays.asList( new CredentialOption( option.getType(), - option.getRequestData(), + requestData, option.getCandidateQueryData(), request, "", @@ -201,6 +196,67 @@ return result; } + private <T extends ResultReceiver> @Nullable ResultReceiver toIpcFriendlyResultReceiver( + @Nullable T resultReceiver) { + if (resultReceiver == null) { + return null; + } + android.os.Parcel parcel = android.os.Parcel.obtain(); + try { + resultReceiver.writeToParcel(parcel, 0); + parcel.setDataPosition(0); + return ResultReceiver.CREATOR.createFromParcel(parcel); + } catch (Exception e) { + return null; + } finally { + parcel.recycle(); + } + } + + @VisibleForTesting + public void handleOnReceiveResult( + int code, + @Nullable Bundle data, + Promise<DigitalCredential> result, + Bundle largeResultData) { + if (!result.isPending()) { + return; + } + Log.d(TAG, "Received a response"); + try { + Intent providerData = + data == null + ? null + : IntentUtils.safeGetParcelable(data, BUNDLE_KEY_PROVIDER_DATA); + + if (providerData != null && providerData.hasExtra(EXTRA_PASS_IT_BY_RESULT_RECEIVER)) { + providerData = IntentUtils.safeGetParcelable(largeResultData, RESULT_DATA); + } + + if (code != Activity.RESULT_OK) { + androidx.credentials.exceptions.GetCredentialException exception = + providerData != null + ? PendingIntentHandler.retrieveGetCredentialException(providerData) + : null; + handleGetCredentialException(code, exception, result); + return; + } + var credential = extractDigitalCredentialFromIntent(providerData); + if (credential == null) { + result.reject( + new GetCredentialUnknownException( + "Response does not contain a credential")); + } else { + result.fulfill(credential); + } + } catch (androidx.credentials.exceptions.GetCredentialException e) { + handleGetCredentialException(code, e, result); + } catch (Exception e) { + Log.e(TAG, e.toString()); + result.reject(new GetCredentialUnknownException(e.getMessage())); + } + } + /** * Extracts a DigitalCredential from an intent, or throws an exception if found. *
diff --git a/chrome/browser/webid/android/junit/src/org/chromium/chrome/browser/webid/DigitalCredentialsPresentationDelegateTest.java b/chrome/browser/webid/android/junit/src/org/chromium/chrome/browser/webid/DigitalCredentialsPresentationDelegateTest.java index 09f71c6..3e9f11c 100644 --- a/chrome/browser/webid/android/junit/src/org/chromium/chrome/browser/webid/DigitalCredentialsPresentationDelegateTest.java +++ b/chrome/browser/webid/android/junit/src/org/chromium/chrome/browser/webid/DigitalCredentialsPresentationDelegateTest.java
@@ -8,8 +8,10 @@ import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertThrows; +import android.app.Activity; import android.content.Intent; import android.os.Build; +import android.os.Bundle; import androidx.credentials.GetCredentialResponse; import androidx.credentials.exceptions.GetCredentialException; @@ -21,6 +23,7 @@ import org.junit.runner.RunWith; import org.robolectric.annotation.Config; +import org.chromium.base.Promise; import org.chromium.base.test.BaseRobolectricTestRunner; import org.chromium.chrome.browser.webid.IdentityCredentialsDelegate.DigitalCredential; @@ -82,4 +85,33 @@ DigitalCredentialsPresentationDelegate.extractDigitalCredentialFromIntent( intent)); } + + @Test + public void testHandleOnReceiveResult_LargePayload() + throws androidx.credentials.exceptions.GetCredentialException, JSONException { + DigitalCredentialsPresentationDelegate delegate = + new DigitalCredentialsPresentationDelegate(); + Promise<DigitalCredential> promise = new Promise<>(); + + Bundle data = new Bundle(); + Intent providerData = new Intent(); + providerData.putExtra( + DigitalCredentialsPresentationDelegate.EXTRA_PASS_IT_BY_RESULT_RECEIVER, true); + data.putParcelable( + DigitalCredentialsPresentationDelegate.BUNDLE_KEY_PROVIDER_DATA, providerData); + + Bundle largeResultData = new Bundle(); + Intent realProviderData = new Intent(); + packageResponseJson(JSON_WITH_PROTOCOL, realProviderData); + largeResultData.putParcelable( + DigitalCredentialsPresentationDelegate.RESULT_DATA, realProviderData); + + delegate.handleOnReceiveResult(Activity.RESULT_OK, data, promise, largeResultData); + + org.junit.Assert.assertTrue(promise.isFulfilled()); + DigitalCredential credential = promise.getResult(); + org.junit.Assert.assertNotNull(credential); + org.junit.Assert.assertEquals(JSON_PROTOCOL, credential.mProtocol); + org.junit.Assert.assertEquals(JSON_DATA, credential.mData); + } }
diff --git a/chrome/browser/webshare/share_service_impl.cc b/chrome/browser/webshare/share_service_impl.cc index 158d69f6..8804fd9 100644 --- a/chrome/browser/webshare/share_service_impl.cc +++ b/chrome/browser/webshare/share_service_impl.cc
@@ -305,11 +305,12 @@ blink::mojom::ShareError result) { std::move(callback).Run(result); }, std::move(sharing_service_operation), std::move(callback))); #elif BUILDFLAG(IS_WIN) - // Drop fullscreen mode so the Share UI can be easily clicked away from, - // without clicking back into the web contents - base::ScopedClosureRunner fullscreen_block = - web_contents->ForSecurityDropFullscreen( - /*display_id=*/display::kInvalidDisplayId); + auto blocker = web_contents->ForSecurityDropFullscreen( + /*display_id=*/display::kInvalidDisplayId); + if (!blocker) { + std::move(callback).Run(blink::mojom::ShareError::PERMISSION_DENIED); + return; + } auto share_operation = std::make_unique<webshare::ShareOperation>( title, text, share_url, web_contents); @@ -323,7 +324,7 @@ fullscreen_block.RunAndReset(); std::move(callback).Run(result); }, - std::move(share_operation), std::move(fullscreen_block), + std::move(share_operation), std::move(*blocker), std::move(callback))); #else NOTREACHED();
diff --git a/chrome/build/android-arm32.pgo.txt b/chrome/build/android-arm32.pgo.txt index 2196878..ba685a8 100644 --- a/chrome/build/android-arm32.pgo.txt +++ b/chrome/build/android-arm32.pgo.txt
@@ -1 +1 @@ -chrome-android32-main-1778154835-32f0031ab9a97c06f9d5857484d06d9267513064-0d6c30befc36434298de9e0e7347b30ce3cf0406.profdata +chrome-android32-main-1778176723-38a58fbdd5e8a1fb944a551c3b6564036e3b1a95-e42bf157f662f0b9ff54386810546b8b6147949d.profdata
diff --git a/chrome/build/android-arm64.orderfile.txt b/chrome/build/android-arm64.orderfile.txt index 1ae4360..b350dd6d 100644 --- a/chrome/build/android-arm64.orderfile.txt +++ b/chrome/build/android-arm64.orderfile.txt
@@ -1 +1 @@ -4OMAaKvG-y208iDi3vpM5dP7h0_gtkY8FS0j0t1evJ0C +N8_4XF1U5SMaSDfibzT7BhIadc4iEqrN96ld_Cm8m30C
diff --git a/chrome/build/android-desktop-x64.pgo.txt b/chrome/build/android-desktop-x64.pgo.txt index de59510..abce022 100644 --- a/chrome/build/android-desktop-x64.pgo.txt +++ b/chrome/build/android-desktop-x64.pgo.txt
@@ -1 +1 @@ -chrome-android-desktop-x64-main-1778154835-a93e938abe2de62d0fcea867aac513dc61f7910f-0d6c30befc36434298de9e0e7347b30ce3cf0406.profdata +chrome-android-desktop-x64-main-1778198280-f9a597a4e6043991a6cd9c904ebbad8e6614fdbf-81c154bee3c796d05d993b89e44bf43e3ac92bd9.profdata
diff --git a/chrome/build/linux.pgo.txt b/chrome/build/linux.pgo.txt index f6a13d2..86db20f0 100644 --- a/chrome/build/linux.pgo.txt +++ b/chrome/build/linux.pgo.txt
@@ -1 +1 @@ -chrome-linux-main-1778111893-b4c550efae63d12d76d9f4aab1a4a952432dcec1-056c4390d5d620fec1e33a361b631bc5b0491409.profdata +chrome-linux-main-1778198280-f9ceea6ea63faf88662a561934c72721e68842a7-81c154bee3c796d05d993b89e44bf43e3ac92bd9.profdata
diff --git a/chrome/build/mac-arm.pgo.txt b/chrome/build/mac-arm.pgo.txt index 04bb4ad..df7330c 100644 --- a/chrome/build/mac-arm.pgo.txt +++ b/chrome/build/mac-arm.pgo.txt
@@ -1 +1 @@ -chrome-mac-arm-main-1778169554-879ddf5e40b4109350b8bd1dc363e7aac04b3053-468fd16c73671e3bd7ac8df0dd95a35fb945e6c7.profdata +chrome-mac-arm-main-1778205527-7aa757b006974fb6ee9d8216057ba1b0a4161f21-2f1f2cf46c2b32c957b44218dc259684b88ecccf.profdata
diff --git a/chrome/build/mac.pgo.txt b/chrome/build/mac.pgo.txt index 1c4e49b..286603c5 100644 --- a/chrome/build/mac.pgo.txt +++ b/chrome/build/mac.pgo.txt
@@ -1 +1 @@ -chrome-mac-main-1778154835-53569bdec7cb3b30ba572a0efdaad9f8e7ec084b-0d6c30befc36434298de9e0e7347b30ce3cf0406.profdata +chrome-mac-main-1778198280-9912b086bb7c5ceeac570ffab34409932c5162e1-81c154bee3c796d05d993b89e44bf43e3ac92bd9.profdata
diff --git a/chrome/build/win-arm64.pgo.txt b/chrome/build/win-arm64.pgo.txt index 362ba04..85ebf115 100644 --- a/chrome/build/win-arm64.pgo.txt +++ b/chrome/build/win-arm64.pgo.txt
@@ -1 +1 @@ -chrome-win-arm64-main-1778154835-646d35c7cb6ba98b7625c7750951327336014b7d-0d6c30befc36434298de9e0e7347b30ce3cf0406.profdata +chrome-win-arm64-main-1778176723-ea2ded5f14daaabf8e66a313a73ddffdfa478b94-e42bf157f662f0b9ff54386810546b8b6147949d.profdata
diff --git a/chrome/build/win32.pgo.txt b/chrome/build/win32.pgo.txt index 9138466..35dc11bc 100644 --- a/chrome/build/win32.pgo.txt +++ b/chrome/build/win32.pgo.txt
@@ -1 +1 @@ -chrome-win32-main-1778154835-e05c7c597968c12a797614f315c57a194c8c834d-0d6c30befc36434298de9e0e7347b30ce3cf0406.profdata +chrome-win32-main-1778165700-778b774698ca966b0204af72fda845d1e6c4df38-10b52502eb702c8c97994ca48d27a6c6f4a57521.profdata
diff --git a/chrome/build/win64.pgo.txt b/chrome/build/win64.pgo.txt index 12150b1..071c5170 100644 --- a/chrome/build/win64.pgo.txt +++ b/chrome/build/win64.pgo.txt
@@ -1 +1 @@ -chrome-win64-main-1778154835-f3ecd85235e1880edef9919ab9353d0b5bc6a0a8-0d6c30befc36434298de9e0e7347b30ce3cf0406.profdata +chrome-win64-main-1778176723-83f3943cc22cf72c87bc6aa6e41453fe89a6c0f4-e42bf157f662f0b9ff54386810546b8b6147949d.profdata
diff --git a/chrome/chrome_paks.gni b/chrome/chrome_paks.gni index 7a5b319f..c40d75e 100644 --- a/chrome/chrome_paks.gni +++ b/chrome/chrome_paks.gni
@@ -175,14 +175,7 @@ "//chrome/browser:resources", "//chrome/browser/glic/resources:browser_resources", "//chrome/browser/resources", - "//chrome/browser/resources/contextual_tasks:resources", - "//chrome/browser/resources/glic:resources", - "//chrome/browser/resources/saved_tab_groups_unsupported:resources", "//chrome/common:resources", - "//components/autofill/core/browser:autofill_address_rewriter_resources", - "//components/autofill/core/browser:autofill_alternative_state_name_map_resources", - "//components/enterprise/connectors/resources", - "//components/metrics:server_urls_grd", "//components/resources", "//content:content_resources", "//content/browser/resources", @@ -224,6 +217,7 @@ "$root_gen_dir/chrome/dev_ui_resources.pak", "$root_gen_dir/chrome/downloads_resources.pak", "$root_gen_dir/chrome/drive_picker_host_resources.pak", + "$root_gen_dir/chrome/drive_picker_host_untrusted_resources.pak", "$root_gen_dir/chrome/feedback_resources.pak", "$root_gen_dir/chrome/gaia_auth_host_resources.pak", "$root_gen_dir/chrome/glic_experimental_opt_in_resources.pak", @@ -270,19 +264,6 @@ deps += [ "//chrome/browser/actor/resources:browser_resources", "//chrome/browser/indigo/resources:browser_resources", - "//chrome/browser/resources:dev_ui_paks", - "//chrome/browser/resources/actor_overlay:resources", - "//chrome/browser/resources/autofill_ml_internals:resources", - "//chrome/browser/resources/data_sharing:resources", - "//chrome/browser/resources/drive_picker_host:resources", - "//chrome/browser/resources/indigo:resources", - "//chrome/browser/resources/lens/overlay:resources", - "//chrome/browser/resources/lens/shared:resources", - "//chrome/browser/resources/search_engine_choice:resources", - "//chrome/browser/resources/side_panel/comments:resources", - "//chrome/browser/resources/tab_group_home:resources", - "//chrome/browser/resources/webnn_internals:resources", - "//chrome/browser/resources/webui_toolbar:resources", "//components/headless/command_handler", "//content/browser/tracing:resources", ] @@ -292,11 +273,7 @@ "$root_gen_dir/chrome/media_router_feedback_resources.pak", "$root_gen_dir/chrome/preinstalled_web_apps_resources.pak", ] - deps += [ - "//chrome/browser/nearby_sharing/internal/icons", - "//chrome/browser/resources:preinstalled_web_apps_resources", - "//chrome/browser/resources/media_router/cast_feedback:resources", - ] + deps += [ "//chrome/browser/nearby_sharing/internal/icons" ] } if (!optimize_webui) { # Only add when optimize_webui=false, since in other cases the same @@ -476,17 +453,6 @@ "//ash/webui/vc_background_ui/resources", "//ash/wm/overview/birch/resources:coral_resources", "//chrome/browser/resources:office_web_app_resources", - "//chrome/browser/resources/chromeos/account_manager:resources", - "//chrome/browser/resources/chromeos/cryptohome:resources", - "//chrome/browser/resources/chromeos/dlp_internals:resources", - "//chrome/browser/resources/chromeos/drive_internals:resources", - "//chrome/browser/resources/chromeos/emulator:resources", - "//chrome/browser/resources/chromeos/floating_workspace:resources", - "//chrome/browser/resources/chromeos/kerberos:resources", - "//chrome/browser/resources/chromeos/power:resources", - "//chrome/browser/resources/chromeos/slow:resources", - "//chrome/browser/resources/chromeos/smb_shares:resources", - "//chrome/browser/resources/chromeos/sys_internals:resources", "//chrome/common/chromeos/extensions:resources", "//chromeos/ash/components/emoji:resources", "//chromeos/ash/experiences/arc/input_overlay/resources",
diff --git a/chrome/common/BUILD.gn b/chrome/common/BUILD.gn index f3d81a8..fafc17c 100644 --- a/chrome/common/BUILD.gn +++ b/chrome/common/BUILD.gn
@@ -503,6 +503,7 @@ static_library("url_constants") { visibility = [ "//chrome/browser/contextual_tasks/*", + "//chrome/browser/ui/android", "//chrome/common/*", ] sources = [
diff --git a/chrome/common/chrome_features.cc b/chrome/common/chrome_features.cc index 89dfa7c3..15fb26b4 100644 --- a/chrome/common/chrome_features.cc +++ b/chrome/common/chrome_features.cc
@@ -206,6 +206,11 @@ BASE_FEATURE(kGlicActor, base::FEATURE_ENABLED_BY_DEFAULT); #endif +BASE_FEATURE(kGlicActorApcComparison, base::FEATURE_ENABLED_BY_DEFAULT); + +const base::FeatureParam<double> kGlicActorApcComparisonSamplingRate{ + &kGlicActorApcComparison, "sampling-rate", 0.1}; + BASE_FEATURE(kGlicExperimentalTriggering, base::FEATURE_DISABLED_BY_DEFAULT); BASE_FEATURE(kGlicExperimentalTriggeringOptInBypass, base::FEATURE_DISABLED_BY_DEFAULT);
diff --git a/chrome/common/chrome_features.h b/chrome/common/chrome_features.h index a7fd884..189f4e7 100644 --- a/chrome/common/chrome_features.h +++ b/chrome/common/chrome_features.h
@@ -155,6 +155,16 @@ COMPONENT_EXPORT(CHROME_FEATURES) BASE_DECLARE_FEATURE(kGeoLanguage); COMPONENT_EXPORT(CHROME_FEATURES) BASE_DECLARE_FEATURE(kGlicActor); + +// Enables comparison of AnnotatedPageContent. This is potentially expensive for +// large pages and is gated for performance monitoring. +COMPONENT_EXPORT(CHROME_FEATURES) +BASE_DECLARE_FEATURE(kGlicActorApcComparison); + +// The sampling rate to log metrics for APC comparison, default to 0.1. +COMPONENT_EXPORT(CHROME_FEATURES) +extern const base::FeatureParam<double> kGlicActorApcComparisonSamplingRate; + COMPONENT_EXPORT(CHROME_FEATURES) BASE_DECLARE_FEATURE(kGlicExperimentalTriggering); COMPONENT_EXPORT(CHROME_FEATURES)
diff --git a/chrome/common/extensions/api/api_sources.gni b/chrome/common/extensions/api/api_sources.gni index a13ab13..73d203b 100644 --- a/chrome/common/extensions/api/api_sources.gni +++ b/chrome/common/extensions/api/api_sources.gni
@@ -80,7 +80,7 @@ "command_line_private.json", "crash_report_private.webidl", "enterprise_reporting_private.webidl", - "experimental_actor.idl", + "experimental_actor.webidl", "experimental_ai_data.webidl", "image_writer_private.idl", "indigo_private.webidl", @@ -118,7 +118,7 @@ "enterprise_platform_keys_internal.webidl", "enterprise_platform_keys_private.json", "file_manager_private.idl", - "file_manager_private_internal.idl", + "file_manager_private_internal.webidl", "file_system_provider.idl", "file_system_provider_internal.idl", "image_loader_private.idl",
diff --git a/chrome/common/extensions/api/experimental_actor.webidl b/chrome/common/extensions/api/experimental_actor.webidl new file mode 100644 index 0000000..1286a19cf --- /dev/null +++ b/chrome/common/extensions/api/experimental_actor.webidl
@@ -0,0 +1,35 @@ +// Copyright 2026 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// Experimental API to handle acting and returning new browser state. +[implemented_in="chrome/browser/extensions/api/experimental_actor/experimental_actor_api.h"] +interface ExperimentalActor { + // Stops a task. + // |taskId|: id of the task to stop. + // |Returns|: a closure that is called when the task is stopped. + [requiredCallback] static Promise<undefined> stopTask(long taskId); + + // Creates a new task. The callback will contain the task ID for the newly + // created task. + // |PromiseValue|: taskId + [requiredCallback] static Promise<long> createTask(); + + // Executes one or more actions according to request. + // |actionsProto|: encoded optimization_guide.proto.Actions + // |Returns|: encoded optimization_guide.proto.ActionsResult + // |PromiseValue|: data + [requiredCallback] + static Promise<ArrayBuffer> performActions(ArrayBuffer actionsProto); + + // Requests a TabObservation for a given tab. + // |tabId|: The session tabId to observe. + // |Returns|: encoded optimization_guide.proto.TabObservation + // |PromiseValue|: data + [requiredCallback] + static Promise<ArrayBuffer> requestTabObservation(long tabId); +}; + +partial interface Browser { + static attribute ExperimentalActor experimentalActor; +};
diff --git a/chrome/common/extensions/api/file_manager_private_internal.webidl b/chrome/common/extensions/api/file_manager_private_internal.webidl new file mode 100644 index 0000000..65876f6 --- /dev/null +++ b/chrome/common/extensions/api/file_manager_private_internal.webidl
@@ -0,0 +1,218 @@ +// Copyright 2026 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +[ExternalExtensionType="fileManagerPrivate.SearchType"] +typedef object FileManagerPrivateSearchType; + +[ExternalExtensionType="fileManagerPrivate.FileCategory"] +typedef object FileManagerPrivateFileCategory; + +[ExternalExtensionType="fileManagerPrivate.EntryProperties"] +typedef object FileManagerPrivateEntryProperties; + +[ExternalExtensionType="fileSystemProvider.Action"] +typedef object FileSystemProviderAction; + +[ExternalExtensionType="fileManagerPrivate.MediaMetadata"] +typedef object FileManagerPrivateMediaMetadata; + +[ExternalExtensionType="fileManagerPrivate.TaskResult"] +typedef object FileManagerPrivateTaskResult; + +[ExternalExtensionType="fileManagerPrivate.ResultingTasks"] +typedef object FileManagerPrivateResultingTasks; + +[ExternalExtensionType="fileManagerPrivate.DlpMetadata"] +typedef object FileManagerPrivateDlpMetadata; + +[ExternalExtensionType="fileManagerPrivate.DriveQuotaMetadata"] +typedef object FileManagerPrivateDriveQuotaMetadata; + +[ExternalExtensionType="fileManagerPrivate.EntryPropertyName"] +typedef object FileManagerPrivateEntryPropertyName; + +[ExternalExtensionType="fileManagerPrivate.FileTaskDescriptor"] +typedef object FileManagerPrivateFileTaskDescriptor; + +[ExternalExtensionType="fileManagerPrivate.GetVolumeRootOptions"] +typedef object FileManagerPrivateGetVolumeRootOptions; + +[ExternalExtensionType="fileManagerPrivate.SourceRestriction"] +typedef object FileManagerPrivateSourceRestriction; + +[ExternalExtensionType="fileManagerPrivate.IoTaskType"] +typedef object FileManagerPrivateIoTaskType; + +// Entry information that renderers need to create an Entry instance. +dictionary EntryDescription { + required DOMString fileSystemName; + required DOMString fileSystemRoot; + required DOMString fileFullPath; + required boolean fileIsDirectory; +}; + +dictionary IOTaskParams { + DOMString destinationFolderUrl; + DOMString password; + boolean showNotification; +}; + +dictionary ParsedTrashInfoFile { + required EntryDescription restoreEntry; + required DOMString trashInfoFileName; + required double deletionDate; +}; + +dictionary SearchFilesParams { + DOMString rootUrl; + required DOMString query; + required FileManagerPrivateSearchType types; + required long maxResults; + required double modifiedTimestamp; + required FileManagerPrivateFileCategory category; +}; + +dictionary CrostiniSharedPathResponse { + required sequence<EntryDescription> entries; + required boolean firstForSession; +}; + +// Internal, used by fileManagerPrivate's custom bindings. +[platforms=("chromeos"), + implemented_in="chrome/browser/ash/extensions/file_manager/file_manager_private_api_functions.h"] +interface FileManagerPrivateInternal { + // |PromiseValue|: entries + [requiredCallback] + static Promise<sequence<EntryDescription>> resolveIsolatedEntries( + sequence<DOMString> urls); + + // |PromiseValue|: entryProperties + [requiredCallback] + static Promise<sequence<FileManagerPrivateEntryProperties>> + getEntryProperties(sequence<DOMString> urls, + sequence<FileManagerPrivateEntryPropertyName> names); + + // |PromiseValue|: success + [requiredCallback] static Promise<boolean?> addFileWatch(DOMString url); + + // |PromiseValue|: success + [requiredCallback] static Promise<boolean?> removeFileWatch(DOMString url); + + // |PromiseValue|: actions + [requiredCallback] + static Promise<sequence<FileSystemProviderAction>> getCustomActions( + sequence<DOMString> urls); + + [requiredCallback] static Promise<undefined> executeCustomAction( + sequence<DOMString> urls, + DOMString actionId); + + // |PromiseValue|: result + [requiredCallback] + static Promise<DOMString> getContentMimeType(DOMString blobUUID); + + // |PromiseValue|: result + [requiredCallback] + static Promise<FileManagerPrivateMediaMetadata> getContentMetadata( + DOMString blobUUID, + DOMString mimeType, + boolean includeImages); + + [requiredCallback] static Promise<undefined> pinDriveFile( + DOMString url, + boolean pin); + + // |PromiseValue|: result + [requiredCallback] static Promise<FileManagerPrivateTaskResult> executeTask( + FileManagerPrivateFileTaskDescriptor descriptor, + sequence<DOMString> urls); + + // |PromiseValue|: entries + [requiredCallback] static Promise<sequence<EntryDescription>> searchFiles( + SearchFilesParams searchParams); + + [requiredCallback] static Promise<undefined> setDefaultTask( + FileManagerPrivateFileTaskDescriptor descriptor, + sequence<DOMString> urls, + sequence<DOMString> mimeTypes); + + // |PromiseValue|: resultingTasks + [requiredCallback] + static Promise<FileManagerPrivateResultingTasks> getFileTasks( + sequence<DOMString> urls, + sequence<DOMString> dlpSourceUrls); + + // |PromiseValue|: entries + [requiredCallback] + static Promise<sequence<EntryDescription>> getDisallowedTransfers( + sequence<DOMString> entries, + DOMString destinationEntry, + boolean isMove); + + // |PromiseValue|: entries + [requiredCallback] + static Promise<sequence<FileManagerPrivateDlpMetadata>> getDlpMetadata( + sequence<DOMString> entries); + + // |PromiseValue|: driveQuotaMetadata + [requiredCallback] + static Promise<FileManagerPrivateDriveQuotaMetadata?> getDriveQuotaMetadata( + DOMString url); + + // |PromiseValue|: result + [requiredCallback] static Promise<boolean> validatePathNameLength( + DOMString parentUrl, + DOMString name); + + // |PromiseValue|: size + [requiredCallback] static Promise<double> getDirectorySize(DOMString url); + + // |PromiseValue|: rootDir + [requiredCallback] static Promise<EntryDescription> getVolumeRoot( + FileManagerPrivateGetVolumeRootOptions options); + + // |PromiseValue|: entries + [requiredCallback] static Promise<sequence<EntryDescription>> getRecentFiles( + FileManagerPrivateSourceRestriction restriction, + DOMString query, + long cutoff_days, + FileManagerPrivateFileCategory file_category, + boolean invalidate_cache); + + [requiredCallback] static Promise<undefined> sharePathsWithCrostini( + DOMString vmName, + sequence<DOMString> urls, + boolean persist); + + [requiredCallback] static Promise<undefined> unsharePathWithCrostini( + DOMString vmName, + DOMString url); + + // |PromiseValue|: response + [requiredCallback] + static Promise<CrostiniSharedPathResponse> getCrostiniSharedPaths( + boolean observeFirstForSession, + DOMString vmName); + + static undefined importCrostiniImage(DOMString url); + + static Promise<undefined> toggleAddedToHoldingSpace( + sequence<DOMString> urls, + boolean add); + + // |PromiseValue|: taskId + static Promise<long> startIOTask( + FileManagerPrivateIoTaskType type, + sequence<DOMString> urls, + IOTaskParams params); + + // |PromiseValue|: files + [requiredCallback] + static Promise<sequence<ParsedTrashInfoFile>> parseTrashInfoFiles( + sequence<DOMString> urls); +}; + +partial interface Browser { + static attribute FileManagerPrivateInternal fileManagerPrivateInternal; +};
diff --git a/chrome/common/pref_names.h b/chrome/common/pref_names.h index ed07378..d2281a31 100644 --- a/chrome/common/pref_names.h +++ b/chrome/common/pref_names.h
@@ -602,24 +602,11 @@ inline constexpr char kManagedSessionUseFullLoginWarning[] = "managed_session.use_full_warning"; - -// Last time the kChildScreenTimeMilliseconds was saved. -inline constexpr char kLastChildScreenTimeSaved[] = - "last_child_screen_time_saved"; - -// Last time that the kChildScreenTime pref was reset. -inline constexpr char kLastChildScreenTimeReset[] = - "last_child_screen_time_reset"; - // Amount of times the release notes suggestion chip should be // shown before it disappears. inline constexpr char kReleaseNotesSuggestionChipTimesLeftToShow[] = "times_left_to_show_release_notes_suggestion_chip"; - - - - // A string pref storing the path of device wallpaper image file. inline constexpr char kDeviceWallpaperImageFilePath[] = "policy.device_wallpaper_image_file_path"; @@ -2643,21 +2630,6 @@ inline constexpr char kEchoCheckedOffers[] = "EchoCheckedOffers"; #endif // BUILDFLAG(IS_CHROMEOS) -// Device identifier used by CryptAuth stored in local state. This ID is -// combined with a user ID before being registered with the CryptAuth server, -// so it can't correlate users on the same device. -// Note: This constant was previously specific to EasyUnlock, so the string -// constant contains "easy_unlock". -inline constexpr char kCryptAuthDeviceId[] = "easy_unlock.device_id"; - -// The most recently retrieved Instance ID and Instance ID token for the app ID, -// "com.google.chrome.cryptauth", used by the CryptAuth client. These prefs are -// used to track how often (if ever) the Instance ID and Instance ID token -// rotate because CryptAuth assumes the Instance ID is static. -inline constexpr char kCryptAuthInstanceId[] = "cryptauth.instance_id"; -inline constexpr char kCryptAuthInstanceIdToken[] = - "cryptauth.instance_id_token"; - #if BUILDFLAG(ENABLE_EXTENSIONS_CORE) // Policy that indicates how to handle animated images. inline constexpr char kAnimationPolicy[] = "settings.a11y.animation_policy";
diff --git a/chrome/renderer/accessibility/read_anything/read_anything_app_model.cc b/chrome/renderer/accessibility/read_anything/read_anything_app_model.cc index 683da4b..72a4c14 100644 --- a/chrome/renderer/accessibility/read_anything/read_anything_app_model.cc +++ b/chrome/renderer/accessibility/read_anything/read_anything_app_model.cc
@@ -960,7 +960,7 @@ case ax::mojom::Event::kEndOfTest: case ax::mojom::Event::kFocus: case ax::mojom::Event::kFocusAfterMenuClose: - case ax::mojom::Event::kFocusContext: + case ax::mojom::Event::kFocusContextDeprecated: case ax::mojom::Event::kHide: case ax::mojom::Event::kHitTestResult: case ax::mojom::Event::kHover:
diff --git a/chrome/test/BUILD.gn b/chrome/test/BUILD.gn index f2143ed..8334c01 100644 --- a/chrome/test/BUILD.gn +++ b/chrome/test/BUILD.gn
@@ -1734,6 +1734,7 @@ "../browser/extensions/extension_override_apitest.cc", "../browser/extensions/extension_tab_util_browsertest.cc", "../browser/extensions/extension_unload_browsertest.cc", + "../browser/extensions/extension_user_activation_service_browsertest.cc", "../browser/extensions/extension_user_script_loader_browsertest.cc", "../browser/extensions/extensions_disabled_browsertest.cc", "../browser/extensions/gpu_browsertest.cc", @@ -1745,6 +1746,7 @@ "../browser/extensions/permissions/permissions_manager_browsertest.cc", "../browser/extensions/permissions/permissions_updater_browsertest.cc", "../browser/extensions/permissions/site_permissions_helper_browsertest.cc", + "../browser/extensions/process_map_browsertest.cc", "../browser/extensions/sandboxed_pages_apitest.cc", "../browser/extensions/script_executor_browsertest.cc", "../browser/extensions/service_worker_apitest.cc", @@ -1760,7 +1762,9 @@ "../browser/extensions/system_memory_apitest.cc", "../browser/extensions/test_resources_browsertest.cc", "../browser/extensions/unpacked_installer_browsertest.cc", + "../browser/extensions/user_host_restrictions_browsertest.cc", "../browser/extensions/user_script_extension_browsertest.cc", + "../browser/extensions/user_script_listener_browsertest.cc", "../browser/extensions/wasm_mv3_browsertest.cc", "../browser/extensions/web_accessible_resources_browsertest.cc", "../browser/extensions/web_contents_browsertest.cc", @@ -2022,7 +2026,6 @@ "../browser/profiles/profile_browsertest_android.cc", "../browser/renderer_host/javascript_optimizer_feature_browsertest.cc", "../browser/ssl/chrome_security_state_client_browsertest.cc", - "../browser/ui/android/autofill/save_update_address_profile_flow_manager_browsertest.cc", "../browser/ui/webui/policy/policy_test_ui_browsertest.cc", "../browser/ui/webui/policy/policy_ui_browsertest.cc", "../browser/webid/identity_provider_service_browsertest.cc", @@ -2106,6 +2109,7 @@ "//chrome/browser/touch_to_fill/password_manager/password_generation/android:public", "//chrome/browser/trusted_vault:android_browsertests", "//chrome/browser/ui:ui_features", + "//chrome/browser/ui/android/autofill:browser_tests", "//chrome/browser/ui/android/hats:browser_tests", "//chrome/browser/ui/autofill", "//chrome/browser/ui/browser_window", @@ -2273,9 +2277,8 @@ # TODO(crbug.com/473636857): Include once all Android builds correctly use BrowserWindowInterface. if (is_desktop_android) { - sources += - [ "../browser/ui/android/browser_navigator_android_browsertest.cc" ] deps += [ + "//chrome/browser/ui/android:browser_tests", "//chrome/browser/ui/browser_window:browser_tests", "//chrome/browser/ui/browser_window/internal:android_browsertests", "//chrome/browser/ui/browser_window/test:android_browser_test_support", @@ -4845,7 +4848,6 @@ "../browser/extensions/permissions/active_tab_apitest.cc", "../browser/extensions/process_management_browsertest.cc", "../browser/extensions/process_manager_browsertest.cc", - "../browser/extensions/process_map_browsertest.cc", "../browser/extensions/process_util_browsertest.cc", "../browser/extensions/protocol_handler_apitest.cc", "../browser/extensions/renderer_initialization_browsertest.cc", @@ -4859,8 +4861,6 @@ "../browser/extensions/updater/extension_update_client_base_browsertest.cc", "../browser/extensions/updater/extension_update_client_base_browsertest.h", "../browser/extensions/updater/update_service_browsertest.cc", - "../browser/extensions/user_host_restrictions_browsertest.cc", - "../browser/extensions/user_script_listener_browsertest.cc", "../browser/extensions/user_script_world_browsertest.cc", "../browser/extensions/view_extension_source_browsertest.cc", "../browser/extensions/wasm_app_browsertest.cc", @@ -4988,8 +4988,6 @@ "../browser/extensions/api/certificate_provider/certificate_provider_apitest.cc", "../browser/extensions/api/crash_report_private/crash_report_private_apitest.cc", "../browser/extensions/api/document_scan/document_scan_apitest.cc", - "../browser/extensions/api/document_scan/fake_document_scan_ash.cc", - "../browser/extensions/api/document_scan/fake_document_scan_ash.h", "../browser/extensions/api/enterprise_device_attributes/enterprise_device_attributes_ash_apitest.cc", "../browser/extensions/api/enterprise_networking_attributes/enterprise_networking_attributes_ash_apitest.cc", "../browser/extensions/api/enterprise_platform_keys/enterprise_platform_keys_api_browsertest.cc", @@ -7153,6 +7151,7 @@ "//chrome/browser/permissions:test_support", "//chrome/browser/permissions:unit_tests", "//chrome/browser/persisted_state_db", + "//chrome/browser/personal_context:unit_tests", "//chrome/browser/picture_in_picture", "//chrome/browser/picture_in_picture:unit_tests", "//chrome/browser/plugins:unit_tests", @@ -8069,22 +8068,6 @@ "../browser/search/contextual_search_policy_handler_android_unittest.cc", "../browser/signin/android/signin_manager_android_unittest.cc", "../browser/sync/glue/synced_tab_delegate_android_unittest.cc", - "../browser/ui/android/autofill/at_memory_bottom_sheet_bridge_unittest.cc", - "../browser/ui/android/autofill/at_memory_bottom_sheet_delegate_android_unittest.cc", - "../browser/ui/android/autofill/autofill_ai_save_update_entity_flow_manager_unittest.cc", - "../browser/ui/android/autofill/autofill_cvc_save_message_delegate_unittest.cc", - "../browser/ui/android/autofill/autofill_save_card_bottom_sheet_bridge_unittest.cc", - "../browser/ui/android/autofill/autofill_save_card_delegate_android_unittest.cc", - "../browser/ui/android/autofill/autofill_save_iban_delegate_unittest.cc", - "../browser/ui/android/autofill/autofill_vcn_enroll_bottom_sheet_bridge_unittest.cc", - "../browser/ui/android/autofill/payments/payments_window_bridge_unittest.cc", - "../browser/ui/android/autofill/save_update_address_profile_flow_manager_unittest.cc", - "../browser/ui/android/device_dialog/serial_chooser_dialog_android_unittest.cc", - "../browser/ui/android/device_dialog/usb_chooser_dialog_android_unittest.cc", - "../browser/ui/android/exclusive_access/exclusive_access_bubble_android_unittest.cc", - "../browser/ui/android/tab_contents/chrome_web_contents_view_delegate_android_unittest.cc", - "../browser/ui/android/toolbar/adaptive_toolbar_bridge_unittest.cc", - "../browser/ui/android/toolbar/location_bar_model_android_unittest.cc", "../browser/webauthn/android/cable_module_android_unittest.cc", "../browser/webauthn/android/cable_registration_state_unittest.cc", "../browser/webauthn/android/credential_sorter_android_unittest.cc", @@ -8152,10 +8135,16 @@ "//chrome/browser/touch_to_fill/password_manager/no_passkeys/android:unit_tests", "//chrome/browser/touch_to_fill/password_manager/password_generation/android:unit_tests", "//chrome/browser/translate/android:unit_tests", + "//chrome/browser/ui/android/autofill:unit_tests", + "//chrome/browser/ui/android/autofill/payments:unit_tests", + "//chrome/browser/ui/android/device_dialog:unit_tests", + "//chrome/browser/ui/android/exclusive_access:unit_tests", "//chrome/browser/ui/android/hats:unit_tests", "//chrome/browser/ui/android/hats/test:test_support", + "//chrome/browser/ui/android/tab_contents:unit_tests", "//chrome/browser/ui/android/tab_model:test_support", "//chrome/browser/ui/android/tab_model:unit_tests", + "//chrome/browser/ui/android/toolbar:unit_tests", "//chrome/browser/ui/autofill/payments:test_support", "//chrome/browser/ui/browser_window/internal:unit_tests", "//chrome/browser/ui/browser_window/test:native_unit_test_support_java", @@ -9019,8 +9008,6 @@ "../browser/extensions/api/document_scan/document_scan_api_handler_unittest.cc", "../browser/extensions/api/document_scan/document_scan_api_unittest.cc", "../browser/extensions/api/document_scan/document_scan_type_converters_unittest.cc", - "../browser/extensions/api/document_scan/fake_document_scan_ash.cc", - "../browser/extensions/api/document_scan/fake_document_scan_ash.h", "../browser/extensions/api/terminal/startup_status_unittest.cc", "../browser/hid/hid_pinned_notification_unittest.cc", "../browser/lifetime/application_lifetime_chromeos_unittest.cc", @@ -10286,7 +10273,6 @@ # chrome_browser_cloud_management_register_watcher_unittest.cc is # modularized. "//chrome/browser/policy:impl", - "//chrome/browser/ui/browser_window/test:test_support", ] if (is_linux || is_win || is_mac) {
diff --git a/chrome/test/android/BUILD.gn b/chrome/test/android/BUILD.gn index 8656482..fc3f646 100644 --- a/chrome/test/android/BUILD.gn +++ b/chrome/test/android/BUILD.gn
@@ -299,6 +299,8 @@ "//chrome/browser/ui/android/omnibox:java", "//chrome/browser/ui/android/pdf:java", "//chrome/browser/ui/android/pdf:java_resources", + "//chrome/browser/ui/android/pdf:pdf_page_java", + "//chrome/browser/ui/android/pdf/internal:java", "//chrome/browser/ui/android/toolbar:java", "//chrome/browser/ui/messages/android:java", "//chrome/browser/url_constants/android:java",
diff --git a/chrome/test/android/javatests/src/org/chromium/chrome/test/transit/page/PdfCtaPageStation.java b/chrome/test/android/javatests/src/org/chromium/chrome/test/transit/page/PdfCtaPageStation.java index 23050007..6410fbd7 100644 --- a/chrome/test/android/javatests/src/org/chromium/chrome/test/transit/page/PdfCtaPageStation.java +++ b/chrome/test/android/javatests/src/org/chromium/chrome/test/transit/page/PdfCtaPageStation.java
@@ -44,7 +44,10 @@ instrumentationThreadConditionWithResult( "PDF coordinator", pdfNativePageElement, - pdfPage -> fulfilled().withResult(pdfPage.mPdfCoordinator))); + pdfPage -> + fulfilled() + .withResult( + (PdfCoordinator) pdfPage.mPdfCoordinator))); declareEnterCondition( instrumentationThreadCondition( "PDF document is loaded successfully",
diff --git a/chrome/test/data/banners/manifest_tabbed_display_override.json b/chrome/test/data/banners/manifest_tabbed_display_override.json index 2fa9b152..481c1e4 100644 --- a/chrome/test/data/banners/manifest_tabbed_display_override.json +++ b/chrome/test/data/banners/manifest_tabbed_display_override.json
@@ -7,7 +7,7 @@ "type": "image/png" } ], - "start_url": "manifest_test_page.html", + "start_url": "manifest_test_page.html?manifest=manifest_tabbed_display_override.json", "display": "standalone", "display_override": [ "tabbed" ] } \ No newline at end of file
diff --git a/chrome/test/data/extensions/api_test/tab_groups/move_to_window/background.js b/chrome/test/data/extensions/api_test/tab_groups/move_to_window/background.js new file mode 100644 index 0000000..65aad5c --- /dev/null +++ b/chrome/test/data/extensions/api_test/tab_groups/move_to_window/background.js
@@ -0,0 +1,30 @@ +// Copyright 2026 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +chrome.test.runTests([ + async function moveGroup() { + // Create other tabs in window 1 to add to a group. + await chrome.tabs.create({url: 'about:blank'}); + await chrome.tabs.create({url: 'about:blank'}); + + // Get the tabs in window 1. There should now be three of them. + const tabs = await chrome.tabs.query({currentWindow: true}); + chrome.test.assertEq(3, tabs.length); + + // Create a tab group in window 1 with two tabs. + const groupId = await chrome.tabs.group({tabIds: [tabs[1].id, tabs[2].id]}); + + // Create a new window. + const window2 = await chrome.windows.create({url: 'about:blank'}); + + // Move the tab group to window 2 and wait for it to process. + await chrome.tabGroups.move(groupId, {windowId: window2.id, index: 0}); + + // Two tabs moved, so window 2 now has a total of 3 tabs. + const tabs2 = await chrome.tabs.query({windowId: window2.id}); + chrome.test.assertEq(3, tabs2.length); + + chrome.test.succeed(); + }, +]);
diff --git a/chrome/test/data/extensions/api_test/tab_groups/move_to_window/manifest.json b/chrome/test/data/extensions/api_test/tab_groups/move_to_window/manifest.json new file mode 100644 index 0000000..f4c1843 --- /dev/null +++ b/chrome/test/data/extensions/api_test/tab_groups/move_to_window/manifest.json
@@ -0,0 +1,12 @@ +{ + "name": "Move Group Test", + "version": "1.0", + "manifest_version": 3, + "background": { + "service_worker": "background.js" + }, + "permissions": [ + "tabs", + "tabGroups" + ] +}
diff --git a/chrome/test/data/web_apps/intent_picker_nav_capture/index.html b/chrome/test/data/web_apps/intent_picker_nav_capture/index.html index 4223138..c10428e 100644 --- a/chrome/test/data/web_apps/intent_picker_nav_capture/index.html +++ b/chrome/test/data/web_apps/intent_picker_nav_capture/index.html
@@ -1,7 +1,14 @@ <!DOCTYPE html> <html> -<head> - <link rel="manifest" href="manifest.json"> + <script> + const params = new URLSearchParams(window.location.search); + const manifest = params.get('manifest') || 'manifest.json'; + const link = document.createElement('link'); + link.rel = 'manifest'; + link.id = 'manifest'; + link.href = manifest; + document.head.appendChild(link); + </script> <link rel="icon" href="basic-48.png"> </head> <script>
diff --git a/chrome/test/data/web_apps/intent_picker_nav_capture/manifest_focus.json b/chrome/test/data/web_apps/intent_picker_nav_capture/manifest_focus.json new file mode 100644 index 0000000..4a38c1ad --- /dev/null +++ b/chrome/test/data/web_apps/intent_picker_nav_capture/manifest_focus.json
@@ -0,0 +1,21 @@ +{ + "name": "Simple web app with separate scope from other test sites (Focus)", + "icons": [ + { + "src": "basic-48.png", + "sizes": "48x48", + "type": "image/png" + }, + { + "src": "basic-192.png", + "sizes": "192x192", + "type": "image/png" + } + ], + "start_url": "index.html?manifest=manifest_focus.json", + "display": "standalone", + "scope": ".", + "launch_handler": { + "client_mode": "focus-existing" + } +}
diff --git a/chrome/test/data/web_apps/intent_picker_nav_capture/manifest_navigate.json b/chrome/test/data/web_apps/intent_picker_nav_capture/manifest_navigate.json new file mode 100644 index 0000000..1785d82 --- /dev/null +++ b/chrome/test/data/web_apps/intent_picker_nav_capture/manifest_navigate.json
@@ -0,0 +1,21 @@ +{ + "name": "Simple web app with separate scope from other test sites (Navigate)", + "icons": [ + { + "src": "basic-48.png", + "sizes": "48x48", + "type": "image/png" + }, + { + "src": "basic-192.png", + "sizes": "192x192", + "type": "image/png" + } + ], + "start_url": "index.html?manifest=manifest_navigate.json", + "display": "standalone", + "scope": ".", + "launch_handler": { + "client_mode": "navigate-existing" + } +}
diff --git a/chrome/test/data/webui/accessibility_annotator_info/accessibility_annotator_info_test.ts b/chrome/test/data/webui/accessibility_annotator_info/accessibility_annotator_info_test.ts index 370215d9..16276c13 100644 --- a/chrome/test/data/webui/accessibility_annotator_info/accessibility_annotator_info_test.ts +++ b/chrome/test/data/webui/accessibility_annotator_info/accessibility_annotator_info_test.ts
@@ -6,7 +6,6 @@ import type {AccessibilityAnnotatorInfoElement} from 'chrome://accessibility-annotator-info/accessibility_annotator_info.js'; import {AccessibilityAnnotatorInfoBrowserProxy} from 'chrome://accessibility-annotator-info/browser_proxy.js'; -import {loadTimeData} from 'chrome://resources/js/load_time_data.js'; import {assertEquals, assertFalse, assertTrue} from 'chrome://webui-test/chai_assert.js'; import {microtasksFinished} from 'chrome://webui-test/test_util.js'; @@ -90,24 +89,4 @@ assertTrue(!!accountInfoDiv); assertTrue(accountInfoDiv.hidden); }); - - test('RendersTriggerCard', function() { - const triggerCard = accessibilityAnnotatorInfoElement.shadowRoot - ?.querySelector<HTMLElement>('#triggerCard'); - assertTrue(!!triggerCard); - - // Assert the formatting is there. - const pillFormatting = triggerCard.querySelector<HTMLElement>('.pill'); - assertTrue(!!pillFormatting); - - // Assert the constructed string is correct. - const textWithPlaceholder = - loadTimeData.getString('accessibilityAnnotatorInfoCard1'); - const placeholder = - loadTimeData.getString('accessibilityAnnotatorTriggerText'); - const expectedText = textWithPlaceholder.replace('$1', placeholder); - const actualText = - (triggerCard.textContent || '').replace(/\s+/g, ' ').trim(); - assertEquals(expectedText, actualText); - }); });
diff --git a/chrome/test/data/webui/contextual_tasks/composebox_misc_inputs_test.ts b/chrome/test/data/webui/contextual_tasks/composebox_misc_inputs_test.ts index a5fc6ba..b86142f 100644 --- a/chrome/test/data/webui/contextual_tasks/composebox_misc_inputs_test.ts +++ b/chrome/test/data/webui/contextual_tasks/composebox_misc_inputs_test.ts
@@ -381,7 +381,7 @@ assertEquals( 1, metrics.count( - 'ContextualTasks.VoiceSearch.State', + 'ContextualTasks.VoiceSearch.StateV2', /* VOICE_SEARCH_BUTTON_CLICKED */ 0), 'Voice search button clicked metric count is incorrect'); }); @@ -401,7 +401,7 @@ assertEquals( 1, metrics.count( - 'ContextualTasks.VoiceSearch.State', + 'ContextualTasks.VoiceSearch.StateV2', /* VOICE_SEARCH_BUTTON_CLICKED */ 0), 'Voice search button clicked metric count is incorrect'); const [callback] = await windowProxy.whenCalled('setTimeout'); @@ -439,7 +439,7 @@ assertEquals( 1, metrics.count( - 'ContextualTasks.VoiceSearch.State', + 'ContextualTasks.VoiceSearch.StateV2', /* VOICE_SEARCH_TRANSCRIPTION_SUCCESS */ 1), 'Voice transcription success metric count is wrong: helloworld2'); await new Promise(resolve => requestAnimationFrame(resolve)); @@ -494,7 +494,7 @@ assertEquals( 1, metrics.count( - 'ContextualTasks.VoiceSearch.State', + 'ContextualTasks.VoiceSearch.StateV2', /* VOICE_SEARCH_ERROR */ 2), 'Voice search error metric count is incorrect'); }); @@ -519,7 +519,7 @@ assertEquals( 1, metrics.count( - 'ContextualTasks.VoiceSearch.State', + 'ContextualTasks.VoiceSearch.StateV2', /* VOICE_SEARCH_BUTTON_CLICKED */ 0), 'Voice search button clicked metric count is incorrect'); @@ -553,7 +553,7 @@ assertEquals( 1, metrics.count( - 'ContextualTasks.VoiceSearch.State', + 'ContextualTasks.VoiceSearch.StateV2', /* VOICE_SEARCH_ERROR */ 2), 'Voice search error metric count is incorrect'); }); @@ -593,7 +593,7 @@ assertEquals( 1, metrics.count( - 'ContextualTasks.VoiceSearch.State', + 'ContextualTasks.VoiceSearch.StateV2', /* VOICE_SEARCH_USER_CANCELED*/ 4), 'Voice search canceled metric count is incorrect'); });
diff --git a/chrome/test/data/webui/contextual_tasks/composebox_zero_state_test.ts b/chrome/test/data/webui/contextual_tasks/composebox_zero_state_test.ts index 578b63e..cfb8add 100644 --- a/chrome/test/data/webui/contextual_tasks/composebox_zero_state_test.ts +++ b/chrome/test/data/webui/contextual_tasks/composebox_zero_state_test.ts
@@ -342,6 +342,24 @@ await contextualTasksApp.updateComplete; await microtasksFinished(); + // Simulate the initial load. + let resolveInitNavigation: () => void; + const initNavigationFinished = new Promise<void>(r => resolveInitNavigation = r); + contextualTasksApp.setOnLoadStartFinishedCallbackForTesting( + resolveInitNavigation!); + + const initEvent = new Event('loadstart'); + Object.assign(initEvent, {url: 'http://example.com', isTopLevel: true}); + contextualTasksApp.onThreadFrameLoadStartForTesting( + initEvent as chrome.webviewTag.LoadStartEvent); + + const initCommitEvent = new Event('loadcommit'); + Object.assign(initCommitEvent, {url: 'http://example.com', isTopLevel: true}); + contextualTasksApp.onThreadFrameLoadCommitForTesting( + initCommitEvent as chrome.webviewTag.LoadCommitEvent); + + await initNavigationFinished; + const composeboxWrapper = contextualTasksApp.$.composebox; const headerWrapper = contextualTasksApp.$.composeboxHeaderWrapper;
diff --git a/chrome/test/data/webui/cr_components/composebox/composebox_test.ts b/chrome/test/data/webui/cr_components/composebox/composebox_test.ts index ab38da805..32183f7 100644 --- a/chrome/test/data/webui/cr_components/composebox/composebox_test.ts +++ b/chrome/test/data/webui/cr_components/composebox/composebox_test.ts
@@ -58,6 +58,7 @@ voiceListening: 'Listening', voiceDetails: 'Details', voiceClose: 'Close', + voiceStop: 'Stop', dismissButton: 'Dismiss', composeboxDragAndDropHint: 'Hint', removeSuggestion: 'Remove',
diff --git a/chrome/test/data/webui/cr_components/help_bubble/help_bubble_mixin_lit_test.ts b/chrome/test/data/webui/cr_components/help_bubble/help_bubble_mixin_lit_test.ts index 35bced7..07eabf44 100644 --- a/chrome/test/data/webui/cr_components/help_bubble/help_bubble_mixin_lit_test.ts +++ b/chrome/test/data/webui/cr_components/help_bubble/help_bubble_mixin_lit_test.ts
@@ -28,6 +28,8 @@ const EVENT2_NAME: string = 'kSecondExampleCustomEvent'; const CLOSE_BUTTON_ALT_TEXT: string = 'Close help bubble.'; const BODY_ICON_ALT_TEXT: string = 'Icon help bubble.'; +const CUSTOM_CONTAINER_NATIVE_ID: string = + 'kHelpBubbleMixinTestCustomContainerElementId'; const HelpBubbleMixinTestElementBase = HelpBubbleMixinLit(CrLitElement); @@ -45,6 +47,7 @@ let bulletListBubble: HelpBubbleController; let spanBubble: HelpBubbleController; let nestedChildBubble: HelpBubbleController; +let customContainerBubble: HelpBubbleController; // HelpBubbleMixinTestElement class HelpBubbleMixinTestElement extends HelpBubbleMixinTestElementBase { @@ -64,6 +67,8 @@ <span style="display: block;">Span text</span> <help-bubble-mixin-test-container id="container-element"> </help-bubble-mixin-test-container> + <div id="custom-container"></div> + <div id="custom-anchor">Custom Anchor</div> </div>`; } @@ -81,6 +86,14 @@ // using different types of selectors to test query mechanism nestedChildBubble = this.registerHelpBubble( NESTED_CHILD_NATIVE_ID, ['#container-element', '.child-element'])!; + + const customContainer = + this.shadowRoot.querySelector<HTMLElement>('#custom-container'); + assertTrue( + customContainer !== null, 'connectedCallback: custom container exists'); + customContainerBubble = this.registerHelpBubble( + CUSTOM_CONTAINER_NATIVE_ID, '#custom-anchor', + {containerElement: customContainer})!; } } @@ -321,6 +334,21 @@ }); test( + 'help bubble mixin shows bubble attached to custom containerElement', + () => { + assertFalse(container.isHelpBubbleShowing()); + assertFalse(customContainerBubble.isBubbleShowing()); + container.showHelpBubble(customContainerBubble, defaultParams); + assertTrue(container.isHelpBubbleShowing()); + assertTrue(customContainerBubble.isBubbleShowing()); + const bubble = container.getHelpBubbleForTesting('custom-anchor'); + assertTrue(!!bubble, 'bubble exists'); + assertEquals( + container.shadowRoot.querySelector('#custom-container'), + bubble.parentElement, 'bubble is child of custom container'); + }); + + test( 'help bubble mixin can pierce shadow dom to anchor to deep query', () => { const containerElement = container.shadowRoot @@ -528,6 +556,7 @@ [LIST_NATIVE_ID, true], [SPAN_NATIVE_ID, true], [NESTED_CHILD_NATIVE_ID, true], + [CUSTOM_CONTAINER_NATIVE_ID, true], ]), testProxy.getTrackedElementHandler().visibility); }); @@ -543,6 +572,7 @@ [LIST_NATIVE_ID, false], [SPAN_NATIVE_ID, false], [NESTED_CHILD_NATIVE_ID, false], + [CUSTOM_CONTAINER_NATIVE_ID, false], ]), testProxy.getTrackedElementHandler().visibility); });
diff --git a/chrome/test/data/webui/glic/browser_tests/glic_api_browsertest.ts b/chrome/test/data/webui/glic/browser_tests/glic_api_browsertest.ts index 8e31858c..1a3e4740 100644 --- a/chrome/test/data/webui/glic/browser_tests/glic_api_browsertest.ts +++ b/chrome/test/data/webui/glic/browser_tests/glic_api_browsertest.ts
@@ -931,18 +931,6 @@ assertTrue(await actuationOnWebState.next()); } - async testWebActuationSettingIsUndefinedWhenFeatureDisabled() { - assertTrue(this.host.getActuationOnWebSetting === undefined); - } - - async testGetWebActuationSetting() { - assertDefined(this.host.getActuationOnWebSetting); - const webActuationSetting = - observeSequence(this.host.getActuationOnWebSetting()); - assertFalse(await webActuationSetting.next() as boolean); - await this.advanceToNextStep(); - assertTrue(await webActuationSetting.next() as boolean); - } async testGetFormFactor() { assertDefined(this.host.getFormFactor);
diff --git a/chrome/test/data/webui/glic/browser_tests/new_glic_api_browsertest.ts b/chrome/test/data/webui/glic/browser_tests/new_glic_api_browsertest.ts index e60635e..9da7f5f 100644 --- a/chrome/test/data/webui/glic/browser_tests/new_glic_api_browsertest.ts +++ b/chrome/test/data/webui/glic/browser_tests/new_glic_api_browsertest.ts
@@ -64,6 +64,19 @@ 'Pinned tabs should remain empty when auto-pinning is disabled.'); } + async testWebActuationSettingIsUndefinedWhenFeatureDisabled() { + assertTrue(this.host.getActuationOnWebSetting === undefined); + } + + async testGetWebActuationSetting() { + assertDefined(this.host.getActuationOnWebSetting); + const webActuationSetting = + observeSequence(this.host.getActuationOnWebSetting()); + assertFalse(await webActuationSetting.next() as boolean); + await this.advanceToNextStep(); + assertTrue(await webActuationSetting.next() as boolean); + } + async testInvocationSource() { const expectedSource = this.testParams as number; await observeSequence(this.client.panelOpenData)
diff --git a/chrome/test/data/webui/js/tracked_element/tracked_element_test.ts b/chrome/test/data/webui/js/tracked_element/tracked_element_test.ts index 9a05aee..cfd1b76 100644 --- a/chrome/test/data/webui/js/tracked_element/tracked_element_test.ts +++ b/chrome/test/data/webui/js/tracked_element/tracked_element_test.ts
@@ -440,4 +440,32 @@ {x: rect.x, y: rect.y, width: rect.width, height: rect.height}, lastCall[2]); }); + + test('clickElement_ waits until not disabled', async () => { + const button = document.createElement('button'); + button.id = 'button'; + document.body.appendChild(button); + + let clicked = false; + button.addEventListener('click', () => { + clicked = true; + }); + + manager.startTracking(button, NATIVE_ID); + await waitForVisibilityEvents(); + + // Disable the button and try to click it. + button.disabled = true; + const clickPromise = managerRemote.clickElement(NATIVE_ID); + + // Give it some time to make sure it hasn't clicked. + await microtasksFinished(); + assertFalse(clicked); + + // Enable the button and wait for the click to complete. + button.disabled = false; + const result = await clickPromise; + assertTrue(result.success); + assertTrue(clicked); + }); });
diff --git a/chrome/test/data/webui/new_tab_page/composebox/file_thumbnail_test.ts b/chrome/test/data/webui/new_tab_page/composebox/file_thumbnail_test.ts index 4d53754..fa67ef6 100644 --- a/chrome/test/data/webui/new_tab_page/composebox/file_thumbnail_test.ts +++ b/chrome/test/data/webui/new_tab_page/composebox/file_thumbnail_test.ts
@@ -5,7 +5,7 @@ import {ComposeboxFileThumbnailElement} from 'chrome://new-tab-page/lazy_load.js'; import {ContextUploadStatus} from 'chrome://resources/cr_components/composebox/composebox_query.mojom-webui.js'; import {loadTimeData} from 'chrome://resources/js/load_time_data.js'; -import {assertEquals, assertTrue} from 'chrome://webui-test/chai_assert.js'; +import {assertEquals, assertFalse, assertTrue} from 'chrome://webui-test/chai_assert.js'; import {eventToPromise, microtasksFinished} from 'chrome://webui-test/test_util.js'; import {createComposeboxFile} from './test_support.js'; @@ -276,4 +276,102 @@ fileThumbnailElement.shadowRoot.querySelector('#removeTabButton'); assertEquals(null, removeButton); }); + + test('shows animation for entering attachment', async () => { + // Arrange. + fileThumbnailElement = new ComposeboxFileThumbnailElement(); + document.body.appendChild(fileThumbnailElement); + + let resolveAnimation: (value: any) => void; + fileThumbnailElement.getAnimations = () => { + return [{ + finished: new Promise(resolve => { + resolveAnimation = resolve; + }), + } as Animation]; + }; + + fileThumbnailElement.file = + createComposeboxFile(1, {type: 'image/jpeg', objectUrl: 'data:foo'}); + await microtasksFinished(); + + await new Promise(resolve => requestAnimationFrame(resolve)); + assertTrue(fileThumbnailElement.classList.contains('entering')); + + // Simulate all animations finishing. + resolveAnimation!(undefined); + await microtasksFinished(); + + assertFalse(fileThumbnailElement.classList.contains('entering')); + }); + + test('shows animation for exiting attachment', async () => { + fileThumbnailElement.file = + createComposeboxFile(1, {type: 'image/jpeg', objectUrl: 'data:foo'}); + await microtasksFinished(); + // Ensure the entering is completed before setting up the exiting mock. + await new Promise(resolve => requestAnimationFrame(resolve)); + + let resolveAnimation: (value: any) => void; + fileThumbnailElement.getAnimations = () => { + return [{ + finished: new Promise(resolve => { + resolveAnimation = resolve; + }), + } as Animation]; + }; + + let eventFired = false; + fileThumbnailElement.addEventListener('delete-file', () => { + eventFired = true; + }); + + fileThumbnailElement.$.removeImgButton.click(); + + assertTrue(fileThumbnailElement.classList.contains('exiting')); + assertFalse(eventFired); + + // Simulate all animations finishing. + resolveAnimation!(undefined); + await microtasksFinished(); + + assertTrue(eventFired); + assertFalse(fileThumbnailElement.classList.contains('exiting')); + }); + + test('ignores delete button clicks while already exiting', async () => { + fileThumbnailElement.file = + createComposeboxFile(1, {type: 'image/jpeg', objectUrl: 'data:foo'}); + await microtasksFinished(); + // Ensure the entering is completed before setting up the exiting mock. + await new Promise(resolve => requestAnimationFrame(resolve)); + + let resolveAnimation: (value: any) => void; + fileThumbnailElement.getAnimations = () => { + return [{ + finished: new Promise(resolve => { + resolveAnimation = resolve; + }), + } as Animation]; + }; + + let eventCount = 0; + fileThumbnailElement.addEventListener('delete-file', () => { + eventCount++; + }); + + // First click initiates exiting animation. + fileThumbnailElement.$.removeImgButton.click(); + assertTrue(fileThumbnailElement.classList.contains('exiting')); + + // Second click should be ignored by the early return. + fileThumbnailElement.$.removeImgButton.click(); + + // Complete the animation. + resolveAnimation!(undefined); + await microtasksFinished(); + + // Only one delete-file event should have been fired. + assertEquals(1, eventCount); + }); });
diff --git a/chrome/test/data/webui/settings/privacy_guide_completion_fragment_test.ts b/chrome/test/data/webui/settings/privacy_guide_completion_fragment_test.ts index b15a2f9..04de5a5 100644 --- a/chrome/test/data/webui/settings/privacy_guide_completion_fragment_test.ts +++ b/chrome/test/data/webui/settings/privacy_guide_completion_fragment_test.ts
@@ -6,14 +6,13 @@ import {flush} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js'; import type {PrivacyGuideCompletionFragmentElement} from 'chrome://settings/lazy_load.js'; import type {CrLinkRowElement} from 'chrome://settings/settings.js'; -import {loadTimeData, MetricsBrowserProxyImpl, OpenWindowProxyImpl, PrivacyGuideInteractions, PrivacySandboxBrowserProxyImpl, resetRouterForTesting, Router, routes} from 'chrome://settings/settings.js'; +import {loadTimeData, MetricsBrowserProxyImpl, OpenWindowProxyImpl, PrivacyGuideInteractions, resetRouterForTesting, Router, routes} from 'chrome://settings/settings.js'; import {assertEquals, assertFalse, assertTrue} from 'chrome://webui-test/chai_assert.js'; import {flushTasks} from 'chrome://webui-test/polymer_test_util.js'; import {TestOpenWindowProxy} from 'chrome://webui-test/test_open_window_proxy.js'; import {eventToPromise, isChildVisible, isVisible} from 'chrome://webui-test/test_util.js'; import {TestMetricsBrowserProxy} from './test_metrics_browser_proxy.js'; -import {TestPrivacySandboxBrowserProxy} from './test_privacy_sandbox_browser_proxy.js'; /** Fire a sign in status change event and flush the UI. */ function setSignInState(signedIn: boolean) { @@ -28,7 +27,6 @@ let fragment: PrivacyGuideCompletionFragmentElement; let testMetricsBrowserProxy: TestMetricsBrowserProxy; let openWindowProxy: TestOpenWindowProxy; - let testPrivacySandboxBrowserProxy: TestPrivacySandboxBrowserProxy; suiteSetup(function() { loadTimeData.overrideValues({ @@ -44,10 +42,6 @@ assertTrue(loadTimeData.getBoolean('showPrivacyGuide')); testMetricsBrowserProxy = new TestMetricsBrowserProxy(); MetricsBrowserProxyImpl.setInstance(testMetricsBrowserProxy); - testPrivacySandboxBrowserProxy = new TestPrivacySandboxBrowserProxy(); - testPrivacySandboxBrowserProxy - .setShouldShowPrivacySandboxAdTopicsContentParity(false); - PrivacySandboxBrowserProxyImpl.setInstance(testPrivacySandboxBrowserProxy); openWindowProxy = new TestOpenWindowProxy(); OpenWindowProxyImpl.setInstance(openWindowProxy); createPage(); @@ -123,7 +117,8 @@ '#privacySandboxRow'); assertTrue(!!privacySandboxRow); assertEquals( - fragment.i18n('privacyGuideCompletionCardPrivacySandboxSubLabel'), + fragment.i18n( + 'privacyGuideCompletionCardPrivacySandboxSubLabelAdTopics'), privacySandboxRow.subLabel); privacySandboxRow.click(); flush(); @@ -264,46 +259,6 @@ }); }); -suite('CompletionFragmentWithAdTopicsCard', function() { - let fragment: PrivacyGuideCompletionFragmentElement; - let testPrivacySandboxBrowserProxy: TestPrivacySandboxBrowserProxy; - - suiteSetup(function() { - loadTimeData.overrideValues({ - isPrivacySandboxRestricted: false, - isPrivacySandboxRestrictedNoticeEnabled: false, - isPrivacySandboxAdPrivacyUxDeprecationEnabled: false, - }); - resetRouterForTesting(); - }); - - setup(function() { - document.body.innerHTML = window.trustedTypes!.emptyHTML; - - assertTrue(loadTimeData.getBoolean('showPrivacyGuide')); - - testPrivacySandboxBrowserProxy = new TestPrivacySandboxBrowserProxy(); - testPrivacySandboxBrowserProxy - .setShouldShowPrivacySandboxAdTopicsContentParity(true); - PrivacySandboxBrowserProxyImpl.setInstance(testPrivacySandboxBrowserProxy); - fragment = document.createElement('privacy-guide-completion-fragment'); - document.body.appendChild(fragment); - - return flushTasks(); - }); - - test('TestAdTopicsCrLinkRowSubLabel', function() { - const privacySandboxRow = - fragment.shadowRoot!.querySelector<CrLinkRowElement>( - '#privacySandboxRow'); - assertTrue(!!privacySandboxRow); - assertEquals( - fragment.i18n( - 'privacyGuideCompletionCardPrivacySandboxSubLabelAdTopics'), - privacySandboxRow.subLabel); - }); -}); - suite('CompletionFragmentAdPrivacyDeprecationEnabled', function() { let fragment: PrivacyGuideCompletionFragmentElement;
diff --git a/chrome/test/data/webui/settings/privacy_sandbox_page_test.ts b/chrome/test/data/webui/settings/privacy_sandbox_page_test.ts index f1c19c6a..f642df1 100644 --- a/chrome/test/data/webui/settings/privacy_sandbox_page_test.ts +++ b/chrome/test/data/webui/settings/privacy_sandbox_page_test.ts
@@ -936,13 +936,13 @@ assertFalse(isVisible(page.shadowRoot!.querySelector('#footer'))); assertTrue(isVisible(page.shadowRoot!.querySelector('#footerV2'))); const footerDisclaimer = - page.shadowRoot!.querySelector('#footerDisclaimer'); + page.shadowRoot!.querySelector('#footerDisclaimerV2'); assertTrue(isVisible(footerDisclaimer)); }); test('privacyPolicyLink', async function() { const privacyPolicyLink = - page.shadowRoot!.querySelector<HTMLElement>('#privacyPolicyLink'); + page.shadowRoot!.querySelector<HTMLElement>('#privacyPolicyLinkV2'); assertTrue(!!privacyPolicyLink); privacyPolicyLink.click(); assertEquals( @@ -1912,7 +1912,6 @@ }); }); -// PrivacySandboxAdsTopicsContentParity is enabled. suite('TopicsSubpageAdTopicsContentParity', function() { let page: SettingsPrivacySandboxTopicsSubpageElement; let testPrivacySandboxBrowserProxy: TestPrivacySandboxBrowserProxy; @@ -1929,8 +1928,6 @@ setup(async function() { testPrivacySandboxBrowserProxy = new TestPrivacySandboxBrowserProxy(); - testPrivacySandboxBrowserProxy - .setShouldShowPrivacySandboxAdTopicsContentParity(true); PrivacySandboxBrowserProxyImpl.setInstance(testPrivacySandboxBrowserProxy); metricsBrowserProxy = new TestMetricsBrowserProxy(); MetricsBrowserProxyImpl.setInstance(metricsBrowserProxy); @@ -1978,70 +1975,3 @@ await metricsBrowserProxy.whenCalled('recordAction')); }); }); - -// PrivacySandboxAdsTopicsContentParity is disabled -suite('TopicsSubpageAdTopicsContentParityDisabled', function() { - let page: SettingsPrivacySandboxTopicsSubpageElement; - let testPrivacySandboxBrowserProxy: TestPrivacySandboxBrowserProxy; - let settingsPrefs: SettingsPrefsElement; - let metricsBrowserProxy: TestMetricsBrowserProxy; - - suiteSetup(function() { - loadTimeData.overrideValues({ - isPrivacySandboxRestricted: false, - }); - settingsPrefs = document.createElement('settings-prefs'); - return CrSettingsPrefs.initialized; - }); - - setup(async function() { - testPrivacySandboxBrowserProxy = new TestPrivacySandboxBrowserProxy(); - testPrivacySandboxBrowserProxy - .setShouldShowPrivacySandboxAdTopicsContentParity(false); - PrivacySandboxBrowserProxyImpl.setInstance(testPrivacySandboxBrowserProxy); - metricsBrowserProxy = new TestMetricsBrowserProxy(); - MetricsBrowserProxyImpl.setInstance(metricsBrowserProxy); - - document.body.innerHTML = window.trustedTypes!.emptyHTML; - document.body.appendChild(settingsPrefs); - page = document.createElement('settings-privacy-sandbox-topics-subpage'); - page.prefs = settingsPrefs.prefs!; - page.set('prefs.privacy_sandbox.m1.topics_enabled', {value: true}); - Router.getInstance().navigateTo(routes.PRIVACY_SANDBOX_TOPICS); - document.body.appendChild(page); - await testPrivacySandboxBrowserProxy.whenCalled('getTopicsState'); - return flushTasks(); - }); - - teardown(function() { - Router.getInstance().resetRouteForTesting(); - }); - - test('privacyPolicyLink', async function() { - const privacyPolicyLink = - page.shadowRoot!.querySelector<HTMLElement>('#privacyPolicyLink'); - assertTrue(!!privacyPolicyLink); - privacyPolicyLink.click(); - assertEquals( - 'Settings.PrivacySandbox.AdTopics.PrivacyPolicyLinkClicked', - await metricsBrowserProxy.whenCalled('recordAction')); - }); - - test('AdTopicsContentParityNotShown', function() { - const topicsToggle = - page.shadowRoot!.querySelector<SettingsToggleButtonElement>( - '#topicsToggle'); - assert(topicsToggle); - assertTrue(isVisible(topicsToggle)); - assertEquals( - loadTimeData.getString('topicsPageToggleSubLabel'), - topicsToggle.subLabel); - assertFalse( - isVisible(page.shadowRoot!.querySelector('#footerDisclaimerV2'))); - assertFalse(isVisible( - page.shadowRoot!.querySelector('#currentTopicsDescriptionV2'))); - assertTrue(isVisible(page.shadowRoot!.querySelector('#footerDisclaimer'))); - assertTrue( - isVisible(page.shadowRoot!.querySelector('#currentTopicsDescription'))); - }); -});
diff --git a/chrome/test/data/webui/settings/settings_browsertest.cc b/chrome/test/data/webui/settings/settings_browsertest.cc index 0ecb239d..f44479d 100644 --- a/chrome/test/data/webui/settings/settings_browsertest.cc +++ b/chrome/test/data/webui/settings/settings_browsertest.cc
@@ -1530,12 +1530,6 @@ "CompletionFragmentPrivacySandboxRestrictedWithNoticeEnabled')"); } -IN_PROC_BROWSER_TEST_F(SettingsPrivacyGuideTest, - CompletionFragmentWithAdTopicsCard) { - RunTest("settings/privacy_guide_completion_fragment_test.js", - "runMochaSuite('CompletionFragmentWithAdTopicsCard')"); -} - // TODO(crbug.com/410848707): Re-enable this test #if BUILDFLAG(IS_MAC) #define MAYBE_AdTopicsFragment DISABLED_AdTopicsFragment @@ -1799,12 +1793,6 @@ "runMochaSuite('TopicsSubpageAdTopicsContentParity')"); } -IN_PROC_BROWSER_TEST_F(SettingsPrivacySandboxPageTest, - TopicsSubpageAdTopicsContentParityDisabled) { - RunTest("settings/privacy_sandbox_page_test.js", - "runMochaSuite('TopicsSubpageAdTopicsContentParityDisabled')"); -} - using SettingsRouteTest = SettingsBrowserTest; IN_PROC_BROWSER_TEST_F(SettingsRouteTest, Basic) {
diff --git a/chrome/test/data/webui/settings/test_privacy_sandbox_browser_proxy.ts b/chrome/test/data/webui/settings/test_privacy_sandbox_browser_proxy.ts index c7a2ca5..dca5309 100644 --- a/chrome/test/data/webui/settings/test_privacy_sandbox_browser_proxy.ts +++ b/chrome/test/data/webui/settings/test_privacy_sandbox_browser_proxy.ts
@@ -12,7 +12,6 @@ private firstLevelTopicsState_: FirstLevelTopicsState = {firstLevelTopics: [], blockedTopics: []}; private childTopicsCurrentlyAssigned_: CanonicalTopic[] = []; - private shouldShowAdTopicsContentParity = false; constructor() { super([ @@ -23,7 +22,6 @@ 'setFledgeJoiningAllowed', 'setTopicAllowed', 'topicsToggleChanged', - 'shouldShowPrivacySandboxAdTopicsContentParity', ]); } @@ -44,10 +42,6 @@ this.fledgeState_ = fledgeState; } - setShouldShowPrivacySandboxAdTopicsContentParity(shouldShow: boolean) { - this.shouldShowAdTopicsContentParity = shouldShow; - } - // Test Proxy Functions getFledgeState() { this.methodCalled('getFledgeState'); @@ -86,9 +80,4 @@ topic.taxonomyVersion); return Promise.resolve(this.childTopicsCurrentlyAssigned_.slice()); } - - shouldShowPrivacySandboxAdTopicsContentParity() { - this.methodCalled('shouldShowPrivacySandboxAdTopicsContentParity'); - return Promise.resolve(this.shouldShowAdTopicsContentParity); - } }
diff --git a/chrome/test/data/webui/webui_toolbar/BUILD.gn b/chrome/test/data/webui/webui_toolbar/BUILD.gn index eb56f932..1982654 100644 --- a/chrome/test/data/webui/webui_toolbar/BUILD.gn +++ b/chrome/test/data/webui/webui_toolbar/BUILD.gn
@@ -15,7 +15,6 @@ "location_bar_focus_test.ts", "location_bar_high_contrast_focus_test.ts", "location_icon_test.ts", - "permission_chip_test.ts", "readonly_omnibox_test.ts", "readonly_omnibox_focus_test.ts", "pinned_toolbar_action_test.ts",
diff --git a/chrome/test/data/webui/webui_toolbar/permission_chip_test.ts b/chrome/test/data/webui/webui_toolbar/permission_chip_test.ts deleted file mode 100644 index 04f6d6f7..0000000 --- a/chrome/test/data/webui/webui_toolbar/permission_chip_test.ts +++ /dev/null
@@ -1,283 +0,0 @@ -// Copyright 2026 The Chromium Authors -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -import 'chrome://webui-toolbar.top-chrome/app.js'; - -import {assertEquals, assertFalse, assertTrue} from 'chrome://webui-test/chai_assert.js'; -import {TestBrowserProxy} from 'chrome://webui-test/test_browser_proxy.js'; -import {microtasksFinished} from 'chrome://webui-test/test_util.js'; -import {BrowserProxyImpl, LhsChipIdentifier, PermissionAction, PermissionChipTheme, PermissionPromptStyle} from 'chrome://webui-toolbar.top-chrome/app.js'; -import type {PermissionChipElement, PermissionChipState} from 'chrome://webui-toolbar.top-chrome/app.js'; -import type {BrowserControlsServiceInterface} from 'chrome://webui-toolbar.top-chrome/browser_controls_api.mojom-webui.js'; -import type {BrowserProxy} from 'chrome://webui-toolbar.top-chrome/browser_proxy.js'; -import type {ToolbarUIServiceInterface} from 'chrome://webui-toolbar.top-chrome/toolbar_ui_api.mojom-webui.js'; - -class TestToolbarUiHandler extends TestBrowserProxy implements - ToolbarUIServiceInterface { - constructor() { - super([ - 'onLhsChipMousePressed', - 'onLhsChipClicked', - 'onLhsChipCollapseAnimationEnded', - 'onLhsChipExpandAnimationEnded', - 'onLhsChipPointerEntered', - 'onLhsChipPointerExited', - ]); - } - - bind() { - return new Promise<never>(() => {}); - } - showContextMenu() {} - onOmniboxAction() {} - onPageInitialized() {} - showContentSettingsBubble() { - return new Promise<never>(() => {}); - } - invokePinnedToolbarAction() {} - onHomeButtonDropUrl() {} - onHomeButtonDropFile() {} - - onLhsChipMousePressed(id: LhsChipIdentifier) { - this.methodCalled('onLhsChipMousePressed', id); - } - - onLhsChipClicked(id: LhsChipIdentifier, isMouseInteraction: boolean) { - this.methodCalled('onLhsChipClicked', [id, isMouseInteraction]); - } - - onLhsChipCollapseAnimationEnded(id: LhsChipIdentifier) { - this.methodCalled('onLhsChipCollapseAnimationEnded', id); - } - - onLhsChipExpandAnimationEnded(id: LhsChipIdentifier) { - this.methodCalled('onLhsChipExpandAnimationEnded', id); - } - - onLhsChipPointerEntered(id: LhsChipIdentifier) { - this.methodCalled('onLhsChipPointerEntered', id); - } - - onLhsChipPointerExited(id: LhsChipIdentifier) { - this.methodCalled('onLhsChipPointerExited', id); - } -} - -class TestBrowserControlsHandler extends TestBrowserProxy implements - BrowserControlsServiceInterface { - constructor() { - super([]); - } - stopLoad() { - return new Promise<never>(() => {}); - } - reloadFromClick() { - return new Promise<never>(() => {}); - } - splitActiveTab() { - return new Promise<never>(() => {}); - } - back() { - return new Promise<never>(() => {}); - } - forward() { - return new Promise<never>(() => {}); - } - backButtonHovered() { - return new Promise<never>(() => {}); - } - navigateHome() { - return new Promise<never>(() => {}); - } -} - -class TestToolbarBrowserProxy extends TestBrowserProxy implements BrowserProxy { - toolbarUIHandler: TestToolbarUiHandler; - browserControlsHandler: TestBrowserControlsHandler; - - constructor() { - super([ - 'recordInHistogram', - 'addNavigationStateListener', - 'removeNavigationStateListener', - ]); - this.toolbarUIHandler = new TestToolbarUiHandler(); - this.browserControlsHandler = new TestBrowserControlsHandler(); - } - - recordInHistogram() {} - addNavigationStateListener() { - return -1; - } - removeNavigationStateListener() {} -} - -suite('PermissionChipTest', function() { - let chip: PermissionChipElement; - let toolbarUiHandler: TestToolbarUiHandler; - let browserProxy: TestToolbarBrowserProxy; - - function createBaseState(): PermissionChipState { - return { - isFullyCollapsed: false, - accessibilityName: 'Camera', - tooltip: 'Camera in use', - isVisible: true, - iconName: 'kVideocamChromeRefreshIcon', - theme: PermissionChipTheme.kNormalVisibility, - promptStyle: PermissionPromptStyle.kChip, - userDecision: PermissionAction.kGranted, - shouldShowBlockedIcon: false, - message: 'Camera', - }; - } - - setup(function() { - document.body.innerHTML = window.trustedTypes!.emptyHTML; - browserProxy = new TestToolbarBrowserProxy(); - toolbarUiHandler = browserProxy.toolbarUIHandler; - BrowserProxyImpl.setInstance(browserProxy); - - chip = document.createElement('permission-chip'); - chip.id = 'request-chip'; - document.body.appendChild(chip); - }); - - test('Render invisible state', async function() { - const state = createBaseState(); - state.isVisible = false; - chip.chipState = state; - await microtasksFinished(); - - const chipEl = chip.shadowRoot.querySelector<HTMLElement>('#chip'); - assertFalse(!!chipEl); - }); - - test('Render visible state', async function() { - chip.chipState = createBaseState(); - await microtasksFinished(); - - const chipEl = chip.shadowRoot.querySelector<HTMLElement>('#chip'); - assertTrue(!!chipEl); - assertFalse(chipEl.hasAttribute('collapsed')); - - const iconEl = chip.shadowRoot.querySelector<HTMLElement>('#icon'); - assertTrue(!!iconEl); - assertTrue(iconEl.style.maskImage.includes('videocam_chrome_refresh.svg')); - - const messageEl = chip.shadowRoot.querySelector<HTMLElement>('#message'); - assertTrue(!!messageEl); - assertEquals('Camera', messageEl.textContent); - }); - - test('Click events', async function() { - chip.chipState = createBaseState(); - await microtasksFinished(); - - const chipEl = chip.shadowRoot.querySelector<HTMLElement>('#chip'); - assertTrue(!!chipEl); - - // Left press (pointerdown) - chipEl.dispatchEvent(new PointerEvent('pointerdown', {button: 0})); - assertEquals(1, toolbarUiHandler.getCallCount('onLhsChipMousePressed')); - assertEquals( - LhsChipIdentifier.kPermissionRequest, - toolbarUiHandler.getArgs('onLhsChipMousePressed')[0]); - - // Right press should not trigger pressed - chipEl.dispatchEvent(new PointerEvent('pointerdown', {button: 2})); - assertEquals(1, toolbarUiHandler.getCallCount('onLhsChipMousePressed')); - - // Programmatic click (e.g. keyboard) - chipEl.click(); - assertEquals(1, toolbarUiHandler.getCallCount('onLhsChipClicked')); - assertEquals( - LhsChipIdentifier.kPermissionRequest, - toolbarUiHandler.getArgs('onLhsChipClicked')[0][0]); - assertFalse(toolbarUiHandler.getArgs('onLhsChipClicked')[0][1]); - - // Mouse click - chipEl.dispatchEvent(new PointerEvent('click', {pointerType: 'mouse'})); - assertEquals(2, toolbarUiHandler.getCallCount('onLhsChipClicked')); - assertEquals( - LhsChipIdentifier.kPermissionRequest, - toolbarUiHandler.getArgs('onLhsChipClicked')[1][0]); - assertTrue(toolbarUiHandler.getArgs('onLhsChipClicked')[1][1]); - }); - - test('Pointer hover events', async function() { - chip.chipState = createBaseState(); - await microtasksFinished(); - - const chipEl = chip.shadowRoot.querySelector<HTMLElement>('#chip'); - assertTrue(!!chipEl); - - chipEl.dispatchEvent(new PointerEvent('pointerenter')); - assertEquals(1, toolbarUiHandler.getCallCount('onLhsChipPointerEntered')); - assertEquals( - LhsChipIdentifier.kPermissionRequest, - toolbarUiHandler.getArgs('onLhsChipPointerEntered')[0]); - - chipEl.dispatchEvent(new PointerEvent('pointerleave')); - assertEquals(1, toolbarUiHandler.getCallCount('onLhsChipPointerExited')); - assertEquals( - LhsChipIdentifier.kPermissionRequest, - toolbarUiHandler.getArgs('onLhsChipPointerExited')[0]); - }); - - test('Theme colors', async function() { - const state = createBaseState(); - - // Test Activity Indicator - state.theme = PermissionChipTheme.kActivityIndicator; - chip.chipState = {...state}; - await microtasksFinished(); - - let bgColor = chip.style.getPropertyValue('--chip-bg-color').trim(); - let fgColor = chip.style.getPropertyValue('--chip-fg-color').trim(); - assertEquals( - 'var(--color-omnibox-chip-in-use-activity-indicator-background)', - bgColor); - assertEquals( - 'var(--color-omnibox-chip-in-use-activity-indicator-foreground)', - fgColor); - - // Test Blocked Activity Indicator - state.theme = PermissionChipTheme.kBlockedActivityIndicator; - chip.chipState = {...state}; - await microtasksFinished(); - - bgColor = chip.style.getPropertyValue('--chip-bg-color').trim(); - fgColor = chip.style.getPropertyValue('--chip-fg-color').trim(); - assertEquals( - 'var(--color-omnibox-chip-blocked-activity-indicator-background)', - bgColor); - assertEquals( - 'var(--color-omnibox-chip-blocked-activity-indicator-foreground)', - fgColor); - - // Test Normal Visibility with Granted - state.theme = PermissionChipTheme.kNormalVisibility; - state.userDecision = PermissionAction.kGranted; - chip.chipState = {...state}; - await microtasksFinished(); - - bgColor = chip.style.getPropertyValue('--chip-bg-color').trim(); - fgColor = chip.style.getPropertyValue('--chip-fg-color').trim(); - assertEquals('var(--color-omnibox-chip-background)', bgColor); - assertEquals( - 'var(--color-omnibox-chip-foreground-normal-visibility)', fgColor); - - // Test Normal Visibility with Denied - state.userDecision = PermissionAction.kDenied; - chip.chipState = {...state}; - await microtasksFinished(); - - bgColor = chip.style.getPropertyValue('--chip-bg-color').trim(); - fgColor = chip.style.getPropertyValue('--chip-fg-color').trim(); - assertEquals('var(--color-omnibox-chip-background)', bgColor); - assertEquals( - 'var(--color-omnibox-chip-foreground-low-visibility)', fgColor); - }); -});
diff --git a/chrome/test/data/webui/webui_toolbar/webui_toolbar_js_browsertest.cc b/chrome/test/data/webui/webui_toolbar/webui_toolbar_js_browsertest.cc index c290b86..328b003e 100644 --- a/chrome/test/data/webui/webui_toolbar/webui_toolbar_js_browsertest.cc +++ b/chrome/test/data/webui/webui_toolbar/webui_toolbar_js_browsertest.cc
@@ -43,7 +43,3 @@ IN_PROC_BROWSER_TEST_F(WebUiToolbarJsTest, LocationIcon) { RunTest("webui_toolbar/location_icon_test.js", "mocha.run();"); } - -IN_PROC_BROWSER_TEST_F(WebUiToolbarJsTest, PermissionChip) { - RunTest("webui_toolbar/permission_chip_test.js", "mocha.run();"); -}
diff --git a/chrome/test/fuzzing/kombucha_in_process_fuzzer.cc b/chrome/test/fuzzing/kombucha_in_process_fuzzer.cc index 1444ef4d..5600e03 100644 --- a/chrome/test/fuzzing/kombucha_in_process_fuzzer.cc +++ b/chrome/test/fuzzing/kombucha_in_process_fuzzer.cc
@@ -9,7 +9,6 @@ #include "build/build_config.h" #include "chrome/app/chrome_command_ids.h" #include "chrome/browser/ui/accelerator_utils.h" -#include "chrome/browser/ui/browser_finder.h" #include "chrome/browser/ui/browser_window/public/browser_window_interface_iterator.h" #include "chrome/browser/ui/browser_window/public/global_browser_collection.h" #include "chrome/browser/ui/tabs/tab_group_model.h"
diff --git a/chrome/test/media_router/media_router_e2e_browsertest.cc b/chrome/test/media_router/media_router_e2e_browsertest.cc index 5574df6..a2e6c9c 100644 --- a/chrome/test/media_router/media_router_e2e_browsertest.cc +++ b/chrome/test/media_router/media_router_e2e_browsertest.cc
@@ -10,7 +10,6 @@ #include "base/command_line.h" #include "base/functional/bind.h" #include "chrome/browser/profiles/profile.h" -#include "chrome/browser/ui/browser_finder.h" #include "chrome/browser/ui/browser_window/public/global_browser_collection.h" #include "chrome/browser/ui/tabs/tab_strip_model.h" #include "chrome/test/base/ui_test_utils.h"
diff --git a/chrome/test/media_router/media_router_integration_browsertest.cc b/chrome/test/media_router/media_router_integration_browsertest.cc index 478133c..6a449f0 100644 --- a/chrome/test/media_router/media_router_integration_browsertest.cc +++ b/chrome/test/media_router/media_router_integration_browsertest.cc
@@ -20,7 +20,6 @@ #include "build/build_config.h" #include "chrome/browser/media/router/media_router_feature.h" #include "chrome/browser/media/router/mojo/media_router_desktop.h" -#include "chrome/browser/ui/browser_finder.h" #include "chrome/browser/ui/media_router/media_cast_mode.h" #include "chrome/browser/ui/tabs/tab_strip_model.h" #include "chrome/browser/ui/views/frame/browser_view.h"
diff --git a/chromecast/browser/lru_renderer_cache.cc b/chromecast/browser/lru_renderer_cache.cc index 4a7819dd..f81e4016 100644 --- a/chromecast/browser/lru_renderer_cache.cc +++ b/chromecast/browser/lru_renderer_cache.cc
@@ -31,6 +31,8 @@ base::MemoryConsumerRegistration::CheckRegistryExists::kDisabled), weak_factory_(this) { DCHECK(browser_context_); + // Ensure any already assigned memory limit is honored. + OnUpdateMemoryLimit(); } LRURendererCache::~LRURendererCache() = default;
diff --git a/chromecast/browser/lru_renderer_cache.h b/chromecast/browser/lru_renderer_cache.h index 1f666ae..f163388 100644 --- a/chromecast/browser/lru_renderer_cache.h +++ b/chromecast/browser/lru_renderer_cache.h
@@ -31,7 +31,7 @@ }; // This class maintains a pool of prelaunched (initialized) renderers. -class LRURendererCache : public base::MemoryConsumer { +class LRURendererCache final : public base::MemoryConsumer { public: LRURendererCache(content::BrowserContext* browser_context, size_t max_renderers);
diff --git a/chromecast/media/cma/base/decoder_buffer_adapter.cc b/chromecast/media/cma/base/decoder_buffer_adapter.cc index 1c0ce2f..0852eef 100644 --- a/chromecast/media/cma/base/decoder_buffer_adapter.cc +++ b/chromecast/media/cma/base/decoder_buffer_adapter.cc
@@ -4,7 +4,9 @@ #include "chromecast/media/cma/base/decoder_buffer_adapter.h" +#include "base/logging.h" #include "base/notreached.h" +#include "base/numerics/checked_math.h" #include "chromecast/media/cma/base/cast_decrypt_config_impl.h" #include "chromecast/public/media/cast_decrypt_config.h" #include "media/base/decoder_buffer.h" @@ -44,8 +46,21 @@ buffer_->end_of_stream() ? nullptr : buffer_->decrypt_config(); if (decrypt_config) { std::vector<SubsampleEntry> subsamples; + base::CheckedNumeric<size_t> total_subsample_size = 0; for (const auto& sample : decrypt_config->subsamples()) { subsamples.emplace_back(sample.clear_bytes, sample.cypher_bytes); + total_subsample_size += sample.clear_bytes; + total_subsample_size += sample.cypher_bytes; + } + if (!subsamples.empty() && + (!total_subsample_size.IsValid() || + total_subsample_size.ValueOrDie() != buffer_->size())) { + LOG(ERROR) << "Invalid DecryptConfig: total_subsample_size=" + << static_cast<size_t>(total_subsample_size.ValueOrDefault(0)) + << " vs buffer size=" << buffer_->size(); + // Invalid DecryptConfig, reject the buffer to prevent OOB read/write. + buffer_ = ::media::DecoderBuffer::CreateEOSBuffer(); + return; } if (subsamples.empty()) { // DecryptConfig may contain 0 subsamples if all content is encrypted.
diff --git a/chromecast/media/cma/base/decoder_buffer_adapter.h b/chromecast/media/cma/base/decoder_buffer_adapter.h index 27b93e3c..57a63b9 100644 --- a/chromecast/media/cma/base/decoder_buffer_adapter.h +++ b/chromecast/media/cma/base/decoder_buffer_adapter.h
@@ -48,7 +48,7 @@ ~DecoderBufferAdapter() override; StreamId stream_id_; - scoped_refptr<::media::DecoderBuffer> const buffer_; + scoped_refptr<::media::DecoderBuffer> buffer_; std::unique_ptr<CastDecryptConfig> decrypt_config_; };
diff --git a/chromecast/media/cma/base/decoder_buffer_adapter_unittest.cc b/chromecast/media/cma/base/decoder_buffer_adapter_unittest.cc index 190dbd14..983ec205 100644 --- a/chromecast/media/cma/base/decoder_buffer_adapter_unittest.cc +++ b/chromecast/media/cma/base/decoder_buffer_adapter_unittest.cc
@@ -125,7 +125,10 @@ ::media::DecryptConfig::CreateCencConfig(kKeyId, kIV, subsamples); EXPECT_TRUE(decrypt_config); - scoped_refptr<::media::DecoderBuffer> buffer = MakeDecoderBuffer(); + // Make a buffer that matches the subsamples size + std::vector<uint8_t> dummy_data(37, 0); + scoped_refptr<::media::DecoderBuffer> buffer = + ::media::DecoderBuffer::CopyFrom(dummy_data); buffer->set_decrypt_config(std::move(decrypt_config)); scoped_refptr<DecoderBufferAdapter> buffer_adapter( new DecoderBufferAdapter(buffer)); @@ -152,6 +155,29 @@ EXPECT_EQ(nullptr, buffer_adapter->decrypt_config()); } +TEST(DecoderBufferAdapterTest, RejectInvalidDecryptConfig) { + uint32_t kClearBytes[] = {10, 15}; + uint32_t kCypherBytes[] = {5, 7}; + std::vector<::media::SubsampleEntry> subsamples; + subsamples.emplace_back(kClearBytes[0], kCypherBytes[0]); + subsamples.emplace_back(kClearBytes[1], kCypherBytes[1]); + + // Make a buffer that is too small for these subsamples + scoped_refptr<::media::DecoderBuffer> buffer = + ::media::DecoderBuffer::CopyFrom( + UNSAFE_BUFFERS(base::span<const uint8_t>(kBufferData, 5u))); + + std::unique_ptr<::media::DecryptConfig> decrypt_config = + ::media::DecryptConfig::CreateCencConfig("key-id", kIv, subsamples); + buffer->set_decrypt_config(std::move(decrypt_config)); + + scoped_refptr<DecoderBufferAdapter> buffer_adapter( + new DecoderBufferAdapter(buffer)); + + // Should be converted to an EOS buffer + EXPECT_TRUE(buffer_adapter->end_of_stream()); +} + TEST(DecoderBufferAdapterTest, SetsEncryptionSchemeOfCencDecryptConfig) { scoped_refptr<::media::DecoderBuffer> buffer = MakeDecoderBuffer(); std::unique_ptr<::media::DecryptConfig> decrypt_config =
diff --git a/chromeos/CHROMEOS_LKGM b/chromeos/CHROMEOS_LKGM index e7bdd9fb..370958c 100644 --- a/chromeos/CHROMEOS_LKGM +++ b/chromeos/CHROMEOS_LKGM
@@ -1 +1 @@ -16671.0.0-1077212 \ No newline at end of file +16671.0.0-1077224 \ No newline at end of file
diff --git a/chromeos/ash/components/BUILD.gn b/chromeos/ash/components/BUILD.gn index a49dfaec..4ca1bff9 100644 --- a/chromeos/ash/components/BUILD.gn +++ b/chromeos/ash/components/BUILD.gn
@@ -15,6 +15,7 @@ "//chromeos/ash/components/auto_sign_out:unit_tests", "//chromeos/ash/components/boca:unit_tests", "//chromeos/ash/components/boca/babelorca:unit_tests", + "//chromeos/ash/components/boca/gemini:unit_tests", "//chromeos/ash/components/boca/on_task:unit_tests", "//chromeos/ash/components/boca/receiver:unit_tests", "//chromeos/ash/components/boca/session_api:unit_tests",
diff --git a/chromeos/ash/components/boca/BUILD.gn b/chromeos/ash/components/boca/BUILD.gn index 7cd4b5a0..94aa585 100644 --- a/chromeos/ash/components/boca/BUILD.gn +++ b/chromeos/ash/components/boca/BUILD.gn
@@ -12,8 +12,6 @@ "boca_metrics_manager.h", "boca_metrics_util.cc", "boca_metrics_util.h", - "boca_request.cc", - "boca_request.h", "boca_role_util.cc", "boca_role_util.h", "boca_session_manager.cc", @@ -23,16 +21,14 @@ "boca_window_observer.h", "notifications/boca_notification_handler.cc", "notifications/boca_notification_handler.h", - "retriable_request_sender.h", "screen_presenter_factory.h", "shared_crd_session_wrapper.h", "student_screen_presenter.h", "teacher_screen_presenter.h", - "util.cc", - "util.h", ] deps = [ # TODO(crbug.com/376673450):Clean up unused and unwanted dependencies. + ":boca_request", ":invalidation_delegate", "//ash/constants", "//ash/public/cpp", @@ -61,6 +57,24 @@ ] } +static_library("boca_request") { + sources = [ + "boca_request.cc", + "boca_request.h", + "retriable_request_sender.h", + "util.cc", + "util.h", + ] + deps = [ + "//base", + "//chromeos/ash/components/channel", + "//google_apis/common", + "//net", + "//services/network/public/mojom", + "//url", + ] +} + static_library("invalidations") { sources = [ "invalidations/fcm_handler.cc", @@ -175,6 +189,7 @@ deps = [ ":boca", + ":boca_request", ":invalidation_delegate", ":invalidations", ":spotlight",
diff --git a/chromeos/ash/components/boca/boca_session_manager.cc b/chromeos/ash/components/boca/boca_session_manager.cc index 61e8ab01..206e124 100644 --- a/chromeos/ash/components/boca/boca_session_manager.cc +++ b/chromeos/ash/components/boca/boca_session_manager.cc
@@ -406,6 +406,15 @@ SpotlightCrdStateUpdatedCallback crd_state_callback) { CHECK(ash::features::IsBocaSpotlightRobotRequesterEnabled()); + if (!remoting_client_manager_) { + LOG(ERROR) << "[Boca] Failed to start CRD client: remoting_client_manager " + "is null."; + if (crd_state_callback) { + crd_state_callback.Run(CrdConnectionState::kFailed); + } + return; + } + remoting_client_manager_->StartCrdClient( crd_connection_code, std::move(done_callback), std::move(frame_received_callback),
diff --git a/chromeos/ash/components/boca/boca_session_manager_unittest.cc b/chromeos/ash/components/boca/boca_session_manager_unittest.cc index 2c667f6..f6597d8 100644 --- a/chromeos/ash/components/boca/boca_session_manager_unittest.cc +++ b/chromeos/ash/components/boca/boca_session_manager_unittest.cc
@@ -56,6 +56,7 @@ #include "google_apis/common/request_sender.h" #include "google_apis/gaia/gaia_id.h" #include "testing/gmock/include/gmock/gmock.h" +#include "third_party/webrtc/modules/desktop_capture/desktop_frame.h" #include "testing/gtest/include/gtest/gtest.h" using ::testing::_; @@ -2605,6 +2606,22 @@ task_environment()->FastForwardBy(kDefaultStudentHeartbeatInterval); } +TEST_F(BocaSessionManagerTest, StartCrdClientFailsWithNullManager) { + base::test::ScopedFeatureList scoped_feature_list; + scoped_feature_list.InitAndEnableFeature( + ash::features::kBocaSpotlightRobotRequester); + + base::test::TestFuture<CrdConnectionState> future; + boca_session_manager()->StartCrdClient( + "connection_code", + /*done_callback=*/base::DoNothing(), + /*frame_received_callback=*/base::DoNothing(), + /*crd_state_callback=*/base::BindLambdaForTesting( + [&](CrdConnectionState state) { future.SetValue(state); })); + + EXPECT_EQ(CrdConnectionState::kFailed, future.Get()); +} + class BocaSessionManagerConsumerTest : public BocaSessionManagerTest { protected: void SetUp() override {
diff --git a/chromeos/ash/components/boca/gemini/BUILD.gn b/chromeos/ash/components/boca/gemini/BUILD.gn new file mode 100644 index 0000000..462bfd2 --- /dev/null +++ b/chromeos/ash/components/boca/gemini/BUILD.gn
@@ -0,0 +1,35 @@ +# Copyright 2026 The Chromium Authors +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +assert(is_chromeos) + +static_library("gemini") { + sources = [ + "get_gemini_status_request.cc", + "get_gemini_status_request.h", + ] + + deps = [ + "//base", + "//chromeos/ash/components/boca:boca_request", + "//chromeos/ash/components/boca/session_api", + "//google_apis", + "//google_apis/common", + ] +} + +source_set("unit_tests") { + testonly = true + + sources = [ "get_gemini_status_request_unittest.cc" ] + + deps = [ + ":gemini", + "//base", + "//base/test:test_support", + "//chromeos/ash/components/boca:boca_request", + "//chromeos/ash/components/boca/session_api", + "//testing/gtest", + ] +}
diff --git a/chromeos/ash/components/boca/gemini/get_gemini_status_request.cc b/chromeos/ash/components/boca/gemini/get_gemini_status_request.cc new file mode 100644 index 0000000..3fb5f37 --- /dev/null +++ b/chromeos/ash/components/boca/gemini/get_gemini_status_request.cc
@@ -0,0 +1,66 @@ +// Copyright 2026 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chromeos/ash/components/boca/gemini/get_gemini_status_request.h" + +#include <memory> +#include <optional> +#include <string> +#include <utility> + +#include "base/functional/callback.h" +#include "base/strings/string_util.h" +#include "base/values.h" +#include "chromeos/ash/components/boca/session_api/constants.h" +#include "google_apis/common/api_error_codes.h" + +namespace ash::boca { + +GetGeminiStatusRequest::GetGeminiStatusRequest(std::string gaia_id, + ResponseCallback callback) + : gaia_id_(std::move(gaia_id)), callback_(std::move(callback)) {} + +GetGeminiStatusRequest::~GetGeminiStatusRequest() = default; + +std::string GetGeminiStatusRequest::GetRelativeUrl() { + return base::ReplaceStringPlaceholders(kGetGeminiStatusUrlTemplate, + {gaia_id_}, nullptr); +} + +std::optional<std::string> GetGeminiStatusRequest::GetRequestBody() { + return std::nullopt; +} + +void GetGeminiStatusRequest::OnSuccess(std::unique_ptr<base::Value> response) { + CHECK(callback_); + if (!response) { + std::move(callback_).Run(std::nullopt); + return; + } + + const base::DictValue* response_dict = response->GetIfDict(); + if (!response_dict) { + std::move(callback_).Run(std::nullopt); + return; + } + + const auto* status_ptr = response_dict->FindString(kGeminiStatus); + if (status_ptr && + (*status_ptr == kGeminiEnabled || *status_ptr == kGeminiDisabled)) { + std::move(callback_).Run(*status_ptr == kGeminiEnabled); + return; + } + std::move(callback_).Run(std::nullopt); +} + +void GetGeminiStatusRequest::OnError(google_apis::ApiErrorCode error) { + CHECK(callback_); + std::move(callback_).Run(std::nullopt); +} + +google_apis::HttpRequestMethod GetGeminiStatusRequest::GetRequestType() const { + return google_apis::HttpRequestMethod::kGet; +} + +} // namespace ash::boca
diff --git a/chromeos/ash/components/boca/gemini/get_gemini_status_request.h b/chromeos/ash/components/boca/gemini/get_gemini_status_request.h new file mode 100644 index 0000000..115f70e2 --- /dev/null +++ b/chromeos/ash/components/boca/gemini/get_gemini_status_request.h
@@ -0,0 +1,70 @@ +// Copyright 2026 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CHROMEOS_ASH_COMPONENTS_BOCA_GEMINI_GET_GEMINI_STATUS_REQUEST_H_ +#define CHROMEOS_ASH_COMPONENTS_BOCA_GEMINI_GET_GEMINI_STATUS_REQUEST_H_ + +#include <memory> +#include <optional> +#include <string> + +#include "base/functional/callback_forward.h" +#include "chromeos/ash/components/boca/boca_request.h" +#include "net/traffic_annotation/network_traffic_annotation.h" + +namespace base { +class Value; +} // namespace base + +namespace ash::boca { + +class GetGeminiStatusRequest : public BocaRequest::Delegate { + public: + using ResponseCallback = base::OnceCallback<void(std::optional<bool>)>; + + static constexpr net::NetworkTrafficAnnotationTag kTrafficAnnotation = + net::DefineNetworkTrafficAnnotation( + "ash_boca_gemini_get_gemini_status_request", + R"( + semantics { + sender: "School Tools" + description: "Get the Gemini status for the given user." + trigger: "User opens School Tools app." + data: "Gaia ID to fetch status for." + destination: GOOGLE_OWNED_SERVICE + internal { + contacts { + email: "cros-edu-eng@google.com" + } + } + last_reviewed: "2026-05-06" + } + policy { + cookies_allowed: NO + setting: "This request cannot be stopped in settings." + policy_exception_justification: "Not implemented." + })"); + + GetGeminiStatusRequest(std::string gaia_id, ResponseCallback callback); + + GetGeminiStatusRequest(const GetGeminiStatusRequest&) = delete; + GetGeminiStatusRequest& operator=(const GetGeminiStatusRequest&) = delete; + + ~GetGeminiStatusRequest() override; + + // BocaRequest::Delegate: + std::string GetRelativeUrl() override; + std::optional<std::string> GetRequestBody() override; + void OnSuccess(std::unique_ptr<base::Value> response) override; + void OnError(google_apis::ApiErrorCode error) override; + google_apis::HttpRequestMethod GetRequestType() const override; + + private: + std::string gaia_id_; + ResponseCallback callback_; +}; + +} // namespace ash::boca + +#endif // CHROMEOS_ASH_COMPONENTS_BOCA_GEMINI_GET_GEMINI_STATUS_REQUEST_H_
diff --git a/chromeos/ash/components/boca/gemini/get_gemini_status_request_unittest.cc b/chromeos/ash/components/boca/gemini/get_gemini_status_request_unittest.cc new file mode 100644 index 0000000..5b8d120 --- /dev/null +++ b/chromeos/ash/components/boca/gemini/get_gemini_status_request_unittest.cc
@@ -0,0 +1,127 @@ +// Copyright 2026 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chromeos/ash/components/boca/gemini/get_gemini_status_request.h" + +#include <memory> +#include <optional> +#include <utility> + +#include "base/test/bind.h" +#include "base/values.h" +#include "chromeos/ash/components/boca/session_api/constants.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace ash::boca { +namespace { + +constexpr char kTestGaiaId[] = "12345"; + +TEST(GetGeminiStatusRequestTest, GetRelativeUrl) { + GetGeminiStatusRequest request(kTestGaiaId, base::DoNothing()); + EXPECT_EQ(request.GetRelativeUrl(), "v1/users/12345:getGeminiStatus"); +} + +TEST(GetGeminiStatusRequestTest, GetRequestType) { + GetGeminiStatusRequest request(kTestGaiaId, base::DoNothing()); + EXPECT_EQ(request.GetRequestType(), google_apis::HttpRequestMethod::kGet); +} + +TEST(GetGeminiStatusRequestTest, GetRequestBody) { + GetGeminiStatusRequest request(kTestGaiaId, base::DoNothing()); + EXPECT_EQ(request.GetRequestBody(), std::nullopt); +} + +TEST(GetGeminiStatusRequestTest, OnSuccessEnabled) { + std::optional<bool> callback_result; + GetGeminiStatusRequest request( + kTestGaiaId, base::BindLambdaForTesting( + [&callback_result](std::optional<bool> result) { + callback_result = result; + })); + + base::DictValue response_dict; + response_dict.Set(kGeminiStatus, kGeminiEnabled); + request.OnSuccess(std::make_unique<base::Value>(std::move(response_dict))); + + ASSERT_TRUE(callback_result.has_value()); + EXPECT_TRUE(callback_result.value()); +} + +TEST(GetGeminiStatusRequestTest, OnSuccessDisabled) { + std::optional<bool> callback_result; + GetGeminiStatusRequest request( + kTestGaiaId, base::BindLambdaForTesting( + [&callback_result](std::optional<bool> result) { + callback_result = result; + })); + + base::DictValue response_dict; + response_dict.Set(kGeminiStatus, kGeminiDisabled); + request.OnSuccess(std::make_unique<base::Value>(std::move(response_dict))); + + ASSERT_TRUE(callback_result.has_value()); + EXPECT_FALSE(callback_result.value()); +} + +TEST(GetGeminiStatusRequestTest, OnSuccessInvalidValue) { + std::optional<bool> callback_result = true; + GetGeminiStatusRequest request( + kTestGaiaId, base::BindLambdaForTesting( + [&callback_result](std::optional<bool> result) { + callback_result = result; + })); + + base::DictValue response_dict; + response_dict.Set(kGeminiStatus, "INVALID_STATUS_VALUE"); + request.OnSuccess(std::make_unique<base::Value>(std::move(response_dict))); + + EXPECT_FALSE(callback_result.has_value()); +} + +TEST(GetGeminiStatusRequestTest, OnSuccessMissingStatusKey) { + std::optional<bool> callback_result = true; + GetGeminiStatusRequest request( + kTestGaiaId, base::BindLambdaForTesting( + [&callback_result](std::optional<bool> result) { + callback_result = result; + })); + + base::DictValue response_dict; + request.OnSuccess(std::make_unique<base::Value>(std::move(response_dict))); + + EXPECT_FALSE(callback_result.has_value()); +} + +TEST(GetGeminiStatusRequestTest, OnSuccessInvalidResponseStructure) { + std::optional<bool> callback_result = true; + GetGeminiStatusRequest request( + kTestGaiaId, base::BindLambdaForTesting( + [&callback_result](std::optional<bool> result) { + callback_result = result; + })); + + request.OnSuccess(std::make_unique<base::Value>("not_a_dictionary")); + + EXPECT_FALSE(callback_result.has_value()); +} + +TEST(GetGeminiStatusRequestTest, OnError) { + bool called = false; + std::optional<bool> callback_result = true; + GetGeminiStatusRequest request( + kTestGaiaId, base::BindLambdaForTesting( + [&callback_result, &called](std::optional<bool> result) { + called = true; + callback_result = result; + })); + + request.OnError(google_apis::ApiErrorCode::HTTP_NOT_FOUND); + + EXPECT_TRUE(called); + EXPECT_FALSE(callback_result.has_value()); +} + +} // namespace +} // namespace ash::boca
diff --git a/chromeos/ash/components/boca/receiver/BUILD.gn b/chromeos/ash/components/boca/receiver/BUILD.gn index 2a18e925..4907d621 100644 --- a/chromeos/ash/components/boca/receiver/BUILD.gn +++ b/chromeos/ash/components/boca/receiver/BUILD.gn
@@ -29,6 +29,8 @@ "update_kiosk_receiver_state_request.h", ] + public_deps = [ "//chromeos/ash/components/boca:boca_request" ] + deps = [ ":util", "//ash/constants", @@ -81,6 +83,7 @@ "//base", "//base/test:test_support", "//chromeos/ash/components/boca", + "//chromeos/ash/components/boca:boca_request", "//chromeos/ash/components/boca/proto", "//chromeos/ash/components/boca/session_api", "//components/signin/public/identity_manager",
diff --git a/chromeos/ash/components/boca/session_api/constants.h b/chromeos/ash/components/boca/session_api/constants.h index 75f7fff3..1aab816 100644 --- a/chromeos/ash/components/boca/session_api/constants.h +++ b/chromeos/ash/components/boca/session_api/constants.h
@@ -22,6 +22,9 @@ inline constexpr char kGetSessionUrlTemplate[] = "v1/users/$1/sessions:getActive?device.device_id=$2"; +inline constexpr char kGetGeminiStatusUrlTemplate[] = + "v1/users/$1:getGeminiStatus"; + inline constexpr char kUploadFCMTokenTemplate[] = "v1/users/$1"; inline constexpr char kUpdateSessionUrlTemplate[] = @@ -96,6 +99,9 @@ inline constexpr char kStudentStatusState[] = "state"; inline constexpr char kDeviceStatusState[] = "state"; inline constexpr char kReceiverConnectionState[] = "receiverConnectionState"; +inline constexpr char kGeminiStatus[] = "status"; +inline constexpr char kGeminiEnabled[] = "ENABLEMENT_STATUS_ENABLED"; +inline constexpr char kGeminiDisabled[] = "ENABLEMENT_STATUS_DISABLED"; inline constexpr char kUrl[] = "url"; inline constexpr char kTitle[] = "title"; inline constexpr char kFavIcon[] = "faviconUrl";
diff --git a/chromeos/crosapi/mojom/BUILD.gn b/chromeos/crosapi/mojom/BUILD.gn index c765ecb..019b4193 100644 --- a/chromeos/crosapi/mojom/BUILD.gn +++ b/chromeos/crosapi/mojom/BUILD.gn
@@ -9,7 +9,6 @@ sources = [ "account_manager.mojom", "crosapi.mojom", - "document_scan.mojom", "local_printer.mojom", "nullable_primitives.mojom", "probe_service.mojom",
diff --git a/chromeos/crosapi/mojom/crosapi.mojom b/chromeos/crosapi/mojom/crosapi.mojom index 9b94ad71..b0dc64e 100644 --- a/chromeos/crosapi/mojom/crosapi.mojom +++ b/chromeos/crosapi/mojom/crosapi.mojom
@@ -8,7 +8,6 @@ import "chromeos/components/payments/mojom/payment_app.mojom"; import "chromeos/components/sensors/mojom/cros_sensor_service.mojom"; import "chromeos/crosapi/mojom/account_manager.mojom"; -import "chromeos/crosapi/mojom/document_scan.mojom"; import "chromeos/crosapi/mojom/local_printer.mojom"; import "chromeos/crosapi/mojom/probe_service.mojom"; import "chromeos/crosapi/mojom/telemetry_diagnostic_routine_service.mojom"; @@ -83,12 +82,7 @@ // BindDiagnosticsService@99 was removed. // BindDigitalGoodsFactory@79 was removed. // BindDlp@64 was removed. - - // Binds the DocumentScan interface, which allows Lacros to get scanner access - // via Ash Chrome. - // Added in M104. - [MinVersion=77] BindDocumentScan@80(pending_receiver<DocumentScan> receiver); - + // BindDocumentScan@80 was removed. // BindDownloadController@33 was removed. // BindDriveIntegrationService@34 was removed. // BindEchoPrivate@74 was removed.
diff --git a/chromeos/crosapi/mojom/document_scan.mojom b/chromeos/crosapi/mojom/document_scan.mojom deleted file mode 100644 index 9ad0d44..0000000 --- a/chromeos/crosapi/mojom/document_scan.mojom +++ /dev/null
@@ -1,421 +0,0 @@ -// Copyright 2022 The Chromium Authors -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -module crosapi.mojom; - -// The enum below is exactly the same as its counterpart in -// third_party/cros_system_api/dbus/lorgnette/lorgnette_service.proto - -// The failure mode of a scan job. -[Extensible] -enum ScanFailureMode { - // No failure occurred. - [Default] kNoFailure = 0, - - // An unknown or generic failure occurred. - kUnknown = 1, - - // The device is busy. - kDeviceBusy = 2, - - // The document feeder is jammed. - kAdfJammed = 3, - - // The document feeder is empty. - kAdfEmpty = 4, - - // The flatbed cover is open. - kFlatbedOpen = 5, - - // An error occurred while communicating with the device. - kIoError = 6, -}; - -// The enum below is exactly the same as its counterpart in -// third_party/cros_system_api/dbus/lorgnette/lorgnette_service.proto - -// Indicates the result of each operation performed by the backend. It -// contains the same causes as SANE_Status plus additional statuses that come -// from the IPC layers and image conversion stages. -[Extensible] -enum ScannerOperationResult { - // An unknown or generic failure occurred. - [Default] kUnknown = 0, - - // Operation succeeded. - kSuccess = 1, - - // The operation is not supported. - kUnsupported = 2, - - // The operation was cancelled. - kCancelled = 3, - - // The scanner is busy. - kDeviceBusy = 4, - - // Data or argument is invalid. - kInvalid = 5, - - // Value is the wrong type for the underlying option. - kWrongType = 6, - - // No more data is available. - kEndOfData = 7, - - // The document feeder is jammed. - kAdfJammed = 8, - - // The document feeder is empty. - kAdfEmpty = 9, - - // The flatbed cover is open. - kCoverOpen = 10, - - // An error occurred while communicating with the scanner. - kIoError = 11, - - // The scanner requires authentication. - kAccessDenied = 12, - - // Not enough memory was available to complete the operation. - kNoMemory = 13, - - // The scanner was not reachable. - kDeviceUnreachable = 14, - - // The requested handle was not found. - kDeviceMissing = 15, - - // An internal error occurred. - kInternalError = 16, -}; - -// Information about a scanner that was returned from `GetScannerList()`. -struct ScannerInfo { - // Connection string that can be used with `OpenScanner()`. - string id@0; - - // Printable name for displaying in the UI. - string display_name@1; - - // Scanner manufacturer. - string manufacturer@2; - - // Scanner model, if known, or a generic description. - string model@3; - - // For matching against other `ScannerInfo` entries that point to the same - // physical scanner, e.g. multiple SANE backends or TLS and non-TLS - // connections to the same device. - string device_uuid@4; - - // How the scanner is connected to the computer. - ConnectionType connection_type@5; - - // True if the connection is secure against passive eavesdropping, e.g. USB - // or TLS. This is not an indicator of integrity of the scanned data - // itself. - bool secure@6; - - // MIME types that can be requested for returned scans. - array<string> image_formats@7; - - // The enum below is exactly the same as its counterpart in - // third_party/cros_system_api/dbus/lorgnette/lorgnette_service.proto - [Extensible] - enum ConnectionType { - // Scanner has an unknown connection type. - [Default] kUnspecified = 0, - - // Scanner is connected via USB. - kUsb = 1, - - // Scanner is connected via WiFi or ethernet. - kNetwork = 2, - }; - - // A general description of protocol/backend used to access the scanner, such - // as Mopria, WSD, or Epson2. This is primarily useful for preferring one - // protocol if a device is usable with multiple protocols. - [MinVersion=1] - string? protocol_type@8; -}; - -// A set of criteria that can be passed to `GetScannerList()`. Only -// devices that match all of the criteria will be returned. -struct ScannerEnumFilter { - // Return only directly connected devices. - bool local@0; - - // Return only devices that use a secure transport. - bool secure@1; -}; - -// The enum below is exactly the same as its counterpart in -// third_party/cros_system_api/dbus/lorgnette/lorgnette_service.proto -// -// The type of an option. This is the same set of types as SANE_Value_Type. -[Extensible] -enum OptionType { - [Default] kUnknown = 0, - kBool = 1, - kInt = 2, - kFixed = 3, - kString = 4, - kButton = 5, - kGroup = 6, -}; - -// The enum below is exactly the same as its counterpart in -// third_party/cros_system_api/dbus/lorgnette/lorgnette_service.proto -// -// The unit of measurement for an option. This is the same set of units as -// SANE_Unit. -[Extensible] -enum OptionUnit { - // Value is a unitless number, e.g., threshold. - [Default] kUnitless = 0, - - // Value is a number of pixels, e.g., scan dimensions. - kPixel = 1, - - // Value is the number of bits, e.g., color depth. - kBit = 2, - - // Value is in mm, e.g., scan dimensions. - kMm = 3, - - // Value is in dots per inch, e.g., resolution. - kDpi = 4, - - // Value is a percent, e.g., brightness. - kPercent = 5, - - // Value is in µs, e.g. exposure time. - kMicrosecond = 6, -}; - -// The value of a `ScannerOption`. The active field must match the -// `OptionType` of the option. -union OptionValue { - bool bool_value; - int32 int_value; - array<int32> int_list; - double fixed_value; - array<double> fixed_list; - string string_value; -}; - -// The enum below is exactly the same as its counterpart in -// third_party/cros_system_api/dbus/lorgnette/lorgnette_service.proto -// -// The type of a constraint. These correspond to SANE_Constraint_Type. -[Extensible] -enum OptionConstraintType { - [Default] kNone = 0, - kIntRange = 1, - kFixedRange = 2, - kIntList = 3, - kFixedList = 4, - kStringList = 5, -}; - -// A range of valid values for an option of type `OptionType.kInt`. -struct IntRange { - int32 min@0; - int32 max@1; - int32 quant@2; -}; - -// A range of valid values for an option of type `OptionType.kFixed`. -struct FixedRange { - double min@0; - double max@1; - double quant@2; -}; - -// The specific restriction imposed by an `OptionConstraint`. Each type of -// restriction corresponds to a value of `OptionConstraintType`. -union OptionConstraintRestriction { - IntRange int_range; - FixedRange fixed_range; - array<int32> valid_int; - array<double> valid_fixed; - array<string> valid_string; -}; - -// A constraint on the allowed values of a `ScannerOption`. The `type` field -// indicates which union member of `constraint` is expected to be set. -struct OptionConstraint { - OptionConstraintType type@0; - OptionConstraintRestriction restriction@1; -}; - -// How an option can be configured. An option can be configured through -// software, through hardware, or neither (read-only). The SANE model does not -// permit an option to be configured in both hardware and software. -[Extensible] -enum OptionConfigurability { - // Option is read-only and cannot be changed. - kNotConfigurable = 0, - - // Option can be set in software. - [Default] kSoftwareConfigurable = 1, - - // Option can be set by the user toggling/pushing a hardware button. - kHardwareConfigurable = 2, -}; - -// A self-describing scanner option, in the style of SANE_Option_Descriptor. A -// `ScannerOption` represents an individual option with display information, an -// optional current value, and an optional constraint on valid values. -struct ScannerOption { - // Option name using lowercase a-z, numbers, and dashes. - string name@0; - - // Printable one-line title. - string title@1; - - // Longer description of the option. May contain embedded newlines. - string description@2; - - // Lorgnette type for the option. - OptionType type@3; - - // Unit of the option's value. - OptionUnit unit@4; - - // Current value of the option if relevant. - OptionValue? value@5; - - // Constrain on possible option values. - OptionConstraint? constraint@6; - - // Option can be detected in software. - bool isDetectable@7; - - // How the option value can be changed. - OptionConfigurability configurability@8; - - // Can be automatically set by the backend. - bool isAutoSettable@9; - - // Emulated by the backend instead of implemented in the scanner. - bool isEmulated@10; - - // Option is active and can be set/retrieved. If false, the `value` field - // will not be set. - bool isActive@11; - - // UI should not display this option by default. - bool isAdvanced@12; - - // Option is used for internal configuration and should never be displayed - // in the UI. - bool isInternal@13; -}; - -// The result of combining lorgnette's d-bus response with Chrome's mDNS -// discovery for `GetScannerList()`. -struct GetScannerListResponse { - // The backend's enumeration result. - ScannerOperationResult result@0; - - // A list of scanners that match the criteria supplied to `GetScannerList()`. - array<ScannerInfo> scanners@1; -}; - -// The response from calling `OpenScanner()`. -struct OpenScannerResponse { - // Same scanner_id value passed to `OpenScanner()`. - string scanner_id@0; - - // Backend result of opening the scanner. - ScannerOperationResult result@1; - - // If result is ScannerOperationResult.kSuccess, an exclusive handle to the - // scanner that can be used for further operations. This handle remains - // valid until `CloseScanner()` is called, `OpenScanner()` is called again by - // the same client with the same `scanner_id`, or a hardware change causes - // the scanner to become inaccessible. - string? scanner_handle@2; - - // If result is ScannerOperationResult.kSuccess, a mapping from option names - // to `ScannerOption`. - map<string, ScannerOption>? options@3; -}; - -// Used to set an option from `ScannerOption`. -struct OptionSetting { - // The name of the option. - string name@0; - - // The type of the option. - OptionType type@1; - - // Value to set. Leave unset to request automatic setting for options that - // support it. - OptionValue? value@2; -}; - -// A group containing a list of option names. -struct OptionGroup { - // Printable title, e.g. "Geometry options". - string title@0; - - // Names of contained options. - array<string> members@1; -}; - - -// The result from setting an option. -struct SetOptionResult { - // The name of the option that was set. - string name@0; - - // The backend's result from setting that option. - ScannerOperationResult result@1; -}; - -// The response from calling `SetOptions()`. -struct SetOptionsResponse { - // The same scanner handle that was passed to `SetOptions()`. - string scanner_handle@0; - - // One result for each passed-in `OptionSetting`. - array<SetOptionResult> results@1; - - // An updated mapping from option name to option value. - map<string, ScannerOption>? options@2; -}; - -// The response from calling `GetOptionGroups`. -struct GetOptionGroupsResponse { - // The same scanner handle that was passed to `GetOptionGroups()`. - string scanner_handle@0; - - // The backend's get option groups result. - ScannerOperationResult result@1; - - // If result is kSuccess, a list of the option groups. - array<OptionGroup>? groups@2; -}; - -// This interface is used to access scanners. -// Next version: 6 -// Next method id: 10 -[Uuid="f0c77c02-b5c1-4919-8218-3076ecad58db"] -interface DocumentScan { - // Deprecated@1 (crbug.com/317056194) - // GetScannerNames@0 was removed. - // GetScannerList@2 was removed. - // CancelScan@9 was removed. - // GetOptionGroups@8 was removed. - // CloseScanner@4 was removed. - // ReadScanData@6 was removed. - // StartPreparedScan@5 was removed. - // OpenScanner@3 was removed. - // SetOptions@7 was removed. -};
diff --git a/chromeos/printing/ppd_provider_unittest.cc b/chromeos/printing/ppd_provider_unittest.cc index a26b5bd..8b1a99df 100644 --- a/chromeos/printing/ppd_provider_unittest.cc +++ b/chromeos/printing/ppd_provider_unittest.cc
@@ -125,8 +125,7 @@ // Creates and return a provider for a test that uses the given |options|. scoped_refptr<PpdProvider> CreateProvider( - PpdCacheRunLocation where_ppd_cache_runs, - std::string_view version = "40.8.6753.09") { + PpdCacheRunLocation where_ppd_cache_runs) { switch (where_ppd_cache_runs) { case PpdCacheRunLocation::kOnTestThread: ppd_cache_ = PpdCache::CreateForTesting( @@ -152,7 +151,7 @@ auto remote_ppd_fetcher = std::make_unique<MockRemotePpdFetcher>(); provider_backdoor_.remote_ppd_fetcher = remote_ppd_fetcher.get(); - return PpdProvider::Create(base::Version(version), ppd_cache_, + return PpdProvider::Create(base::Version("40.8.6753.09"), ppd_cache_, std::move(manager), std::move(config_cache), std::move(remote_ppd_fetcher)); } @@ -277,8 +276,7 @@ R"({ "printers": [ { "name": "printer_a", - "emm": "printer_a_ref", - "restriction": { "minMilestone": 30 } + "emm": "printer_a_ref" }, { "name": "printer_b", "emm": "printer_b_ref" @@ -297,11 +295,7 @@ "printer_a_ref": { "ppdMetadata": [ { "name": "printer_a.ppd", - "license": "fake_license", - "restriction": { "minMilestone": 30, "maxMilestone": 251 } - }, { - "name": "printer_e.ppd", - "restriction": { "minMilestone": 251 } + "license": "fake_license" } ] } } @@ -1219,149 +1213,5 @@ "PPD_TOO_LARGE"); } -// Test that all entrypoints will work correctly when the matching printer does -// not meet restrictions. -TEST_F(PpdProviderTest, VersionUnderMinMilestone) { - auto provider = - CreateProvider(PpdCacheRunLocation::kInBackgroundThreads, "29.99.99.99"); - StartFakePpdServer(); - - // Required setup calls to advance past PpdProvider's method deferral. - ASSERT_TRUE(provider_backdoor_.metadata_manager->SetManufacturersForTesting( - kDefaultManufacturersJson)); - provider->ResolvePrinters( - "Manufacturer A", base::BindOnce(&PpdProviderTest::CaptureResolvePrinters, - base::Unretained(this))); - - std::string ref = "pRiNteR_A_reF"; - - Printer::PpdReference ppd_ref; - ppd_ref.effective_make_and_model = ref; - provider->ResolvePpd(ppd_ref, - base::BindOnce(&PpdProviderTest::CaptureResolvePpd, - base::Unretained(this))); - provider->ReverseLookup(ref, - base::BindOnce(&PpdProviderTest::CaptureReverseLookup, - base::Unretained(this))); - PrinterSearchData printer_info; - printer_info.make_and_model = {ref}; - provider->ResolvePpdReference( - printer_info, base::BindOnce(&PpdProviderTest::CaptureResolvePpdReference, - base::Unretained(this))); - provider->ResolvePpdLicense( - ref, base::BindOnce(&PpdProviderTest::CaptureResolvePpdLicense, - base::Unretained(this))); - task_environment_.RunUntilIdle(); - - // Check PpdProvider::ResolvePrinters - ASSERT_EQ(1UL, captured_resolve_printers_.size()); - EXPECT_EQ(PpdProvider::SUCCESS, captured_resolve_printers_[0].first); - EXPECT_EQ(1UL, captured_resolve_printers_[0].second.size()); - EXPECT_EQ("printer_b", captured_resolve_printers_[0].second[0].name); - - // Check PpdProvider::ResolvePpd - ASSERT_EQ(1UL, captured_resolve_ppd_.size()); - EXPECT_EQ(PpdProvider::NOT_FOUND, captured_resolve_ppd_[0].code); - - // Check PpdProvider::ReverseLookup - ASSERT_EQ(1UL, captured_reverse_lookup_.size()); - // TODO(pawliczek) - this should be NOT_FOUND, but it returns SUCCESS. - // EXPECT_EQ(PpdProvider::NOT_FOUND, captured_reverse_lookup_[0].code); - - // Check PpdProvider::ResolvePpdReference - ASSERT_EQ(1UL, captured_resolve_ppd_references_.size()); - EXPECT_EQ(PpdProvider::NOT_FOUND, captured_resolve_ppd_references_[0].code); - - // Check PpdProvider::ResolvePpdLicense - ASSERT_EQ(1UL, captured_resolve_ppd_license_.size()); - EXPECT_EQ(PpdProvider::NOT_FOUND, captured_resolve_ppd_license_[0].code); -} - -// Test that all entrypoints will choose a PPD metadata record that matches the -// version of the client. -TEST_F(PpdProviderTest, VersionMatchingTheSecondPpdMetadata) { - auto provider = - CreateProvider(PpdCacheRunLocation::kInBackgroundThreads, "251.0.1234.0"); - StartFakePpdServer(); - - // Required setup calls to advance past PpdProvider's method deferral. - ASSERT_TRUE(provider_backdoor_.metadata_manager->SetManufacturersForTesting( - kDefaultManufacturersJson)); - provider->ResolvePrinters( - "Manufacturer A", base::BindOnce(&PpdProviderTest::CaptureResolvePrinters, - base::Unretained(this))); - - std::string ref = "pRiNteR_A_reF"; - - Printer::PpdReference ppd_ref; - ppd_ref.effective_make_and_model = ref; - provider->ResolvePpd(ppd_ref, - base::BindOnce(&PpdProviderTest::CaptureResolvePpd, - base::Unretained(this))); - provider->ReverseLookup(ref, - base::BindOnce(&PpdProviderTest::CaptureReverseLookup, - base::Unretained(this))); - PrinterSearchData printer_info; - printer_info.make_and_model = {ref}; - provider->ResolvePpdReference( - printer_info, base::BindOnce(&PpdProviderTest::CaptureResolvePpdReference, - base::Unretained(this))); - provider->ResolvePpdLicense( - ref, base::BindOnce(&PpdProviderTest::CaptureResolvePpdLicense, - base::Unretained(this))); - task_environment_.RunUntilIdle(); - - // Check PpdProvider::ResolvePrinters - ASSERT_EQ(1UL, captured_resolve_printers_.size()); - EXPECT_EQ(PpdProvider::SUCCESS, captured_resolve_printers_[0].first); - EXPECT_EQ(2UL, captured_resolve_printers_[0].second.size()); - EXPECT_EQ("printer_a", captured_resolve_printers_[0].second[0].name); - EXPECT_EQ("printer_b", captured_resolve_printers_[0].second[1].name); - - // Check PpdProvider::ResolvePpd - ASSERT_EQ(1UL, captured_resolve_ppd_.size()); - EXPECT_EQ(PpdProvider::SUCCESS, captured_resolve_ppd_[0].code); - EXPECT_EQ("e", captured_resolve_ppd_[0].ppd_contents); - EXPECT_EQ("printer_e.ppd", captured_resolve_ppd_[0].ppd_name); - - // Check PpdProvider::ReverseLookup - ASSERT_EQ(1UL, captured_reverse_lookup_.size()); - EXPECT_EQ(PpdProvider::SUCCESS, captured_reverse_lookup_[0].code); - EXPECT_EQ("manufacturer_a_en", captured_reverse_lookup_[0].manufacturer); - EXPECT_EQ("printer_a", captured_reverse_lookup_[0].model); - - // Check PpdProvider::ResolvePpdReference - ASSERT_EQ(1UL, captured_resolve_ppd_references_.size()); - EXPECT_EQ(PpdProvider::SUCCESS, captured_resolve_ppd_references_[0].code); - EXPECT_EQ("printer_a_ref", - captured_resolve_ppd_references_[0].ref.effective_make_and_model); - - // Check PpdProvider::ResolvePpdLicense - ASSERT_EQ(1UL, captured_resolve_ppd_license_.size()); - // TODO(pawliczek) - this should be NOT_FOUND, but it returns SUCCESS. - // EXPECT_EQ(PpdProvider::NOT_FOUND, captured_resolve_ppd_license_[0].code); -} - -// Test that maxMilestone restriction works correctly. -TEST_F(PpdProviderTest, VersionEqualsMaxMilestone) { - auto provider = CreateProvider(PpdCacheRunLocation::kInBackgroundThreads, - "250.99.9999.99"); - StartFakePpdServer(); - std::string ref = "pRiNteR_A_reF"; - - Printer::PpdReference ppd_ref; - ppd_ref.effective_make_and_model = ref; - provider->ResolvePpd(ppd_ref, - base::BindOnce(&PpdProviderTest::CaptureResolvePpd, - base::Unretained(this))); - task_environment_.RunUntilIdle(); - - // Check PpdProvider::ResolvePpd - ASSERT_EQ(1UL, captured_resolve_ppd_.size()); - EXPECT_EQ(PpdProvider::SUCCESS, captured_resolve_ppd_[0].code); - EXPECT_EQ(kCupsFilterPpdContents, captured_resolve_ppd_[0].ppd_contents); - EXPECT_EQ("printer_a.ppd", captured_resolve_ppd_[0].ppd_name); -} - } // namespace } // namespace chromeos
diff --git a/chromeos/profiles/arm.afdo.newest.txt b/chromeos/profiles/arm.afdo.newest.txt index 3adab84e..1ecfe23d 100644 --- a/chromeos/profiles/arm.afdo.newest.txt +++ b/chromeos/profiles/arm.afdo.newest.txt
@@ -1 +1 @@ -chromeos-chrome-arm-none-149-7806.0-1777860270-benchmark-150.0.7828.0_pre1625187-r1-redacted.afdo.xz +chromeos-chrome-arm-none-149-7806.0-1777860270-benchmark-150.0.7830.0_pre1626253-r1-redacted.afdo.xz
diff --git a/chromeos/profiles/atom.afdo.newest.txt b/chromeos/profiles/atom.afdo.newest.txt index 9c01947..494da67 100644 --- a/chromeos/profiles/atom.afdo.newest.txt +++ b/chromeos/profiles/atom.afdo.newest.txt
@@ -1 +1 @@ -chromeos-chrome-amd64-atom-149-7806.0-1777860206-benchmark-150.0.7828.0_pre1625187-r1-redacted.afdo.xz +chromeos-chrome-amd64-atom-149-7806.0-1777860206-benchmark-150.0.7830.0_pre1626253-r1-redacted.afdo.xz
diff --git a/chromeos/ui/frame/multitask_menu/multitask_menu.cc b/chromeos/ui/frame/multitask_menu/multitask_menu.cc index 2610a0c..d89240b1 100644 --- a/chromeos/ui/frame/multitask_menu/multitask_menu.cc +++ b/chromeos/ui/frame/multitask_menu/multitask_menu.cc
@@ -40,6 +40,7 @@ set_internal_name("MultitaskMenuBubbleWidget"); set_margins(gfx::Insets()); set_parent_window(parent_widget->GetNativeWindow()); + SetBackgroundColor(ui::kColorSysSurface3); SetAnchorView(anchor); SetArrow(views::BubbleBorder::Arrow::TOP_CENTER); SetEnableArrowKeyTraversal(true);
diff --git a/chromeos/ui/frame/multitask_menu/multitask_menu_view.cc b/chromeos/ui/frame/multitask_menu/multitask_menu_view.cc index b229bf61..dbdad37 100644 --- a/chromeos/ui/frame/multitask_menu/multitask_menu_view.cc +++ b/chromeos/ui/frame/multitask_menu/multitask_menu_view.cc
@@ -192,7 +192,7 @@ DCHECK(window); DCHECK(close_callback_); DCHECK(dismiss_callback_); - SetBackground(views::CreateSolidBackground(ui::kColorSysSurface3)); + SetUseDefaultFillLayout(true); window_observation_.Observe(window);
diff --git a/clank b/clank index 2631570..60a8da0 160000 --- a/clank +++ b/clank
@@ -1 +1 @@ -Subproject commit 26315709f46b62ef07ed9a32c27d6051823c3137 +Subproject commit 60a8da0b6e82b9642ebfd2164e28b063229e9967
diff --git a/clusterfuzz-data b/clusterfuzz-data index 922b2f1..7080c2e9 160000 --- a/clusterfuzz-data +++ b/clusterfuzz-data
@@ -1 +1 @@ -Subproject commit 922b2f13ce9a0192bb7354417de8a2967abbacf0 +Subproject commit 7080c2e947de8422346d5593ea52f87e6b28b13d
diff --git a/components/accessibility_annotator/core/resources/internal b/components/accessibility_annotator/core/resources/internal index 907d2755..93e2b65 160000 --- a/components/accessibility_annotator/core/resources/internal +++ b/components/accessibility_annotator/core/resources/internal
@@ -1 +1 @@ -Subproject commit 907d2755f984daebc630d424b32e4fda1b70b2f3 +Subproject commit 93e2b65b210aecb1fa9421c52289b5194dc43e6e
diff --git a/components/accessibility_annotator/core/resources/strings/accessibility_annotator_strings.grdp b/components/accessibility_annotator/core/resources/strings/accessibility_annotator_strings.grdp index 58f7a01d..029aa34d 100644 --- a/components/accessibility_annotator/core/resources/strings/accessibility_annotator_strings.grdp +++ b/components/accessibility_annotator/core/resources/strings/accessibility_annotator_strings.grdp
@@ -16,20 +16,17 @@ <message name="IDS_ACCESSIBILITY_ANNOTATOR_INFO_CARD_2_ANDROID" desc="" formatter_data="android_java" translateable="false"> Lorem ipsum dolor sit amet. </message> - <message name="IDS_ACCESSIBILITY_ANNOTATOR_INFO_LEARN_MORE_ANDROID" desc="" formatter_data="android_java" translateable="false"> - Lorem ipsum dolor <ph name="BEGIN_LINK"><link></ph>sit amet<ph name="END_LINK"></link></ph>. - </message> <message name="IDS_ACCESSIBILITY_ANNOTATOR_INFO_DESCRIPTION_DESKTOP" desc="" translateable="false"> Lorem ipsum dolor sit amet. </message> <message name="IDS_ACCESSIBILITY_ANNOTATOR_INFO_CARD_1_DESKTOP" desc="" translateable="false"> - Lorem <ph name="TRIGGER_TEXT">$1<ex>@@</ex></ph> ipsum dolor sit amet. + Lorem ipsum dolor sit amet. </message> <message name="IDS_ACCESSIBILITY_ANNOTATOR_INFO_CARD_2_DESKTOP" desc="" translateable="false"> Lorem ipsum dolor sit amet. </message> - <message name="IDS_ACCESSIBILITY_ANNOTATOR_INFO_LEARN_MORE_DESKTOP" desc="" translateable="false"> - Lorem ipsum dolor <ph name="BEGIN_LINK"><a href="#"></ph>sit amet<ph name="END_LINK"></a></ph>. + <message name="IDS_ACCESSIBILITY_ANNOTATOR_INFO_LEARN_MORE" desc="" formatter_data="android_java" translateable="false"> + Lorem ipsum dolor <ph name="BEGIN_LINK"><link></ph>sit amet<ph name="END_LINK"></link></ph>. </message> <message name="IDS_ACCESSIBILITY_ANNOTATOR_INFO_PRIMARY_BUTTON" desc="" formatter_data="android_java" translateable="false"> Lorem
diff --git a/components/accessibility_annotator/core/storage/content_annotations_table.cc b/components/accessibility_annotator/core/storage/content_annotations_table.cc index b6bd7e4..bacf806 100644 --- a/components/accessibility_annotator/core/storage/content_annotations_table.cc +++ b/components/accessibility_annotator/core/storage/content_annotations_table.cc
@@ -12,27 +12,23 @@ #include "components/os_crypt/async/common/encryptor.h" #include "sql/database.h" #include "sql/statement.h" +#include "sql/statement_id.h" +#include "sql/table_management_helpers.h" #include "sql/transaction.h" #include "url/gurl.h" namespace accessibility_annotator { namespace { -// Table creation should be pegged to a specific version number, enforcing -// linear migration-only updates. -constexpr char kContentAnnotationsTableVersion1CreationSql[] = - R"SQL( - CREATE TABLE content_annotations ( - visit_id INTEGER PRIMARY KEY NOT NULL, - url TEXT NOT NULL, - navigation_timestamp INTEGER NOT NULL, - proto_data BLOB NOT NULL, - tab_id INTEGER NOT NULL, - page_title TEXT NOT NULL, - classifier_results TEXT NOT NULL - ) - )SQL"; +constexpr std::string_view kContentAnnotationsTable = "content_annotations"; +constexpr std::string_view kVisitIdColumn = "visit_id"; +constexpr std::string_view kUrlColumn = "url"; +constexpr std::string_view kNavigationTimestampColumn = "navigation_timestamp"; +constexpr std::string_view kProtoDataColumn = "proto_data"; +constexpr std::string_view kTabIdColumn = "tab_id"; +constexpr std::string_view kPageTitleColumn = "page_title"; +constexpr std::string_view kClassifierResultsColumn = "classifier_results"; std::optional<ContentAnnotationsData> ToContentAnnotationsData( sql::Statement& statement, @@ -87,10 +83,17 @@ return false; } - if (!db_->Execute(kContentAnnotationsTableVersion1CreationSql)) { - return false; - } - return true; + return sql::CreateTable(*db_, kContentAnnotationsTable, + /*column_names_and_types=*/ + { + {kVisitIdColumn, "INTEGER PRIMARY KEY NOT NULL"}, + {kUrlColumn, "TEXT NOT NULL"}, + {kNavigationTimestampColumn, "INTEGER NOT NULL"}, + {kProtoDataColumn, "BLOB NOT NULL"}, + {kTabIdColumn, "INTEGER NOT NULL"}, + {kPageTitleColumn, "TEXT NOT NULL"}, + {kClassifierResultsColumn, "TEXT NOT NULL"}, + }); } bool ContentAnnotationsTable::AddContentAnnotation( @@ -114,13 +117,13 @@ return false; } - sql::Statement statement(db_->GetCachedStatement( - SQL_FROM_HERE, - "INSERT OR REPLACE INTO content_annotations " - "(visit_id, url, navigation_timestamp, proto_data, " - "tab_id, page_title, classifier_results) " - "VALUES(?, ?, ?, ?, ?, ?, ?)")); - + sql::Statement statement; + sql::CachedInsertBuilder( + SQL_FROM_HERE, *db_, statement, kContentAnnotationsTable, + /*column_names=*/ + {kVisitIdColumn, kUrlColumn, kNavigationTimestampColumn, kProtoDataColumn, + kTabIdColumn, kPageTitleColumn, kClassifierResultsColumn}, + /*or_replace=*/true); statement.BindInt64(0, visit_id); statement.BindString(1, database_utils::GurlToDatabaseUrl(data.url)); statement.BindTime(2, data.navigation_timestamp); @@ -138,13 +141,13 @@ return std::nullopt; } - sql::Statement statement(db_->GetCachedStatement( - SQL_FROM_HERE, - "SELECT " - "visit_id, url, navigation_timestamp, proto_data, tab_id, " - "page_title, classifier_results " - "FROM content_annotations " - "WHERE visit_id = ?")); + sql::Statement statement; + sql::CachedSelectBuilder( + SQL_FROM_HERE, *db_, statement, kContentAnnotationsTable, + /*columns=*/ + {kVisitIdColumn, kUrlColumn, kNavigationTimestampColumn, kProtoDataColumn, + kTabIdColumn, kPageTitleColumn, kClassifierResultsColumn}, + /*modifiers=*/"WHERE visit_id = ?"); statement.BindInt64(0, visit_id); if (!statement.Step()) { @@ -161,13 +164,13 @@ return results; } - sql::Statement statement(db_->GetCachedStatement( - SQL_FROM_HERE, - "SELECT " - "visit_id, url, navigation_timestamp, proto_data, " - "tab_id, page_title, classifier_results " - "FROM content_annotations " - "ORDER BY visit_id DESC")); + sql::Statement statement; + sql::CachedSelectBuilder( + SQL_FROM_HERE, *db_, statement, kContentAnnotationsTable, + /*columns=*/ + {kVisitIdColumn, kUrlColumn, kNavigationTimestampColumn, kProtoDataColumn, + kTabIdColumn, kPageTitleColumn, kClassifierResultsColumn}, + /*modifiers=*/"ORDER BY visit_id DESC"); while (statement.Step()) { history::VisitID visit_id = statement.ColumnInt64(0); @@ -194,12 +197,13 @@ return {}; } - std::vector<std::string> placeholders(visit_ids.size(), "?"); - std::string query = "DELETE FROM content_annotations WHERE visit_id IN (" + - base::JoinString(placeholders, ",") + - ") RETURNING visit_id"; - - sql::Statement statement(db_->GetUniqueStatement(query)); + std::vector<std::string_view> placeholders(visit_ids.size(), + sql::kPlaceholder); + std::string where_clause = base::StrCat({kVisitIdColumn, " IN (", + base::JoinString(placeholders, ","), + ") RETURNING ", kVisitIdColumn}); + sql::Statement statement; + sql::DeleteBuilder(*db_, statement, kContentAnnotationsTable, where_clause); for (size_t i = 0; i < visit_ids.size(); ++i) { statement.BindInt64(static_cast<int>(i), visit_ids[i]); } @@ -221,7 +225,7 @@ return false; } - return db_->Execute("DELETE FROM content_annotations"); + return sql::DeleteAllRows(*db_, kContentAnnotationsTable); } } // namespace accessibility_annotator
diff --git a/components/accessibility_annotator/core/url_constants.h b/components/accessibility_annotator/core/url_constants.h index fa8e9276..4ccc901 100644 --- a/components/accessibility_annotator/core/url_constants.h +++ b/components/accessibility_annotator/core/url_constants.h
@@ -13,8 +13,6 @@ inline constexpr char kAccessibilityAnnotatorSettingsURL[] = "https://gemini.google.com/personalization-settings"; -inline constexpr char kAccessibilityAnnotatorTriggerText[] = "@@"; - } // namespace accessibility_annotator #endif // COMPONENTS_ACCESSIBILITY_ANNOTATOR_CORE_URL_CONSTANTS_H_
diff --git a/components/autofill/core/browser/foundations/browser_autofill_manager_unittest.cc b/components/autofill/core/browser/foundations/browser_autofill_manager_unittest.cc index 807a99f..4d4f7bd 100644 --- a/components/autofill/core/browser/foundations/browser_autofill_manager_unittest.cc +++ b/components/autofill/core/browser/foundations/browser_autofill_manager_unittest.cc
@@ -3828,15 +3828,13 @@ ASSERT_TRUE(field.global_id() == autofill_field->global_id()); field.set_is_autofilled_according_to_renderer(true); autofill_field->set_autofilled_type(autofill_field->Type().GetAddressType()); - field.set_value(u"Elvis"); + field.set_value(u"Charles"); OnAskForValuesToFill(form, field); // Test that we sent the right values to the external delegate. external_delegate()->CheckSuggestions( field.global_id(), {Suggestion(u"Elvis", u"", Suggestion::Icon::kAccount, SuggestionType::kAddressFieldByFieldFilling), - Suggestion(u"Charles", u"", Suggestion::Icon::kAccount, - SuggestionType::kAddressFieldByFieldFilling), Suggestion(SuggestionType::kSeparator), CreateUndoOrClearFormSuggestion(), CreateManageAddressesSuggestion()}); }
diff --git a/components/autofill/core/browser/metrics/payments/credit_card_save_metrics.cc b/components/autofill/core/browser/metrics/payments/credit_card_save_metrics.cc index d9e70891..92b0819 100644 --- a/components/autofill/core/browser/metrics/payments/credit_card_save_metrics.cc +++ b/components/autofill/core/browser/metrics/payments/credit_card_save_metrics.cc
@@ -62,10 +62,6 @@ "Autofill_CreditCardUpload_LegalMessageLinkClicked")); } -void LogSaveCardCardholderNamePrefilled(bool prefilled) { - UMA_HISTOGRAM_BOOLEAN("Autofill.SaveCardCardholderNamePrefilled", prefilled); -} - void LogSaveCardCardholderNameWasEdited(bool edited) { UMA_HISTOGRAM_BOOLEAN("Autofill.SaveCardCardholderNameWasEdited", edited); }
diff --git a/components/autofill/core/browser/metrics/payments/credit_card_save_metrics.h b/components/autofill/core/browser/metrics/payments/credit_card_save_metrics.h index 8e3ef1e..ce0a492d 100644 --- a/components/autofill/core/browser/metrics/payments/credit_card_save_metrics.h +++ b/components/autofill/core/browser/metrics/payments/credit_card_save_metrics.h
@@ -213,10 +213,6 @@ void LogCreditCardUploadLegalMessageLinkClicked(); -// When a cardholder name fix flow is shown during credit card upload, logs -// whether the cardholder name was prefilled or not. -void LogSaveCardCardholderNamePrefilled(bool prefilled); - // When a cardholder name fix flow is shown during credit card upload and the // user accepts upload, logs whether the final cardholder name was changed // from its prefilled value or not.
diff --git a/components/autofill/core/browser/suggestions/addresses/address_suggestion_generator.cc b/components/autofill/core/browser/suggestions/addresses/address_suggestion_generator.cc index 1b2c7948..64ff1c6 100644 --- a/components/autofill/core/browser/suggestions/addresses/address_suggestion_generator.cc +++ b/components/autofill/core/browser/suggestions/addresses/address_suggestion_generator.cc
@@ -534,37 +534,6 @@ }); CHECK(!profiles_to_suggest.empty()); } - // TODO(crbug.com/393114125): Change to use `AutofillField::field_modifiers_` - // after launching `kAutofillFixIsAutofilled`. - if (trigger_field.is_autofilled_according_to_renderer() && - profiles_to_suggest.size() > 1) { - // This is just a simulation of the logic behind the feature flag in order - // to understand the reason behind the CHECK failure. Profiles are copied so - // that the ones used for suggestions are not modified and the branch - // effectively remains a functional no-op. - std::vector<ProfileWithText> profiles_to_suggest_copy = profiles_to_suggest; - const size_t size_before_filter = profiles_to_suggest.size(); - std::erase_if(profiles_to_suggest_copy, - [&](const ProfileWithText& profile) { - return trigger_field.value() == profile.text; - }); - if (profiles_to_suggest_copy.empty()) { - SCOPED_CRASH_KEY_NUMBER("Autofill", "field_types_size", - field_types.size()); - SCOPED_CRASH_KEY_NUMBER("Autofill", "field_types_contains", - field_types.contains(trigger_field_type)); - SCOPED_CRASH_KEY_NUMBER("Autofill", "trigger_field_type", - std::to_underlying(trigger_field_type)); - SCOPED_CRASH_KEY_NUMBER("Autofill", "size_before_filter", - size_before_filter); - SCOPED_CRASH_KEY_NUMBER("Autofill", "trigger_field_value_size", - trigger_field.value().size()); - SCOPED_CRASH_KEY_NUMBER( - "Autofill", "trigger_field_form_ctrl_type", - std::to_underlying(trigger_field.form_control_type())); - base::debug::DumpWithoutCrashing(); - } - } // Do not show more than `kMaxDisplayedAddressSuggestions` suggestions since // it would result in poor UX.
diff --git a/components/autofill/core/browser/suggestions/payments/credit_card_suggestion_generator_unittest.cc b/components/autofill/core/browser/suggestions/payments/credit_card_suggestion_generator_unittest.cc index 0982c64a8..21dc70f0 100644 --- a/components/autofill/core/browser/suggestions/payments/credit_card_suggestion_generator_unittest.cc +++ b/components/autofill/core/browser/suggestions/payments/credit_card_suggestion_generator_unittest.cc
@@ -2850,7 +2850,8 @@ // BNPL is eligible and there are credit card suggestions. TEST_F(CreditCardSuggestionGeneratorBnplTest, GetCreditCardSuggestionsForTouchToFill_BnplSuggestionAdded) { - payments_data().AddBnplIssuer(test::GetTestUnlinkedBnplIssuer()); + BnplIssuer bnpl_issuer = test::GetTestUnlinkedBnplIssuer(); + payments_data().AddBnplIssuer(bnpl_issuer); ON_CALL(*static_cast<MockAutofillOptimizationGuideDecider*>( autofill_client().GetAutofillOptimizationGuideDecider()), @@ -2871,8 +2872,7 @@ SuggestionType::kBnplEntry, l10n_util::GetStringUTF16(IDS_AUTOFILL_BNPL_PAY_LATER_OPTIONS_TEXT), Suggestion::Icon::kBnplGeneric, - {{Suggestion::Text(l10n_util::GetStringFUTF16( - IDS_AUTOFILL_BNPL_CREDIT_CARD_SUGGESTION_LABEL, u"$35"))}})); + {{Suggestion::Text(bnpl_issuer.GetDisplayName())}})); EXPECT_TRUE(payments_data().IsAutofillHasSeenBnplPrefEnabled()); }
diff --git a/components/autofill/core/browser/ui/payments/card_name_fix_flow_controller_impl.cc b/components/autofill/core/browser/ui/payments/card_name_fix_flow_controller_impl.cc index 4760ecf9..1698e77 100644 --- a/components/autofill/core/browser/ui/payments/card_name_fix_flow_controller_impl.cc +++ b/components/autofill/core/browser/ui/payments/card_name_fix_flow_controller_impl.cc
@@ -41,8 +41,6 @@ name_accepted_callback_ = std::move(name_accepted_callback); inferred_cardholder_name_ = inferred_cardholder_name; - autofill_metrics::LogSaveCardCardholderNamePrefilled( - !inferred_cardholder_name_.empty()); card_name_fix_flow_view_->Show(); AutofillMetrics::LogCardholderNameFixFlowPromptEvent(
diff --git a/components/autofill/core/browser/ui/payments/card_name_fix_flow_controller_impl_unittest.cc b/components/autofill/core/browser/ui/payments/card_name_fix_flow_controller_impl_unittest.cc index a01e3d1..a9228c02 100644 --- a/components/autofill/core/browser/ui/payments/card_name_fix_flow_controller_impl_unittest.cc +++ b/components/autofill/core/browser/ui/payments/card_name_fix_flow_controller_impl_unittest.cc
@@ -97,22 +97,6 @@ AutofillMetrics::CARDHOLDER_NAME_FIX_FLOW_PROMPT_SHOWN, 1); } -TEST_F(CardNameFixFlowControllerImplTest, LogPrefilled) { - base::HistogramTester histogram_tester; - ShowPromptWithInferredName(); - - histogram_tester.ExpectBucketCount("Autofill.SaveCardCardholderNamePrefilled", - true, 1); -} - -TEST_F(CardNameFixFlowControllerImplTest, LogNotPrefilled) { - base::HistogramTester histogram_tester; - ShowPromptWithoutInferredName(); - - histogram_tester.ExpectBucketCount("Autofill.SaveCardCardholderNamePrefilled", - false, 1); -} - TEST_F(CardNameFixFlowControllerImplTest, LogAccepted) { base::HistogramTester histogram_tester; ShowPromptWithInferredName();
diff --git a/components/autofill/core/common/autofill_features.cc b/components/autofill/core/common/autofill_features.cc index ff1c7c6..6190c67 100644 --- a/components/autofill/core/common/autofill_features.cc +++ b/components/autofill/core/common/autofill_features.cc
@@ -683,7 +683,7 @@ // suggestion UI. // TODO(crbug.com/381531027): Remove when launched. BASE_FEATURE(kAutofillImproveAddressFieldSwapping, - base::FEATURE_DISABLED_BY_DEFAULT); + base::FEATURE_ENABLED_BY_DEFAULT); // When enabled, a new grammar for phone numbers is considered and we get // slightly better at detecting cases where the generic regex for
diff --git a/components/autofill/core/common/autofill_payments_features.cc b/components/autofill/core/common/autofill_payments_features.cc index 130c9e2..b2316bb4 100644 --- a/components/autofill/core/common/autofill_payments_features.cc +++ b/components/autofill/core/common/autofill_payments_features.cc
@@ -38,12 +38,12 @@ // of the allowlisted merchant websites. BASE_FEATURE(kAutofillEnableAmountExtraction, #if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_MAC) || BUILDFLAG(IS_LINUX) || \ - BUILDFLAG(IS_CHROMEOS) + BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_ANDROID) base::FEATURE_ENABLED_BY_DEFAULT); #else base::FEATURE_DISABLED_BY_DEFAULT); #endif // BUILDFLAG(IS_WIN) || BUILDFLAG(IS_MAC) || BUILDFLAG(IS_LINUX) || - // BUILDFLAG(IS_CHROMEOS) + // BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_ANDROID) // Enables testing of the result of checkout amount extraction on desktop. // This flag will allow amount extraction to run on any website when a CC @@ -61,12 +61,12 @@ // When enabled, buy now pay later (BNPL) in Autofill will be offered. BASE_FEATURE(kAutofillEnableBuyNowPayLater, #if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_MAC) || BUILDFLAG(IS_LINUX) || \ - BUILDFLAG(IS_CHROMEOS) + BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_ANDROID) base::FEATURE_ENABLED_BY_DEFAULT); #else base::FEATURE_DISABLED_BY_DEFAULT); #endif // BUILDFLAG(IS_WIN) || BUILDFLAG(IS_MAC) || BUILDFLAG(IS_LINUX) || - // BUILDFLAG(IS_CHROMEOS) + // BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_ANDROID) // When enabled, additional steps are required to autofill buy now pay later // (BNPL) issuers that are externally linked. @@ -76,7 +76,8 @@ base::FEATURE_ENABLED_BY_DEFAULT); #else base::FEATURE_DISABLED_BY_DEFAULT); -#endif +#endif // BUILDFLAG(IS_WIN) || BUILDFLAG(IS_MAC) || BUILDFLAG(IS_LINUX) || + // BUILDFLAG(IS_CHROMEOS) // When enabled, buy now pay later (BNPL) for Klarna in Autofill will be // offered. @@ -86,7 +87,8 @@ base::FEATURE_ENABLED_BY_DEFAULT); #else base::FEATURE_DISABLED_BY_DEFAULT); -#endif +#endif // BUILDFLAG(IS_WIN) || BUILDFLAG(IS_MAC) || BUILDFLAG(IS_LINUX) || + // BUILDFLAG(IS_CHROMEOS) // When enabled, buy now pay later (BNPL) data will be synced to Chrome clients. BASE_FEATURE(kAutofillEnableBuyNowPayLaterSyncing, @@ -95,18 +97,19 @@ base::FEATURE_ENABLED_BY_DEFAULT); #else base::FEATURE_DISABLED_BY_DEFAULT); -#endif +#endif // BUILDFLAG(IS_WIN) || BUILDFLAG(IS_MAC) || BUILDFLAG(IS_LINUX) || + // BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_ANDROID) // When enabled, the second line string in a BNPL suggestion is updated to // include the issuer names for better brand recognition. BASE_FEATURE(kAutofillEnableBuyNowPayLaterUpdatedSuggestionSecondLineString, #if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_MAC) || BUILDFLAG(IS_LINUX) || \ - BUILDFLAG(IS_CHROMEOS) + BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_ANDROID) base::FEATURE_ENABLED_BY_DEFAULT); #else base::FEATURE_DISABLED_BY_DEFAULT); #endif // BUILDFLAG(IS_WIN) || BUILDFLAG(IS_MAC) || BUILDFLAG(IS_LINUX) || - // BUILDFLAG(IS_CHROMEOS) + // BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_ANDROID) // When enabled, card benefits offered by American Express will be shown in // Payments Autofill UI. @@ -239,7 +242,7 @@ // When enabled, the Touch To Fill bottom sheet on Android can be reshown after // a BNPL flow is dismissed by a user. BASE_FEATURE(kAutofillEnableTouchToFillReshowForBnpl, - base::FEATURE_DISABLED_BY_DEFAULT); + base::FEATURE_ENABLED_BY_DEFAULT); #endif // BUILDFLAG(IS_ANDROID) // When enabled, travel category and merchant benefits sourced from Curinos will @@ -311,7 +314,7 @@ BASE_FEATURE(kAutofillSyncEwalletAccounts, base::FEATURE_ENABLED_BY_DEFAULT); BASE_FEATURE(kAutofillTouchToFillShowManualFillForVcnFix, - base::FEATURE_DISABLED_BY_DEFAULT); + base::FEATURE_ENABLED_BY_DEFAULT); #endif // BUILDFLAG(IS_ANDROID) // Controls offering credit card upload to Google Payments. Cannot ever be
diff --git a/components/background_task_scheduler/task_ids.h b/components/background_task_scheduler/task_ids.h index 9beeff6..fa0d4a5 100644 --- a/components/background_task_scheduler/task_ids.h +++ b/components/background_task_scheduler/task_ids.h
@@ -131,7 +131,7 @@ // component: UI>Browser>ContentSuggestions>Feed // team_email: feed@chromium.org // owner: dewittj@chromium.org - WEBFEEDS_REFRESH_JOB_ID = 109, + // WEBFEEDS_REFRESH_JOB_ID = 109, // component: Mobile>WebView // team_email: android-webview-dev@chromium.org // owner: ntfschr@chromium.org, torne@chromium.org
diff --git a/components/browser_ui/bottomsheet/android/internal/java/src/org/chromium/components/browser_ui/bottomsheet/BottomSheet.java b/components/browser_ui/bottomsheet/android/internal/java/src/org/chromium/components/browser_ui/bottomsheet/BottomSheet.java index 510e92c..c484e9a 100644 --- a/components/browser_ui/bottomsheet/android/internal/java/src/org/chromium/components/browser_ui/bottomsheet/BottomSheet.java +++ b/components/browser_ui/bottomsheet/android/internal/java/src/org/chromium/components/browser_ui/bottomsheet/BottomSheet.java
@@ -477,7 +477,11 @@ mInsetObserver.addWindowInsetsAnimationListener( new WindowInsetsAnimationListener() { @Override - public void onPrepare(WindowInsetsAnimationCompat animation) {} + public void onPrepare(WindowInsetsAnimationCompat animation) { + for (BottomSheetObserver obs : mObservers) { + obs.beforeInsetAnimationStart(); + } + } @Override public void onStart( @@ -495,6 +499,9 @@ @Override public void onEnd(WindowInsetsAnimationCompat animation) { onInsetChanged(); + for (BottomSheetObserver obs : mObservers) { + obs.onInsetAnimationEnd(); + } } });
diff --git a/components/browser_ui/bottomsheet/android/internal/java/src/org/chromium/components/browser_ui/bottomsheet/BottomSheetControllerImpl.java b/components/browser_ui/bottomsheet/android/internal/java/src/org/chromium/components/browser_ui/bottomsheet/BottomSheetControllerImpl.java index 35c6078..6663071 100644 --- a/components/browser_ui/bottomsheet/android/internal/java/src/org/chromium/components/browser_ui/bottomsheet/BottomSheetControllerImpl.java +++ b/components/browser_ui/bottomsheet/android/internal/java/src/org/chromium/components/browser_ui/bottomsheet/BottomSheetControllerImpl.java
@@ -637,12 +637,17 @@ @Override public void expandSheet() { + expandSheet(true); + } + + @Override + public void expandSheet(boolean animate) { if (mBottomSheet == null || mSuppressionTokens.hasTokens() || mBottomSheet.isHiding()) { return; } if (mBottomSheet.getCurrentSheetContent() == null) return; - mBottomSheet.setSheetState(SheetState.HALF, true); + mBottomSheet.setSheetState(SheetState.HALF, animate); } @Override @@ -660,6 +665,7 @@ /** * Show the next {@link BottomSheetContent} if it is available and peek the sheet. If no content * is available the sheet's content is set to null. + * * @param animate Whether the sheet should animate opened. */ private void showNextContent(boolean animate) {
diff --git a/components/browser_ui/bottomsheet/android/internal/java/src/org/chromium/components/browser_ui/bottomsheet/BottomSheetUnitTest.java b/components/browser_ui/bottomsheet/android/internal/java/src/org/chromium/components/browser_ui/bottomsheet/BottomSheetUnitTest.java index f45a62a..3521dbc 100644 --- a/components/browser_ui/bottomsheet/android/internal/java/src/org/chromium/components/browser_ui/bottomsheet/BottomSheetUnitTest.java +++ b/components/browser_ui/bottomsheet/android/internal/java/src/org/chromium/components/browser_ui/bottomsheet/BottomSheetUnitTest.java
@@ -63,6 +63,7 @@ @Mock private TouchRestrictingFrameLayout mToolbarHolder; @Mock private InsetObserver mInsetObserver; @Mock private KeyboardVisibilityDelegate mKeyboardDelegate; + @Mock private BottomSheetObserver mBottomSheetObserver; @Captor private ArgumentCaptor<InsetObserver.WindowInsetsAnimationListener> @@ -569,4 +570,19 @@ mKeyboardInsetSupplier.set(150); observer.onInsetChanged(); } + + @Test + public void triggerObserverOnInsetChange() { + mBottomSheet.addObserver(mBottomSheetObserver); + verify(mInsetObserver) + .addWindowInsetsAnimationListener(mInsetsAnimationListenerCaptor.capture()); + InsetObserver.WindowInsetsAnimationListener listener = + mInsetsAnimationListenerCaptor.getValue(); + + listener.onPrepare(null); + verify(mBottomSheetObserver).beforeInsetAnimationStart(); + + listener.onEnd(null); + verify(mBottomSheetObserver).onInsetAnimationEnd(); + } }
diff --git a/components/browser_ui/bottomsheet/android/java/src/org/chromium/components/browser_ui/bottomsheet/BottomSheetController.java b/components/browser_ui/bottomsheet/android/java/src/org/chromium/components/browser_ui/bottomsheet/BottomSheetController.java index a715b57..b65598a 100644 --- a/components/browser_ui/bottomsheet/android/java/src/org/chromium/components/browser_ui/bottomsheet/BottomSheetController.java +++ b/components/browser_ui/bottomsheet/android/java/src/org/chromium/components/browser_ui/bottomsheet/BottomSheetController.java
@@ -19,9 +19,9 @@ /** * The public interface for the bottom sheet's controller. Features wishing to show content in the - * sheet UI must implement {@link BottomSheetContent} and call - * {@link #requestShowContent(BottomSheetContent, boolean)} which will return true if the content - * was actually shown (see full doc on method). + * sheet UI must implement {@link BottomSheetContent} and call {@link + * #requestShowContent(BottomSheetContent, boolean)} which will return true if the content was + * actually shown (see full doc on method). */ @NullMarked public interface BottomSheetController { @@ -37,8 +37,8 @@ @Retention(RetentionPolicy.SOURCE) @interface SheetState { /** - * NONE is for internal use only and indicates the sheet is not currently - * transitioning between states. + * NONE is for internal use only and indicates the sheet is not currently transitioning + * between states. */ int NONE = -1; @@ -56,7 +56,7 @@ /** * The different reasons that the sheet's state can change. * - * Needs to stay in sync with BottomSheet.StateChangeReason in enums.xml. These values are + * <p>Needs to stay in sync with BottomSheet.StateChangeReason in enums.xml. These values are * persisted to logs. Entries should not be renumbered and numeric values should never be * reused. */ @@ -92,17 +92,19 @@ /** * Request that some content be shown in the bottom sheet. + * * @param content The content to be shown in the bottom sheet. * @param animate Whether the appearance of the bottom sheet should be animated. * @return True if the content was shown, false if it was suppressed. Content is suppressed if - * higher priority content is in the sheet, the sheet is expanded beyond the peeking - * state, or the browser is in a mode that does not support showing the sheet. + * higher priority content is in the sheet, the sheet is expanded beyond the peeking state, + * or the browser is in a mode that does not support showing the sheet. */ boolean requestShowContent(BottomSheetContent content, boolean animate); /** * Hide content shown in the bottom sheet. If the content is not showing, this call retracts the * request to show it. + * * @param content The content to be hidden. * @param animate Whether the sheet should animate when hiding. * @param hideReason The reason that the content is being hidden. @@ -114,18 +116,30 @@ void hideContent(@Nullable BottomSheetContent content, boolean animate); - /** @param observer The observer to add. */ + /** + * @param observer The observer to add. + */ void addObserver(BottomSheetObserver observer); - /** @param observer The observer to remove. */ + /** + * @param observer The observer to remove. + */ void removeObserver(BottomSheetObserver observer); /** Expand the sheet. If there is no content in the sheet, this is a noop. */ void expandSheet(); /** - * Collapse the current sheet to peek state. Sheet may not change the state if the state - * is not allowed. + * Expand the sheet. If there is no content in the sheet, this is a noop. + * + * @param animate {@code true} for animation effect. + */ + void expandSheet(boolean animate); + + /** + * Collapse the current sheet to peek state. Sheet may not change the state if the state is not + * allowed. + * * @param animate {@code true} for animation effect. * @return {@code true} if the sheet could go to the peek state. */
diff --git a/components/browser_ui/bottomsheet/android/java/src/org/chromium/components/browser_ui/bottomsheet/BottomSheetObserver.java b/components/browser_ui/bottomsheet/android/java/src/org/chromium/components/browser_ui/bottomsheet/BottomSheetObserver.java index 88fdab8..686c48b2 100644 --- a/components/browser_ui/bottomsheet/android/java/src/org/chromium/components/browser_ui/bottomsheet/BottomSheetObserver.java +++ b/components/browser_ui/bottomsheet/android/java/src/org/chromium/components/browser_ui/bottomsheet/BottomSheetObserver.java
@@ -59,4 +59,14 @@ /** Called when the sheet background color override is changed. */ default void onSheetBackgroundColorOverrideChanged() {} + + /** + * Called before the inset animation starts. This event is triggered before any layout changes + * occur. + */ + default void beforeInsetAnimationStart() {} + + /** Called when the inset animation ends. */ + default void onInsetAnimationEnd() {} } +
diff --git a/components/component_updater/component_updater_service.cc b/components/component_updater/component_updater_service.cc index e57b389..1e6dc0e 100644 --- a/components/component_updater/component_updater_service.cc +++ b/components/component_updater/component_updater_service.cc
@@ -336,7 +336,7 @@ // Some components should update even when enterprise policy disables // updates. bool override_component_updates_enabled = - !component.supports_group_policy_enable_component_updates; + !component.supports_group_policy_enable_component_updates; bool should_update = override_component_updates_enabled || component_updates_enabled; crx.updates_enabled = component.allow_updates && should_update; @@ -419,21 +419,15 @@ UMA_HISTOGRAM_ENUMERATION("ComponentUpdater.Calls", UPDATE_TYPE_MANUAL, UPDATE_TYPE_COUNT); - auto crx_data_callback = base::BindOnce(&CrxUpdateService::GetCrxComponents, - weak_ptr_factory_.GetWeakPtr()); - auto update_complete_callback = base::BindOnce( - &CrxUpdateService::OnUpdateComplete, weak_ptr_factory_.GetWeakPtr(), - std::move(callback), base::TimeTicks::Now()); - switch (priority) { - case Priority::FOREGROUND: - update_client_->Install(id, std::move(crx_data_callback), {}, - std::move(update_complete_callback)); - break; - case Priority::BACKGROUND: - update_client_->Update({id}, std::move(crx_data_callback), {}, false, - std::move(update_complete_callback)); - break; - } + update_client_->Update( + {id}, + base::BindOnce(&CrxUpdateService::GetCrxComponents, + weak_ptr_factory_.GetWeakPtr()), + /*crx_state_change_callback=*/{}, + /*is_foreground=*/priority == Priority::FOREGROUND, + base::BindOnce(&CrxUpdateService::OnUpdateComplete, + weak_ptr_factory_.GetWeakPtr(), std::move(callback), + base::TimeTicks::Now())); } bool CrxUpdateService::CheckForUpdates(
diff --git a/components/component_updater/component_updater_service_unittest.cc b/components/component_updater/component_updater_service_unittest.cc index e1764258..29539ae4 100644 --- a/components/component_updater/component_updater_service_unittest.cc +++ b/components/component_updater/component_updater_service_unittest.cc
@@ -141,7 +141,6 @@ UpdateClient::CrxStateChangeCallback, bool is_foreground, Callback callback) { - EXPECT_FALSE(is_foreground); Handle(std::move(callback)); } @@ -330,7 +329,7 @@ // Quit after two update checks have fired. LoopHandler loop_handler(2, quit_closure()); - EXPECT_CALL(update_client(), Update(_, _, _, _, _)) + EXPECT_CALL(update_client(), Update(_, _, _, /*is_foreground=*/false, _)) .WillRepeatedly(Invoke(&loop_handler, &LoopHandler::OnUpdate)); EXPECT_CALL(update_client(), IsUpdating(id1)); @@ -349,7 +348,7 @@ ht.ExpectTotalCount("ComponentUpdater.UpdateCompleteTime", 2); } -// Tests that on-demand updates invoke UpdateClient::Install. +// Tests that on-demand updates invoke UpdateClient::Update. TEST_F(ComponentUpdaterTest, OnDemandUpdate) { base::HistogramTester ht; @@ -366,16 +365,15 @@ &cus, "ihfokbkgjpifnbbojhneepfflplebdkc", OnDemandUpdater::Priority::FOREGROUND); - // Register two components, then call |OnDemand| for each component, with - // foreground and background priorities. Expect calls to |Schedule| because - // components have registered, calls to |Install| and |Update| corresponding - // to each |OnDemand| invocation, and calls to |Stop| when the mocks are - // torn down. + // Register two components, then call `OnDemand` for each component, with + // foreground and background priorities. Expect calls to `Schedule` because + // components have registered, calls `Update` corresponding to each `OnDemand` + // invocation, and calls to `Stop` when the mocks are torn down. LoopHandler loop_handler(2, quit_closure()); EXPECT_CALL(scheduler(), Schedule(_, _, _, _)); - EXPECT_CALL(update_client(), Install(_, _, _, _)) - .WillOnce(Invoke(&loop_handler, &LoopHandler::OnInstall)); - EXPECT_CALL(update_client(), Update(_, _, _, _, _)) + EXPECT_CALL(update_client(), Update(_, _, _, /*is_foreground=*/true, _)) + .WillOnce(Invoke(&loop_handler, &LoopHandler::OnUpdate)); + EXPECT_CALL(update_client(), Update(_, _, _, /*is_foreground=*/false, _)) .WillOnce(Invoke(&loop_handler, &LoopHandler::OnUpdate)); EXPECT_CALL(update_client(), Stop()); EXPECT_CALL(scheduler(), Stop()); @@ -421,7 +419,7 @@ ht.ExpectTotalCount("ComponentUpdater.UpdateCompleteTime", 2); } -// Tests that throttling an update invokes UpdateClient::Install. +// Tests that throttling an update invokes UpdateClient::Update. TEST_F(ComponentUpdaterTest, MaybeThrottle) { base::HistogramTester ht; @@ -429,8 +427,8 @@ ON_CALL(scheduler(), Schedule(_, _, _, _)).WillByDefault(Return()); LoopHandler loop_handler(1, quit_closure()); - EXPECT_CALL(update_client(), Install(_, _, _, _)) - .WillOnce(Invoke(&loop_handler, &LoopHandler::OnInstall)); + EXPECT_CALL(update_client(), Update(_, _, _, /*is_foreground=*/true, _)) + .WillOnce(Invoke(&loop_handler, &LoopHandler::OnUpdate)); EXPECT_CALL(update_client(), Stop()); EXPECT_CALL(scheduler(), Schedule(_, _, _, _)); EXPECT_CALL(scheduler(), Stop());
diff --git a/components/content_settings/core/browser/content_settings_policy_provider.cc b/components/content_settings/core/browser/content_settings_policy_provider.cc index 3d62385..66bcdee 100644 --- a/components/content_settings/core/browser/content_settings_policy_provider.cc +++ b/components/content_settings/core/browser/content_settings_policy_provider.cc
@@ -253,6 +253,8 @@ prefs::kManagedDirectSocketsBlockedForUrls, prefs::kManagedDirectSocketsPrivateNetworkAccessAllowedForUrls, prefs::kManagedDirectSocketsPrivateNetworkAccessBlockedForUrls, + prefs::kManagedSubAppsWithoutPromptsAllowedForOrigins, + prefs::kManagedSubAppsWithoutPromptsBlockedForOrigins, #if BUILDFLAG(IS_CHROMEOS) prefs::kManagedSmartCardConnectAllowedForUrls, prefs::kManagedSmartCardConnectBlockedForUrls, @@ -295,6 +297,7 @@ prefs::kManagedDefaultLocalFontsSetting, prefs::kManagedDefaultWebPrintingSetting, prefs::kManagedDefaultDirectSocketsSetting, + prefs::kManagedDefaultSubAppsWithoutPromptsSetting, prefs::kManagedDefaultDirectSocketsPrivateNetworkAccessSetting, prefs::kManagedDefaultControlledFrameSetting, #if BUILDFLAG(IS_CHROMEOS)
diff --git a/components/content_settings/core/common/pref_names.h b/components/content_settings/core/common/pref_names.h index 6f03edb..a5d161e 100644 --- a/components/content_settings/core/common/pref_names.h +++ b/components/content_settings/core/common/pref_names.h
@@ -75,6 +75,8 @@ inline constexpr char kManagedDefaultDirectSocketsPrivateNetworkAccessSetting[] = "profile.managed_default_content_settings.direct_sockets_pna"; +inline constexpr char kManagedDefaultSubAppsWithoutPromptsSetting[] = + "profile.managed_default_content_settings.sub_apps_without_prompts"; inline constexpr char kManagedDefaultLegacyCookieScope[] = "profile.managed_default_content_settings.legacy_cookie_scope"; inline constexpr char kManagedDefaultControlledFrameSetting[] = @@ -194,6 +196,10 @@ inline constexpr char kManagedDirectSocketsPrivateNetworkAccessBlockedForUrls[] = "profile.managed_direct_sockets_pna_blocked_for_urls"; +inline constexpr char kManagedSubAppsWithoutPromptsAllowedForOrigins[] = + "profile.managed_sub_apps_without_prompts_allowed_for_origins"; +inline constexpr char kManagedSubAppsWithoutPromptsBlockedForOrigins[] = + "profile.managed_sub_apps_without_prompts_blocked_for_origins"; inline constexpr char kManagedLegacyCookieScopeForDomains[] = "profile.managed_legacy_cookie_scope_for_domains"; #if BUILDFLAG(IS_CHROMEOS)
diff --git a/components/enterprise/browser/reporting/chrome_profile_request_generator.cc b/components/enterprise/browser/reporting/chrome_profile_request_generator.cc index d2f22c5..2165712 100644 --- a/components/enterprise/browser/reporting/chrome_profile_request_generator.cc +++ b/components/enterprise/browser/reporting/chrome_profile_request_generator.cc
@@ -111,10 +111,10 @@ bool is_signals_only = generation_config.security_signals_mode == SecuritySignalsMode::kSignalsOnly; - if (is_signals_only) { - profile_report_generator_.set_policies_enabled( - enterprise_signals::features::IsPolicyDataCollectionEnabled()); - } + profile_report_generator_.set_policies_enabled( + is_signals_only + ? enterprise_signals::features::IsPolicyDataCollectionEnabled() + : true); auto barrier_callback = base::BarrierCallback< std::variant<std::unique_ptr<em::BrowserReport>,
diff --git a/components/enterprise/isolated_mode/BUILD.gn b/components/enterprise/isolated_mode/BUILD.gn new file mode 100644 index 0000000..04b055e --- /dev/null +++ b/components/enterprise/isolated_mode/BUILD.gn
@@ -0,0 +1,17 @@ +# Copyright 2026 The Chromium Authors +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +source_set("isolated_mode") { + configs += [ "//components/enterprise:extra_warnings" ] + + sources = [ + "prefs.cc", + "prefs.h", + ] + + deps = [ + "//base", + "//components/prefs", + ] +}
diff --git a/components/enterprise/isolated_mode/prefs.cc b/components/enterprise/isolated_mode/prefs.cc new file mode 100644 index 0000000..4b188bc --- /dev/null +++ b/components/enterprise/isolated_mode/prefs.cc
@@ -0,0 +1,15 @@ +// Copyright 2026 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "components/enterprise/isolated_mode/prefs.h" + +namespace enterprise_isolated_mode { + +const char kEnterpriseIsolatedModeSettings[] = "enterprise.isolated_mode"; + +void RegisterProfilePrefs(PrefRegistrySimple* registry) { + registry->RegisterIntegerPref(kEnterpriseIsolatedModeSettings, 0); +} + +} // namespace enterprise_isolated_mode
diff --git a/components/enterprise/isolated_mode/prefs.h b/components/enterprise/isolated_mode/prefs.h new file mode 100644 index 0000000..bdad221 --- /dev/null +++ b/components/enterprise/isolated_mode/prefs.h
@@ -0,0 +1,21 @@ +// Copyright 2026 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef COMPONENTS_ENTERPRISE_ISOLATED_MODE_PREFS_H_ +#define COMPONENTS_ENTERPRISE_ISOLATED_MODE_PREFS_H_ + +#include "components/prefs/pref_registry_simple.h" + +namespace enterprise_isolated_mode { + +// Pref that maps to the "IsolatedModeSettings" policy. +// It is an int-enum preference. +extern const char kEnterpriseIsolatedModeSettings[]; + +// Registers Enterprise Isolated Mode profile prefs under the given registry. +void RegisterProfilePrefs(PrefRegistrySimple* registry); + +} // namespace enterprise_isolated_mode + +#endif // COMPONENTS_ENTERPRISE_ISOLATED_MODE_PREFS_H_
diff --git a/components/facilitated_payments/core/features/features.cc b/components/facilitated_payments/core/features/features.cc index 55d3cdab..8106498 100644 --- a/components/facilitated_payments/core/features/features.cc +++ b/components/facilitated_payments/core/features/features.cc
@@ -37,6 +37,11 @@ const base::FeatureParam<int> kPixAccountLinkingNativeTriggerDelaySeconds{ &kEnablePixAccountLinkingNative, "trigger_delay_seconds", 3}; +// TODO: Replace with a public YouTube link for production to guarantee access. +const base::FeatureParam<std::string> kVideoUrlOnPrompt{ + &kEnablePixAccountLinkingNative, "video_url_on_prompt", + "https://support.google.com/wallet/answer/14616353?hl=en"}; + // When enabled, static qr code will be supported for pix pay flow. BASE_FEATURE(kEnableStaticQrCodeForPix, base::FEATURE_DISABLED_BY_DEFAULT);
diff --git a/components/facilitated_payments/core/features/features.h b/components/facilitated_payments/core/features/features.h index 6b2dcb5d..1e82d0c3 100644 --- a/components/facilitated_payments/core/features/features.h +++ b/components/facilitated_payments/core/features/features.h
@@ -21,6 +21,7 @@ kPixAccountLinkingNativePromptVariant; extern const base::FeatureParam<int> kPixAccountLinkingNativeTriggerDelaySeconds; +extern const base::FeatureParam<std::string> kVideoUrlOnPrompt; BASE_DECLARE_FEATURE(kEnableStaticQrCodeForPix); BASE_DECLARE_FEATURE(kEnableIframeForPix); BASE_DECLARE_FEATURE(kEnablePixInCct);
diff --git a/components/facilitated_payments_strings.grdp b/components/facilitated_payments_strings.grdp index 0e76988b..9027fac 100644 --- a/components/facilitated_payments_strings.grdp +++ b/components/facilitated_payments_strings.grdp
@@ -132,7 +132,7 @@ Pay with Pix without switching apps </message> <message name="IDS_PIX_ACCOUNT_LINKING_PROMPT_B_VALUE_PROP_MESSAGE_1" desc="First value prop message in the bottom sheet showing Pix account linking prompt (Variation B)." formatter_data="android_java"> - Pay without copying and pasting Pix code. See how it works + Pay without copying and pasting Pix code. <ph name="BEGIN_LINK1"><link1></ph>See how it works<ph name="END_LINK1"></link1></ph> </message> <message name="IDS_PIX_ACCOUNT_LINKING_PROMPT_B_VALUE_PROP_MESSAGE_2" desc="Second value prop message in the bottom sheet showing Pix account linking prompt (Variation B)." formatter_data="android_java"> Google encryption protects your info @@ -143,6 +143,9 @@ <message name="IDS_PIX_ACCOUNT_LINKING_PROMPT_DECLINE_FIRST_TWO_TIMES" desc="Text for the decline button in the bottom sheet showing Pix account linking prompt. This string is used for the first two times the prompt is shown, where we show 'Not now' instead of 'No thanks'." formatter_data="android_java"> Not now </message> + <message name="IDS_PIX_ACCOUNT_LINKING_PROMPT_B_SETTINGS_LINK" desc="Text for the settings link in the bottom sheet showing Pix account linking prompt (Variation B)." formatter_data="android_java"> + To turn off Pix code detection, go to <ph name="BEGIN_LINK1"><link1></ph>Chrome settings<ph name="END_LINK1"></link1></ph> + </message> <message name="IDS_PIX_ACCOUNT_LINKING_PROMPT_TITLE" desc="Title for the bottom sheet showing Pix account linking prompt. It asks users to link their Pix accounts to Google Wallet so they can directly pay on Chrome." formatter_data="android_java"> Pay with Pix directly in Chrome next time </message>
diff --git a/components/facilitated_payments_strings_grdp/IDS_PIX_ACCOUNT_LINKING_PROMPT_B_SETTINGS_LINK.png.sha1 b/components/facilitated_payments_strings_grdp/IDS_PIX_ACCOUNT_LINKING_PROMPT_B_SETTINGS_LINK.png.sha1 new file mode 100644 index 0000000..859fe5f --- /dev/null +++ b/components/facilitated_payments_strings_grdp/IDS_PIX_ACCOUNT_LINKING_PROMPT_B_SETTINGS_LINK.png.sha1
@@ -0,0 +1 @@ +03ba18f3f6a4beba87c4e667754abfb6fe9feef0 \ No newline at end of file
diff --git a/components/facilitated_payments_strings_grdp/IDS_PIX_ACCOUNT_LINKING_PROMPT_B_VALUE_PROP_MESSAGE_1.png.sha1 b/components/facilitated_payments_strings_grdp/IDS_PIX_ACCOUNT_LINKING_PROMPT_B_VALUE_PROP_MESSAGE_1.png.sha1 index afb7114..b77fbf5e 100644 --- a/components/facilitated_payments_strings_grdp/IDS_PIX_ACCOUNT_LINKING_PROMPT_B_VALUE_PROP_MESSAGE_1.png.sha1 +++ b/components/facilitated_payments_strings_grdp/IDS_PIX_ACCOUNT_LINKING_PROMPT_B_VALUE_PROP_MESSAGE_1.png.sha1
@@ -1 +1 @@ -ffdb6d56376e7d77f716c1d89589a3da0853bac7 +ffdb6d56376e7d77f716c1d89589a3da0853bac7 \ No newline at end of file
diff --git a/components/feature_engagement/public/android/java/src/org/chromium/components/feature_engagement/FeatureConstants.java b/components/feature_engagement/public/android/java/src/org/chromium/components/feature_engagement/FeatureConstants.java index 2acb1de..464f6e6 100644 --- a/components/feature_engagement/public/android/java/src/org/chromium/components/feature_engagement/FeatureConstants.java +++ b/components/feature_engagement/public/android/java/src/org/chromium/components/feature_engagement/FeatureConstants.java
@@ -77,8 +77,6 @@ FeatureConstants.IPH_TAB_SWITCHER_XR, FeatureConstants.IPH_TAB_TEARING_XR, FeatureConstants.IPH_TOUCH_TO_SEARCH_CALLOUT, - FeatureConstants.IPH_WEB_FEED_POST_FOLLOW_DIALOG_FEATURE, - FeatureConstants.IPH_WEB_FEED_POST_FOLLOW_DIALOG_FEATURE_WITH_UI_UPDATE, FeatureConstants.KEYBOARD_ACCESSORY_ACCOUNT_NAME_EMAIL_SUGGESTION_FEATURE, FeatureConstants.KEYBOARD_ACCESSORY_ADDRESS_FILL_FEATURE, FeatureConstants.KEYBOARD_ACCESSORY_AT_MEMORY_FEATURE, @@ -316,13 +314,6 @@ /** An IPH feature to inform users that the touch to search panel can be expanded. */ String IPH_TOUCH_TO_SEARCH_CALLOUT = "IPH_TouchToSearchCallout"; - /** A dialog IPH feature to inform users about the WebFeed post-follow. */ - String IPH_WEB_FEED_POST_FOLLOW_DIALOG_FEATURE = "IPH_WebFeedPostFollowDialog"; - - /** A dialog IPH feature to inform users about the WebFeed post-follow after the UI update. */ - String IPH_WEB_FEED_POST_FOLLOW_DIALOG_FEATURE_WITH_UI_UPDATE = - "IPH_WebFeedPostFollowDialogWithUIUpdate"; - String KEYBOARD_ACCESSORY_ACCOUNT_NAME_EMAIL_SUGGESTION_FEATURE = "IPH_AutofillAccountNameEmailSuggestion";
diff --git a/components/feed/core/common/pref_names.cc b/components/feed/core/common/pref_names.cc index 4d41fee2..85a9823 100644 --- a/components/feed/core/common/pref_names.cc +++ b/components/feed/core/common/pref_names.cc
@@ -34,16 +34,12 @@ "feedv2.request_throttler.last_request_time"; const char kDebugStreamData[] = "feedv2.debug_stream_data"; const char kRequestSchedule[] = "feedv2.request_schedule"; -const char kWebFeedsRequestSchedule[] = "webfeed.request_schedule"; const char kMetricsData[] = "feedv2.metrics_data"; const char kClientInstanceId[] = "feedv2.client_instance_id"; // This pref applies to all discover APIs despite the string. const char kDiscoverAPIEndpointOverride[] = "feedv2.actions_endpoint_override"; -const char kEnableWebFeedFollowIntroDebug[] = - "webfeed_follow_intro_debug.enable"; const char kReliabilityLoggingIdSalt[] = "feedv2.reliability_logging_id_salt"; const char kHasStoredData[] = "feedv2.has_stored_data"; -const char kLastSeenFeedType[] = "feedv2.last_seen_feed_type"; const char kFeedOnDeviceUserActionsCollector[] = "feed.user_actions_collection"; const char kInfoCardStates[] = "feed.info_card_states"; const char kExperimentsV3[] = "feedv2.experiments_v3"; @@ -71,7 +67,6 @@ base::Time()); registry->RegisterStringPref(feed::prefs::kDebugStreamData, std::string()); registry->RegisterDictionaryPref(feed::prefs::kRequestSchedule); - registry->RegisterDictionaryPref(feed::prefs::kWebFeedsRequestSchedule); registry->RegisterDictionaryPref(feed::prefs::kMetricsData); registry->RegisterStringPref(feed::prefs::kClientInstanceId, ""); registry->RegisterStringPref(feed::prefs::kDiscoverAPIEndpointOverride, ""); @@ -81,11 +76,8 @@ registry->RegisterIntegerPref(feed::prefs::kNoticeCardViewsCount, 0); registry->RegisterIntegerPref(feed::prefs::kNoticeCardClicksCount, 0); - registry->RegisterBooleanPref(feed::prefs::kEnableWebFeedFollowIntroDebug, - false); registry->RegisterUint64Pref(feed::prefs::kReliabilityLoggingIdSalt, 0); registry->RegisterBooleanPref(feed::prefs::kHasStoredData, false); - registry->RegisterIntegerPref(feed::prefs::kLastSeenFeedType, 0); registry->RegisterListPref(feed::prefs::kFeedOnDeviceUserActionsCollector, PrefRegistry::LOSSY_PREF); registry->RegisterDictionaryPref(feed::prefs::kInfoCardStates, 0);
diff --git a/components/feed/core/common/pref_names.h b/components/feed/core/common/pref_names.h index 99723b4f..fee83f5 100644 --- a/components/feed/core/common/pref_names.h +++ b/components/feed/core/common/pref_names.h
@@ -58,32 +58,22 @@ extern const char kDebugStreamData[]; // The pref names for storing the request schedules. extern const char kRequestSchedule[]; -extern const char kWebFeedsRequestSchedule[]; // The pref name for storing the persistent metrics data. extern const char kMetricsData[]; // The pref name for storing client instance id. extern const char kClientInstanceId[]; // The pref name for the Discover API endpoint override. extern const char kDiscoverAPIEndpointOverride[]; -// If set to true, the WebFeed follow intro bypasses some gates and only checks -// for recommended and scroll status. -extern const char kEnableWebFeedFollowIntroDebug[]; // Random bytes used in generating reliability logging ID. extern const char kReliabilityLoggingIdSalt[]; // Whether the Feed may have data stored, which should be deleted if the Feed // is ever turned off. extern const char kHasStoredData[]; -// The last feed type that the user was viewing. -extern const char kLastSeenFeedType[]; // The pref name for storing user actions. Used for personalizing feed for // unsigned users. The list is sorted by ascenting time stamp. extern const char kFeedOnDeviceUserActionsCollector[]; // The pref name for the keys of the info cards. extern const char kInfoCardStates[]; -// The pref name for whether the user has opened/seen web feed at least once. -extern const char kHasSeenWebFeed[]; -// The pref name for when the user last saw badge animation for web feed. -extern const char kLastBadgeAnimationTime[]; // The pref name for storing the server experiments the client is in. extern const char kExperimentsV3[]; // Contains a dictionary of tracking states for all info cards in the feed.
diff --git a/components/feed/core/v2/api_test/feed_api_reliability_logging_unittest.cc b/components/feed/core/v2/api_test/feed_api_reliability_logging_unittest.cc index 2a07a0d..3bd3131 100644 --- a/components/feed/core/v2/api_test/feed_api_reliability_logging_unittest.cc +++ b/components/feed/core/v2/api_test/feed_api_reliability_logging_unittest.cc
@@ -423,8 +423,7 @@ MakeTypicalInitialModelState( /*first_cluster_id=*/0, kTestTimeEpoch - GetFeedConfig().GetStalenessThreshold( - StreamType(StreamKind::kForYou), - /*is_web_feed_subscriber=*/true) - + StreamType(StreamKind::kForYou)) - base::Minutes(1)), base::DoNothing()); @@ -457,8 +456,7 @@ MakeTypicalInitialModelState( /*first_cluster_id=*/0, kTestTimeEpoch - GetFeedConfig().GetStalenessThreshold( - StreamType(StreamKind::kForYou), - /*is_web_feed_subscriber=*/true) - + StreamType(StreamKind::kForYou)) - base::Minutes(1)), base::DoNothing());
diff --git a/components/feed/core/v2/api_test/feed_api_stream_unittest.cc b/components/feed/core/v2/api_test/feed_api_stream_unittest.cc index 41c40429..051e130f 100644 --- a/components/feed/core/v2/api_test/feed_api_stream_unittest.cc +++ b/components/feed/core/v2/api_test/feed_api_stream_unittest.cc
@@ -663,8 +663,7 @@ TEST_F(FeedApiTest, LoadFromNetworkBecauseStoreIsStale_NetworkStaleAge) { base::TimeDelta default_staleness_threshold = - GetFeedConfig().GetStalenessThreshold(StreamType(StreamKind::kForYou), - /*is_web_feed_subscriber=*/true); + GetFeedConfig().GetStalenessThreshold(StreamType(StreamKind::kForYou)); base::TimeDelta server_staleness_threshold = default_staleness_threshold / 2; { @@ -761,8 +760,7 @@ kTestTimeEpoch - // TODO(crbug.com/407797637): Remove hardcoded false once WebFeed // is fully removed. - GetFeedConfig().GetStalenessThreshold( - GetStreamType(), /*is_web_feed_subscriber=*/false) - + GetFeedConfig().GetStalenessThreshold(GetStreamType()) - base::Minutes(1)), base::DoNothing());
diff --git a/components/feed/core/v2/config.cc b/components/feed/core/v2/config.cc index 91004f6..5058f13 100644 --- a/components/feed/core/v2/config.cc +++ b/components/feed/core/v2/config.cc
@@ -55,14 +55,6 @@ kInterestFeedV2, "content_expiration_threshold_seconds", config.content_expiration_threshold.InSecondsF())); - if (base::FeatureList::IsEnabled(kWebFeedOnboarding)) { - config.subscriptionless_content_expiration_threshold = - base::Seconds(base::GetFieldTrialParamByFeatureAsDouble( - kWebFeedOnboarding, - "subscriptionless_content_expiration_threshold_seconds", - config.subscriptionless_content_expiration_threshold.InSecondsF())); - } - config.background_refresh_window_length = base::Seconds(base::GetFieldTrialParamByFeatureAsDouble( kInterestFeedV2, "background_refresh_window_length_seconds", @@ -113,15 +105,6 @@ kInterestFeedV2, "max_prefetch_image_requests_per_refresh", config.max_prefetch_image_requests_per_refresh); - if (base::FeatureList::IsEnabled(kWebFeedOnboarding)) { - config.subscriptionless_web_feed_stale_content_threshold = - base::Seconds(base::GetFieldTrialParamByFeatureAsDouble( - kWebFeedOnboarding, - "subscriptionless_web_feed_stale_content_threshold_seconds", - config.subscriptionless_web_feed_stale_content_threshold - .InSecondsF())); - } - // Erase any capabilities with "enable_CAPABILITY = false" set. base::EraseIf(config.experimental_capabilities, CapabilityDisabled); @@ -169,15 +152,9 @@ Config::Config(const Config& other) = default; Config::~Config() = default; -base::TimeDelta Config::GetStalenessThreshold(const StreamType& stream_type, - bool has_subscriptions) const { - if (stream_type.IsForYou()) { - return stale_content_threshold; - } - if (has_subscriptions) { - return web_feed_stale_content_threshold; - } - return subscriptionless_web_feed_stale_content_threshold; +base::TimeDelta Config::GetStalenessThreshold( + const StreamType& stream_type) const { + return stale_content_threshold; } } // namespace feed
diff --git a/components/feed/core/v2/config.h b/components/feed/core/v2/config.h index 4c45783..432c43ba 100644 --- a/components/feed/core/v2/config.h +++ b/components/feed/core/v2/config.h
@@ -26,9 +26,6 @@ base::TimeDelta stale_content_threshold = base::Hours(24); // Content older than this threshold will not be shown to the user. base::TimeDelta content_expiration_threshold = base::Hours(48); - // For users with no follows, content older than this will not be shown. - base::TimeDelta subscriptionless_content_expiration_threshold = - base::Days(14); // How long the window is for background refresh tasks. If the task cannot be // scheduled in the window, the background refresh is aborted. base::TimeDelta background_refresh_window_length = base::Hours(24); @@ -73,10 +70,6 @@ // How long before Web Feed content is considered stale. base::TimeDelta web_feed_stale_content_threshold = base::Hours(1); - // How long before Web Feed content is considered stale if there are no - // subscriptions. - base::TimeDelta subscriptionless_web_feed_stale_content_threshold = - base::Days(7); // TimeDelta after startup to fetch recommended and subscribed Web Feeds if // they are stale. If zero, no fetching is done. // This delay is also used to trigger retrying stored follow/unfollow requests @@ -123,8 +116,7 @@ Config(const Config& other); ~Config(); - base::TimeDelta GetStalenessThreshold(const StreamType& stream_type, - bool is_web_feed_subscriber) const; + base::TimeDelta GetStalenessThreshold(const StreamType& stream_type) const; }; // Gets the current configuration.
diff --git a/components/feed/core/v2/feed_stream.cc b/components/feed/core/v2/feed_stream.cc index ca62071d..6394607 100644 --- a/components/feed/core/v2/feed_stream.cc +++ b/components/feed/core/v2/feed_stream.cc
@@ -546,11 +546,6 @@ return true; } -bool FeedStream::IsWebFeedEnabled() { - return feed::IsWebFeedEnabledForLocale(delegate_->GetCountry()) && - !base::FeatureList::IsEnabled(kWebFeedKillSwitch); -} - void FeedStream::EnabledPreferencesChanged() { // Assume there might be stored data if the Feed is ever enabled. if (IsEnabledAndVisible())
diff --git a/components/feed/core/v2/feed_stream.h b/components/feed/core/v2/feed_stream.h index ba385d1f..fa6d5c6 100644 --- a/components/feed/core/v2/feed_stream.h +++ b/components/feed/core/v2/feed_stream.h
@@ -333,8 +333,6 @@ bool IsEnabledAndVisible(); - bool IsWebFeedEnabled(); - PrefService* profile_prefs() const { return profile_prefs_; } base::WeakPtr<FeedStream> GetWeakPtr() {
diff --git a/components/feed/core/v2/prefs.cc b/components/feed/core/v2/prefs.cc index f174687..1e97fd9 100644 --- a/components/feed/core/v2/prefs.cc +++ b/components/feed/core/v2/prefs.cc
@@ -22,8 +22,6 @@ switch (task_id) { case feed::RefreshTaskId::kRefreshForYouFeed: return kRequestSchedule; - case feed::RefreshTaskId::kRefreshWebFeed: - return kWebFeedsRequestSchedule; } }
diff --git a/components/feed/core/v2/proto_util.cc b/components/feed/core/v2/proto_util.cc index accd211..8a73934 100644 --- a/components/feed/core/v2/proto_util.cc +++ b/components/feed/core/v2/proto_util.cc
@@ -150,10 +150,8 @@ feed_request.add_client_capability(capability); feed_request.add_client_capability(Capability::READ_LATER); - // Cormorant is only enabled for en.* locales - if (feed::IsCormorantEnabledForLocale(request_metadata.country)) { - feed_request.add_client_capability(Capability::OPEN_WEB_FEED_COMMAND); - } + // TODO(crbug.com/407797637): remove OPEN_WEB_FEED_COMMAND from + // components/feed/core/proto/v2/wire/capability.proto if (base::FeatureList::IsEnabled(kPersonalizeFeedUnsignedUsers)) { feed_request.add_client_capability(Capability::ON_DEVICE_USER_PROFILE);
diff --git a/components/feed/core/v2/public/stream_type.cc b/components/feed/core/v2/public/stream_type.cc index a447f4e..97d2ae1 100644 --- a/components/feed/core/v2/public/stream_type.cc +++ b/components/feed/core/v2/public/stream_type.cc
@@ -22,8 +22,6 @@ switch (task_id) { case RefreshTaskId::kRefreshForYouFeed: return StreamType(StreamKind::kForYou); - case RefreshTaskId::kRefreshWebFeed: - return StreamType(StreamKind::kFollowing); } }
diff --git a/components/feed/core/v2/public/types.h b/components/feed/core/v2/public/types.h index a3df1ec0..8cac873 100644 --- a/components/feed/core/v2/public/types.h +++ b/components/feed/core/v2/public/types.h
@@ -38,9 +38,6 @@ enum class RefreshTaskId { kRefreshForYouFeed, - // TODO(crbug.com/40158714): Refresh is not currently used for the Web Feed. - // Remove this code if we don't need it. - kRefreshWebFeed, }; enum class AccountTokenFetchStatus {
diff --git a/components/feed/core/v2/scheduling.cc b/components/feed/core/v2/scheduling.cc index 5a2aa9c..75796e7e 100644 --- a/components/feed/core/v2/scheduling.cc +++ b/components/feed/core/v2/scheduling.cc
@@ -110,15 +110,14 @@ bool ShouldWaitForNewContent(const feedstore::Metadata& metadata, const StreamType& stream_type, - base::TimeDelta content_age, - bool is_web_feed_subscriber) { + base::TimeDelta content_age) { const feedstore::Metadata::StreamMetadata* stream_metadata = feedstore::FindMetadataForStream(metadata, stream_type); if (stream_metadata && stream_metadata->is_known_stale()) return true; - base::TimeDelta staleness_threshold = GetFeedConfig().GetStalenessThreshold( - stream_type, is_web_feed_subscriber); + base::TimeDelta staleness_threshold = + GetFeedConfig().GetStalenessThreshold(stream_type); if (stream_metadata && stream_metadata->has_content_lifetime()) { staleness_threshold = GetThresholdTime( staleness_threshold, @@ -130,18 +129,12 @@ bool ContentInvalidFromAge(const feedstore::Metadata& metadata, const StreamType& stream_type, - base::TimeDelta content_age, - bool is_web_feed_subscriber) { + base::TimeDelta content_age) { const feedstore::Metadata::StreamMetadata* stream_metadata = feedstore::FindMetadataForStream(metadata, stream_type); base::TimeDelta content_expiration_threshold = GetFeedConfig().content_expiration_threshold; - if (base::FeatureList::IsEnabled(kWebFeedOnboarding) && - !is_web_feed_subscriber && stream_type.IsWebFeed()) { - content_expiration_threshold = - GetFeedConfig().subscriptionless_content_expiration_threshold; - } if (stream_metadata && stream_metadata->has_content_lifetime()) { content_expiration_threshold = GetThresholdTime( content_expiration_threshold,
diff --git a/components/feed/core/v2/scheduling.h b/components/feed/core/v2/scheduling.h index 0a2dd442..5352e6d8 100644 --- a/components/feed/core/v2/scheduling.h +++ b/components/feed/core/v2/scheduling.h
@@ -47,13 +47,11 @@ // Returns whether we should wait for new content before showing stream content. bool ShouldWaitForNewContent(const feedstore::Metadata& metadata, const StreamType& stream_type, - base::TimeDelta content_age, - bool is_web_feed_subscriber); + base::TimeDelta content_age); bool ContentInvalidFromAge(const feedstore::Metadata& metadata, const StreamType& stream_type, - base::TimeDelta content_age, - bool is_web_feed_subscriber); + base::TimeDelta content_age); } // namespace feed #endif // COMPONENTS_FEED_CORE_V2_SCHEDULING_H_
diff --git a/components/feed/core/v2/scheduling_unittest.cc b/components/feed/core/v2/scheduling_unittest.cc index 3491a4d..4191837 100644 --- a/components/feed/core/v2/scheduling_unittest.cc +++ b/components/feed/core/v2/scheduling_unittest.cc
@@ -183,123 +183,103 @@ }; TEST_F(ContentLifetimeTest, ShouldWaitForNewContent_DefaultThreshold) { - EXPECT_FALSE(ShouldWaitForNewContent( - metadata_, StreamType(StreamKind::kForYou), kDefaultStaleContentThreshold, - /*is_web_feed_subscriber=*/true)); + EXPECT_FALSE(ShouldWaitForNewContent(metadata_, + StreamType(StreamKind::kForYou), + kDefaultStaleContentThreshold)); + EXPECT_TRUE( + ShouldWaitForNewContent(metadata_, StreamType(StreamKind::kForYou), + WithEpsilon(kDefaultStaleContentThreshold))); EXPECT_TRUE(ShouldWaitForNewContent( - metadata_, StreamType(StreamKind::kForYou), - WithEpsilon(kDefaultStaleContentThreshold), true)); - EXPECT_TRUE(ShouldWaitForNewContent( - metadata_, StreamType(StreamKind::kForYou), base::Hours(5), - /*is_web_feed_subscriber=*/true)); + metadata_, StreamType(StreamKind::kForYou), base::Hours(5))); EXPECT_FALSE(ShouldWaitForNewContent( - metadata_, StreamType(StreamKind::kForYou), base::Hours(3), - /*is_web_feed_subscriber=*/true)); + metadata_, StreamType(StreamKind::kForYou), base::Hours(3))); // If the web feed onboarding feature is turned off, then we should return // true even if the user is not subscribed. EXPECT_TRUE(ShouldWaitForNewContent( - metadata_, StreamType(StreamKind::kForYou), base::Days(8), - /*is_web_feed_subscriber=*/false)); + metadata_, StreamType(StreamKind::kForYou), base::Days(8))); EXPECT_TRUE(ShouldWaitForNewContent( - metadata_, StreamType(StreamKind::kForYou), base::Days(6), - /*is_web_feed_subscriber=*/false)); + metadata_, StreamType(StreamKind::kForYou), base::Days(6))); } TEST_F(ContentLifetimeTest, ShouldWaitForNewContent_ServerThreshold_Valid) { set_stale_age(base::Minutes(60)); EXPECT_TRUE(ShouldWaitForNewContent( - metadata_, StreamType(StreamKind::kForYou), base::Minutes(61), - /*is_web_feed_subscriber=*/true)); + metadata_, StreamType(StreamKind::kForYou), base::Minutes(61))); EXPECT_FALSE(ShouldWaitForNewContent( - metadata_, StreamType(StreamKind::kForYou), base::Minutes(59), - /*is_web_feed_subscriber=*/true)); + metadata_, StreamType(StreamKind::kForYou), base::Minutes(59))); } TEST_F(ContentLifetimeTest, ShouldWaitForNewContent_ServerThreshold_Invalid) { // We ignore stale ages greater than the default. - EXPECT_TRUE(ShouldWaitForNewContent( - metadata_, StreamType(StreamKind::kForYou), - WithEpsilon(kDefaultStaleContentThreshold), true)); + EXPECT_TRUE( + ShouldWaitForNewContent(metadata_, StreamType(StreamKind::kForYou), + WithEpsilon(kDefaultStaleContentThreshold))); set_stale_age(kDefaultStaleContentThreshold + base::Minutes(1)); EXPECT_TRUE( ShouldWaitForNewContent(metadata_, StreamType(StreamKind::kForYou), - WithEpsilon(kDefaultStaleContentThreshold), - /*is_web_feed_subscriber=*/true)); + WithEpsilon(kDefaultStaleContentThreshold))); // We ignore zero durations. set_stale_age(base::Days(0)); - EXPECT_FALSE(ShouldWaitForNewContent( - metadata_, StreamType(StreamKind::kForYou), kDefaultStaleContentThreshold, - /*is_web_feed_subscriber=*/true)); - EXPECT_TRUE(ShouldWaitForNewContent( - metadata_, StreamType(StreamKind::kForYou), - WithEpsilon(kDefaultStaleContentThreshold), true)); + EXPECT_FALSE(ShouldWaitForNewContent(metadata_, + StreamType(StreamKind::kForYou), + kDefaultStaleContentThreshold)); + EXPECT_TRUE( + ShouldWaitForNewContent(metadata_, StreamType(StreamKind::kForYou), + WithEpsilon(kDefaultStaleContentThreshold))); // We ignore negative durations. set_stale_age(base::Days(-1)); - EXPECT_FALSE(ShouldWaitForNewContent( - metadata_, StreamType(StreamKind::kForYou), kDefaultStaleContentThreshold, - /*is_web_feed_subscriber=*/true)); + EXPECT_FALSE(ShouldWaitForNewContent(metadata_, + StreamType(StreamKind::kForYou), + kDefaultStaleContentThreshold)); EXPECT_TRUE( ShouldWaitForNewContent(metadata_, StreamType(StreamKind::kForYou), - WithEpsilon(kDefaultStaleContentThreshold), - /*is_web_feed_subscriber=*/true)); + WithEpsilon(kDefaultStaleContentThreshold))); } TEST_F(ContentLifetimeTest, ContentInvalidFromAge_DefaultThreshold) { EXPECT_FALSE(ContentInvalidFromAge(metadata_, StreamType(StreamKind::kForYou), - kDefaultContentExpiration, - /*is_web_feed_subscriber=*/true)); + kDefaultContentExpiration)); EXPECT_TRUE( ContentInvalidFromAge(metadata_, StreamType(StreamKind::kForYou), - kDefaultContentExpiration + base::Milliseconds(1), - /*is_web_feed_subscriber=*/true)); + kDefaultContentExpiration + base::Milliseconds(1))); EXPECT_TRUE(ContentInvalidFromAge(metadata_, StreamType(StreamKind::kForYou), - base::Hours(25), - /*is_web_feed_subscriber=*/true)); + base::Hours(25))); EXPECT_FALSE(ContentInvalidFromAge(metadata_, StreamType(StreamKind::kForYou), - base::Hours(23), - /*is_web_feed_subscriber=*/true)); + base::Hours(23))); } TEST_F(ContentLifetimeTest, ContentInvalidFromAge_ServerThreshold_Valid) { set_invalid_age(base::Minutes(60)); EXPECT_TRUE(ContentInvalidFromAge(metadata_, StreamType(StreamKind::kForYou), - base::Minutes(61), - /*is_web_feed_subscriber=*/true)); + base::Minutes(61))); EXPECT_FALSE(ContentInvalidFromAge(metadata_, StreamType(StreamKind::kForYou), - base::Minutes(59), - /*is_web_feed_subscriber=*/true)); + base::Minutes(59))); } TEST_F(ContentLifetimeTest, ContentInvalidFromAge_ServerThreshold_Invalid) { // We ignore stale ages greater than the default. EXPECT_TRUE(ContentInvalidFromAge(metadata_, StreamType(StreamKind::kForYou), - WithEpsilon(kDefaultContentExpiration), - /*is_web_feed_subscriber=*/true)); + WithEpsilon(kDefaultContentExpiration))); set_invalid_age(kDefaultContentExpiration + base::Minutes(1)); EXPECT_TRUE(ContentInvalidFromAge(metadata_, StreamType(StreamKind::kForYou), - WithEpsilon(kDefaultContentExpiration), - /*is_web_feed_subscriber=*/true)); + WithEpsilon(kDefaultContentExpiration))); // We ignore zero durations. set_invalid_age(base::Days(0)); EXPECT_FALSE(ContentInvalidFromAge(metadata_, StreamType(StreamKind::kForYou), - kDefaultContentExpiration, - /*is_web_feed_subscriber=*/true)); + kDefaultContentExpiration)); EXPECT_TRUE(ContentInvalidFromAge( metadata_, StreamType(StreamKind::kForYou), - WithEpsilon(kDefaultSubscriptionlessContentExpiration), - /*is_web_feed_subscriber=*/true)); + WithEpsilon(kDefaultSubscriptionlessContentExpiration))); // We ignore negative durations. set_invalid_age(base::Days(-1)); EXPECT_FALSE(ContentInvalidFromAge(metadata_, StreamType(StreamKind::kForYou), - kDefaultContentExpiration, - /*is_web_feed_subscriber=*/true)); + kDefaultContentExpiration)); EXPECT_TRUE(ContentInvalidFromAge(metadata_, StreamType(StreamKind::kForYou), - WithEpsilon(kDefaultContentExpiration), - /*is_web_feed_subscriber=*/true)); + WithEpsilon(kDefaultContentExpiration))); } } // namespace
diff --git a/components/feed/core/v2/tasks/load_stream_from_store_task.cc b/components/feed/core/v2/tasks/load_stream_from_store_task.cc index d8de872..eb183bc 100644 --- a/components/feed/core/v2/tasks/load_stream_from_store_task.cc +++ b/components/feed/core/v2/tasks/load_stream_from_store_task.cc
@@ -85,10 +85,7 @@ const feedstore::Metadata& metadata = feed_stream_->GetMetadata(); - // TODO(crbug.com/407797637): Remove hardcoded false once WebFeed is fully - // removed. - if (ContentInvalidFromAge(metadata, result.stream_type, content_age_, - /*is_web_feed_subscriber=*/false)) { + if (ContentInvalidFromAge(metadata, result.stream_type, content_age_)) { Complete(LoadStreamStatus::kDataInStoreIsExpired, feedwire::DiscoverCardReadCacheResult::STALE); return; @@ -96,8 +93,7 @@ if (content_age_.is_negative()) { stale_reason_ = LoadStreamStatus::kDataInStoreIsStaleTimestampInFuture; } else if (ShouldWaitForNewContent(metadata, result.stream_type, - content_age_, - /*is_web_feed_subscriber=*/false)) { + content_age_)) { stale_reason_ = LoadStreamStatus::kDataInStoreIsStale; } else if (missed_last_refresh_) { stale_reason_ = LoadStreamStatus::kDataInStoreStaleMissedLastRefresh;
diff --git a/components/feed/feed_feature_list.cc b/components/feed/feed_feature_list.cc index 1fc4a9a8..317e25a 100644 --- a/components/feed/feed_feature_list.cc +++ b/components/feed/feed_feature_list.cc
@@ -29,12 +29,6 @@ BASE_FEATURE(kFeedImageMemoryCacheSizePercentage, base::FEATURE_DISABLED_BY_DEFAULT); -BASE_FEATURE(kWebFeedOnboarding, base::FEATURE_ENABLED_BY_DEFAULT); - -bool IsCormorantEnabledForLocale(std::string country) { - return IsWebFeedEnabledForLocale(country); -} - BASE_FEATURE(kPersonalizeFeedUnsignedUsers, base::FEATURE_DISABLED_BY_DEFAULT); // TODO(crbug.com/40764861): Remove this helper, directly use kSignin instead. @@ -56,8 +50,6 @@ BASE_FEATURE(kFeedContainment, base::FEATURE_ENABLED_BY_DEFAULT); -BASE_FEATURE(kWebFeedKillSwitch, base::FEATURE_ENABLED_BY_DEFAULT); - BASE_FEATURE(kFeedRecyclerBinderUnmountOnDetach, base::FEATURE_DISABLED_BY_DEFAULT); @@ -67,11 +59,4 @@ BASE_FEATURE(kAndroidOpenIncognitoAsWindow, base::FEATURE_DISABLED_BY_DEFAULT); -bool IsWebFeedEnabledForLocale(const std::string& country) { - const std::vector<std::string> launched_countries = {"AU", "CA", "GB", - "NZ", "US", "ZA"}; - return std::ranges::contains(launched_countries, country) && - !base::FeatureList::IsEnabled(kWebFeedKillSwitch); -} - } // namespace feed
diff --git a/components/feed/feed_feature_list.h b/components/feed/feed_feature_list.h index 34bd5cc..aec0f57 100644 --- a/components/feed/feed_feature_list.h +++ b/components/feed/feed_feature_list.h
@@ -5,8 +5,6 @@ #ifndef COMPONENTS_FEED_FEED_FEATURE_LIST_H_ #define COMPONENTS_FEED_FEED_FEATURE_LIST_H_ -#include <string> - #include "base/component_export.h" #include "base/feature_list.h" #include "base/time/time.h" @@ -37,13 +35,6 @@ COMPONENT_EXPORT(COMPONENTS_FEED_FEATURE_LIST) BASE_DECLARE_FEATURE(kFeedImageMemoryCacheSizePercentage); -// Feature that provides the user assistance in using the web feed. -COMPONENT_EXPORT(COMPONENTS_FEED_FEATURE_LIST) -BASE_DECLARE_FEATURE(kWebFeedOnboarding); - -COMPONENT_EXPORT(COMPONENTS_FEED_FEATURE_LIST) -bool IsCormorantEnabledForLocale(std::string country); - // Personalize feed for unsigned users. COMPONENT_EXPORT(COMPONENTS_FEED_FEATURE_LIST) BASE_DECLARE_FEATURE(kPersonalizeFeedUnsignedUsers); @@ -78,10 +69,6 @@ COMPONENT_EXPORT(COMPONENTS_FEED_FEATURE_LIST) BASE_DECLARE_FEATURE(kFeedContainment); -// Kill-switch for the web feed feature. -COMPONENT_EXPORT(COMPONENTS_FEED_FEATURE_LIST) -BASE_DECLARE_FEATURE(kWebFeedKillSwitch); - // Feature that unmount RecyclerBinder on view detach to fix a memory leak. COMPONENT_EXPORT(COMPONENTS_FEED_FEATURE_LIST) BASE_DECLARE_FEATURE(kFeedRecyclerBinderUnmountOnDetach); @@ -98,9 +85,6 @@ COMPONENT_EXPORT(COMPONENTS_FEED_FEATURE_LIST) BASE_DECLARE_FEATURE(kAndroidOpenIncognitoAsWindow); -COMPONENT_EXPORT(COMPONENTS_FEED_FEATURE_LIST) -bool IsWebFeedEnabledForLocale(const std::string& country); - } // namespace feed #endif // COMPONENTS_FEED_FEED_FEATURE_LIST_H_
diff --git a/components/feedback/BUILD.gn b/components/feedback/BUILD.gn index 1f90b6e0..6036b43 100644 --- a/components/feedback/BUILD.gn +++ b/components/feedback/BUILD.gn
@@ -32,6 +32,7 @@ public_deps = [ "//base" ] deps = [ + "//build:branding_buildflags", "//components/feedback/proto", "//components/feedback/redaction_tool", "//components/keyed_service/core",
diff --git a/components/feedback/feedback_uploader.cc b/components/feedback/feedback_uploader.cc index cfcf3880..546d667 100644 --- a/components/feedback/feedback_uploader.cc +++ b/components/feedback/feedback_uploader.cc
@@ -14,6 +14,8 @@ #include "base/task/single_thread_task_runner.h" #include "base/task/task_traits.h" #include "base/task/thread_pool.h" +#include "build/branding_buildflags.h" +#include "build/build_config.h" #include "components/feedback/features.h" #include "components/feedback/feedback_report.h" #include "components/feedback/feedback_switches.h" @@ -42,8 +44,12 @@ constexpr base::FilePath::CharType kFeedbackReportPath[] = FILE_PATH_LITERAL("Feedback Reports"); +#if BUILDFLAG(GOOGLE_CHROME_BRANDING) constexpr char kFeedbackPostUrl[] = "https://www.google.com/tools/feedback/chrome/__submit"; +#else +constexpr char kFeedbackPostUrl[] = ""; +#endif constexpr char kProtoBufMimeType[] = "application/x-protobuf"; @@ -345,4 +351,10 @@ } } +GURL FeedbackUploader::SetFeedbackGURLForTesting(GURL url) { + GURL previous_url = feedback_post_url_; + feedback_post_url_ = std::move(url); + return previous_url; +} + } // namespace feedback
diff --git a/components/feedback/feedback_uploader.h b/components/feedback/feedback_uploader.h index 7c46f8d4..2ca5f0a 100644 --- a/components/feedback/feedback_uploader.h +++ b/components/feedback/feedback_uploader.h
@@ -112,6 +112,11 @@ return report_being_dispatched_; } + // For testing feedback upload in non-branded builds with a real URL. Returns + // the previous URL so that tests can restore on TearDown() to avoid leaking + // state. + GURL SetFeedbackGURLForTesting(GURL url); + private: friend class FeedbackUploaderTest; @@ -164,7 +169,7 @@ scoped_refptr<FeedbackReport> report_being_dispatched_; - const GURL feedback_post_url_; + GURL feedback_post_url_; // Priority queue of reports prioritized by the time the report is supposed // to be uploaded at.
diff --git a/components/feedback/feedback_uploader_dispatch_unittest.cc b/components/feedback/feedback_uploader_dispatch_unittest.cc index 77fdf9a4..7f3788c 100644 --- a/components/feedback/feedback_uploader_dispatch_unittest.cc +++ b/components/feedback/feedback_uploader_dispatch_unittest.cc
@@ -50,7 +50,9 @@ scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory) : FeedbackUploader(is_off_the_record, state_path, - std::move(url_loader_factory)) {} + std::move(url_loader_factory)) { + SetFeedbackGURLForTesting(GURL(kFeedbackPostUrl)); + } TestFeedbackUploader(const TestFeedbackUploader&) = delete; TestFeedbackUploader& operator=(const TestFeedbackUploader&) = delete;
diff --git a/components/guest_view/browser/guest_view_base.cc b/components/guest_view/browser/guest_view_base.cc index da57530..4092932e 100644 --- a/components/guest_view/browser/guest_view_base.cc +++ b/components/guest_view/browser/guest_view_base.cc
@@ -26,6 +26,7 @@ #include "content/public/browser/render_view_host.h" #include "content/public/browser/render_widget_host.h" #include "content/public/browser/render_widget_host_view.h" +#include "content/public/browser/security_principal.h" #include "content/public/browser/site_instance.h" #include "content/public/browser/web_contents.h" #include "content/public/common/content_features.h" @@ -598,7 +599,10 @@ } const GURL& GuestViewBase::GetOwnerSiteURL() const { - return owner_rfh()->GetSiteInstance()->GetSiteURL(); + return owner_rfh() + ->GetSiteInstance() + ->GetSecurityPrincipal() + .GetDeprecatedSiteURL(); } void GuestViewBase::SetAttachParams(const base::DictValue& params) {
diff --git a/components/guest_view/browser/slim_web_view/slim_web_view_guest.cc b/components/guest_view/browser/slim_web_view/slim_web_view_guest.cc index 856561d..c545e6c8 100644 --- a/components/guest_view/browser/slim_web_view/slim_web_view_guest.cc +++ b/components/guest_view/browser/slim_web_view/slim_web_view_guest.cc
@@ -23,6 +23,7 @@ #include "content/public/browser/navigation_entry.h" #include "content/public/browser/navigation_handle.h" #include "content/public/browser/render_process_host.h" +#include "content/public/browser/security_principal.h" #include "content/public/browser/storage_partition_config.h" #include "content/public/browser/web_contents.h" #include "content/public/common/content_features.h" @@ -530,10 +531,14 @@ bool persist_storage = false; ParsePartitionParam(create_params, &storage_partition_id, &persist_storage); content::StoragePartitionConfig partition_config = - content::StoragePartitionConfig::Create( - browser_context(), - owner_rfh()->GetSiteInstance()->GetSiteURL().GetHost(), - storage_partition_id, !persist_storage); + content::StoragePartitionConfig::Create(browser_context(), + owner_rfh() + ->GetSiteInstance() + ->GetSecurityPrincipal() + .GetDeprecatedSiteURL() + .GetHost(), + storage_partition_id, + !persist_storage); scoped_refptr<content::SiteInstance> guest_site_instance = content::SiteInstance::CreateForGuest(browser_context(),
diff --git a/components/mirroring/service/mirroring_features.cc b/components/mirroring/service/mirroring_features.cc index 7e540bf1..40cf858 100644 --- a/components/mirroring/service/mirroring_features.cc +++ b/components/mirroring/service/mirroring_features.cc
@@ -15,5 +15,8 @@ // Enables 60FPS support for Cast Mirroring sessions. BASE_FEATURE(kCastStreaming60fps, base::FEATURE_DISABLED_BY_DEFAULT); +BASE_FEATURE(kCastStreamingOfferHardwareFirst, + base::FEATURE_ENABLED_BY_DEFAULT); + } // namespace features } // namespace mirroring
diff --git a/components/mirroring/service/mirroring_features.h b/components/mirroring/service/mirroring_features.h index 3b96e87..77b4cbca 100644 --- a/components/mirroring/service/mirroring_features.h +++ b/components/mirroring/service/mirroring_features.h
@@ -18,6 +18,11 @@ COMPONENT_EXPORT(MIRRORING_SERVICE) BASE_DECLARE_FEATURE(kCastStreaming60fps); +// Killswitch for two stage offer change. +// crbug.com/510476629 +COMPONENT_EXPORT(MIRRORING_SERVICE) +BASE_DECLARE_FEATURE(kCastStreamingOfferHardwareFirst); + } // namespace features } // namespace mirroring
diff --git a/components/mirroring/service/openscreen_session_host.cc b/components/mirroring/service/openscreen_session_host.cc index 0e7b6ac..4b3cca8 100644 --- a/components/mirroring/service/openscreen_session_host.cc +++ b/components/mirroring/service/openscreen_session_host.cc
@@ -372,6 +372,7 @@ openscreen::cast::SenderSession::ConfiguredSenders senders, Recommendations capture_recommendations) { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + offering_fallback_codecs_ = false; if (state_ == State::kStopped) { return; } @@ -606,10 +607,23 @@ ReportAndLogError(SessionError::ANSWER_NOT_OK, error.ToString()); return; - case openscreen::Error::Code::kNoStreamSelected: + case openscreen::Error::Code::kNoStreamSelected: { + const bool should_send_fallback_offer = + state_ == State::kMirroring && !offering_fallback_codecs_ && + base::FeatureList::IsEnabled( + mirroring::features::kCastStreamingOfferHardwareFirst); + if (should_send_fallback_offer) { + logger_.LogInfo( + "No stream selected for ideal, hardware-accelerated codecs. " + "Attempting fallback to software codecs."); + offering_fallback_codecs_ = true; + NegotiateMirroring(); + return; + } ReportAndLogError(SessionError::ANSWER_NO_AUDIO_OR_VIDEO, error.ToString()); return; + } // If remoting is not supported, the session will continue but // OnCapabilitiesDetermined() will never be called and the media remoter @@ -862,6 +876,7 @@ } state_ = State::kStopped; + offering_fallback_codecs_ = false; StopStreaming(); bandwidth_update_timer_.Stop(); @@ -1133,6 +1148,7 @@ } if (session_params_.type != SessionType::AUDIO_ONLY) { + bool offered_hardware_codec = false; // First, check if hardware encoders are available and should be offered. for (auto codec : kSupportedVideoCodecs) { if (media::cast::encoding_support::IsHardwareEnabled( @@ -1142,18 +1158,25 @@ config.use_hardware_encoder = true; last_offered_video_configs_.push_back(config); video_configs.push_back(ToOpenscreenVideoConfig(config)); + offered_hardware_codec = true; } } - // Then add any enabled software encoders. - for (auto codec : kSupportedVideoCodecs) { - if (!media::cast::encoding_support::IsHardwareEnabled( - codec, supported_profiles_) && - media::cast::encoding_support::IsSoftwareEnabled(codec)) { - auto config = MirrorSettings::GetDefaultVideoConfig(codec); - UpdateConfigUsingSessionParameters(session_params_, config); - last_offered_video_configs_.push_back(config); - video_configs.push_back(ToOpenscreenVideoConfig(config)); + const bool should_offer_software = + !base::FeatureList::IsEnabled( + mirroring::features::kCastStreamingOfferHardwareFirst) || + offering_fallback_codecs_ || !offered_hardware_codec; + + if (should_offer_software) { + for (auto codec : kSupportedVideoCodecs) { + if (!media::cast::encoding_support::IsHardwareEnabled( + codec, supported_profiles_) && + media::cast::encoding_support::IsSoftwareEnabled(codec)) { + auto config = MirrorSettings::GetDefaultVideoConfig(codec); + UpdateConfigUsingSessionParameters(session_params_, config); + last_offered_video_configs_.push_back(config); + video_configs.push_back(ToOpenscreenVideoConfig(config)); + } } } }
diff --git a/components/mirroring/service/openscreen_session_host.h b/components/mirroring/service/openscreen_session_host.h index 5f8b0ad..e229b3d 100644 --- a/components/mirroring/service/openscreen_session_host.h +++ b/components/mirroring/service/openscreen_session_host.h
@@ -319,6 +319,7 @@ // video codec configurations. std::optional<media::cast::FrameSenderConfig> last_offered_audio_config_; std::vector<media::cast::FrameSenderConfig> last_offered_video_configs_; + bool offering_fallback_codecs_ = false; // Created after OFFER/ANSWER exchange succeeds. std::unique_ptr<AudioRtpStream> audio_stream_;
diff --git a/components/mirroring/service/openscreen_session_host_unittest.cc b/components/mirroring/service/openscreen_session_host_unittest.cc index fbd823ed..9fb86fd1 100644 --- a/components/mirroring/service/openscreen_session_host_unittest.cc +++ b/components/mirroring/service/openscreen_session_host_unittest.cc
@@ -469,6 +469,12 @@ Mock::VerifyAndClear(&remoting_source_); } + void SignalNoStreamSelected() { + session_host_->OnError( + session_host_->session_.get(), + openscreen::Error(openscreen::Error::Code::kNoStreamSelected)); + } + void SendRemotingCapabilities() { static const openscreen::cast::RemotingCapabilities capabilities{ {openscreen::cast::AudioCapability::kBaselineSet, @@ -1056,6 +1062,87 @@ EXPECT_FALSE(session_host().GetMirroringStats().empty()); } +TEST_F(OpenscreenSessionHostTest, TwoStageNegotiationFallback) { + // 1. Force enable VP9, H264 for this test. + std::vector<base::test::FeatureRef> features = { + media::kCastStreamingVp9, + mirroring::features::kCastStreamingOfferHardwareFirst}; +#if BUILDFLAG(IS_WIN) + features.push_back(media::kCastStreamingWinHardwareH264); +#endif + base::test::ScopedFeatureList feature_list; + feature_list.InitWithFeatures(features, {}); + + // 2. Create the session. (Triggers initial asynchronous offer with no HW + // profiles). + CreateSession(SessionType::VIDEO_ONLY); + + // 3. Now set HW profiles to enable HW H264. + SetSupportedProfiles( + std::vector<media::VideoEncodeAccelerator::SupportedProfile>{ + media::VideoEncodeAccelerator::SupportedProfile( + media::VideoCodecProfile::H264PROFILE_MIN, + gfx::Size{1920, 1080})}); + + // 4. Manually trigger "Stage 1" negotiation with HW H264 active. + EXPECT_CALL(*this, OnOutboundMessage(SenderMessage::Type::kOffer)); + NegotiateMirroring(); + task_environment().RunUntilIdle(); + + // Verify Stage 1 only offered HW H264 (no SW VP9). + AssertCodecWasOffered(media::VideoCodec::kH264, true); + const auto& offer1 = std::get<Offer>(last_sent_offer().body); + ASSERT_FALSE(std::any_of( + offer1.video_streams.begin(), offer1.video_streams.end(), + [](const VideoStream& stream) { + return stream.codec == + media::cast::ToOpenscreenVideoCodec(media::VideoCodec::kVP9); + })); + + // 5. Expect a second OFFER when we trigger the fallback (Stage 2). + EXPECT_CALL(*this, OnOutboundMessage(SenderMessage::Type::kOffer)); + + // 6. Trigger the kNoStreamSelected error (receiver rejected H264). + SignalNoStreamSelected(); + task_environment().RunUntilIdle(); + + // 7. Verify that Stage 2 OFFER now contains VP9 SW. + AssertCodecWasOffered(media::VideoCodec::kVP9, false); +} + +TEST_F(OpenscreenSessionHostTest, SingleStageOfferOffersAllSupportedCodecs) { + // 1. Force enable VP9, H264 for this test. + std::vector<base::test::FeatureRef> features = { + media::kCastStreamingVp9, + }; +#if BUILDFLAG(IS_WIN) + features.push_back(media::kCastStreamingWinHardwareH264); +#endif + base::test::ScopedFeatureList feature_list; + feature_list.InitWithFeatures( + features, {mirroring::features::kCastStreamingOfferHardwareFirst}); + + // 2. Create the session. (Triggers initial asynchronous offer with no HW + // profiles). + CreateSession(SessionType::VIDEO_ONLY); + + // 3. Now set HW profiles to enable HW H264. + SetSupportedProfiles( + std::vector<media::VideoEncodeAccelerator::SupportedProfile>{ + media::VideoEncodeAccelerator::SupportedProfile( + media::VideoCodecProfile::H264PROFILE_MIN, + gfx::Size{1920, 1080})}); + + // 4. Manually trigger "Stage 1" negotiation with HW H264 active. + EXPECT_CALL(*this, OnOutboundMessage(SenderMessage::Type::kOffer)); + NegotiateMirroring(); + task_environment().RunUntilIdle(); + + // Verify Stage 1 only offered HW H264 and software VP9; + AssertCodecWasOffered(media::VideoCodec::kH264, true); + AssertCodecWasOffered(media::VideoCodec::kVP9, false); +} + TEST_F(OpenscreenSessionHostTest, CreateRemotingDataStreamSenderReturnsNullBeforeRemoting) { CreateSession(SessionType::AUDIO_AND_VIDEO);
diff --git a/components/optimization_guide/core/BUILD.gn b/components/optimization_guide/core/BUILD.gn index 21103417..c91bb07 100644 --- a/components/optimization_guide/core/BUILD.gn +++ b/components/optimization_guide/core/BUILD.gn
@@ -320,6 +320,8 @@ "model_execution/on_device_model_service_controller.h", "model_execution/on_device_model_validator.cc", "model_execution/on_device_model_validator.h", + "model_execution/on_device_telemetry_logger.cc", + "model_execution/on_device_telemetry_logger.h", "model_execution/performance_class.cc", "model_execution/performance_class.h", "model_execution/repetition_checker.cc",
diff --git a/components/optimization_guide/core/model_execution/manifest_broker/manifest_asset_manager.cc b/components/optimization_guide/core/model_execution/manifest_broker/manifest_asset_manager.cc index c596c35..b971b41 100644 --- a/components/optimization_guide/core/model_execution/manifest_broker/manifest_asset_manager.cc +++ b/components/optimization_guide/core/model_execution/manifest_broker/manifest_asset_manager.cc
@@ -462,6 +462,14 @@ return true; } if (!disk_space_status_.CanSupportOnDemandInstall()) { + std::optional<base::ByteCount> free_space = + disk_space_status_.GetFreeSpace(); + if (free_space) { + base::UmaHistogramCounts100( + "OptimizationGuide.ModelExecution.OnDeviceModelInstallCriteria." + "AtRegistration.DiskSpaceWhenNotEnoughAvailable", + free_space->InGiB()); + } return false; } if (active_assets_by_id_.contains(context.asset_id())) { @@ -588,6 +596,11 @@ } else { context->SetRegistered(); } + base::UmaHistogramBoolean( + "OptimizationGuide.ModelExecution." + "OnDeviceModelInstalledAtRegistrationTime." + + ConvertComponentKeyToUmaModelName(context->asset_id()), + is_already_installed); const proto::OnDemandComponent* component = factory_->manifest().GetAssetByPublicKey(public_key);
diff --git a/components/optimization_guide/core/model_execution/manifest_broker/manifest_asset_manager.h b/components/optimization_guide/core/model_execution/manifest_broker/manifest_asset_manager.h index 209cdb09..812cdd6b 100644 --- a/components/optimization_guide/core/model_execution/manifest_broker/manifest_asset_manager.h +++ b/components/optimization_guide/core/model_execution/manifest_broker/manifest_asset_manager.h
@@ -260,6 +260,7 @@ bool IsFresh() const; bool CanSupportOnDemandInstall() const; bool CanSupportProactiveDownload() const; + std::optional<base::ByteCount> GetFreeSpace() const { return free_space_; } private: std::optional<base::ByteCount> free_space_;
diff --git a/components/optimization_guide/core/model_execution/manifest_broker/manifest_asset_manager_unittest.cc b/components/optimization_guide/core/model_execution/manifest_broker/manifest_asset_manager_unittest.cc index 59ccca9..c00fead 100644 --- a/components/optimization_guide/core/model_execution/manifest_broker/manifest_asset_manager_unittest.cc +++ b/components/optimization_guide/core/model_execution/manifest_broker/manifest_asset_manager_unittest.cc
@@ -275,18 +275,37 @@ DummyAsset asset = DummyAsset::For("compose"); usage_tracker_.OnDeviceEligibleUseCaseUsed(asset.use_case); MakeAssetsInstallable(DummyManifest().Add(asset)); + + // First startup to install the asset. Startup(); EXPECT_TRUE(component_state_.WaitForRegistration(asset.ToInstallTarget())); + SimulateShutdown(); + + // Second startup. Now it is already installed. + base::HistogramTester histogram_tester; + UpdateManifest(DummyManifest().Add(asset)); + Startup(); + EXPECT_TRUE(component_state_.WaitForRegistration(asset.ToInstallTarget())); + // Because it was already installed, it shouldn't request an on-demand update. EXPECT_FALSE(component_state_.WasOnDemandUpdateRequested(asset.public_key)); + histogram_tester.ExpectUniqueSample( + "OptimizationGuide.ModelExecution." + "OnDeviceModelInstalledAtRegistrationTime.Unknown", + true, 1); } TEST_F(ManifestAssetManagerTest, NotYetInstalledFlow) { + base::HistogramTester histogram_tester; DummyAsset asset = DummyAsset::For("compose"); usage_tracker_.OnDeviceEligibleUseCaseUsed(asset.use_case); UpdateManifest(DummyManifest().Add(asset)); Startup(); EXPECT_TRUE(component_state_.WaitForRegistration(asset.ToInstallTarget())); + histogram_tester.ExpectUniqueSample( + "OptimizationGuide.ModelExecution." + "OnDeviceModelInstalledAtRegistrationTime.Unknown", + false, 1); } TEST_F(ManifestAssetManagerTest, SimulatesAssetReady) { @@ -612,6 +631,7 @@ } TEST_F(ManifestAssetManagerTest, DoesNotInstallWhenNotEnoughDiskSpace) { + base::HistogramTester histogram_tester; DummyAsset asset = DummyAsset::For("compose"); usage_tracker_.OnDeviceEligibleUseCaseUsed(asset.use_case); // 20gb is the default in `IsFreeDiskSpaceSufficientForOnDeviceModelInstall`. @@ -621,6 +641,10 @@ Startup(); task_environment_.RunUntilIdle(); EXPECT_FALSE(component_state_.IsRegistered(asset.ToInstallTarget())); + histogram_tester.ExpectUniqueSample( + "OptimizationGuide.ModelExecution.OnDeviceModelInstallCriteria." + "AtRegistration.DiskSpaceWhenNotEnoughAvailable", + 19, 1); } TEST_F(ManifestAssetManagerTest, DoesNotInstallWhenEligibleUseCaseUseTooOld) {
diff --git a/components/optimization_guide/core/model_execution/manifest_broker/manifest_broker_state.cc b/components/optimization_guide/core/model_execution/manifest_broker/manifest_broker_state.cc index 313470e..239ae35 100644 --- a/components/optimization_guide/core/model_execution/manifest_broker/manifest_broker_state.cc +++ b/components/optimization_guide/core/model_execution/manifest_broker/manifest_broker_state.cc
@@ -69,6 +69,8 @@ weak_ptr_factory_.GetWeakPtr())); manifest_monitor_.SetCallback(base::BindRepeating( &ManifestBrokerState::OnManifestUpdated, weak_ptr_factory_.GetWeakPtr())); + base::UmaHistogramBoolean( + "OptimizationGuide.OnDeviceModel.ManifestBrokerInstantiated", true); } ManifestBrokerState::~ManifestBrokerState() = default;
diff --git a/components/optimization_guide/core/model_execution/manifest_broker/manifest_broker_state_unittest.cc b/components/optimization_guide/core/model_execution/manifest_broker/manifest_broker_state_unittest.cc index 6517e4cc..af003d9 100644 --- a/components/optimization_guide/core/model_execution/manifest_broker/manifest_broker_state_unittest.cc +++ b/components/optimization_guide/core/model_execution/manifest_broker/manifest_broker_state_unittest.cc
@@ -52,6 +52,7 @@ // should fail, rather than hang, and the service should shut down after the // performance class check. TEST_F(ManifestBrokerStateTest, CreateSessionFailedOnDeviceIncapable) { + base::HistogramTester histogram_tester; ScenarioBuilder::MinimalTestScenario(fake_.component_state()); // Ensure the device is considered incapable of using on-device models. @@ -62,7 +63,6 @@ on_device_model::mojom::PerformanceClass::kVeryLow; fake_.Startup(); - base::HistogramTester histogram_tester; base::test::TestFuture<ModelBrokerClient::CreateSessionResult> session_future; fake_.client().CreateSession(mojom::OnDeviceFeature::kTest, SessionConfigParams{}, @@ -75,6 +75,8 @@ histogram_tester.ExpectUniqueSample( "OptimizationGuide.ModelExecution.OnDeviceModelPerformanceClass", OnDeviceModelPerformanceClass::kVeryLow, 1); + histogram_tester.ExpectUniqueSample( + "OptimizationGuide.OnDeviceModel.ManifestBrokerInstantiated", true, 1); EXPECT_TRUE(fake_.launcher().did_launch_service()); // Service should idle-out again after the performance class check. EXPECT_TRUE(base::test::RunUntil( @@ -84,6 +86,7 @@ // When the device is incapable of downloading models due to low disk space, // session creations attempts should fail, rather than hang. TEST_F(ManifestBrokerStateTest, CreateSessionFailedOnNotEnoughDiskSpace) { + base::HistogramTester histogram_tester; ScenarioBuilder::MinimalTestScenario(fake_.component_state()); // Ensure the device is considered incapable of using on-device models. @@ -93,7 +96,6 @@ fake_.component_state().SetFreeDiskSpace(base::ByteCount(1)); fake_.Startup(); - base::HistogramTester histogram_tester; base::test::TestFuture<ModelBrokerClient::CreateSessionResult> session_future; fake_.client().CreateSession(mojom::OnDeviceFeature::kTest, SessionConfigParams{}, @@ -101,6 +103,8 @@ // Broker should have a Manifest that supports no features, so session // creations should fail (kNotSupported). ASSERT_FALSE(session_future.Take()); + histogram_tester.ExpectUniqueSample( + "OptimizationGuide.OnDeviceModel.ManifestBrokerInstantiated", true, 1); } class TestOnDeviceModelAvailabilityObserver
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 3cfbda2..9a44b14 100644 --- a/components/optimization_guide/core/model_execution/on_device_execution.cc +++ b/components/optimization_guide/core/model_execution/on_device_execution.cc
@@ -74,37 +74,6 @@ has_repeats); } -void LogResponseCompleteTime(mojom::OnDeviceFeature feature, - base::TimeDelta time_to_completion) { - base::UmaHistogramMediumTimes( - base::StrCat( - {"OptimizationGuide.ModelExecution.OnDeviceResponseCompleteTime.", - GetVariantName(feature)}), - time_to_completion); -} - -void LogResponseCompleteTokens(mojom::OnDeviceFeature feature, - uint32_t tokens) { - base::UmaHistogramCounts10000( - base::StrCat( - {"OptimizationGuide.ModelExecution.OnDeviceResponseCompleteTokens.", - GetVariantName(feature)}), - tokens); -} - -void LogResponseTimeToNextToken(mojom::OnDeviceFeature feature, - uint32_t tokens, - base::TimeDelta token_time) { - if (tokens == 0) { - return; - } - base::UmaHistogramTimes( - base::StrCat({"OptimizationGuide.ModelExecution." - "OnDeviceResponseTokensTimeToNextToken.", - GetVariantName(feature)}), - token_time / tokens); -} - std::string GenerateExecutionId() { return "on-device:" + base::Uuid::GenerateRandomV4().AsLowercaseString(); } @@ -129,12 +98,12 @@ opts_(std::move(opts)), last_message_(std::move(message)), constraint_(std::move(constraint)), + telemetry_logger_(feature), histogram_logger_(std::move(logger)), callback_(std::move(callback)), cleanup_callback_(std::move(cleanup_callback)) { exec_log_.set_execution_id(GenerateExecutionId()); exec_log_.mutable_on_device_model_execution_info()->add_execution_infos(); - start_ = base::TimeTicks::Now(); *(exec_log_.mutable_on_device_model_execution_info() ->mutable_model_versions()) = opts_.model_versions; // Note: if on-device fails for some reason, the result will be changed. @@ -146,11 +115,7 @@ if (histogram_logger_) { histogram_logger_->set_result(Result::kDestroyedWhileWaitingForResponse); } - base::UmaHistogramMediumTimes( - base::StrCat({"OptimizationGuide.ModelExecution." - "OnDeviceDestroyedWhileWaitingForResponseTime.", - GetVariantName(feature_)}), - base::TimeTicks::Now() - start_); + telemetry_logger_.RecordDestroyedWhileWaiting(); } } @@ -274,15 +239,9 @@ MutableLoggedResponse(); if (current_response_.empty()) { - first_response_time_ = base::TimeTicks::Now(); - base::TimeDelta time_to_first_response = first_response_time_ - start_; - base::UmaHistogramMediumTimes( - base::StrCat( - {"OptimizationGuide.ModelExecution.OnDeviceFirstResponseTime.", - GetVariantName(feature_)}), - time_to_first_response); + telemetry_logger_.RecordFirstResponse(); logged_response->set_time_to_first_response_millis( - time_to_first_response.InMilliseconds()); + telemetry_logger_.GetTimeToFirstResponse().InMilliseconds()); } if (GetOnDeviceModelWithholdNewlines()) { @@ -331,19 +290,14 @@ on_device_model::mojom::ResponseSummaryPtr summary) { TRACE_EVENT("optimization_guide", "OnDeviceExecution::OnComplete", "feature", base::ToString(feature_)); - base::TimeTicks completion_time = base::TimeTicks::Now(); - base::TimeDelta time_to_completion = completion_time - start_; receiver_.reset(); // Suppress expected disconnect bool has_repeats = MutableLoggedResponse()->has_repeats(); LogResponseHasRepeats(feature_, has_repeats); - LogResponseCompleteTokens(feature_, num_response_tokens_); - LogResponseCompleteTime(feature_, time_to_completion); - LogResponseTimeToNextToken(feature_, num_response_tokens_, - completion_time - first_response_time_); + telemetry_logger_.RecordCompletion(num_response_tokens_); MutableLoggedResponse()->set_time_to_completion_millis( - time_to_completion.InMilliseconds()); + telemetry_logger_.GetTimeToCompletion().InMilliseconds()); output_token_count_ = summary->output_token_count;
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 fbcfbd2..96aa414 100644 --- a/components/optimization_guide/core/model_execution/on_device_execution.h +++ b/components/optimization_guide/core/model_execution/on_device_execution.h
@@ -18,6 +18,7 @@ #include "components/optimization_guide/core/model_execution/on_device_capability.h" #include "components/optimization_guide/core/model_execution/on_device_context.h" #include "components/optimization_guide/core/model_execution/on_device_model_feature_adapter.h" +#include "components/optimization_guide/core/model_execution/on_device_telemetry_logger.h" #include "components/optimization_guide/core/model_execution/repetition_checker.h" #include "components/optimization_guide/core/model_execution/safety_checker.h" #include "components/optimization_guide/core/model_execution/substitution.h" @@ -202,10 +203,8 @@ MultimodalMessage last_message_; // A constraint defining structured output requirements for the response. on_device_model::mojom::ResponseConstraintPtr constraint_; - // Time ExecuteModel() was called. - base::TimeTicks start_; - // Time we receive the first token. - base::TimeTicks first_response_time_; + // Handles telemetry logging for the execution. + OnDeviceRequestTelemetryLogger telemetry_logger_; // Used to log the result of ExecuteModel(). std::unique_ptr<ResultLogger> histogram_logger_; // Used to log execution information for the request.
diff --git a/components/optimization_guide/core/model_execution/on_device_model_component.cc b/components/optimization_guide/core/model_execution/on_device_model_component.cc index c91436b..5713ef6 100644 --- a/components/optimization_guide/core/model_execution/on_device_model_component.cc +++ b/components/optimization_guide/core/model_execution/on_device_model_component.cc
@@ -191,11 +191,10 @@ } } -std::string ConvertModelNameToUmaModelName(const std::string& model_name) { - if (model_name == "v3Nano") { +std::string GetUmaModelNameFromState(OnDeviceModelComponentState* state) { + if (!state || state->GetBaseModelSpec().model_name == "v3Nano") { return "V3Nano"; } else { - // Treat obsolete models as unknown. return "Unknown"; } } @@ -484,7 +483,8 @@ } base::UmaHistogramBoolean( "OptimizationGuide.ModelExecution." - "OnDeviceModelInstalledAtRegistrationTime", + "OnDeviceModelInstalledAtRegistrationTime." + + GetUmaModelNameFromState(state_.get()), state_ != nullptr); UpdateRegistration(); } @@ -661,11 +661,7 @@ if (uninstall_reason.has_value()) { // If `state_` is null, the uninstallation is happening before the model is // ready, so `Unknown` is logged. - std::string uma_model_name = "Unknown"; - if (state_) { - uma_model_name = - ConvertModelNameToUmaModelName(state_->GetBaseModelSpec().model_name); - } + std::string uma_model_name = GetUmaModelNameFromState(state_.get()); base::UmaHistogramEnumeration( "OptimizationGuide.ModelExecution.OnDeviceModelUninstallReason." + uma_model_name,
diff --git a/components/optimization_guide/core/model_execution/on_device_model_component_unittest.cc b/components/optimization_guide/core/model_execution/on_device_model_component_unittest.cc index 9c445df..2a3693e 100644 --- a/components/optimization_guide/core/model_execution/on_device_model_component_unittest.cc +++ b/components/optimization_guide/core/model_execution/on_device_model_component_unittest.cc
@@ -207,7 +207,7 @@ ASSERT_TRUE(WaitUntilInstallerRegistered()); histograms_.ExpectUniqueSample( "OptimizationGuide.ModelExecution." - "OnDeviceModelInstalledAtRegistrationTime", + "OnDeviceModelInstalledAtRegistrationTime.Unknown", true, 1); histograms_.ExpectTotalCount( "OptimizationGuide.OnDeviceModel.NewModelInstalled", 0); @@ -220,7 +220,7 @@ ASSERT_TRUE(WaitUntilInstallerRegistered()); histograms_.ExpectUniqueSample( "OptimizationGuide.ModelExecution." - "OnDeviceModelInstalledAtRegistrationTime", + "OnDeviceModelInstalledAtRegistrationTime.V3Nano", false, 1); } @@ -446,7 +446,7 @@ [&] { return broker_.component_state().uninstall_called(); })); histograms_.ExpectUniqueSample( - "OptimizationGuide.ModelExecution.OnDeviceModelUninstallReason.Unknown", + "OptimizationGuide.ModelExecution.OnDeviceModelUninstallReason.V3Nano", OnDeviceModelComponentStateManager::RegistrationCriteria:: UninstallReason::kInsufficientDisk, 1);
diff --git a/components/optimization_guide/core/model_execution/on_device_model_service_controller_unittest.cc b/components/optimization_guide/core/model_execution/on_device_model_service_controller_unittest.cc index bdd1fbc3..c9ed00e6 100644 --- a/components/optimization_guide/core/model_execution/on_device_model_service_controller_unittest.cc +++ b/components/optimization_guide/core/model_execution/on_device_model_service_controller_unittest.cc
@@ -341,6 +341,23 @@ "OptimizationGuide.ModelExecution.OnDeviceModelEligibilityReason.Compose", OnDeviceModelEligibilityReason::kSuccess, 1); + histogram_tester.ExpectTotalCount( + "OptimizationGuide.ModelExecution." + "OnDeviceFirstResponseTime.Compose", + 1); + histogram_tester.ExpectTotalCount( + "OptimizationGuide.ModelExecution." + "OnDeviceResponseCompleteTime.Compose", + 1); + histogram_tester.ExpectTotalCount( + "OptimizationGuide.ModelExecution." + "OnDeviceResponseCompleteTokens.Compose", + 1); + histogram_tester.ExpectTotalCount( + "OptimizationGuide.ModelExecution." + "OnDeviceResponseTokensTimeToNextToken.Compose", + 1); + // If we destroy all sessions and wait long enough, everything should idle out // and the service should get terminated. session.reset();
diff --git a/components/optimization_guide/core/model_execution/on_device_telemetry_logger.cc b/components/optimization_guide/core/model_execution/on_device_telemetry_logger.cc new file mode 100644 index 0000000..5361db3 --- /dev/null +++ b/components/optimization_guide/core/model_execution/on_device_telemetry_logger.cc
@@ -0,0 +1,92 @@ +// Copyright 2026 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "components/optimization_guide/core/model_execution/on_device_telemetry_logger.h" + +#include "base/metrics/histogram_functions.h" +#include "base/strings/strcat.h" +#include "components/optimization_guide/core/model_execution/on_device_features.h" + +namespace optimization_guide { + +OnDeviceRequestTelemetryLogger::OnDeviceRequestTelemetryLogger( + mojom::OnDeviceFeature feature) + : feature_(feature), start_time_(base::TimeTicks::Now()) {} + +OnDeviceRequestTelemetryLogger::~OnDeviceRequestTelemetryLogger() = default; + +void OnDeviceRequestTelemetryLogger::RecordFirstResponse() { + if (!first_response_time_.is_null()) { + return; // Already recorded + } + first_response_time_ = base::TimeTicks::Now(); + base::TimeDelta time_to_first_response = first_response_time_ - start_time_; + base::UmaHistogramMediumTimes( + base::StrCat( + {"OptimizationGuide.ModelExecution.OnDeviceFirstResponseTime.", + GetVariantName(feature_)}), + time_to_first_response); +} + +void OnDeviceRequestTelemetryLogger::RecordContextTime() { + if (!context_time_.is_null()) { + return; + } + context_time_ = base::TimeTicks::Now(); +} + +void OnDeviceRequestTelemetryLogger::RecordCompletion(uint32_t num_tokens) { + completion_time_ = base::TimeTicks::Now(); + base::TimeDelta time_to_completion = completion_time_ - start_time_; + + base::UmaHistogramMediumTimes( + base::StrCat( + {"OptimizationGuide.ModelExecution.OnDeviceResponseCompleteTime.", + GetVariantName(feature_)}), + time_to_completion); + + base::UmaHistogramCounts10000( + base::StrCat( + {"OptimizationGuide.ModelExecution.OnDeviceResponseCompleteTokens.", + GetVariantName(feature_)}), + num_tokens); + + if (num_tokens > 0 && !first_response_time_.is_null()) { + base::UmaHistogramTimes( + base::StrCat({"OptimizationGuide.ModelExecution." + "OnDeviceResponseTokensTimeToNextToken.", + GetVariantName(feature_)}), + (completion_time_ - first_response_time_) / num_tokens); + } +} + +void OnDeviceRequestTelemetryLogger::RecordDestroyedWhileWaiting() { + base::UmaHistogramMediumTimes( + base::StrCat({"OptimizationGuide.ModelExecution." + "OnDeviceDestroyedWhileWaitingForResponseTime.", + GetVariantName(feature_)}), + base::TimeTicks::Now() - start_time_); +} + +base::TimeDelta OnDeviceRequestTelemetryLogger::GetTimeToFirstResponse() const { + CHECK(!first_response_time_.is_null()); + return first_response_time_ - start_time_; +} + +base::TimeDelta OnDeviceRequestTelemetryLogger::GetTimeToContextProcessing() + const { + CHECK(!context_time_.is_null()); + return context_time_ - start_time_; +} + +base::TimeDelta OnDeviceRequestTelemetryLogger::GetTimeToCompletion() const { + CHECK(!completion_time_.is_null()); + return completion_time_ - start_time_; +} + +base::TimeTicks OnDeviceRequestTelemetryLogger::GetStartTime() const { + return start_time_; +} + +} // namespace optimization_guide
diff --git a/components/optimization_guide/core/model_execution/on_device_telemetry_logger.h b/components/optimization_guide/core/model_execution/on_device_telemetry_logger.h new file mode 100644 index 0000000..a4210bba --- /dev/null +++ b/components/optimization_guide/core/model_execution/on_device_telemetry_logger.h
@@ -0,0 +1,49 @@ +// Copyright 2026 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef COMPONENTS_OPTIMIZATION_GUIDE_CORE_MODEL_EXECUTION_ON_DEVICE_TELEMETRY_LOGGER_H_ +#define COMPONENTS_OPTIMIZATION_GUIDE_CORE_MODEL_EXECUTION_ON_DEVICE_TELEMETRY_LOGGER_H_ + +#include "base/time/time.h" +#include "components/optimization_guide/public/mojom/model_broker.mojom-shared.h" + +namespace optimization_guide { + +// A helper class to log telemetry for a single on-device model execution +// request. This is shared between OnDeviceExecution and custom execution paths +// like Prompt API. This class should be created per call/request to a session. +class OnDeviceRequestTelemetryLogger { + public: + explicit OnDeviceRequestTelemetryLogger(mojom::OnDeviceFeature feature); + ~OnDeviceRequestTelemetryLogger(); + + // Records the time to the first response chunk. + void RecordFirstResponse(); + + // Records the context processing time (prefill time). + void RecordContextTime(); + + // Records metrics on completion of response generation. + void RecordCompletion(uint32_t num_tokens); + + // Records the time when destroyed while waiting for response. + void RecordDestroyedWhileWaiting(); + + // Getters for durations (valid after respective Record calls). + base::TimeDelta GetTimeToFirstResponse() const; + base::TimeDelta GetTimeToContextProcessing() const; + base::TimeDelta GetTimeToCompletion() const; + base::TimeTicks GetStartTime() const; + + private: + const mojom::OnDeviceFeature feature_; + base::TimeTicks start_time_; + base::TimeTicks first_response_time_; + base::TimeTicks context_time_; + base::TimeTicks completion_time_; +}; + +} // namespace optimization_guide + +#endif // COMPONENTS_OPTIMIZATION_GUIDE_CORE_MODEL_EXECUTION_ON_DEVICE_TELEMETRY_LOGGER_H_
diff --git a/components/optimization_guide/internal b/components/optimization_guide/internal index 4cdea65..16736d9 160000 --- a/components/optimization_guide/internal +++ b/components/optimization_guide/internal
@@ -1 +1 @@ -Subproject commit 4cdea65aa8a2b6e12cb76f98b88652194dddd6c1 +Subproject commit 16736d9dc9992b20fc79ed10118edab62c722d48
diff --git a/components/page_content_annotations/content/annotate_page_content_request.cc b/components/page_content_annotations/content/annotate_page_content_request.cc index abccb04..4e2dd47 100644 --- a/components/page_content_annotations/content/annotate_page_content_request.cc +++ b/components/page_content_annotations/content/annotate_page_content_request.cc
@@ -283,7 +283,8 @@ extraction_timer_ = std::nullopt; page_content_extraction_service_->OnNewNavigation( - get_tab_id_callback_.Run(web_contents()), web_contents()); + get_tab_id_callback_.Run(web_contents()), web_contents(), + is_same_document); } void AnnotatedPageContentRequest::MaybeScheduleExtraction(bool on_hide) {
diff --git a/components/page_content_annotations/content/page_content_extraction_service.cc b/components/page_content_annotations/content/page_content_extraction_service.cc index 337d69d..9b753f99 100644 --- a/components/page_content_annotations/content/page_content_extraction_service.cc +++ b/components/page_content_annotations/content/page_content_extraction_service.cc
@@ -37,6 +37,16 @@ namespace { +// LINT.IfChange(EnablementSource) +enum class EnablementSource { + kNone = 0, + kFeatureFlag = 1, + kObserverPresent = 2, + kBoth = 3, + kMaxValue = kBoth, +}; +// LINT.ThenChange(//tools/metrics/histograms/metadata/optimization/enums.xml:PageContentExtractionEnablementSource) + WebStateWrapper ToWebStateWrapper(content::WebContents* web_contents) { return WebStateWrapper( web_contents->GetBrowserContext()->IsOffTheRecord(), @@ -295,11 +305,34 @@ void PageContentExtractionService::OnNewNavigation( std::optional<int64_t> tab_id, - content::WebContents* web_contents) { + content::WebContents* web_contents, + bool is_same_document) { if (is_page_content_cache_enabled_) { page_content_cache_handler_->OnNewNavigation( tab_id, ToWebStateWrapper(web_contents)); } + + if (!is_same_document) { + bool feature_enabled = base::FeatureList::IsEnabled( + page_content_annotations::features::kAnnotatedPageContentExtraction); + bool has_observers = !observers_.empty(); + + EnablementSource source = EnablementSource::kNone; + if (feature_enabled && has_observers) { + source = EnablementSource::kBoth; + } else if (feature_enabled) { + source = EnablementSource::kFeatureFlag; + } else if (has_observers) { + source = EnablementSource::kObserverPresent; + } + + base::UmaHistogramEnumeration( + "OptimizationGuide.PageContentExtraction.EnablementSourcePerNavigation", + source); + base::UmaHistogramCounts100( + "OptimizationGuide.PageContentExtraction.ObserverCountPerNavigation", + std::distance(observers_.begin(), observers_.end())); + } } void PageContentExtractionService::RunCleanUpTasksWithActiveTabs(
diff --git a/components/page_content_annotations/content/page_content_extraction_service.h b/components/page_content_annotations/content/page_content_extraction_service.h index 7fef8a5..ccc8e14 100644 --- a/components/page_content_annotations/content/page_content_extraction_service.h +++ b/components/page_content_annotations/content/page_content_extraction_service.h
@@ -173,7 +173,8 @@ // Called when a new navigation happens in a WebContents. void OnNewNavigation(std::optional<int64_t> tab_id, - content::WebContents* web_contents); + content::WebContents* web_contents, + bool is_same_document); // Called when all the tab models are initialized to perform cleanup of stale // entries in the page content cache.
diff --git a/components/page_content_annotations/content/page_content_extraction_service_unittest.cc b/components/page_content_annotations/content/page_content_extraction_service_unittest.cc index b1906de..951f704 100644 --- a/components/page_content_annotations/content/page_content_extraction_service_unittest.cc +++ b/components/page_content_annotations/content/page_content_extraction_service_unittest.cc
@@ -5,6 +5,7 @@ #include "components/page_content_annotations/content/page_content_extraction_service.h" #include "base/files/scoped_temp_dir.h" +#include "base/test/metrics/histogram_tester.h" #include "base/test/scoped_feature_list.h" #include "base/test/task_environment.h" #include "components/feature_engagement/public/feature_constants.h" @@ -93,4 +94,137 @@ } #endif // BUILDFLAG(IS_ANDROID) +class TestObserver : public PageContentExtractionService::Observer { + public: + TestObserver() = default; + ~TestObserver() override = default; +}; + +TEST_F(PageContentExtractionServiceTest, OnNewNavigation_Metrics) { + PageContentExtractionService service(os_crypt_async_.get(), + temp_dir_.GetPath(), &mock_tracker_); + + // Case 1: Feature disabled, no observers -> kNone + { + base::HistogramTester histogram_tester; + scoped_feature_list_.InitAndDisableFeature( + features::kAnnotatedPageContentExtraction); + + service.OnNewNavigation(std::nullopt, nullptr, /*is_same_document=*/false); + + histogram_tester.ExpectUniqueSample( + "OptimizationGuide.PageContentExtraction.EnablementSourcePerNavigation", + 0, 1); // kNone + histogram_tester.ExpectUniqueSample( + "OptimizationGuide.PageContentExtraction.ObserverCountPerNavigation", 0, + 1); + } + + // Case 2: Feature enabled, no observers -> kFeatureFlag + { + base::HistogramTester histogram_tester; + base::test::ScopedFeatureList feature_list; + feature_list.InitAndEnableFeature( + features::kAnnotatedPageContentExtraction); + + service.OnNewNavigation(std::nullopt, nullptr, /*is_same_document=*/false); + + histogram_tester.ExpectUniqueSample( + "OptimizationGuide.PageContentExtraction.EnablementSourcePerNavigation", + 1, 1); // kFeatureFlag + histogram_tester.ExpectUniqueSample( + "OptimizationGuide.PageContentExtraction.ObserverCountPerNavigation", 0, + 1); + } + + // Case 3: Feature disabled, with observer -> kObserverPresent + { + base::HistogramTester histogram_tester; + base::test::ScopedFeatureList feature_list; + feature_list.InitAndDisableFeature( + features::kAnnotatedPageContentExtraction); + + TestObserver observer; + service.AddObserver(&observer); + + service.OnNewNavigation(std::nullopt, nullptr, /*is_same_document=*/false); + + histogram_tester.ExpectUniqueSample( + "OptimizationGuide.PageContentExtraction.EnablementSourcePerNavigation", + 2, 1); // kObserverPresent + histogram_tester.ExpectUniqueSample( + "OptimizationGuide.PageContentExtraction.ObserverCountPerNavigation", 1, + 1); + + service.RemoveObserver(&observer); + } + + // Case 4: Feature enabled, with observer -> kBoth + { + base::HistogramTester histogram_tester; + base::test::ScopedFeatureList feature_list; + feature_list.InitAndEnableFeature( + features::kAnnotatedPageContentExtraction); + + TestObserver observer; + service.AddObserver(&observer); + + service.OnNewNavigation(std::nullopt, nullptr, /*is_same_document=*/false); + + histogram_tester.ExpectUniqueSample( + "OptimizationGuide.PageContentExtraction.EnablementSourcePerNavigation", + 3, 1); // kBoth + histogram_tester.ExpectUniqueSample( + "OptimizationGuide.PageContentExtraction.ObserverCountPerNavigation", 1, + 1); + + service.RemoveObserver(&observer); + } + + // Case 5: Same document navigation -> No logging + { + base::HistogramTester histogram_tester; + base::test::ScopedFeatureList feature_list; + feature_list.InitAndEnableFeature( + features::kAnnotatedPageContentExtraction); + + service.OnNewNavigation(std::nullopt, nullptr, /*is_same_document=*/true); + + histogram_tester.ExpectTotalCount( + "OptimizationGuide.PageContentExtraction.EnablementSourcePerNavigation", + 0); + histogram_tester.ExpectTotalCount( + "OptimizationGuide.PageContentExtraction.ObserverCountPerNavigation", + 0); + } + + // Case 6: Multiple observers -> count is correct + { + base::HistogramTester histogram_tester; + base::test::ScopedFeatureList feature_list; + feature_list.InitAndDisableFeature( + features::kAnnotatedPageContentExtraction); + + TestObserver observer1; + TestObserver observer2; + TestObserver observer3; + service.AddObserver(&observer1); + service.AddObserver(&observer2); + service.AddObserver(&observer3); + + service.OnNewNavigation(std::nullopt, nullptr, /*is_same_document=*/false); + + histogram_tester.ExpectUniqueSample( + "OptimizationGuide.PageContentExtraction.EnablementSourcePerNavigation", + 2, 1); // kObserverPresent + histogram_tester.ExpectUniqueSample( + "OptimizationGuide.PageContentExtraction.ObserverCountPerNavigation", 3, + 1); + + service.RemoveObserver(&observer1); + service.RemoveObserver(&observer2); + service.RemoveObserver(&observer3); + } +} + } // namespace page_content_annotations
diff --git a/components/performance_manager/public/v8_memory/v8_detailed_memory.h b/components/performance_manager/public/v8_memory/v8_detailed_memory.h index 9ca301f..c57dcae 100644 --- a/components/performance_manager/public/v8_memory/v8_detailed_memory.h +++ b/components/performance_manager/public/v8_memory/v8_detailed_memory.h
@@ -7,6 +7,7 @@ #include <optional> #include <string> +#include <vector> #include "base/byte_size.h" #include "base/functional/callback.h" @@ -15,6 +16,7 @@ #include "base/sequence_checker.h" #include "base/time/time.h" #include "base/types/pass_key.h" +#include "third_party/blink/public/common/tokens/tokens.h" namespace performance_manager { @@ -189,8 +191,8 @@ class V8DetailedMemoryProcessData { public: - V8DetailedMemoryProcessData() = default; - virtual ~V8DetailedMemoryProcessData() = default; + V8DetailedMemoryProcessData(); + virtual ~V8DetailedMemoryProcessData(); bool operator==(const V8DetailedMemoryProcessData& other) const { return detached_v8_memory_used_ == other.detached_v8_memory_used_ && @@ -237,6 +239,25 @@ blink_memory_used_ = blink_memory_used; } + // Memory entry for a non-main-world V8 context (e.g. extension content + // scripts, other isolated worlds). The browser side can inspect + // |world_stable_id| to classify the entry. + struct NonMainWorldMemoryEntry { + std::string world_stable_id; + blink::ExecutionContextToken frame_token; + base::ByteSize memory_used; + }; + + // Returns V8 memory used by non-main-world contexts in this process. + const std::vector<NonMainWorldMemoryEntry>& non_main_world_entries() const { + return non_main_world_entries_; + } + + void set_non_main_world_entries( + std::vector<NonMainWorldMemoryEntry> entries) { + non_main_world_entries_ = std::move(entries); + } + // Returns process data for the given node, or nullptr if no measurement has // been taken. The returned pointer must only be accessed on the graph // sequence and may go invalid at any time after leaving the calling scope. @@ -252,6 +273,7 @@ base::ByteSize detached_canvas_memory_used_; base::ByteSize shared_v8_memory_used_; base::ByteSize blink_memory_used_; + std::vector<NonMainWorldMemoryEntry> non_main_world_entries_; }; class V8DetailedMemoryObserver : public base::CheckedObserver {
diff --git a/components/performance_manager/v8_memory/v8_detailed_memory.cc b/components/performance_manager/v8_memory/v8_detailed_memory.cc index 29652b6..abb47a8 100644 --- a/components/performance_manager/v8_memory/v8_detailed_memory.cc +++ b/components/performance_manager/v8_memory/v8_detailed_memory.cc
@@ -286,6 +286,10 @@ //////////////////////////////////////////////////////////////////////////////// // V8DetailedMemoryProcessData +V8DetailedMemoryProcessData::V8DetailedMemoryProcessData() = default; + +V8DetailedMemoryProcessData::~V8DetailedMemoryProcessData() = default; + const V8DetailedMemoryProcessData* V8DetailedMemoryProcessData::ForProcessNode( const ProcessNode* node) { return V8DetailedMemoryDecorator::GetProcessData(node);
diff --git a/components/performance_manager/v8_memory/v8_detailed_memory_decorator.cc b/components/performance_manager/v8_memory/v8_detailed_memory_decorator.cc index 6b6aa3ca..271e5f0 100644 --- a/components/performance_manager/v8_memory/v8_detailed_memory_decorator.cc +++ b/components/performance_manager/v8_memory/v8_detailed_memory_decorator.cc
@@ -495,9 +495,19 @@ v8_memory; std::vector<std::pair<ExecutionContextToken, PerContextCanvasMemoryUsagePtr>> canvas_memory; + std::vector<V8DetailedMemoryProcessData::NonMainWorldMemoryEntry> + non_main_world_entries; for (auto& isolate : result->isolates) { for (auto& entry : isolate->contexts) { - v8_memory.emplace_back(entry->token, std::move(entry)); + if (entry->world_stable_id.has_value()) { + // Non-main-world context — collect with frame token and stable ID. + non_main_world_entries.push_back( + {std::move(entry->world_stable_id.value()), entry->token, + entry->memory_used}); + } else { + // Main-world context — goes into the per-frame lookup map. + v8_memory.emplace_back(entry->token, std::move(entry)); + } } for (auto& entry : isolate->canvas_contexts) { canvas_memory.emplace_back(entry->token, std::move(entry)); @@ -581,6 +591,7 @@ data_.set_detached_canvas_memory_used(detached_canvas_memory_used); data_.set_shared_v8_memory_used(shared_v8_memory_used); data_.set_blink_memory_used(blink_memory_used); + data_.set_non_main_world_entries(std::move(non_main_world_entries)); // Schedule another measurement for this process node unless one is already // scheduled.
diff --git a/components/personal_context/proto/BUILD.gn b/components/personal_context/proto/BUILD.gn new file mode 100644 index 0000000..36279b3 --- /dev/null +++ b/components/personal_context/proto/BUILD.gn
@@ -0,0 +1,5 @@ +import("//third_party/protobuf/proto_library.gni") + +proto_library("personal_context_proto") { + sources = [ "context_memory_service.proto" ] +}
diff --git a/components/personal_context/proto/context_memory_service.proto b/components/personal_context/proto/context_memory_service.proto new file mode 100644 index 0000000..bb692f1f --- /dev/null +++ b/components/personal_context/proto/context_memory_service.proto
@@ -0,0 +1,41 @@ +// Copyright 2026 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +syntax = "proto3"; + +package personal_context.proto; + +option optimize_for = LITE_RUNTIME; + +message FetchContextRequest { + // The feature that this request is for. + ContextMemoryFeature feature = 1; + + // The metadata associated with this request. + Any request_metadata = 2; +} + +message FetchContextResponse { + oneof response { + // The metadata for the response returned for the feature. + Any response_metadata = 1; + } + + // UUID generated by the service for the request. + string server_request_id = 2; +} + +// Enum for features that are supported by the service. +enum ContextMemoryFeature { + CONTEXT_MEMORY_FEATURE_UNSPECIFIED = 0; +} + +message Any { + // A URL/resource name that uniquely identifies the type of the serialized + // protocol buffer message. + string type_url = 1; + + // Must be a valid serialized protocol buffer of the above specified type. + bytes value = 2; +} \ No newline at end of file
diff --git a/components/policy/resources/templates/policies.yaml b/components/policy/resources/templates/policies.yaml index e9a95403..fb20ac9bb 100644 --- a/components/policy/resources/templates/policies.yaml +++ b/components/policy/resources/templates/policies.yaml
@@ -1446,6 +1446,10 @@ 1445: KioskPinchToZoomAllowed 1446: SecuritySignalsClientCertificatesSelectors 1447: AllowSocketPoolSizeRandomizationForProxies + 1448: DefaultSubAppsWithoutPromptsSetting + 1449: SubAppsWithoutPromptsAllowedForOrigins + 1450: SubAppsWithoutPromptsBlockedForOrigins + 1451: IsolatedModeSettings atomic_groups: 1: Homepage @@ -1511,3 +1515,4 @@ 61: LegacyCookieScopeSettings 62: GeolocationSettings 63: SocketPoolSizeSettings + 64: SubAppsWithoutPromptsSettings
diff --git a/components/policy/resources/templates/policy_definitions/ContentSettings/DefaultSubAppsWithoutPromptsSetting.yaml b/components/policy/resources/templates/policy_definitions/ContentSettings/DefaultSubAppsWithoutPromptsSetting.yaml new file mode 100644 index 0000000..5c2ef91e --- /dev/null +++ b/components/policy/resources/templates/policy_definitions/ContentSettings/DefaultSubAppsWithoutPromptsSetting.yaml
@@ -0,0 +1,42 @@ +caption: Control use of the Sub Apps Add API +default: null +desc: |- + The policy controls whether Isolated Web Apps (IWAs) will by default be able to add Sub Apps. + + Setting the policy to 1 allows IWA origins to add Sub Apps and bypass user gesture or user confirmation requirements. + + Setting the policy to 2 blocks IWA origins from adding Sub Apps. + + Setting the policy to 3 or leaving it unset allows Isolated Web App origins to add Sub Apps upon user confirmation. + + This policy can be overridden for specific Isolated Web App origins using the <ph name="SUB_APPS__WITHOUT_PROMPTS_ALLOWED_FOR_ORIGINS_POLICY_NAME">SubAppsWithoutPromptsAllowedForOrigins</ph> and <ph name="SUB_APPS_WITHOUT_PROMPTS_BLOCKED_FOR_POLICY_NAME">SubAppsWithoutPromptsBlockedorOrigins</ph> policies. + +example_value: 3 +features: + dynamic_refresh: true + per_profile: true +supported_on: +- chrome_os:150- +future_on: +- chrome.* +items: +- caption: Allow Isolated Web App origins to add Sub Apps without user prompt + name: AllowSubApps + value: 1 +- caption: Block Isolated Web App origins from adding Sub Apps + name: BlockSubApps + value: 2 +- caption: Allow Isolated Web App origins to add Sub Apps but require user prompt + name: AskSubApps + value: 3 +owners: + - file://chrome/browser/web_applications/OWNERS + - file://components/webapps/isolated_web_apps/PLATFORM_OWNERS +schema: + enum: + - 1 + - 2 + - 3 + type: integer +tags: [] +type: int-enum
diff --git a/components/policy/resources/templates/policy_definitions/ContentSettings/SubAppsWithoutPromptsAllowedForOrigins.yaml b/components/policy/resources/templates/policy_definitions/ContentSettings/SubAppsWithoutPromptsAllowedForOrigins.yaml new file mode 100644 index 0000000..65049d23 --- /dev/null +++ b/components/policy/resources/templates/policy_definitions/ContentSettings/SubAppsWithoutPromptsAllowedForOrigins.yaml
@@ -0,0 +1,27 @@ +caption: Allow Isolated Web Apps to use Sub Apps Add API without user prompt +desc: |- + By default, Isolated Web Apps (IWAs) can add Sub Apps provided the user confirms the prompt interactively. + + This policy controls which IWA origins will be able to add Sub Apps without having the user prompted. + + This policy overrides the behavior of <ph name="DEFAULT_SUB_APPS_WITHOUT_PROMPTS_SETTING_POLICY_NAME">DefaultSubAppsWithoutPromptsSetting</ph> and is overridden by <ph name="SUB_APPS_WITHOUT_PROMPTS_BLOCKED_FOR_ORIGINS_POLICY_NAME">SubAppsWithoutPromptsBlockedForOrigins</ph>. + +example_value: +- isolated-app://egoxo6biqdjrk62rman4vvr5cbq2ozsyydig7jmdxcmohdob2ecaaaic +- isolated-app://anayaszofsyqapbofoli7ljxoxkp32qkothweire2o6t7xy6taz6oaacai +features: + dynamic_refresh: true + per_profile: true +supported_on: +- chrome_os:150- +future_on: +- chrome.* +owners: +- file://chrome/browser/web_applications/OWNERS +- file://components/webapps/isolated_web_apps/PLATFORM_OWNERS +schema: + items: + type: string + type: array +tags: [] +type: list
diff --git a/components/policy/resources/templates/policy_definitions/ContentSettings/SubAppsWithoutPromptsBlockedForOrigins.yaml b/components/policy/resources/templates/policy_definitions/ContentSettings/SubAppsWithoutPromptsBlockedForOrigins.yaml new file mode 100644 index 0000000..b18d181 --- /dev/null +++ b/components/policy/resources/templates/policy_definitions/ContentSettings/SubAppsWithoutPromptsBlockedForOrigins.yaml
@@ -0,0 +1,27 @@ +caption: Block Isolated Web Apps from using Sub Apps Add API without user prompt +desc: |- + By default, Isolated Web Apps (IWAs) can add Sub Apps provided that user confirms the prompt interactively. + + This policy controls which IWA origins will be blocked from adding Sub Apps without having the user prompted interactively. + + This policy overrides the behavior of <ph name="DEFAULT_SUB_APPS_WITHOUT_PROMPTS_SETTING_POLICY_NAME">DefaultSubAppsWithoutPromptsSetting</ph> and <ph name="SUB_APPS_WITHOUT_PROMPTS_ALLOWED_FOR_ORIGINS_POLICY_NAME">SubAppsWithoutPromptsAllowedForOrigins</ph>. + +example_value: +- isolated-app://egoxo6biqdjrk62rman4vvr5cbq2ozsyydig7jmdxcmohdob2ecaaaic +- isolated-app://anayaszofsyqapbofoli7ljxoxkp32qkothweire2o6t7xy6taz6oaacai +features: + dynamic_refresh: true + per_profile: true +supported_on: +- chrome_os:150- +future_on: +- chrome.* +owners: +- file://chrome/browser/web_applications/OWNERS +- file://components/webapps/isolated_web_apps/PLATFORM_OWNERS +schema: + items: + type: string + type: array +tags: [] +type: list
diff --git a/components/policy/resources/templates/policy_definitions/Miscellaneous/IsolatedModeSettings.yaml b/components/policy/resources/templates/policy_definitions/Miscellaneous/IsolatedModeSettings.yaml new file mode 100644 index 0000000..259d493d --- /dev/null +++ b/components/policy/resources/templates/policy_definitions/Miscellaneous/IsolatedModeSettings.yaml
@@ -0,0 +1,42 @@ +caption: Replace Incognito Mode with Isolated Mode +default: 0 +desc: |- + Specifies whether to replace Incognito Mode with Isolated Mode. + + Isolated Mode is an Enterprise-only ephemeral browsing mode which replaces Incognito mode. + Unlike Incognito mode, Isolated mode supports DLP-related Enterprise policies and force-installed extensions. + + Note that Incognito-related policies are not applied to Isolated Mode. + + If set to 1 - Enabled, Isolated Mode is enabled and replaces Incognito Mode. + + If set to 0 - Disabled or left unset, Isolated Mode is disabled, and standard Incognito behavior is used. + + This policy requires a Chrome Enterprise Premium license to take effect. +example_value: 1 +features: + cloud_only: true + dynamic_refresh: false + per_profile: true +future_on: +- chrome.* +- chrome_os +- android +- ios +items: +- caption: Isolated mode disabled + name: Disabled + value: 0 +- caption: Isolated mode available + name: Enabled + value: 1 +owners: +- file://components/policy/OWNERS +- proberge@google.com +schema: + enum: + - 0 + - 1 + type: integer +tags: [] +type: int-enum
diff --git a/components/policy/test/data/pref_mapping/DefaultSubAppsWithoutPromptsSetting.json b/components/policy/test/data/pref_mapping/DefaultSubAppsWithoutPromptsSetting.json new file mode 100644 index 0000000..06d23e27 --- /dev/null +++ b/components/policy/test/data/pref_mapping/DefaultSubAppsWithoutPromptsSetting.json
@@ -0,0 +1,19 @@ +[ + { + "os": [ + "chromeos", + "linux", + "mac", + "win" + ], + "simple_policy_pref_mapping_test": { + "pref_name": "profile.managed_default_content_settings.sub_apps_without_prompts", + "default_value": 0, + "values_to_test": [ + 1, + 2, + 3 + ] + } + } +]
diff --git a/components/policy/test/data/pref_mapping/IsolatedModeSettings.json b/components/policy/test/data/pref_mapping/IsolatedModeSettings.json new file mode 100644 index 0000000..9c082c4 --- /dev/null +++ b/components/policy/test/data/pref_mapping/IsolatedModeSettings.json
@@ -0,0 +1,19 @@ +[ + { + "os": [ + "win", + "linux", + "mac", + "chromeos", + "android" + ], + "simple_policy_pref_mapping_test": { + "pref_name": "enterprise.isolated_mode", + "default_value": 0, + "values_to_test": [ + 0, + 1 + ] + } + } +]
diff --git a/components/policy/test/data/pref_mapping/SubAppsWithoutPromptsAllowedForOrigins.json b/components/policy/test/data/pref_mapping/SubAppsWithoutPromptsAllowedForOrigins.json new file mode 100644 index 0000000..b47f2fb4 --- /dev/null +++ b/components/policy/test/data/pref_mapping/SubAppsWithoutPromptsAllowedForOrigins.json
@@ -0,0 +1,20 @@ +[ + { + "os": [ + "chromeos", + "linux", + "mac", + "win" + ], + "simple_policy_pref_mapping_test": { + "pref_name": "profile.managed_sub_apps_without_prompts_allowed_for_origins", + "default_value": [], + "values_to_test": [ + [ + "isolated-app://aerugqztij5biqquuk3mfwpsaibuegaqcitgfchwuosuofdjabzqaaic", + "isolated-app://anayaszofsyqapbofoli7ljxoxkp32qkothweire2o6t7xy6taz6oaacai/" + ] + ] + } + } +]
diff --git a/components/policy/test/data/pref_mapping/SubAppsWithoutPromptsBlockedForOrigins.json b/components/policy/test/data/pref_mapping/SubAppsWithoutPromptsBlockedForOrigins.json new file mode 100644 index 0000000..cf7021f1 --- /dev/null +++ b/components/policy/test/data/pref_mapping/SubAppsWithoutPromptsBlockedForOrigins.json
@@ -0,0 +1,22 @@ +[ + { + "os": [ + "chromeos", + "linux", + "mac", + "win" + ], + "simple_policy_pref_mapping_test": { + "pref_name": "profile.managed_sub_apps_without_prompts_blocked_for_origins", + "default_value": [], + "values_to_test": [ + [ + "isolated-app://aerugqztij5biqquuk3mfwpsaibuegaqcitgfchwuosuofdjabzqaaic" + ], + [ + "*" + ] + ] + } + } +]
diff --git a/components/privacy_sandbox_chrome_strings.grdp b/components/privacy_sandbox_chrome_strings.grdp index e7862d3..fbe44c7 100644 --- a/components/privacy_sandbox_chrome_strings.grdp +++ b/components/privacy_sandbox_chrome_strings.grdp
@@ -145,9 +145,6 @@ <message name="IDS_SETTINGS_TOPICS_PAGE_ACTIVE_TOPICS_HEADING" desc="A label above a list of the user's current topics of interest." formatter_data="android_java"> Active topics </message> - <message name="IDS_SETTINGS_TOPICS_PAGE_ACTIVE_TOPICS_DESCRIPTION" desc="A description that appears beneath the 'Active topics' label." formatter_data="android_java"> - Topics older than 4 weeks get auto-deleted - </message> <!-- Privacy Sandbox v4 - Topics Consent Details --> <message name="IDS_PRIVACY_SANDBOX_TOPICS_CONSENT_STATUS_LABEL" desc="Label for the text which displays the state of the user's Topics consent">
diff --git a/components/privacy_sandbox_chrome_strings_grdp/IDS_SETTINGS_TOPICS_PAGE_ACTIVE_TOPICS_DESCRIPTION.png.sha1 b/components/privacy_sandbox_chrome_strings_grdp/IDS_SETTINGS_TOPICS_PAGE_ACTIVE_TOPICS_DESCRIPTION.png.sha1 deleted file mode 100644 index f41b6cf0..0000000 --- a/components/privacy_sandbox_chrome_strings_grdp/IDS_SETTINGS_TOPICS_PAGE_ACTIVE_TOPICS_DESCRIPTION.png.sha1 +++ /dev/null
@@ -1 +0,0 @@ -83fb8a879ea4714fc0efa202a0c3510c800c45ed \ No newline at end of file
diff --git a/components/privacy_sandbox_strings.grd b/components/privacy_sandbox_strings.grd index f872466..fc17b147 100644 --- a/components/privacy_sandbox_strings.grd +++ b/components/privacy_sandbox_strings.grd
@@ -386,9 +386,6 @@ Opens Privacy Policy in a new tab </message> <!--Ad topics page - Ads API UX Enhancement--> - <message name="IDS_SETTINGS_AD_TOPICS_PAGE_DISCLAIMER" desc="This string appears at the bottom of the “Ad topics” settings page. “Sites” refers to websites that the user visits. The tone is informative. “Companies” refers to businesses or organizations that these websites belong to. This is intended to explain the possible user risks currently available with our evolving privacy-preserving technologies, in neutral language."> - Google requires companies to state publicly that they won't use this data to track you across sites. Some sites may use your activity to personalize your experience for more than just ads. They may also combine it with other information they already know about you. Companies are responsible for letting you know how they use your data. Learn more in our <ph name="BEGIN_LINK1"><a href="$1" aria-description="$2" on-click="$3" id="$4" target="_blank"></ph>Privacy Policy<ph name="LINK_END1"></a></ph>. - </message> <message name="IDS_SETTINGS_AD_TOPICS_PAGE_DISCLAIMER_CLANK" desc="This string appears at the bottom of the “Ad topics” settings page. “Sites” refers to websites that the user visits. The tone is informative. “Companies” refers to businesses or organizations that these websites belong to. This is intended to explain the possible user risks currently available with our evolving privacy-preserving technologies, in neutral language." formatter_data="android_java"> Google requires companies to state publicly that they won't use this data to track you across sites. Some sites may use your activity to personalize your experience for more than just ads. They may also combine it with other information they already know about you. Companies are responsible for letting you know how they use your data. Learn more in our <ph name="BEGIN_LINK"><link></ph>Privacy Policy<ph name="END_LINK"></link></ph>. </message>
diff --git a/components/privacy_sandbox_strings_grd/IDS_SETTINGS_AD_TOPICS_PAGE_DISCLAIMER.png.sha1 b/components/privacy_sandbox_strings_grd/IDS_SETTINGS_AD_TOPICS_PAGE_DISCLAIMER.png.sha1 deleted file mode 100644 index d6733a7e..0000000 --- a/components/privacy_sandbox_strings_grd/IDS_SETTINGS_AD_TOPICS_PAGE_DISCLAIMER.png.sha1 +++ /dev/null
@@ -1 +0,0 @@ -27d86afb38c3af9068faee8f1a503c62dfaefd4e \ No newline at end of file
diff --git a/components/resources/BUILD.gn b/components/resources/BUILD.gn index abb1244e..e78c8479 100644 --- a/components/resources/BUILD.gn +++ b/components/resources/BUILD.gn
@@ -39,9 +39,12 @@ ":components_resources", ":components_scaled_resources", ":dev_ui_components_resources", + "../autofill/core/browser:autofill_address_rewriter_resources", + "../autofill/core/browser:autofill_alternative_state_name_map_resources", "../autofill/core/browser/autofill_and_password_manager_internals:resources", "../crash/core/browser/resources", "../gcm_driver/resources", + "../metrics:server_urls_grd", "../net_log/resources", "../signin/core/browser/resources", "../translate/translate_internals:resources",
diff --git a/components/search_engines/reconciling_template_url_data_holder.cc b/components/search_engines/reconciling_template_url_data_holder.cc index 1de23b5e5..46586142 100644 --- a/components/search_engines/reconciling_template_url_data_holder.cc +++ b/components/search_engines/reconciling_template_url_data_holder.cc
@@ -148,7 +148,7 @@ const TemplateURLData& data_to_match) const { // Search for potential migrations. It is prioritised over the regional // engines, which can change across runs. That's a very strict check (see - // `Resolver::MatchesEngineUnderMigration`), so there is no risk to match the + // `Resolver::CompareEngineUnderMigration`), so there is no risk to match the // wrong engine. if (std::unique_ptr<TemplateURLData> engine = prepopulate_data_resolver_->TryGetMigratedEngine(data_to_match);
diff --git a/components/search_engines/template_url_prepopulate_data_resolver.cc b/components/search_engines/template_url_prepopulate_data_resolver.cc index b9ed62c..8ebaa4b 100644 --- a/components/search_engines/template_url_prepopulate_data_resolver.cc +++ b/components/search_engines/template_url_prepopulate_data_resolver.cc
@@ -7,6 +7,7 @@ #include <optional> #include "base/logging.h" +#include "base/metrics/histogram_functions.h" #include "base/not_fatal_until.h" #include "base/notreached.h" #include "components/prefs/pref_service.h" @@ -17,6 +18,7 @@ #include "components/search_engines/keyword_web_data_service.h" #include "components/search_engines/template_url_data.h" #include "components/search_engines/template_url_prepopulate_data.h" +#include "url/gurl.h" namespace TemplateURLPrepopulateData { @@ -117,13 +119,25 @@ return std::nullopt; } -bool Resolver::MatchesEngineUnderMigration( +bool Resolver::IsMatch(MigrationMatch match) { + switch (match) { + case MigrationMatch::kExactMatch: + case MigrationMatch::kHostMatch: + return true; + case MigrationMatch::kIdsDontMatch: + case MigrationMatch::kInvalidCheckedUrl: + case MigrationMatch::kUrlMismatch: + return false; + } +} + +Resolver::MigrationMatch Resolver::CompareEngineUnderMigration( const TemplateURLData& checked_data, const PrepopulatedEngine* deprecated_engine) const { CHECK(deprecated_engine->migrate_to_id != 0, base::NotFatalUntil::M149); if (checked_data.prepopulate_id != deprecated_engine->id) { - return false; + return MigrationMatch::kIdsDontMatch; } // Don't only check the IDs, also check the URLs. The prepopulated @@ -131,7 +145,25 @@ // but only adds one version to regional engines sets. Checking the URL // ensures that the engine being migrated corresponds to the expected // regional version. - return checked_data.url() == deprecated_engine->search_url; + if (checked_data.url() == deprecated_engine->search_url) { + return MigrationMatch::kExactMatch; + } + + GURL incoming_gurl(checked_data.url()); + if (!incoming_gurl.is_valid()) { + return MigrationMatch::kInvalidCheckedUrl; + } + + GURL built_in_gurl(deprecated_engine->search_url); + // Assumption checked in `TemplateURLPrepopulateDataTest.ValidSearchURLs` + // May still fail if we end up migrating Google to another prepopulate ID. + CHECK(built_in_gurl.is_valid()); + + if (incoming_gurl.host() == built_in_gurl.host()) { + return MigrationMatch::kHostMatch; + } + + return MigrationMatch::kUrlMismatch; } std::unique_ptr<TemplateURLData> Resolver::TryGetMigratedEngine( @@ -149,7 +181,13 @@ const auto& migrating_engines = regional_capabilities::GetMigratingPrepopulatedEngines(); for (const auto& [new_engine_id, deprecated_engine] : migrating_engines) { - if (MatchesEngineUnderMigration(pre_migration_engine, deprecated_engine)) { + MigrationMatch match = + CompareEngineUnderMigration(pre_migration_engine, deprecated_engine); + if (match != MigrationMatch::kIdsDontMatch) { + base::UmaHistogramEnumeration( + "Omnibox.TemplateUrl.DseReconciler.MigrationMatch", match); + } + if (IsMatch(match)) { auto new_engine = GetEngineFromFullList(new_engine_id); // By design there should be an entry for this ID, see
diff --git a/components/search_engines/template_url_prepopulate_data_resolver.h b/components/search_engines/template_url_prepopulate_data_resolver.h index e6ca133..ff3c7675 100644 --- a/components/search_engines/template_url_prepopulate_data_resolver.h +++ b/components/search_engines/template_url_prepopulate_data_resolver.h
@@ -77,10 +77,31 @@ std::optional<BuiltinKeywordsMetadata> ComputeDatabaseUpdateRequirements( const WDKeywordsResult::Metadata& keywords_database_metadata) const; + // Granular reasons for TemplateURLs to match (or not). See `IsMatch` to + // interpret the values. + // TODO(crbug.com/507355138): Remove the enum and revert to just using bool. + // LINT.IfChange(MigrationMatch) + enum class MigrationMatch { + kExactMatch = 0, + kHostMatch = 1, + kIdsDontMatch = 2, + kInvalidCheckedUrl = 3, + kUrlMismatch = 4, + kMaxValue = kUrlMismatch, + }; + // LINT.ThenChange(/tools/metrics/histograms/metadata/omnibox/enums.xml:MigrationMatch) + + // Interprets the output of `CompareEngineUnderMigration(checked_data, + // deprecated_engine)`. + // Returns true if `match` indicates that `deprecated_engine`'s migration + // instruction can be applied to `checked_data`. + static bool IsMatch(MigrationMatch match); + // Returns whether `deprecated_engine`'s migration instruction can be applied // to `checked_data`. This needs to be checked to ensure that we are not // incorrectly migrating custom or variant engine definitions. - bool MatchesEngineUnderMigration( + // Use `IsMatch(MigrationMatch)` to interpret the output. + MigrationMatch CompareEngineUnderMigration( const TemplateURLData& checked_data, const PrepopulatedEngine* deprecated_engine) const;
diff --git a/components/search_engines/template_url_prepopulate_data_unittest.cc b/components/search_engines/template_url_prepopulate_data_unittest.cc index ba0c4a1..07f65d3 100644 --- a/components/search_engines/template_url_prepopulate_data_unittest.cc +++ b/components/search_engines/template_url_prepopulate_data_unittest.cc
@@ -207,6 +207,22 @@ search_engines::SearchEnginesTestEnvironment search_engines_test_environment_; }; +TEST_F(TemplateURLPrepopulateDataTest, ValidSearchURLs) { + // Validates the assumption in + // TemplateURLPrepopulateData::Resolver::MatchesEngineUnderMigration that all + // prepopulated search engine URLs can be parsed without needing to resolve + // replacements. Google is the exception, but that's accounted for. + for (const PrepopulatedEngine* engine : + TemplateURLPrepopulateData::kAllEngines) { + if (engine == &TemplateURLPrepopulateData::google) { + continue; // URL is not valid, it has a lot of replacements that need to + // be processed before use. + } + EXPECT_TRUE(GURL(engine->search_url).is_valid()) + << "Invalid search url: " << engine->search_url; + } +} + // Verifies the set of prepopulate data doesn't contain entries with duplicate // ids. TEST_F(TemplateURLPrepopulateDataTest, UniqueIDs) {
diff --git a/components/search_engines/util.cc b/components/search_engines/util.cc index 9ee3ae4..cc7d317c 100644 --- a/components/search_engines/util.cc +++ b/components/search_engines/util.cc
@@ -23,6 +23,8 @@ #include "base/metrics/histogram_functions.h" #include "base/not_fatal_until.h" #include "base/strings/string_number_conversions.h" +#include "base/strings/string_util.h" +#include "base/strings/stringprintf.h" #include "base/strings/utf_string_conversions.h" #include "base/time/time.h" #include "components/country_codes/country_codes.h" @@ -151,6 +153,15 @@ return result_url; } +std::string StringifyDuplicates( + const std::map<int, size_t>& duplicate_counts_by_id) { + std::vector<std::string> pieces; + for (const auto& [id, count] : duplicate_counts_by_id) { + pieces.push_back(base::StringPrintf("%d:%zu", id, count + 1)); + } + return base::JoinString(pieces, ", "); +} + const PrepopulatedEngine* GetMigrationSource(int migrated_engine_id) { if (!base::FeatureList::IsEnabled(switches::kPrepopulatedEnginesMigration)) { return nullptr; @@ -637,8 +648,12 @@ // prepopulated ID. const TemplateURL* existing_url = existing_url_iter->second; - if (template_url_data_resolver.MatchesEngineUnderMigration( - existing_url->data(), pre_migration_engine)) { + TemplateURLPrepopulateData::Resolver::MigrationMatch match = + template_url_data_resolver.CompareEngineUnderMigration( + existing_url->data(), pre_migration_engine); + base::UmaHistogramEnumeration( + "Omnibox.TemplateUrl.DBRefresh.MigrationMatch", match); + if (TemplateURLPrepopulateData::Resolver::IsMatch(match)) { return {existing_url_iter, TemplateURLMergeOption::kSplitPrepopulatedEntry}; } @@ -673,6 +688,8 @@ // have a non-zero prepopulate_id()). std::map<int, TemplateURL*> id_to_turl; + std::map<int, size_t> duplicate_counts_by_id; + // Tracking of existing entries that match the DSP, and of the one that is // selected as best representative for it. int entries_matching_dsp_to_reconcile = 0; @@ -687,6 +704,9 @@ } int prepopulate_id = turl->prepopulate_id(); if (prepopulate_id > 0) { + if (id_to_turl.contains(prepopulate_id)) { + ++duplicate_counts_by_id[prepopulate_id]; + } id_to_turl[prepopulate_id] = turl.get(); } if (MatchesDefaultSearchProvider(turl.get(), default_search_provider, @@ -705,42 +725,61 @@ RecordDefaultSearchMatchCount(entries_matching_dsp_to_reconcile, /*is_unreconciled_count=*/false); - // Debugging https://crbug.com/492852740 - bool is_dsp_from_policy = - default_search_provider && default_search_provider->enforced_by_policy(); - bool is_dsp_from_extension = - default_search_provider && - (default_search_provider->type() == TemplateURL::OMNIBOX_API_EXTENSION || - default_search_provider->type() == - TemplateURL::NORMAL_CONTROLLED_BY_EXTENSION); + size_t total_duplicates = 0; + for (const auto& [id, count] : duplicate_counts_by_id) { + total_duplicates += count; + } + base::UmaHistogramCounts100("Omnibox.TemplateUrl.DBRefresh.TotalDuplicates", + total_duplicates); + // Debugging https://crbug.com/507355138 SCOPED_CRASH_KEY_BOOL("KwdbRefresh", "has_dsp_match", dsp_match != nullptr); - // Breakdown of the explanations for a DSP mismatch. + // Breakdown of the accepted explanations for a DSP mismatch. bool has_mismatch_explanation = // There is no DSP. !default_search_provider || // There is no set of existing turls to get a match from. existing_urls.empty(); - // - Confirmed and expected reasons + // - Confirmed and expected reasons: + // * No DSP preloaded from prefs. SCOPED_CRASH_KEY_BOOL("KwdbRefresh", "has_no_preloaded_dsp", default_search_provider == nullptr); - SCOPED_CRASH_KEY_BOOL("KwdbRefresh", "is_existing_urls_empty", - existing_urls.empty()); + // * No existing URLs to get a match from. + SCOPED_CRASH_KEY_NUMBER("KwdbRefresh", "existing_urls_count", + existing_urls.size()); + // - Other hypotheses // Not confirmed because they should normally not be brought up through // pre-loading DSP, or their first appearance should come after the keywords // DB is loaded, and then they should have been added to it. - SCOPED_CRASH_KEY_BOOL("KwdbRefresh", "is_dsp_from_policy", - is_dsp_from_policy); + SCOPED_CRASH_KEY_BOOL( + "KwdbRefresh", "is_dsp_prepopulated", + default_search_provider && default_search_provider->prepopulate_id() > 0); + + SCOPED_CRASH_KEY_BOOL( + "KwdbRefresh", "is_dsp_from_policy", + default_search_provider && default_search_provider->enforced_by_policy()); + SCOPED_CRASH_KEY_BOOL("KwdbRefresh", "is_dsp_from_extension", - is_dsp_from_extension); + default_search_provider && + (default_search_provider->type() == + TemplateURL::OMNIBOX_API_EXTENSION || + default_search_provider->type() == + TemplateURL::NORMAL_CONTROLLED_BY_EXTENSION)); + SCOPED_CRASH_KEY_BOOL( "KwdbRefresh", "is_from_reg_program", default_search_provider && default_search_provider->CreatedByRegulatoryProgram()); + SCOPED_CRASH_KEY_NUMBER("KwdbRefresh", "entries_matching_dsp", + entries_matching_dsp_to_reconcile); + + SCOPED_CRASH_KEY_STRING256("KwdbRefresh", "prepop_duplicates", + StringifyDuplicates(duplicate_counts_by_id)); + if (!dsp_match && !has_mismatch_explanation) { // This is not implemented with a `CHECK` for various reasons: // - It's a pre-existing behaviour
diff --git a/components/signin/public/base/oauth_consumer_id.h b/components/signin/public/base/oauth_consumer_id.h index f720cc2..aefe85e 100644 --- a/components/signin/public/base/oauth_consumer_id.h +++ b/components/signin/public/base/oauth_consumer_id.h
@@ -119,7 +119,8 @@ kIndigo = 96, kGlicInvokeApi = 97, kSecureGatewayService = 98, - kMaxValue = kSecureGatewayService, + kDrivePickerHost = 99, + kMaxValue = kDrivePickerHost, }; // LINT.ThenChange(//tools/metrics/histograms/metadata/signin/enums.xml:OAuthConsumerId)
diff --git a/components/signin/public/base/oauth_consumer_registry.cc b/components/signin/public/base/oauth_consumer_registry.cc index 30a35d2..fa0cad2 100644 --- a/components/signin/public/base/oauth_consumer_registry.cc +++ b/components/signin/public/base/oauth_consumer_registry.cc
@@ -327,6 +327,7 @@ "actor_login_permission_service"; constexpr char kGapisServiceName[] = "gapis_service"; constexpr char kOneTimeTokenServiceName[] = "one_time_token_service"; +constexpr char kDrivePickerHostName[] = "drive_picker_host"; constexpr char kMultistepFilterName[] = "multistep_filter"; } // namespace @@ -776,6 +777,10 @@ return OAuthConsumer( /*name=*/kSecureGatewayServiceName, /*scopes=*/{GaiaConstants::kSecureGatewayOAuth2Scope}); + case OAuthConsumerId::kDrivePickerHost: + return OAuthConsumer( + /*name=*/kDrivePickerHostName, + /*scopes=*/{kDriveReadOnlyOAuth2Scope}); } }
diff --git a/components/spellcheck/browser/spelling_service_client.cc b/components/spellcheck/browser/spelling_service_client.cc index 16a5c204..14e2cb04 100644 --- a/components/spellcheck/browser/spelling_service_client.cc +++ b/components/spellcheck/browser/spelling_service_client.cc
@@ -19,7 +19,6 @@ #include "base/notreached.h" #include "base/strings/stringprintf.h" #include "base/strings/utf_string_conversions.h" -#include "base/time/time.h" #include "base/values.h" #include "components/enterprise/connectors/core/common.h" #include "components/prefs/pref_service.h" @@ -178,8 +177,7 @@ loader->DownloadToStringOfUnboundedSizeUntilCrashAndDie( url_loader_factory.get(), base::BindOnce(&SpellingServiceClient::OnSimpleLoaderComplete, - base::Unretained(this), std::move(it), - base::TimeTicks::Now())); + base::Unretained(this), std::move(it))); return true; } @@ -287,22 +285,22 @@ } // Check for errors from spelling service. - const base::Value* error = value->Find(kErrorPath); - if (error) { + const base::Value* error = value->Find(kErrorPath); + if (error) { return false; - } + } // Retrieve the array of Misspelling objects. When the input text does not // have misspelled words, it returns an empty JSON. (In this case, its HTTP // status is 200.) We just return true for this case. - const base::ListValue* misspellings = - value->FindListByDottedPath(kMisspellingsRestPath); + const base::ListValue* misspellings = + value->FindListByDottedPath(kMisspellingsRestPath); - if (!misspellings) { + if (!misspellings) { return true; - } + } - for (const base::Value& misspelling : *misspellings) { + for (const base::Value& misspelling : *misspellings) { // Retrieve the i-th misspelling region and put it to the given vector. When // the Spelling service sends two or more suggestions, we read only the // first one because SpellCheckResult can store only one suggestion. @@ -331,7 +329,7 @@ SpellCheckResult result(spellcheck::Decoration::SPELLING, *start, *length, base::UTF8ToUTF16(*replacement)); results->push_back(result); - } + } return true; } @@ -348,11 +346,7 @@ void SpellingServiceClient::OnSimpleLoaderComplete( SpellCheckLoaderList::iterator it, - base::TimeTicks request_start, std::optional<std::string> response_body) { - UMA_HISTOGRAM_TIMES("SpellCheck.SpellingService.RequestDuration", - base::TimeTicks::Now() - request_start); - TextCheckCompleteCallback callback = std::move(it->get()->callback); std::u16string text = it->get()->text; bool success = false;
diff --git a/components/spellcheck/browser/spelling_service_client.h b/components/spellcheck/browser/spelling_service_client.h index 4a65fb6..d681fa4 100644 --- a/components/spellcheck/browser/spelling_service_client.h +++ b/components/spellcheck/browser/spelling_service_client.h
@@ -11,17 +11,12 @@ #include <string> #include <vector> -#include "base/compiler_specific.h" #include "base/functional/callback.h" #include "net/traffic_annotation/network_traffic_annotation.h" #include "url/gurl.h" struct SpellCheckResult; -namespace base { -class TimeTicks; -} - namespace content { class BrowserContext; } @@ -150,7 +145,6 @@ std::list<std::unique_ptr<TextCheckCallbackData>>; void OnSimpleLoaderComplete(SpellCheckLoaderList::iterator it, - base::TimeTicks request_start, std::optional<std::string> response_body); // List of loaders in use.
diff --git a/components/sqlite_vfs/BUILD.gn b/components/sqlite_vfs/BUILD.gn index ef51abf0..fa56ad76 100644 --- a/components/sqlite_vfs/BUILD.gn +++ b/components/sqlite_vfs/BUILD.gn
@@ -31,6 +31,8 @@ sources = [ "constants.h", + "file_system_id.cc", + "file_system_id.h", "lock_state.h", "metrics_util.cc", "metrics_util.h", @@ -39,11 +41,8 @@ "shared_locks.cc", "shared_locks.h", "sqlite_database_vfs_file_set.cc", - "sqlite_database_vfs_file_set.h", "sqlite_sandboxed_vfs.cc", - "sqlite_sandboxed_vfs.h", "vfs_utils.cc", - "vfs_utils.h", ] deps = [ "//third_party/sqlite" ] @@ -74,6 +73,7 @@ # Cross-process file locks are not available on Fuchsia. if (!is_fuchsia) { sources = [ + "file_system_id_unittest.cc", "sandboxed_file_unittest.cc", "shared_locks_unittest.cc", "sqlite_database_vfs_file_set_unittest.cc",
diff --git a/components/sqlite_vfs/file_system_id.cc b/components/sqlite_vfs/file_system_id.cc new file mode 100644 index 0000000..ac8cb4e --- /dev/null +++ b/components/sqlite_vfs/file_system_id.cc
@@ -0,0 +1,52 @@ +// Copyright 2026 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "components/sqlite_vfs/file_system_id.h" + +#include "base/files/file.h" +#include "base/metrics/histogram_functions.h" +#include "build/build_config.h" +#include "components/sqlite_vfs/file_type.h" +#include "components/sqlite_vfs/metrics_util.h" + +#if BUILDFLAG(IS_WIN) +#include <windows.h> +#elif BUILDFLAG(IS_POSIX) +#include <sys/stat.h> +#endif + +namespace sqlite_vfs { + +std::optional<FileSystemId> GetFileSystemId(Client client, + const base::File& file) { + base::File::Error error = base::File::FILE_ERROR_FAILED; + if (file.IsValid()) { +#if BUILDFLAG(IS_WIN) + BY_HANDLE_FILE_INFORMATION info; + if (::GetFileInformationByHandle(file.GetPlatformFile(), &info)) { + return FileSystemId{ + .volume_serial_number = info.dwVolumeSerialNumber, + .file_index_high = info.nFileIndexHigh, + .file_index_low = info.nFileIndexLow, + }; + } +#elif BUILDFLAG(IS_POSIX) + base::stat_wrapper_t stat_info; + if (base::File::Fstat(file.GetPlatformFile(), &stat_info) == 0) { + return FileSystemId{ + .dev = static_cast<dev_t>(stat_info.st_dev), + .ino = static_cast<ino_t>(stat_info.st_ino), + }; + } +#endif + error = base::File::GetLastFileError(); + } + + base::UmaHistogramExactLinear( + GetHistogramName(client, "GetFileSystemIdError"), -error, + -base::File::FILE_ERROR_MAX); + return std::nullopt; +} + +} // namespace sqlite_vfs
diff --git a/components/sqlite_vfs/file_system_id.h b/components/sqlite_vfs/file_system_id.h new file mode 100644 index 0000000..8a6a065 --- /dev/null +++ b/components/sqlite_vfs/file_system_id.h
@@ -0,0 +1,47 @@ +// Copyright 2026 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef COMPONENTS_SQLITE_VFS_FILE_SYSTEM_ID_H_ +#define COMPONENTS_SQLITE_VFS_FILE_SYSTEM_ID_H_ + +#include <optional> + +#include "base/component_export.h" +#include "build/build_config.h" + +#if BUILDFLAG(IS_WIN) +#include "base/win/windows_types.h" +#elif BUILDFLAG(IS_POSIX) +#include <sys/types.h> +#endif + +namespace base { +class File; +} // namespace base + +namespace sqlite_vfs { + +enum class Client; + +struct COMPONENT_EXPORT(SQLITE_VFS) FileSystemId { +#if BUILDFLAG(IS_WIN) + DWORD volume_serial_number; + DWORD file_index_high; + DWORD file_index_low; +#elif BUILDFLAG(IS_POSIX) + dev_t dev; + ino_t ino; +#endif + + friend bool operator==(const FileSystemId&, const FileSystemId&) = default; +}; + +// Returns a unique identifier for the physical file on disk. +COMPONENT_EXPORT(SQLITE_VFS) +std::optional<FileSystemId> GetFileSystemId(Client client, + const base::File& file); + +} // namespace sqlite_vfs + +#endif // COMPONENTS_SQLITE_VFS_FILE_SYSTEM_ID_H_
diff --git a/components/sqlite_vfs/file_system_id_unittest.cc b/components/sqlite_vfs/file_system_id_unittest.cc new file mode 100644 index 0000000..a2d797f8 --- /dev/null +++ b/components/sqlite_vfs/file_system_id_unittest.cc
@@ -0,0 +1,74 @@ +// Copyright 2026 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "components/sqlite_vfs/file_system_id.h" + +#include "base/files/file.h" +#include "base/files/file_path.h" +#include "base/files/file_util.h" +#include "base/files/scoped_temp_dir.h" +#include "base/test/gmock_expected_support.h" +#include "base/test/metrics/histogram_tester.h" +#include "components/sqlite_vfs/client.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace sqlite_vfs { + +class FileSystemIdTest : public testing::Test { + protected: + void SetUp() override { ASSERT_TRUE(temp_dir_.CreateUniqueTempDir()); } + + base::ScopedTempDir temp_dir_; +}; + +TEST_F(FileSystemIdTest, InvalidFileReturnsDefaultId) { + base::HistogramTester histogram_tester; + base::File invalid_file; + std::optional<FileSystemId> id = GetFileSystemId(Client::kTest, invalid_file); + + EXPECT_EQ(id, std::nullopt); + histogram_tester.ExpectUniqueSample("SandboxedVfs.GetFileSystemIdError.Test", + -base::File::FILE_ERROR_FAILED, 1); +} + +TEST_F(FileSystemIdTest, SameFileSameId) { + base::FilePath file_path = temp_dir_.GetPath().AppendASCII("test_file"); + + // Open handle 1 + base::File file1(file_path, base::File::FLAG_CREATE_ALWAYS | + base::File::FLAG_READ | + base::File::FLAG_WRITE); + ASSERT_TRUE(file1.IsValid()); + + // Open handle 2 to the same file + base::File file2(file_path, base::File::FLAG_OPEN | base::File::FLAG_READ); + ASSERT_TRUE(file2.IsValid()); + + ASSERT_OK_AND_ASSIGN(FileSystemId id1, GetFileSystemId(Client::kTest, file1)); + ASSERT_OK_AND_ASSIGN(FileSystemId id2, GetFileSystemId(Client::kTest, file2)); + + EXPECT_EQ(id1, id2); +} + +TEST_F(FileSystemIdTest, DifferentFilesDifferentIds) { + base::FilePath file_path1 = temp_dir_.GetPath().AppendASCII("test_file_1"); + base::FilePath file_path2 = temp_dir_.GetPath().AppendASCII("test_file_2"); + + base::File file1(file_path1, base::File::FLAG_CREATE_ALWAYS | + base::File::FLAG_READ | + base::File::FLAG_WRITE); + ASSERT_TRUE(file1.IsValid()); + + base::File file2(file_path2, base::File::FLAG_CREATE_ALWAYS | + base::File::FLAG_READ | + base::File::FLAG_WRITE); + ASSERT_TRUE(file2.IsValid()); + + ASSERT_OK_AND_ASSIGN(FileSystemId id1, GetFileSystemId(Client::kTest, file1)); + ASSERT_OK_AND_ASSIGN(FileSystemId id2, GetFileSystemId(Client::kTest, file2)); + + EXPECT_NE(id1, id2); +} + +} // namespace sqlite_vfs
diff --git a/components/sqlite_vfs/multiprocess_unittest.cc b/components/sqlite_vfs/multiprocess_unittest.cc index 18b1a15a..5a49553 100644 --- a/components/sqlite_vfs/multiprocess_unittest.cc +++ b/components/sqlite_vfs/multiprocess_unittest.cc
@@ -1173,6 +1173,123 @@ ElementsAre(static_cast<int>(sql::SqliteErrorCode::kIoLock))); } +TEST_P(SqliteVfsMultiprocessTest, AbandonAndReconnect) { + ASSERT_OK_AND_ASSIGN( + auto file_set_1, + CreateAndBindFileSet(file_set_directory(), base::FilePath(kBaseName), + journal_mode_wal())); + { + auto unregister_runner_1 = + SqliteSandboxedVfsDelegate::GetInstance()->RegisterSandboxedFiles( + file_set_1); + + sql::Database db_1(MakeDatabaseOptionsForFileSet(file_set_1), + sql::Database::Tag("Test")); + ::testing::StrictMock<ScopedMockErrorCallback> error_mock(db_1); + ASSERT_TRUE(db_1.Open(file_set_1.GetDbVirtualFilePath())); + + // Create table. + ASSERT_TRUE(db_1.Execute("CREATE TABLE test (val TEXT)")); + ASSERT_TRUE(db_1.Execute("INSERT INTO test (val) VALUES ('initial')")); + } + + // Spawn child. + mojo::Remote<sqlite_vfs::mojom::SqliteVfsMultiprocessTestHelper> child = + SpawnChild("SqliteVfsChild"); + + // Pass read-only file set to child. + ASSERT_OK_AND_ASSIGN( + auto pending_file_set_1, + ShareConnection(file_set_directory(), base::FilePath(kBaseName), + file_set_1, /*read_write=*/false)); + mojo::Remote<sqlite_vfs::mojom::ReadOnlyConnection> connection_1 = + OpenDatabaseReadOnly(child, std::move(pending_file_set_1)); + ASSERT_TRUE(connection_1.is_bound()); + + // Child should see 'initial'. + { + base::test::TestFuture<base::expected<std::string, int32_t>> future; + connection_1->Read(future.GetCallback()); + EXPECT_THAT(future.Take(), ValueIs("initial")); + } + + // Parent Abandons the file set. + file_set_1.Abandon(); + + // Parent creates a new connection to the same physical database files. + ASSERT_OK_AND_ASSIGN( + auto pending_file_set_2, + MakePendingFileSet(Client::kTest, file_set_directory(), + base::FilePath(kBaseName), + /*single_connection=*/false, journal_mode_wal())); + ASSERT_OK_AND_ASSIGN( + auto file_set_2, + SqliteVfsFileSet::Bind(Client::kTest, std::move(pending_file_set_2))); + auto unregister_runner_2 = + SqliteSandboxedVfsDelegate::GetInstance()->RegisterSandboxedFiles( + file_set_2); + + { + sql::Database db_2(MakeDatabaseOptionsForFileSet(file_set_2), + sql::Database::Tag("Test")); + ::testing::StrictMock<ScopedMockErrorCallback> error_mock(db_2); + ASSERT_TRUE(db_2.Open(file_set_2.GetDbVirtualFilePath())); + ASSERT_TRUE(db_2.Execute("INSERT INTO test (val) VALUES ('new_value')")); + } + + // Parent shares this new connection with the child. + ASSERT_OK_AND_ASSIGN( + auto pending_file_set_2_ro, + ShareConnection(file_set_directory(), base::FilePath(kBaseName), + file_set_2, /*read_write=*/false)); + + // Child succeeds in opening a second connection because the first has been + // abandoned. + base::test::TestFuture< + mojo::PendingRemote<sqlite_vfs::mojom::ReadOnlyConnection>> + future_2; + child->OpenDatabaseReadOnly(std::move(pending_file_set_2_ro), + future_2.GetCallback()); + auto remote_2 = future_2.Take(); + ASSERT_TRUE(remote_2.is_valid()); + mojo::Remote<sqlite_vfs::mojom::ReadOnlyConnection> connection_2( + std::move(remote_2)); + + // Verify that child can use the new connection to read the new value. + { + base::test::TestFuture<base::expected<std::string, int32_t>> future; + connection_2->Read(future.GetCallback()); + EXPECT_THAT(future.Take(), ValueIs(std::string("new_value"))); + } + + // Verify that child gets a kIoLock error on connection_1. + { + base::test::TestFuture<base::expected<std::string, int32_t>> future; + connection_1->Read(future.GetCallback()); + EXPECT_THAT(future.Take(), + ErrorIs(static_cast<int>(sql::SqliteErrorCode::kIoLock))); + } + + // Cleanup. + { + base::test::TestFuture<std::vector<int32_t>> close_future; + connection_1->CloseDatabase(base::BindOnce( + [](base::OnceCallback<void(std::vector<int32_t>)> cb, + const std::vector<int32_t>& errors) { std::move(cb).Run(errors); }, + close_future.GetCallback())); + EXPECT_THAT(close_future.Take(), + ElementsAre(static_cast<int>(sql::SqliteErrorCode::kIoLock))); + } + { + base::test::TestFuture<std::vector<int32_t>> close_future; + connection_2->CloseDatabase(base::BindOnce( + [](base::OnceCallback<void(std::vector<int32_t>)> cb, + const std::vector<int32_t>& errors) { std::move(cb).Run(errors); }, + close_future.GetCallback())); + EXPECT_THAT(close_future.Take(), IsEmpty()); + } +} + INSTANTIATE_TEST_SUITE_P(, SqliteVfsMultiprocessTest, Bool(),
diff --git a/components/sqlite_vfs/sandboxed_file.cc b/components/sqlite_vfs/sandboxed_file.cc index 6103d2d..db1c883a 100644 --- a/components/sqlite_vfs/sandboxed_file.cc +++ b/components/sqlite_vfs/sandboxed_file.cc
@@ -16,6 +16,7 @@ #include "base/notreached.h" #include "base/numerics/checked_math.h" #include "base/numerics/safe_conversions.h" +#include "components/sqlite_vfs/file_system_id.h" #include "components/sqlite_vfs/file_type.h" #include "components/sqlite_vfs/metrics_util.h" #include "third_party/sqlite/sqlite3.h" @@ -27,16 +28,24 @@ base::File file, AccessRights access_rights, std::optional<SharedLocks> shared_locks, + base::UnguessableToken shared_locks_id, base::File wal_index_file) : client_(client), file_type_(file_type), underlying_file_(std::move(file)), access_rights_(access_rights), shared_locks_(std::move(shared_locks)), + shared_locks_id_(std::move(shared_locks_id)), wal_index_file_(std::move(wal_index_file)) { CHECK(!shared_locks_ || file_type_ == FileType::kMainDb); CHECK(!wal_index_file_.IsValid() || file_type_ == FileType::kMainDb); CHECK(!wal_index_file_.IsValid() || shared_locks_.has_value()); + + // Cache the ID of the main database file for double-open protection; see + // RegisterSandboxedFiles. + if (file_type_ == FileType::kMainDb && underlying_file_.IsValid()) { + file_system_id_ = GetFileSystemId(client_, underlying_file_); + } } SandboxedFile::~SandboxedFile() = default; @@ -87,6 +96,12 @@ return state; } +bool SandboxedFile::IsAbandoned() const { + CHECK_EQ(file_type_, FileType::kMainDb); + CHECK(!is_single_connection()); + return shared_locks_->IsAbandoned(); +} + int SandboxedFile::Read(void* buffer, int size, sqlite3_int64 offset) { // Make a safe span from the pair <buffer, size>. The buffer and the // size are received from sqlite.
diff --git a/components/sqlite_vfs/sandboxed_file.h b/components/sqlite_vfs/sandboxed_file.h index 00121711..da250aa 100644 --- a/components/sqlite_vfs/sandboxed_file.h +++ b/components/sqlite_vfs/sandboxed_file.h
@@ -14,6 +14,8 @@ #include "base/component_export.h" #include "base/files/file.h" #include "base/files/memory_mapped_file.h" +#include "base/unguessable_token.h" +#include "components/sqlite_vfs/file_system_id.h" #include "components/sqlite_vfs/lock_state.h" #include "components/sqlite_vfs/shared_locks.h" #include "sql/sandboxed_vfs_file.h" @@ -43,6 +45,7 @@ base::File file, AccessRights access_rights, std::optional<SharedLocks> shared_locks = std::nullopt, + base::UnguessableToken shared_locks_id = {}, base::File wal_index_file = {}); SandboxedFile(SandboxedFile& other) = delete; SandboxedFile& operator=(const SandboxedFile& other) = delete; @@ -77,6 +80,11 @@ Client client() const { return client_; } FileType file_type() const { return file_type_; } + const std::optional<FileSystemId>& file_system_id() const { + return file_system_id_; + } + base::UnguessableToken shared_locks_id() const { return shared_locks_id_; } + // Returns a reference to the instance's file regardless of whether it is // open (`IsValid()` returns true) or closed (otherwise). The reference is // invalidated upon open/close. @@ -115,6 +123,9 @@ // connection. See `SqliteVfsFileSet::Abandon()` for details. LockState Abandon(); + // Returns true if the database lock has been marked as abandoned. + bool IsAbandoned() const; + private: // Returns true if this instance is likely opened for exclusive access. // Take care: this is only valid for FileType::kMainDb files. @@ -142,11 +153,15 @@ // protocols are implemented. Otherwise, this file is opened in exclusive mode // so no shared locks are required. Only used for the main database file. std::optional<SharedLocks> shared_locks_; + base::UnguessableToken shared_locks_id_; // The WAL-index file. Only used for the main database file, and only when // opened for sharing (exclusive locking mode off). base::File wal_index_file_; + // ID of the main database file for double-open detection. + std::optional<FileSystemId> file_system_id_; + // Mapped pages of the WAL-index file. Only used for the main database file, // and only when opened for sharing (exclusive locking mode off). std::vector<std::unique_ptr<base::MemoryMappedFile>> shm_mappings_;
diff --git a/components/sqlite_vfs/shared_locks.cc b/components/sqlite_vfs/shared_locks.cc index 22a7463..938d991 100644 --- a/components/sqlite_vfs/shared_locks.cc +++ b/components/sqlite_vfs/shared_locks.cc
@@ -228,6 +228,12 @@ return LockState::kNotHeld; } +bool SharedLocks::IsAbandoned() const { + return (const_cast<SharedLocks*>(this)->GetDatabaseLock().load( + std::memory_order_relaxed) & + kAbandonedBit) != 0; +} + int SharedLocks::ShmLock(int lock_index, int num_locks, LockOperation operation,
diff --git a/components/sqlite_vfs/shared_locks.h b/components/sqlite_vfs/shared_locks.h index 8535374..6646e39 100644 --- a/components/sqlite_vfs/shared_locks.h +++ b/components/sqlite_vfs/shared_locks.h
@@ -78,6 +78,9 @@ // requesting such will properly detect that the lock has been abandoned. LockState Abandon(); + // Returns true if the database lock has been marked as abandoned. + bool IsAbandoned() const; + // Acquires or releases `num_locks` WAL locks beginning at index `lock_index`. // Shared locks are acquired and released individually (`num_locks` == // 1). Exclusive locks are acquired in a range (`num_locks` >= 1).
diff --git a/components/sqlite_vfs/sqlite_database_vfs_file_set.cc b/components/sqlite_vfs/sqlite_database_vfs_file_set.cc index d75f3c6f..c52db57 100644 --- a/components/sqlite_vfs/sqlite_database_vfs_file_set.cc +++ b/components/sqlite_vfs/sqlite_database_vfs_file_set.cc
@@ -59,9 +59,14 @@ ? SandboxedFile::AccessRights::kReadWrite : SandboxedFile::AccessRights::kReadOnly; + const base::UnguessableToken shared_locks_id = + pending_file_set.shared_lock.IsValid() + ? pending_file_set.shared_lock.GetGUID() + : base::UnguessableToken(); + auto db_file = std::make_unique<SandboxedFile>( client, FileType::kMainDb, std::move(pending_file_set.db_file), - access_rights, std::move(shared_locks), + access_rights, std::move(shared_locks), shared_locks_id, std::move(pending_file_set.wal_index_file)); auto journal_file = std::make_unique<SandboxedFile>( client, FileType::kMainJournal, std::move(pending_file_set.journal_file),
diff --git a/components/sqlite_vfs/sqlite_sandboxed_vfs.cc b/components/sqlite_vfs/sqlite_sandboxed_vfs.cc index a8406a7..1aceaab 100644 --- a/components/sqlite_vfs/sqlite_sandboxed_vfs.cc +++ b/components/sqlite_vfs/sqlite_sandboxed_vfs.cc
@@ -13,9 +13,11 @@ #include "base/files/file.h" #include "base/files/file_util.h" #include "base/metrics/histogram_functions.h" +#include "base/not_fatal_until.h" #include "base/notreached.h" #include "base/strings/strcat.h" #include "base/synchronization/lock.h" +#include "components/sqlite_vfs/file_system_id.h" #include "components/sqlite_vfs/file_type.h" #include "components/sqlite_vfs/metrics_util.h" #include "components/sqlite_vfs/sandboxed_file.h" @@ -57,6 +59,13 @@ NOTREACHED(); } +// Returns true if `file1` and `file2` have the same cached ID. +bool IsSameFile(const SandboxedFile& file1, const SandboxedFile& file2) { + const auto& id1 = file1.file_system_id(); + const auto& id2 = file2.file_system_id(); + return id1.has_value() && id2.has_value() && *id1 == *id2; +} + } // namespace SqliteSandboxedVfsDelegate::SqliteSandboxedVfsDelegate() { @@ -201,6 +210,39 @@ const SqliteVfsFileSet& sqlite_vfs_file_set) { base::AutoLock lock(files_map_lock_); + const SandboxedFile* incoming_db = sqlite_vfs_file_set.GetSandboxedDbFile(); + const base::UnguessableToken incoming_shm_guid = + sqlite_vfs_file_set.is_single_connection() + ? base::UnguessableToken() + : sqlite_vfs_file_set.GetSharedLock().GetGUID(); + + for (const auto& [virtual_path, sandboxed_file] : sandboxed_files_map_) { + if (sandboxed_file->file_type() == FileType::kMainDb && + IsSameFile(*sandboxed_file, *incoming_db)) { + const base::UnguessableToken registered_shm_guid = + sandboxed_file->shared_locks_id(); + + // If either database connection is opened in single-connection mode (no + // shared locks segment GUID), it cannot co-exist with any other + // connection targeting the same physical file in this process. + CHECK(!registered_shm_guid.is_empty() && !incoming_shm_guid.is_empty(), + base::NotFatalUntil::M151); + + // Allow duplicate registrations of shared databases if at least one of + // the two has been abandoned. In this case, only one of the two will be + // able to use the database; the other will consistently fail with + // SQLITE_IOERR_LOCK. + if (sandboxed_file->IsAbandoned() || incoming_db->IsAbandoned()) { + continue; + } + + // Two connections to the same database file with different shared locks + // results in data corruption. + CHECK_EQ(registered_shm_guid, incoming_shm_guid, + base::NotFatalUntil::M151); + } + } + auto [it, inserted] = sandboxed_files_map_.emplace(sqlite_vfs_file_set.GetDbVirtualFilePath(), sqlite_vfs_file_set.GetSandboxedDbFile());
diff --git a/components/sqlite_vfs/sqlite_sandboxed_vfs_unittest.cc b/components/sqlite_vfs/sqlite_sandboxed_vfs_unittest.cc index 96460744..020a1e2c 100644 --- a/components/sqlite_vfs/sqlite_sandboxed_vfs_unittest.cc +++ b/components/sqlite_vfs/sqlite_sandboxed_vfs_unittest.cc
@@ -12,6 +12,7 @@ #include "base/files/file_util.h" #include "base/files/scoped_temp_dir.h" #include "base/test/gmock_expected_support.h" +#include "base/test/gtest_util.h" #include "base/test/metrics/histogram_tester.h" #include "base/types/expected_macros.h" #include "components/sqlite_vfs/client.h" @@ -37,11 +38,12 @@ void SetUp() override { ASSERT_TRUE(temp_dir_.CreateUniqueTempDir()); } - std::optional<SqliteVfsFileSet> CreateFilesAndBuildVfsFileSet() { + std::optional<SqliteVfsFileSet> CreateFilesAndBuildVfsFileSet( + bool single_connection = false) { std::optional<SqliteVfsFileSet> file_set; if (auto pending_file_set = MakePendingFileSet( Client::kTest, temp_dir_.GetPath(), base::FilePath(kTestBaseName), - /*single_connection=*/false, /*journal_mode_wal=*/false); + single_connection, /*journal_mode_wal=*/false); !pending_file_set.has_value()) { ADD_FAILURE() << "Failed creating pending file set"; } else { @@ -309,4 +311,90 @@ EXPECT_TRUE(db.Open(vfs_file_set.GetDbVirtualFilePath())); } +TEST_F(SqliteSandboxedVfsTest, RegisterSandboxedFilesSharedSuccess) { + ASSERT_OK_AND_ASSIGN(SqliteVfsFileSet vfs_file_set_1, + CreateFilesAndBuildVfsFileSet()); + ASSERT_OK_AND_ASSIGN(SqliteVfsFileSet vfs_file_set_2, + GetReadOnlyVfsFileSet(vfs_file_set_1)); + + ASSERT_EQ(vfs_file_set_1.GetSharedLock().GetGUID(), + vfs_file_set_2.GetSharedLock().GetGUID()); + + SqliteSandboxedVfsDelegate::UnregisterRunner unregister_runner_1 = + SqliteSandboxedVfsDelegate::GetInstance()->RegisterSandboxedFiles( + vfs_file_set_1); + + SqliteSandboxedVfsDelegate::UnregisterRunner unregister_runner_2 = + SqliteSandboxedVfsDelegate::GetInstance()->RegisterSandboxedFiles( + vfs_file_set_2); +} + +// These tests trigger a CHECK(NotFatalUntil), so they only crash unofficial +// builds. +#if !defined(OFFICIAL_BUILD) +using SqliteSandboxedVfsDeathTest = SqliteSandboxedVfsTest; + +TEST_F(SqliteSandboxedVfsDeathTest, RegisterSandboxedFilesDuplicateFailure) { + ASSERT_OK_AND_ASSIGN(SqliteVfsFileSet vfs_file_set_1, + CreateFilesAndBuildVfsFileSet()); + ASSERT_OK_AND_ASSIGN(SqliteVfsFileSet vfs_file_set_2, + CreateFilesAndBuildVfsFileSet()); + + ASSERT_NE(vfs_file_set_1.GetSharedLock().GetGUID(), + vfs_file_set_2.GetSharedLock().GetGUID()); + + SqliteSandboxedVfsDelegate::UnregisterRunner unregister_runner_1 = + SqliteSandboxedVfsDelegate::GetInstance()->RegisterSandboxedFiles( + vfs_file_set_1); + + EXPECT_CHECK_DEATH({ + auto runner = + SqliteSandboxedVfsDelegate::GetInstance()->RegisterSandboxedFiles( + vfs_file_set_2); + }); +} + +#if BUILDFLAG(IS_POSIX) +TEST_F(SqliteSandboxedVfsDeathTest, + RegisterSandboxedFilesSingleConnectionFailure) { + ASSERT_OK_AND_ASSIGN( + SqliteVfsFileSet vfs_file_set_1, + CreateFilesAndBuildVfsFileSet(/*single_connection=*/true)); + ASSERT_OK_AND_ASSIGN( + SqliteVfsFileSet vfs_file_set_2, + CreateFilesAndBuildVfsFileSet(/*single_connection=*/true)); + + SqliteSandboxedVfsDelegate::UnregisterRunner unregister_runner_1 = + SqliteSandboxedVfsDelegate::GetInstance()->RegisterSandboxedFiles( + vfs_file_set_1); + + EXPECT_CHECK_DEATH({ + auto runner = + SqliteSandboxedVfsDelegate::GetInstance()->RegisterSandboxedFiles( + vfs_file_set_2); + }); +} + +TEST_F(SqliteSandboxedVfsDeathTest, + RegisterSandboxedFilesSingleAndSharedConnectionFailure) { + ASSERT_OK_AND_ASSIGN( + SqliteVfsFileSet vfs_file_set_1, + CreateFilesAndBuildVfsFileSet(/*single_connection=*/false)); + ASSERT_OK_AND_ASSIGN( + SqliteVfsFileSet vfs_file_set_2, + CreateFilesAndBuildVfsFileSet(/*single_connection=*/true)); + + SqliteSandboxedVfsDelegate::UnregisterRunner unregister_runner_1 = + SqliteSandboxedVfsDelegate::GetInstance()->RegisterSandboxedFiles( + vfs_file_set_1); + + EXPECT_CHECK_DEATH({ + auto runner = + SqliteSandboxedVfsDelegate::GetInstance()->RegisterSandboxedFiles( + vfs_file_set_2); + }); +} +#endif // BUILDFLAG(IS_POSIX) +#endif // !defined(OFFICIAL_BUILD) + } // namespace sqlite_vfs
diff --git a/components/sqlite_vfs/vfs_utils.cc b/components/sqlite_vfs/vfs_utils.cc index 9e6bab47..fc4314d 100644 --- a/components/sqlite_vfs/vfs_utils.cc +++ b/components/sqlite_vfs/vfs_utils.cc
@@ -198,7 +198,9 @@ // automatically deleted once all handles are closed. Since this file is // mapped into the address spaces of all processes connection to a // database with the generated file set, it is not reliable to delete the - // file via DeleteFile. + // file via DeleteFile. Additionally, FLAG_WIN_TEMPORARY is used as a hint + // to the OS that the data does not need to be written to disk. This will + // avoid I/O provided that there is sufficient cache to hold the index. // // - On POSIX systems, a second read-only handle to the file is opened // immediately and then the file is unlinked. The read-only handle is kept @@ -209,6 +211,7 @@ base::File::FLAG_CREATE_ALWAYS | base::File::FLAG_WIN_EXCLUSIVE_READ | base::File::FLAG_WIN_EXCLUSIVE_WRITE | + base::File::FLAG_WIN_TEMPORARY | base::File::FLAG_DELETE_ON_CLOSE); #else pending_file_set.wal_index_file = base::File(
diff --git a/components/startup_metric_utils/browser/startup_metric_utils.cc b/components/startup_metric_utils/browser/startup_metric_utils.cc index fb690f3..329cd3e 100644 --- a/components/startup_metric_utils/browser/startup_metric_utils.cc +++ b/components/startup_metric_utils/browser/startup_metric_utils.cc
@@ -420,6 +420,21 @@ now - message_loop_start_ticks_); } +void BrowserStartupMetricRecorder:: + RecordFirstWebContentsNonEmptyPaintForOsLaunch(base::TimeTicks now) { + const base::TimeTicks web_contents_start_ticks = GetWebContentsStartTicks(); + DCHECK(!web_contents_start_ticks.is_null()); + GetCommon().AssertFirstCallInSession(FROM_HERE); + + if (!ShouldLogStartupHistogram()) { + return; + } + + base::UmaHistogramLongTimes100( + "Startup.FirstWebContents.NonEmptyPaint3.AutoLaunchByOs", + now - web_contents_start_ticks); +} + void BrowserStartupMetricRecorder::RecordFirstWebContentsMainNavigationStart( base::TimeTicks ticks) { const base::TimeTicks web_contents_start_ticks = GetWebContentsStartTicks();
diff --git a/components/startup_metric_utils/browser/startup_metric_utils.h b/components/startup_metric_utils/browser/startup_metric_utils.h index 18028f3..2fecad4e 100644 --- a/components/startup_metric_utils/browser/startup_metric_utils.h +++ b/components/startup_metric_utils/browser/startup_metric_utils.h
@@ -113,6 +113,10 @@ base::TimeTicks now, base::TimeTicks render_process_host_init_time); + // Similar to `RecordFirstWebContentsNonEmptyPaint`, but only for auto + // launches by the OS. + void RecordFirstWebContentsNonEmptyPaintForOsLaunch(base::TimeTicks now); + // Call this with the time when the first web contents began navigating its // main frame / successfully committed its navigation for the main frame. // These functions must be called after RecordApplicationStartTime(), because
diff --git a/components/sync_device_info/device_info.h b/components/sync_device_info/device_info.h index 1b3c17ca5..09217a3c 100644 --- a/components/sync_device_info/device_info.h +++ b/components/sync_device_info/device_info.h
@@ -175,8 +175,10 @@ kUnavailable = 0, kNeedsOptIn = 1, kReady = 2, + kMaxValue = kReady, }; // LINT.ThenChange(//components/sync/protocol/sync_enums.proto:GlicExperimentalTriggeringState) + // LINT.ThenChange(//tools/metrics/histograms/metadata/glic/enums.xml:GlicExperimentalTriggeringState) DeviceInfo( const std::string& guid,
diff --git a/components/test/data/viz/render_pass_data/top_real_world_desktop/espn_2018/0463.json b/components/test/data/viz/render_pass_data/top_real_world_desktop/espn_2018/0463.json index be692a89..34605d5 100644 --- a/components/test/data/viz/render_pass_data/top_real_world_desktop/espn_2018/0463.json +++ b/components/test/data/viz/render_pass_data/top_real_world_desktop/espn_2018/0463.json
@@ -252,12 +252,6 @@ "render_pass_id": "12", "resources": [ ], "shared_quad_state_index": 0, - "tex_coord_rect": { - "height": 40.0, - "width": 40.0, - "x": 0.0, - "y": 0.0 - }, "visible_rect": { "height": 40, "width": 40, @@ -960,12 +954,6 @@ "render_pass_id": "6", "resources": [ ], "shared_quad_state_index": 0, - "tex_coord_rect": { - "height": 40.0, - "width": 40.0, - "x": 0.0, - "y": 0.0 - }, "visible_rect": { "height": 40, "width": 40, @@ -1004,12 +992,6 @@ "render_pass_id": "7", "resources": [ ], "shared_quad_state_index": 1, - "tex_coord_rect": { - "height": 40.0, - "width": 40.0, - "x": 0.0, - "y": 0.0 - }, "visible_rect": { "height": 40, "width": 40, @@ -1573,12 +1555,6 @@ "render_pass_id": "11", "resources": [ ], "shared_quad_state_index": 6, - "tex_coord_rect": { - "height": 40.0, - "width": 40.0, - "x": 0.0, - "y": 0.0 - }, "visible_rect": { "height": 40, "width": 40, @@ -1643,12 +1619,6 @@ "render_pass_id": "9", "resources": [ ], "shared_quad_state_index": 8, - "tex_coord_rect": { - "height": 251.0, - "width": 825.0, - "x": 0.0, - "y": 0.0 - }, "visible_rect": { "height": 251, "width": 825, @@ -1765,12 +1735,6 @@ "render_pass_id": "5", "resources": [ ], "shared_quad_state_index": 10, - "tex_coord_rect": { - "height": 24.0, - "width": 437.0, - "x": 0.0, - "y": 0.0 - }, "visible_rect": { "height": 24, "width": 437,
diff --git a/components/thin_webview/BUILD.gn b/components/thin_webview/BUILD.gn index 82d0bb98..45e553a5 100644 --- a/components/thin_webview/BUILD.gn +++ b/components/thin_webview/BUILD.gn
@@ -25,6 +25,7 @@ ] deps = [ + "//base:callback_java", "//components/embedder_support/android:context_menu_java", "//components/embedder_support/android:web_contents_delegate_java", "//content/public/android:content_java",
diff --git a/components/thin_webview/internal/compositor_view_impl.cc b/components/thin_webview/internal/compositor_view_impl.cc index 358eb4b..cfe121f 100644 --- a/components/thin_webview/internal/compositor_view_impl.cc +++ b/components/thin_webview/internal/compositor_view_impl.cc
@@ -6,6 +6,7 @@ #include <memory> +#include "base/android/callback_android.h" #include "base/android/jni_android.h" #include "cc/slim/layer.h" #include "cc/slim/solid_color_layer.h" @@ -114,6 +115,15 @@ compositor_->SetNeedsComposite(); } +void CompositorViewImpl::RunOnNextFrame(JNIEnv* env, + base::OnceClosure callback) { + compositor_->RequestSuccessfulPresentationTimeForNextFrame(base::BindOnce( + [](base::OnceClosure cb, const viz::FrameTimingDetails& details) { + std::move(cb).Run(); + }, + std::move(callback))); +} + void CompositorViewImpl::SetRootLayer(scoped_refptr<cc::slim::Layer> layer) { const auto& children = root_layer_->children(); DCHECK(children.size() <= 1);
diff --git a/components/thin_webview/internal/compositor_view_impl.h b/components/thin_webview/internal/compositor_view_impl.h index 09b34775..60ff942 100644 --- a/components/thin_webview/internal/compositor_view_impl.h +++ b/components/thin_webview/internal/compositor_view_impl.h
@@ -9,6 +9,7 @@ #include "base/android/jni_android.h" #include "base/android/scoped_java_ref.h" +#include "base/functional/callback.h" #include "components/thin_webview/compositor_view.h" #include "content/public/browser/android/compositor_client.h" @@ -45,6 +46,7 @@ void Destroy(JNIEnv* env); void SetNeedsComposite(JNIEnv* env); + void RunOnNextFrame(JNIEnv* env, base::OnceClosure callback); void SurfaceCreated(JNIEnv* env); void SurfaceDestroyed(JNIEnv* env); void SurfaceChanged(JNIEnv* env,
diff --git a/components/thin_webview/internal/java/src/org/chromium/components/thinwebview/internal/CompositorViewImpl.java b/components/thin_webview/internal/java/src/org/chromium/components/thinwebview/internal/CompositorViewImpl.java index 47cf2ff..73688510 100644 --- a/components/thin_webview/internal/java/src/org/chromium/components/thinwebview/internal/CompositorViewImpl.java +++ b/components/thin_webview/internal/java/src/org/chromium/components/thinwebview/internal/CompositorViewImpl.java
@@ -15,6 +15,7 @@ import org.jni_zero.CalledByNative; import org.jni_zero.JNINamespace; +import org.jni_zero.JniType; import org.jni_zero.NativeMethods; import org.chromium.build.annotations.NullMarked; @@ -103,6 +104,12 @@ } @Override + public void runOnNextFrame(Runnable runnable) { + if (mNativeCompositorViewImpl == 0) return; + CompositorViewImplJni.get().runOnNextFrame(mNativeCompositorViewImpl, runnable); + } + + @Override public void setAlpha(float alpha) { assert mViewConstraints.supportsOpacity; if (mNativeCompositorViewImpl == 0) return; @@ -297,5 +304,8 @@ void setNeedsComposite(long nativeCompositorViewImpl); boolean shouldUseSurfaceView(); + + void runOnNextFrame( + long nativeCompositorViewImpl, @JniType("base::OnceClosure") Runnable runnable); } }
diff --git a/components/thin_webview/internal/java/src/org/chromium/components/thinwebview/internal/ThinWebViewImpl.java b/components/thin_webview/internal/java/src/org/chromium/components/thinwebview/internal/ThinWebViewImpl.java index 71d76513..faca115 100644 --- a/components/thin_webview/internal/java/src/org/chromium/components/thinwebview/internal/ThinWebViewImpl.java +++ b/components/thin_webview/internal/java/src/org/chromium/components/thinwebview/internal/ThinWebViewImpl.java
@@ -196,6 +196,11 @@ } @Override + public void runOnNextFrame(Runnable runnable) { + mCompositorView.runOnNextFrame(runnable); + } + + @Override protected void onSizeChanged(int w, int h, int oldw, int oldh) { if (mNativeThinWebViewImpl == 0) return; if (w != oldw || h != oldh) {
diff --git a/components/thin_webview/java/src/org/chromium/components/thinwebview/CompositorView.java b/components/thin_webview/java/src/org/chromium/components/thinwebview/CompositorView.java index ce3cdba..e35a769 100644 --- a/components/thin_webview/java/src/org/chromium/components/thinwebview/CompositorView.java +++ b/components/thin_webview/java/src/org/chromium/components/thinwebview/CompositorView.java
@@ -27,9 +27,15 @@ void requestRender(); /** - * /** - * Sets opacity for the view. {@link ThinWebViewConstraints#supportsOpacity} must be true for - * using this method. + * Registers a callback that is run when the next frame successfully makes it to the screen. + * + * @param runnable The runnable to be run. + */ + void runOnNextFrame(Runnable runnable); + + /** + * /** Sets opacity for the view. {@link ThinWebViewConstraints#supportsOpacity} must be true + * for using this method. */ void setAlpha(float alpha); }
diff --git a/components/thin_webview/java/src/org/chromium/components/thinwebview/ThinWebView.java b/components/thin_webview/java/src/org/chromium/components/thinwebview/ThinWebView.java index 74670cd..8c602e9 100644 --- a/components/thin_webview/java/src/org/chromium/components/thinwebview/ThinWebView.java +++ b/components/thin_webview/java/src/org/chromium/components/thinwebview/ThinWebView.java
@@ -36,6 +36,16 @@ */ void setAlpha(float alpha); + /** + * Registers a callback that is run when the next frame successfully makes it to the screen. + * + * <p>This may be useful for operations that should be synchronized to renders that occur after + * a layout change. + * + * @param runnable The runnable to be run. + */ + void runOnNextFrame(Runnable runnable); + /** Should be called for cleanup when the CompositorView instance is no longer used. */ void destroy(); }
diff --git a/components/update_client/update_client.h b/components/update_client/update_client.h index 1aee8eba..68c79989 100644 --- a/components/update_client/update_client.h +++ b/components/update_client/update_client.h
@@ -72,13 +72,13 @@ // Otherwise, the version of the CRX set in the CrxComponent may not be correct. // // The UpdateClient public interface includes two functions: Install and -// Update. These functions correspond to installing one CRX immediately as a -// foreground activity (Install), and updating a group of CRXs silently in the -// background (Update). This distinction is important. Background updates are -// queued up and their actions run serially, one at a time, for the purpose of -// conserving local resources such as CPU, network, and I/O. -// On the other hand, installs are never queued up but run concurrently, as -// requested by the user. +// Update. The primary distinction between them relates to concurrency and +// queuing. Calls to Update result in a queuing behavior, where the execution of +// each call is serialized for the purpose of conserving local resources such +// as CPU, network, and I/O. On the other hand, calls to Install are never +// queued up but run concurrently. While Install is used for immediate +// foreground installation of a single item, Update supports both background +// and foreground operations for a set of items. // // The update client introduces a runtime constraint regarding interleaving // updates and installs. If installs or updates for a given CRX are in progress, @@ -453,12 +453,11 @@ // instances of CrxComponent to be used for this update. Provides state change // notifications through invocations of the optional // |crx_state_change_callback| callback. - // The |Update| function is intended to be used for background updates of - // several CRXs. Overlapping calls to this function result in a queuing - // behavior, and the execution of each call is serialized. In addition, - // updates are always queued up when installs are running. The |is_foreground| - // parameter must be set to true if the invocation of this function is a - // result of a user initiated update. + // The |Update| function updates the specified CRXs. Overlapping calls to + // this function result in a queuing behavior, and the execution of each call + // is serialized. In addition, updates are always queued up when installs are + // running. The |is_foreground| parameter must be set to true if the + // invocation of this function is a result of a user initiated update. virtual void Update(const std::vector<std::string>& ids, CrxDataCallback crx_data_callback, CrxStateChangeCallback crx_state_change_callback,
diff --git a/components/viz/common/features.cc b/components/viz/common/features.cc index 78c3b173..f69ddb7 100644 --- a/components/viz/common/features.cc +++ b/components/viz/common/features.cc
@@ -188,12 +188,6 @@ BASE_FEATURE(kVSyncAlignedPresentation, base::FEATURE_DISABLED_BY_DEFAULT); #endif -// Sends a CopyOutputRequest completion Ack early for view transitions so it can -// proceed with navigation. ViewTransition Animate still waits though for -// CopyOutputRequests to be actually fulfilled. -BASE_FEATURE(kAckCopyOutputRequestEarlyForViewTransition, - base::FEATURE_ENABLED_BY_DEFAULT); - // If enabled, other frame sinks are throttled when a frame sink is handling // user interaction. BASE_FEATURE(kThrottleFrameSinksOnInteraction, @@ -562,9 +556,4 @@ } #endif // BUILDFLAG(IS_ANDROID) -bool ShouldAckCOREarlyForViewTransition() { - return base::FeatureList::IsEnabled( - features::kAckCopyOutputRequestEarlyForViewTransition); -} - } // namespace features
diff --git a/components/viz/common/features.h b/components/viz/common/features.h index 03c8606..b61498262 100644 --- a/components/viz/common/features.h +++ b/components/viz/common/features.h
@@ -86,8 +86,6 @@ VIZ_COMMON_EXPORT extern const char kTargetForVSyncInteraction[]; #endif -VIZ_COMMON_EXPORT BASE_DECLARE_FEATURE( - kAckCopyOutputRequestEarlyForViewTransition); VIZ_COMMON_EXPORT BASE_DECLARE_FEATURE(kThrottleFrameSinksOnInteraction); VIZ_COMMON_EXPORT bool ShouldThrottleWhenInteractiveFrameSinks(); VIZ_COMMON_EXPORT BASE_DECLARE_FEATURE(kAllowUndamagedNonrootRenderPassToSkip); @@ -193,8 +191,6 @@ #endif // BUILDFLAG(IS_ANDROID) -VIZ_COMMON_EXPORT bool ShouldAckCOREarlyForViewTransition(); - } // namespace features #endif // COMPONENTS_VIZ_COMMON_FEATURES_H_
diff --git a/components/viz/common/quads/aggregated_render_pass.cc b/components/viz/common/quads/aggregated_render_pass.cc index 219b685..cf3b21d 100644 --- a/components/viz/common/quads/aggregated_render_pass.cc +++ b/components/viz/common/quads/aggregated_render_pass.cc
@@ -96,7 +96,7 @@ shared_quad_state_list.back(), quad->rect, quad->visible_rect, quad->needs_blending, render_pass_id, quad->mask_resource_id(), quad->mask_uv_rect, quad->mask_texture_size, quad->filters_scale, - quad->filters_origin, quad->tex_coord_rect, quad->force_anti_aliasing_off, + quad->filters_origin, quad->force_anti_aliasing_off, quad->backdrop_filter_quality, quad->intersects_damage_under, render_pass.filters, render_pass.backdrop_filters, render_pass.backdrop_filter_bounds);
diff --git a/components/viz/common/quads/aggregated_render_pass_draw_quad.cc b/components/viz/common/quads/aggregated_render_pass_draw_quad.cc index 36a49331..5e7d83b 100644 --- a/components/viz/common/quads/aggregated_render_pass_draw_quad.cc +++ b/components/viz/common/quads/aggregated_render_pass_draw_quad.cc
@@ -34,7 +34,6 @@ backdrop_filter_bounds == other.backdrop_filter_bounds && filters_scale == other.filters_scale && filters_origin == other.filters_origin && - tex_coord_rect == other.tex_coord_rect && backdrop_filter_quality == other.backdrop_filter_quality && force_anti_aliasing_off == other.force_anti_aliasing_off && intersects_damage_under == other.intersects_damage_under && @@ -53,7 +52,6 @@ ResourceId mask_resource_id, const gfx::RectF& mask_uv_rect, const gfx::Size& mask_texture_size, - const gfx::RectF& tex_coord_rect, bool force_anti_aliasing_off) { DCHECK(render_pass); @@ -67,9 +65,9 @@ std::optional<SkPath> pass_backdrop_filter_bounds; SetAll(shared_quad_state, rect, visible_rect, needs_blending, render_pass, mask_resource_id, mask_uv_rect, mask_texture_size, filters_scale, - filters_origin, tex_coord_rect, force_anti_aliasing_off, - backdrop_filter_quality, intersects_damage_under, pass_filters, - pass_backdrop_filters, pass_backdrop_filter_bounds); + filters_origin, force_anti_aliasing_off, backdrop_filter_quality, + intersects_damage_under, pass_filters, pass_backdrop_filters, + pass_backdrop_filter_bounds); } void AggregatedRenderPassDrawQuad::SetFilters( @@ -102,7 +100,6 @@ mask_texture_size = other.mask_texture_size; filters_scale = other.filters_scale; filters_origin = other.filters_origin; - tex_coord_rect = other.tex_coord_rect; force_anti_aliasing_off = other.force_anti_aliasing_off; backdrop_filter_quality = other.backdrop_filter_quality; intersects_damage_under = other.intersects_damage_under; @@ -122,7 +119,6 @@ const gfx::Size& mask_texture_size, const gfx::Vector2dF& filters_scale, const gfx::PointF& filters_origin, - const gfx::RectF& tex_coord_rect, bool force_anti_aliasing_off, float backdrop_filter_quality, bool intersects_damage_under, @@ -139,7 +135,6 @@ this->mask_texture_size = mask_texture_size; this->filters_scale = filters_scale; this->filters_origin = filters_origin; - this->tex_coord_rect = tex_coord_rect; this->force_anti_aliasing_off = force_anti_aliasing_off; this->backdrop_filter_quality = backdrop_filter_quality; this->intersects_damage_under = intersects_damage_under;
diff --git a/components/viz/common/quads/aggregated_render_pass_draw_quad.h b/components/viz/common/quads/aggregated_render_pass_draw_quad.h index 08f43e1..6697dfc 100644 --- a/components/viz/common/quads/aggregated_render_pass_draw_quad.h +++ b/components/viz/common/quads/aggregated_render_pass_draw_quad.h
@@ -37,7 +37,6 @@ ResourceId mask_resource_id, const gfx::RectF& mask_uv_rect, const gfx::Size& mask_texture_size, - const gfx::RectF& tex_coord_rect, bool force_anti_aliasing_off); void SetFilters(cc::FilterOperations filters, @@ -59,7 +58,6 @@ const gfx::Size& mask_texture_size, const gfx::Vector2dF& filters_scale, const gfx::PointF& filters_origin, - const gfx::RectF& tex_coord_rect, bool force_anti_aliasing_off, float backdrop_filter_quality, bool intersects_damage_under, @@ -67,6 +65,8 @@ cc::FilterOperations backdrop_filters, std::optional<SkPath> backdrop_filter_bounds); + gfx::RectF tex_coord_rect() const { return gfx::RectF(rect.size()); } + AggregatedRenderPassId render_pass_id; // Post-processing filters, applied to the pixels in the render pass' texture.
diff --git a/components/viz/common/quads/compositor_frame_metadata.h b/components/viz/common/quads/compositor_frame_metadata.h index 4d8390e0..44d9f06f 100644 --- a/components/viz/common/quads/compositor_frame_metadata.h +++ b/components/viz/common/quads/compositor_frame_metadata.h
@@ -89,7 +89,7 @@ gfx::SizeF scrollable_viewport_size; - // The size of the viewport for the visible region in DIP. + // The size of the viewport for the visible region in pixels. gfx::Size visible_viewport_size; gfx::ContentColorUsage content_color_usage = gfx::ContentColorUsage::kSRGB;
diff --git a/components/viz/common/quads/compositor_render_pass_draw_quad.cc b/components/viz/common/quads/compositor_render_pass_draw_quad.cc index f2d96103..6272486 100644 --- a/components/viz/common/quads/compositor_render_pass_draw_quad.cc +++ b/components/viz/common/quads/compositor_render_pass_draw_quad.cc
@@ -27,7 +27,6 @@ ResourceId mask_resource_id, const gfx::RectF& mask_uv_rect, const gfx::Size& mask_texture_size, - const gfx::RectF& tex_coord_rect, bool force_anti_aliasing_off) { DCHECK(render_pass); @@ -38,8 +37,8 @@ float backdrop_filter_quality = 1.0f; SetAll(shared_quad_state, rect, visible_rect, needs_blending, render_pass, mask_resource_id, mask_uv_rect, mask_texture_size, filters_scale, - filters_origin, tex_coord_rect, force_anti_aliasing_off, - backdrop_filter_quality, intersects_damage_under); + filters_origin, force_anti_aliasing_off, backdrop_filter_quality, + intersects_damage_under); } void CompositorRenderPassDrawQuad::SetAll( @@ -53,7 +52,6 @@ const gfx::Size& mask_texture_size, const gfx::Vector2dF& filters_scale, const gfx::PointF& filters_origin, - const gfx::RectF& tex_coord_rect, bool force_anti_aliasing_off, float backdrop_filter_quality, bool intersects_damage_under) { @@ -67,7 +65,6 @@ this->mask_texture_size = mask_texture_size; this->filters_scale = filters_scale; this->filters_origin = filters_origin; - this->tex_coord_rect = tex_coord_rect; this->force_anti_aliasing_off = force_anti_aliasing_off; this->backdrop_filter_quality = backdrop_filter_quality; this->intersects_damage_under = intersects_damage_under;
diff --git a/components/viz/common/quads/compositor_render_pass_draw_quad.h b/components/viz/common/quads/compositor_render_pass_draw_quad.h index fe1dd32..e2c48fb 100644 --- a/components/viz/common/quads/compositor_render_pass_draw_quad.h +++ b/components/viz/common/quads/compositor_render_pass_draw_quad.h
@@ -33,7 +33,6 @@ ResourceId mask_resource_id, const gfx::RectF& mask_uv_rect, const gfx::Size& mask_texture_size, - const gfx::RectF& tex_coord_rect, bool force_anti_aliasing_off); void SetAll(const SharedQuadState* shared_quad_state, @@ -46,7 +45,6 @@ const gfx::Size& mask_texture_size, const gfx::Vector2dF& filters_scale, const gfx::PointF& filters_origin, - const gfx::RectF& tex_coord_rect, bool force_anti_aliasing_off, float backdrop_filter_quality, bool intersects_damage_under);
diff --git a/components/viz/common/quads/compositor_render_pass_unittest.cc b/components/viz/common/quads/compositor_render_pass_unittest.cc index b429d127..26842ced 100644 --- a/components/viz/common/quads/compositor_render_pass_unittest.cc +++ b/components/viz/common/quads/compositor_render_pass_unittest.cc
@@ -219,8 +219,7 @@ auto pass_quad = std::make_unique<CompositorRenderPassDrawQuad>(); pass_quad->SetNew(pass->shared_quad_state_list.back(), contrib_output_rect, contrib_output_rect, contrib_id, ResourceId(1u), - gfx::RectF(), gfx::Size(), gfx::RectF(), false); - + gfx::RectF(), gfx::Size(), false); pass_list.push_back(std::move(pass)); pass_list.push_back(std::move(contrib));
diff --git a/components/viz/common/quads/draw_quad_unittest.cc b/components/viz/common/quads/draw_quad_unittest.cc index a7a0149..d83e6a8 100644 --- a/components/viz/common/quads/draw_quad_unittest.cc +++ b/components/viz/common/quads/draw_quad_unittest.cc
@@ -173,20 +173,20 @@ { QUAD_DATA quad_new->SetNew(shared_state, quad_rect, __VA_ARGS__); } \ SETUP_AND_COPY_QUAD_NEW(Type, quad_new); -#define CREATE_QUAD_ALL_RP(Type, a, b, c, d, e, f, g, h, i, j, k, copy_a) \ +#define CREATE_QUAD_ALL_RP(Type, a, b, c, d, e, f, g, i, j, k, copy_a) \ Type* quad_all = render_pass->CreateAndAppendDrawQuad<Type>(); \ { \ QUAD_DATA quad_all->SetAll(shared_state, quad_rect, a, needs_blending, b, \ - c, d, e, f, g, h, i, j, k); \ + c, d, e, f, g, i, j, k); \ } \ SETUP_AND_COPY_QUAD_ALL_RP(Type, quad_all, copy_a); -#define CREATE_QUAD_NEW_RP(Type, a, b, c, d, e, f, g, h, i, j, copy_a) \ - Type* quad_new = render_pass->CreateAndAppendDrawQuad<Type>(); \ - { \ - QUAD_DATA quad_new->SetNew(shared_state, quad_rect, a, b, c, d, e, h, i); \ - quad_new->SetFilters(f, g, j); \ - } \ +#define CREATE_QUAD_NEW_RP(Type, a, b, c, d, e, f, g, i, j, copy_a) \ + Type* quad_new = render_pass->CreateAndAppendDrawQuad<Type>(); \ + { \ + QUAD_DATA quad_new->SetNew(shared_state, quad_rect, a, b, c, d, e, i); \ + quad_new->SetFilters(f, g, j); \ + } \ SETUP_AND_COPY_QUAD_NEW_RP(Type, quad_new, copy_a); TEST(DrawQuadTest, CopyDebugBorderDrawQuad) { @@ -215,7 +215,6 @@ gfx::Size mask_texture_size(128, 134); gfx::Vector2dF filters_scale(1.0f, 1.0f); gfx::PointF filters_origin; - gfx::RectF tex_coord_rect(1, 1, 255, 254); bool force_anti_aliasing_off = false; float backdrop_filter_quality = 1.0f; bool intersects_damage_under = false; @@ -225,9 +224,9 @@ CREATE_QUAD_ALL_RP(CompositorRenderPassDrawQuad, visible_rect, render_pass_id, mask_resource_id, mask_uv_rect, mask_texture_size, - filters_scale, filters_origin, tex_coord_rect, - force_anti_aliasing_off, backdrop_filter_quality, - intersects_damage_under, copied_render_pass_id); + filters_scale, filters_origin, force_anti_aliasing_off, + backdrop_filter_quality, intersects_damage_under, + copied_render_pass_id); EXPECT_EQ(DrawQuad::Material::kCompositorRenderPass, copy_quad->material); EXPECT_EQ(visible_rect, copy_quad->visible_rect); EXPECT_EQ(copied_render_pass_id, copy_quad->render_pass_id); @@ -237,7 +236,6 @@ copy_quad->mask_texture_size.ToString()); EXPECT_EQ(filters_scale, copy_quad->filters_scale); EXPECT_EQ(filters_origin, copy_quad->filters_origin); - EXPECT_EQ(tex_coord_rect.ToString(), copy_quad->tex_coord_rect.ToString()); EXPECT_EQ(force_anti_aliasing_off, copy_quad->force_anti_aliasing_off); EXPECT_EQ(backdrop_filter_quality, copy_quad->backdrop_filter_quality); EXPECT_EQ(intersects_damage_under, copy_quad->intersects_damage_under); @@ -459,7 +457,6 @@ gfx::Size mask_texture_size(128, 134); gfx::Vector2dF filters_scale(2.f, 3.f); gfx::PointF filters_origin(0.f, 0.f); - gfx::RectF tex_coord_rect(1.f, 1.f, 33.f, 19.f); bool force_anti_aliasing_off = false; float backdrop_filter_quality = 1.0f; CompositorRenderPassId copied_render_pass_id{235}; @@ -467,16 +464,15 @@ CREATE_SHARED_STATE(); CREATE_QUAD_NEW_RP(CompositorRenderPassDrawQuad, visible_rect, render_pass_id, mask_resource_id, mask_uv_rect, mask_texture_size, - filters_scale, filters_origin, tex_coord_rect, - force_anti_aliasing_off, backdrop_filter_quality, - copied_render_pass_id); + filters_scale, filters_origin, force_anti_aliasing_off, + backdrop_filter_quality, copied_render_pass_id); EXPECT_EQ(mask_resource_id, quad_new->mask_resource_id()); ResourceId new_mask_resource_id = kInvalidResourceId; gfx::Rect quad_rect(30, 40, 50, 60); quad_new->SetNew(shared_state, quad_rect, visible_rect, render_pass_id, new_mask_resource_id, mask_uv_rect, mask_texture_size, - tex_coord_rect, force_anti_aliasing_off); + force_anti_aliasing_off); quad_new->SetFilters(filters_scale, filters_origin, backdrop_filter_quality); EXPECT_EQ(kInvalidResourceId, quad_new->mask_resource_id()); }
diff --git a/components/viz/common/quads/render_pass_draw_quad_internal.cc b/components/viz/common/quads/render_pass_draw_quad_internal.cc index 7482413..daefce6 100644 --- a/components/viz/common/quads/render_pass_draw_quad_internal.cc +++ b/components/viz/common/quads/render_pass_draw_quad_internal.cc
@@ -33,7 +33,6 @@ cc::MathUtil::AddToTracedValue("mask_texture_size", mask_texture_size, value); cc::MathUtil::AddToTracedValue("filters_scale", filters_scale, value); cc::MathUtil::AddToTracedValue("filters_origin", filters_origin, value); - cc::MathUtil::AddToTracedValue("tex_coord_rect", tex_coord_rect, value); value->SetDouble("backdrop_filter_quality", backdrop_filter_quality); value->SetBoolean("force_anti_aliasing_off", force_anti_aliasing_off); value->SetBoolean("intersects_damage_under", intersects_damage_under);
diff --git a/components/viz/common/quads/render_pass_draw_quad_internal.h b/components/viz/common/quads/render_pass_draw_quad_internal.h index a3531fc..ba3226b 100644 --- a/components/viz/common/quads/render_pass_draw_quad_internal.h +++ b/components/viz/common/quads/render_pass_draw_quad_internal.h
@@ -34,8 +34,6 @@ // crop rects, lights, etc. gfx::PointF filters_origin; - gfx::RectF tex_coord_rect; - float backdrop_filter_quality = 1.0f; bool force_anti_aliasing_off = false;
diff --git a/components/viz/common/quads/render_pass_io.cc b/components/viz/common/quads/render_pass_io.cc index 894846e2..233bf79 100644 --- a/components/viz/common/quads/render_pass_io.cc +++ b/components/viz/common/quads/render_pass_io.cc
@@ -1187,7 +1187,6 @@ dict->Set("mask_texture_size", SizeToDict(draw_quad->mask_texture_size)); dict->Set("filters_scale", Vector2dFToDict(draw_quad->filters_scale)); dict->Set("filters_origin", PointFToDict(draw_quad->filters_origin)); - dict->Set("tex_coord_rect", RectFToDict(draw_quad->tex_coord_rect)); dict->Set("backdrop_filter_quality", draw_quad->backdrop_filter_quality); dict->Set("force_anti_aliasing_off", draw_quad->force_anti_aliasing_off); dict->Set("intersects_damage_under", draw_quad->intersects_damage_under); @@ -1344,7 +1343,6 @@ const base::DictValue* mask_texture_size = dict.FindDict("mask_texture_size"); const base::DictValue* filters_scale = dict.FindDict("filters_scale"); const base::DictValue* filters_origin = dict.FindDict("filters_origin"); - const base::DictValue* tex_coord_rect = dict.FindDict("tex_coord_rect"); std::optional<double> backdrop_filter_quality = dict.FindDouble("backdrop_filter_quality"); std::optional<bool> force_anti_aliasing_off = @@ -1353,12 +1351,12 @@ dict.FindBool("intersects_damage_under"); if (!render_pass_id || !mask_uv_rect || !mask_texture_size || - !filters_scale || !filters_origin || !tex_coord_rect || - !backdrop_filter_quality || !force_anti_aliasing_off) { + !filters_scale || !filters_origin || !backdrop_filter_quality || + !force_anti_aliasing_off) { return false; } uint64_t render_pass_id_as_int; - gfx::RectF t_mask_uv_rect, t_tex_coord_rect; + gfx::RectF t_mask_uv_rect; gfx::Size t_mask_texture_size; gfx::Vector2dF t_filters_scale; gfx::PointF t_filters_origin; @@ -1366,19 +1364,18 @@ !RectFFromDict(*mask_uv_rect, &t_mask_uv_rect) || !SizeFromDict(*mask_texture_size, &t_mask_texture_size) || !Vector2dFFromDict(*filters_scale, &t_filters_scale) || - !PointFFromDict(*filters_origin, &t_filters_origin) || - !RectFFromDict(*tex_coord_rect, &t_tex_coord_rect)) { + !PointFFromDict(*filters_origin, &t_filters_origin)) { return false; } CompositorRenderPassId t_render_pass_id{render_pass_id_as_int}; ResourceId mask_resource_id = common.resource_id; - draw_quad->SetAll( - common.shared_quad_state, common.rect, common.visible_rect, - common.needs_blending, t_render_pass_id, mask_resource_id, t_mask_uv_rect, - t_mask_texture_size, t_filters_scale, t_filters_origin, t_tex_coord_rect, - force_anti_aliasing_off.value(), backdrop_filter_quality.value(), - intersects_damage_under && intersects_damage_under.value()); + draw_quad->SetAll(common.shared_quad_state, common.rect, common.visible_rect, + common.needs_blending, t_render_pass_id, mask_resource_id, + t_mask_uv_rect, t_mask_texture_size, t_filters_scale, + t_filters_origin, force_anti_aliasing_off.value(), + backdrop_filter_quality.value(), + intersects_damage_under && intersects_damage_under.value()); return true; }
diff --git a/components/viz/common/quads/render_pass_io_unittest.cc b/components/viz/common/quads/render_pass_io_unittest.cc index e61f68a..e30f15c7 100644 --- a/components/viz/common/quads/render_pass_io_unittest.cc +++ b/components/viz/common/quads/render_pass_io_unittest.cc
@@ -258,8 +258,8 @@ gfx::Rect(2, 3, 100, 50), gfx::Rect(2, 3, 100, 50), true, CompositorRenderPassId{198u}, ResourceId(81u), gfx::RectF(0.1f, 0.2f, 0.5f, 0.6f), gfx::Size(800, 600), - gfx::Vector2dF(1.1f, 0.9f), gfx::PointF(0.01f, 0.02f), - gfx::RectF(0.2f, 0.3f, 0.3f, 0.4f), true, 0.88f, true); + gfx::Vector2dF(1.1f, 0.9f), gfx::PointF(0.01f, 0.02f), true, + 0.88f, true); ++sqs_index; ++quad_count; }
diff --git a/components/viz/service/display/occlusion_culler_unittest.cc b/components/viz/service/display/occlusion_culler_unittest.cc index ec6bab50..1a64132 100644 --- a/components/viz/service/display/occlusion_culler_unittest.cc +++ b/components/viz/service/display/occlusion_culler_unittest.cc
@@ -225,7 +225,7 @@ .AllocateAndConstruct<AggregatedRenderPassDrawQuad>(); quad->SetNew(shared_quad_state, render_pass_1, render_pass_1, frame.render_pass_list.at(0)->id, ResourceId(1), gfx::RectF(), - gfx::Size(), gfx::RectF(), false); + gfx::Size(), false); } { SharedQuadState* shared_quad_state = @@ -241,7 +241,7 @@ .AllocateAndConstruct<AggregatedRenderPassDrawQuad>(); quad->SetNew(shared_quad_state, render_pass_2, render_pass_2, frame.render_pass_list.at(1)->id, ResourceId(2), gfx::RectF(), - gfx::Size(), gfx::RectF(), false); + gfx::Size(), false); } EXPECT_EQ(NumVisibleRects(root_render_pass->quad_list), 3u); @@ -313,7 +313,7 @@ .AllocateAndConstruct<AggregatedRenderPassDrawQuad>(); quad->SetNew(shared_quad_state, foreground_filter_rect_1, foreground_filter_rect_1, foreground_render_pass_1->id, - ResourceId(1), gfx::RectF(), gfx::Size(), gfx::RectF(), false); + ResourceId(1), gfx::RectF(), gfx::Size(), false); quad->SetFilters( /*filters=*/cc::FilterOperations( {cc::FilterOperation::CreateBlurFilter(5.0)}), @@ -337,7 +337,7 @@ .AllocateAndConstruct<AggregatedRenderPassDrawQuad>(); quad->SetNew(shared_quad_state, foreground_filter_rect_2, foreground_filter_rect_2, foreground_render_pass_2->id, - ResourceId(2), gfx::RectF(), gfx::Size(), gfx::RectF(), false); + ResourceId(2), gfx::RectF(), gfx::Size(), false); quad->SetFilters( /*filters=*/cc::FilterOperations( {cc::FilterOperation::CreateOpacityFilter(5.0)}), @@ -419,7 +419,7 @@ .AllocateAndConstruct<AggregatedRenderPassDrawQuad>(); quad->SetNew(shared_quad_state, backdrop_filter_rect_1, backdrop_filter_rect_1, backdrop_render_pass_1->id, - ResourceId(2), gfx::RectF(), gfx::Size(), gfx::RectF(), false); + ResourceId(2), gfx::RectF(), gfx::Size(), false); quad->SetFilters(/*filters=*/{}, backdrop_filters, SkPath::Rect(gfx::RectToSkRect(backdrop_filter_rect_1)), /*filters_scale=*/gfx::Vector2dF(1.0f, 1.0f), @@ -454,7 +454,7 @@ .AllocateAndConstruct<AggregatedRenderPassDrawQuad>(); quad->SetNew(shared_quad_state, backdrop_filter_rect_2, backdrop_filter_rect_2, backdrop_render_pass_2->id, - ResourceId(3), gfx::RectF(), gfx::Size(), gfx::RectF(), false); + ResourceId(3), gfx::RectF(), gfx::Size(), false); quad->SetFilters(/*filters=*/{}, backdrop_filters, SkPath::Rect(gfx::RectToSkRect(backdrop_filter_rect_2)), /*filters_scale=*/gfx::Vector2dF(1.0f, 1.0f), @@ -541,7 +541,7 @@ .AllocateAndConstruct<AggregatedRenderPassDrawQuad>(); quad->SetNew(shared_quad_state, backdrop_filter_rect_1, backdrop_filter_rect_1, backdrop_render_pass_1->id, - ResourceId(2), gfx::RectF(), gfx::Size(), gfx::RectF(), false); + ResourceId(2), gfx::RectF(), gfx::Size(), false); quad->SetFilters( /*filters=*/{}, /*backdrop_filters=*/ cc::FilterOperations({cc::FilterOperation::CreateBlurFilter(5.0)}), @@ -2679,8 +2679,7 @@ /*sorting_context=*/0, /*layer_id=*/0u, /*fast_rounded_corner=*/false); quad->SetNew(shared_quad_state, rect1, rect1, SkColors::kBlack, false); quad1->SetNew(shared_quad_state2, rect1, rect1, render_pass_id, - mask_resource_id, gfx::RectF(), gfx::Size(), gfx::RectF(), - false); + mask_resource_id, gfx::RectF(), gfx::Size(), false); EXPECT_EQ(1u, NumVisibleRects(frame.render_pass_list.front()->quad_list)); EXPECT_EQ(1u, frame.render_pass_list.at(1)->quad_list.size()); @@ -2931,11 +2930,9 @@ /*sorting_context=*/0, /*layer_id=*/0u, /*fast_rounded_corner=*/false); R1->SetNew(shared_quad_state, rect1, rect1, render_pass_id, - mask_resource_id, gfx::RectF(), gfx::Size(), gfx::RectF(), - false); + mask_resource_id, gfx::RectF(), gfx::Size(), false); R2->SetNew(shared_quad_state, rect2, rect2, render_pass_id, - mask_resource_id, gfx::RectF(), gfx::Size(), gfx::RectF(), - false); + mask_resource_id, gfx::RectF(), gfx::Size(), false); D1->SetNew(shared_quad_state3, rect3, rect3, SkColors::kBlack, false); D2->SetNew(shared_quad_state4, rect4, rect4, SkColors::kBlack, false); EXPECT_EQ(4u, NumVisibleRects(frame.render_pass_list.front()->quad_list)); @@ -2986,11 +2983,9 @@ /*sorting_context=*/0, /*layer_id=*/0u, /*fast_rounded_corner=*/false); R1->SetNew(shared_quad_state, rect5, rect5, render_pass_id, - mask_resource_id, gfx::RectF(), gfx::Size(), gfx::RectF(), - false); + mask_resource_id, gfx::RectF(), gfx::Size(), false); R2->SetNew(shared_quad_state, rect1, rect1, render_pass_id, - mask_resource_id, gfx::RectF(), gfx::Size(), gfx::RectF(), - false); + mask_resource_id, gfx::RectF(), gfx::Size(), false); D1->SetNew(shared_quad_state3, rect3, rect3, SkColors::kBlack, false); D2->SetNew(shared_quad_state4, rect6, rect6, SkColors::kBlack, false); EXPECT_EQ(4u, NumVisibleRects(frame.render_pass_list.front()->quad_list)); @@ -3040,11 +3035,9 @@ /*sorting_context=*/0, /*layer_id=*/0u, /*fast_rounded_corner=*/false); R1->SetNew(shared_quad_state, rect5, rect5, render_pass_id, - mask_resource_id, gfx::RectF(), gfx::Size(), gfx::RectF(), - false); + mask_resource_id, gfx::RectF(), gfx::Size(), false); R2->SetNew(shared_quad_state, rect1, rect1, render_pass_id, - mask_resource_id, gfx::RectF(), gfx::Size(), gfx::RectF(), - false); + mask_resource_id, gfx::RectF(), gfx::Size(), false); D1->SetNew(shared_quad_state3, rect3, rect3, SkColors::kBlack, false); D2->SetNew(shared_quad_state4, rect7, rect7, SkColors::kBlack, false); EXPECT_EQ(4u, NumVisibleRects(frame.render_pass_list.front()->quad_list));
diff --git a/components/viz/service/display/overlay_ca_unittest.cc b/components/viz/service/display/overlay_ca_unittest.cc index a313ec9..0f86829 100644 --- a/components/viz/service/display/overlay_ca_unittest.cc +++ b/components/viz/service/display/overlay_ca_unittest.cc
@@ -486,7 +486,7 @@ TEST_F(CALayerOverlayRPDQTest, RenderPassDrawQuadNoFilters) { quad_->SetNew(pass_->shared_quad_state_list.back(), kOverlayRect, kOverlayRect, render_pass_id_, kInvalidResourceId, gfx::RectF(), - gfx::Size(), gfx::RectF(), false); + gfx::Size(), false); ProcessForOverlays(); EXPECT_EQ(1U, test::NumOverlaysExcludingPrimaryPlane(ca_layer_list_)); @@ -495,7 +495,7 @@ TEST_F(CALayerOverlayRPDQTest, RenderPassDrawQuadAllValidFilters) { quad_->SetNew(pass_->shared_quad_state_list.back(), kOverlayRect, kOverlayRect, render_pass_id_, kInvalidResourceId, gfx::RectF(), - gfx::Size(), gfx::RectF(), false); + gfx::Size(), false); cc::FilterOperations filters; filters.Append(cc::FilterOperation::CreateGrayscaleFilter(0.1f)); filters.Append(cc::FilterOperation::CreateSepiaFilter(0.2f)); @@ -522,7 +522,7 @@ TEST_F(CALayerOverlayRPDQTest, RenderPassDrawQuadOpacityFilterScale) { quad_->SetNew(pass_->shared_quad_state_list.back(), kOverlayRect, kOverlayRect, render_pass_id_, kInvalidResourceId, gfx::RectF(), - gfx::Size(), gfx::RectF(), false); + gfx::Size(), false); quad_->SetFilters( /*filters=*/cc::FilterOperations( {cc::FilterOperation::CreateOpacityFilter(0.8f)}), @@ -537,7 +537,7 @@ TEST_F(CALayerOverlayRPDQTest, RenderPassDrawQuadBlurFilterScale) { quad_->SetNew(pass_->shared_quad_state_list.back(), kOverlayRect, kOverlayRect, render_pass_id_, kInvalidResourceId, gfx::RectF(), - gfx::Size(), gfx::RectF(), false); + gfx::Size(), false); quad_->SetFilters( /*filters=*/cc::FilterOperations( {cc::FilterOperation::CreateBlurFilter(0.8f)}), @@ -552,7 +552,7 @@ TEST_F(CALayerOverlayRPDQTest, RenderPassDrawQuadDropShadowFilterScale) { quad_->SetNew(pass_->shared_quad_state_list.back(), kOverlayRect, kOverlayRect, render_pass_id_, kInvalidResourceId, gfx::RectF(), - gfx::Size(), gfx::RectF(), false); + gfx::Size(), false); quad_->SetFilters( /*filters=*/cc::FilterOperations( {cc::FilterOperation::CreateDropShadowFilter(gfx::Point(10, 20), 1.0f, @@ -568,7 +568,7 @@ TEST_F(CALayerOverlayRPDQTest, RenderPassDrawQuadBackgroundFilter) { quad_->SetNew(pass_->shared_quad_state_list.back(), kOverlayRect, kOverlayRect, render_pass_id_, kInvalidResourceId, gfx::RectF(), - gfx::Size(), gfx::RectF(), false); + gfx::Size(), false); quad_->SetFilters( /*filters=*/{}, /*backdrop_filters=*/ @@ -583,7 +583,7 @@ TEST_F(CALayerOverlayRPDQTest, RenderPassDrawQuadMask) { quad_->SetNew(pass_->shared_quad_state_list.back(), kOverlayRect, kOverlayRect, render_pass_id_, ResourceId(2), gfx::RectF(), - gfx::Size(), gfx::RectF(), false); + gfx::Size(), false); ProcessForOverlays(); EXPECT_EQ(1U, test::NumOverlaysExcludingPrimaryPlane(ca_layer_list_)); } @@ -591,7 +591,7 @@ TEST_F(CALayerOverlayRPDQTest, RenderPassDrawQuadUnsupportedFilter) { quad_->SetNew(pass_->shared_quad_state_list.back(), kOverlayRect, kOverlayRect, render_pass_id_, kInvalidResourceId, gfx::RectF(), - gfx::Size(), gfx::RectF(), false); + gfx::Size(), false); quad_->SetFilters( /*filters=*/cc::FilterOperations( {cc::FilterOperation::CreateZoomFilter(0.9f, 1)}), @@ -607,12 +607,12 @@ int count = 35; quad_->SetNew(pass_->shared_quad_state_list.back(), kOverlayRect, kOverlayRect, render_pass_id_, ResourceId(2), gfx::RectF(), - gfx::Size(), gfx::RectF(), false); + gfx::Size(), false); for (int i = 1; i < count; ++i) { auto* quad = pass_->CreateAndAppendDrawQuad<AggregatedRenderPassDrawQuad>(); quad->SetNew(pass_->shared_quad_state_list.back(), kOverlayRect, kOverlayRect, render_pass_id_, ResourceId(2), gfx::RectF(), - gfx::Size(), gfx::RectF(), false); + gfx::Size(), false); } ProcessForOverlays();
diff --git a/components/viz/service/display/overlay_candidate_factory_unittest.cc b/components/viz/service/display/overlay_candidate_factory_unittest.cc index 3d061f1..a73791e 100644 --- a/components/viz/service/display/overlay_candidate_factory_unittest.cc +++ b/components/viz/service/display/overlay_candidate_factory_unittest.cc
@@ -256,7 +256,7 @@ auto* rpdq = render_pass->CreateAndAppendDrawQuad<AggregatedRenderPassDrawQuad>(); rpdq->SetNew(quad_state, quad_rect, quad_rect, rpid, kInvalidResourceId, - gfx::RectF(), gfx::Size(), gfx::RectF(), false); + gfx::RectF(), gfx::Size(), false); return rpdq; } @@ -668,8 +668,7 @@ quad_list.AllocateAndConstruct<AggregatedRenderPassDrawQuad>(); rpdq->SetNew(render_pass.CreateAndAppendSharedQuadState(), gfx::Rect(1, 1, 1, 1), gfx::Rect(1, 1, 1, 1), render_pass_id, - kInvalidResourceId, gfx::RectF(), gfx::Size(), gfx::RectF(), - false); + kInvalidResourceId, gfx::RectF(), gfx::Size(), false); // The actual filter operation doesn't matter in this case. rpdq->SetFilters( /*filters=*/{}, /*backdrop_filters=*/
diff --git a/components/viz/service/display/overlay_dc_unittest.cc b/components/viz/service/display/overlay_dc_unittest.cc index 1052f27..a34363d6d 100644 --- a/components/viz/service/display/overlay_dc_unittest.cc +++ b/components/viz/service/display/overlay_dc_unittest.cc
@@ -267,7 +267,7 @@ AggregatedRenderPassDrawQuad* quad = render_pass->CreateAndAppendDrawQuad<AggregatedRenderPassDrawQuad>(); quad->SetNew(shared_quad_state, rect, rect, render_pass_id, ResourceId(2), - gfx::RectF(), gfx::Size(), gfx::RectF(), false); + gfx::RectF(), gfx::Size(), false); return quad; } @@ -2711,7 +2711,6 @@ /*mask_resource_id=*/kInvalidResourceId, /*mask_uv_rect=*/gfx::RectF(), /*mask_texture_size=*/gfx::Size(), - /*tex_coord_rect=*/gfx::RectF(pass_list_->back()->output_rect), /*force_anti_aliasing_off=*/false); // Pretend that our old root pass is actually the root pass of a
diff --git a/components/viz/service/display/overlay_unittest.cc b/components/viz/service/display/overlay_unittest.cc index dade1e4..cd405a13 100644 --- a/components/viz/service/display/overlay_unittest.cc +++ b/components/viz/service/display/overlay_unittest.cc
@@ -2667,7 +2667,7 @@ pass->CreateAndAppendDrawQuad<AggregatedRenderPassDrawQuad>(); quad->SetNew(pass->shared_quad_state_list.back(), kOverlayRect, kOverlayRect, render_pass_id, kInvalidResourceId, gfx::RectF(), gfx::Size(), - gfx::RectF(), false); + false); quad->SetFilters( /*filters=*/{}, /*backdrop_filters=*/ @@ -2700,7 +2700,7 @@ pass->CreateAndAppendDrawQuad<AggregatedRenderPassDrawQuad>(); quad->SetNew(pass->shared_quad_state_list.back(), kOverlayRect, kOverlayRect, render_pass_id, kInvalidResourceId, gfx::RectF(), gfx::Size(), - gfx::RectF(), false); + false); CreateCandidateQuadAt( pass->shared_quad_state_list.back(), pass.get(), pass->output_rect, @@ -4793,7 +4793,7 @@ pass->CreateAndAppendDrawQuad<AggregatedRenderPassDrawQuad>(); quad->SetNew(pass->shared_quad_state_list.back(), kOverlayRect, kOverlayRect, render_pass_id, kInvalidResourceId, gfx::RectF(), - gfx::Size(), gfx::RectF(), false); + gfx::Size(), false); // First, we want the overlay to be scaled by 0.5 and have it rejected. float res_scale = 1.0f / (initial_scaling * (1.0f - kUVTopLeft.x())); @@ -5012,7 +5012,7 @@ root_pass->CreateAndAppendDrawQuad<AggregatedRenderPassDrawQuad>(); quad->SetNew(root_pass->shared_quad_state_list.back(), kOverlayRect, kOverlayRect, child_id, kInvalidResourceId, gfx::RectF(), - gfx::Size(), gfx::RectF(), false); + gfx::Size(), false); AggregatedRenderPassList pass_list; SurfaceDamageRectList surface_damage_rect_list; @@ -5152,7 +5152,7 @@ pass->CreateAndAppendDrawQuad<AggregatedRenderPassDrawQuad>(); quad->SetNew(pass->shared_quad_state_list.back(), kSmallCandidateRect, kSmallCandidateRect, render_pass_id, kInvalidResourceId, - gfx::RectF(), gfx::Size(), gfx::RectF(), false); + gfx::RectF(), gfx::Size(), false); pass->shared_quad_state_list.back()->clip_rect = kTestClip; // Check for potential candidates.
diff --git a/components/viz/service/display/renderer_pixeltest.cc b/components/viz/service/display/renderer_pixeltest.cc index b7d8def..606460b 100644 --- a/components/viz/service/display/renderer_pixeltest.cc +++ b/components/viz/service/display/renderer_pixeltest.cc
@@ -416,8 +416,7 @@ auto* pass_quad = root_pass->CreateAndAppendDrawQuad<AggregatedRenderPassDrawQuad>(); pass_quad->SetNew(sqs, child_pass_rect, child_pass_rect, child_pass_id, - kInvalidResourceId, gfx::RectF(), gfx::Size(), - gfx::RectF(child_pass_rect), false); + kInvalidResourceId, gfx::RectF(), gfx::Size(), false); } { auto* sqs = @@ -497,8 +496,7 @@ auto* pass_quad = root_pass->CreateAndAppendDrawQuad<AggregatedRenderPassDrawQuad>(); pass_quad->SetNew(sqs, child_pass_rect, child_pass_rect, child_pass_id, - kInvalidResourceId, gfx::RectF(), gfx::Size(), - gfx::RectF(child_pass_rect), false); + kInvalidResourceId, gfx::RectF(), gfx::Size(), false); } { auto* sqs = @@ -589,7 +587,7 @@ child_pass->CreateAndAppendDrawQuad<AggregatedRenderPassDrawQuad>(); pass_quad->SetNew(sqs, grand_child_pass_rect, grand_child_pass_rect, grand_child_pass_id, kInvalidResourceId, gfx::RectF(), - gfx::Size(), gfx::RectF(grand_child_pass_rect), false); + gfx::Size(), false); pass_list.push_back(std::move(child_pass)); } @@ -604,8 +602,7 @@ auto* pass_quad = root_pass->CreateAndAppendDrawQuad<AggregatedRenderPassDrawQuad>(); pass_quad->SetNew(sqs, child_pass_rect, child_pass_rect, child_pass_id, - kInvalidResourceId, gfx::RectF(), gfx::Size(), - gfx::RectF(child_pass_rect), false); + kInvalidResourceId, gfx::RectF(), gfx::Size(), false); } { auto* sqs = @@ -686,7 +683,7 @@ child_pass->CreateAndAppendDrawQuad<AggregatedRenderPassDrawQuad>(); pass_quad->SetNew(sqs, grand_child_pass_rect, grand_child_pass_rect, grand_child_pass_id, kInvalidResourceId, gfx::RectF(), - gfx::Size(), gfx::RectF(grand_child_pass_rect), false); + gfx::Size(), false); pass_list.push_back(std::move(child_pass)); } @@ -701,8 +698,7 @@ auto* pass_quad = root_pass->CreateAndAppendDrawQuad<AggregatedRenderPassDrawQuad>(); pass_quad->SetNew(sqs, child_pass_rect, child_pass_rect, child_pass_id, - kInvalidResourceId, gfx::RectF(), gfx::Size(), - gfx::RectF(child_pass_rect), false); + kInvalidResourceId, gfx::RectF(), gfx::Size(), false); } { auto* sqs = @@ -785,7 +781,7 @@ child_pass->CreateAndAppendDrawQuad<AggregatedRenderPassDrawQuad>(); pass_quad->SetNew(sqs, grand_child_pass_rect, grand_child_pass_rect, grand_child_pass_id, kInvalidResourceId, gfx::RectF(), - gfx::Size(), gfx::RectF(grand_child_pass_rect), false); + gfx::Size(), false); pass_list.push_back(std::move(child_pass)); } @@ -800,8 +796,7 @@ auto* pass_quad = root_pass->CreateAndAppendDrawQuad<AggregatedRenderPassDrawQuad>(); pass_quad->SetNew(sqs, child_pass_rect, child_pass_rect, child_pass_id, - kInvalidResourceId, gfx::RectF(), gfx::Size(), - gfx::RectF(child_pass_rect), false); + kInvalidResourceId, gfx::RectF(), gfx::Size(), false); } { auto* sqs = @@ -891,7 +886,7 @@ root_pass->CreateAndAppendDrawQuad<AggregatedRenderPassDrawQuad>(); pass_quad->SetNew(sqs, backdrop_pass_rect, backdrop_pass_rect, backdrop_pass_id, kInvalidResourceId, gfx::RectF(), - gfx::Size(), gfx::RectF(backdrop_pass_rect), false); + gfx::Size(), false); pass_quad->SetFilters( /*filters=*/{}, /*backdrop_filters=*/ cc::FilterOperations({cc::FilterOperation::CreateBlurFilter( @@ -1316,7 +1311,7 @@ root_pass->CreateAndAppendDrawQuad<AggregatedRenderPassDrawQuad>(); render_pass_quad->SetNew(pass_shared_state, pass_rect, pass_rect, child_pass_id, kInvalidResourceId, gfx::RectF(), - gfx::Size(), gfx::RectF(pass_rect), false); + gfx::Size(), false); render_pass_quad->SetFilters(filters, /*backdrop_filters=*/{}, /*backdrop_filter_bounds=*/std::nullopt, /*filters_scale=*/gfx::Vector2dF(1.0f, 1.0f), @@ -1378,7 +1373,7 @@ root_pass->CreateAndAppendDrawQuad<AggregatedRenderPassDrawQuad>(); render_pass_quad->SetNew(pass_shared_state, pass_rect, pass_rect, child_pass_id, kInvalidResourceId, gfx::RectF(), - gfx::Size(), gfx::RectF(pass_rect), false); + gfx::Size(), false); render_pass_quad->SetFilters( /*filters=*/cc::FilterOperations( {cc::FilterOperation::CreateSaturateFilter(0.5f)}), @@ -1446,7 +1441,7 @@ root_pass->CreateAndAppendDrawQuad<AggregatedRenderPassDrawQuad>(); render_pass_quad->SetNew(pass_shared_state, pass_rect, pass_rect, child_pass_id, kInvalidResourceId, gfx::RectF(), - gfx::Size(), gfx::RectF(pass_rect), false); + gfx::Size(), false); render_pass_quad->SetFilters(filters, /*backdrop_filters=*/{}, /*backdrop_filter_bounds=*/std::nullopt, /*filters_scale=*/gfx::Vector2dF(1.0f, 1.0f), @@ -1532,7 +1527,7 @@ root_pass->CreateAndAppendDrawQuad<AggregatedRenderPassDrawQuad>(); render_pass_quad->SetNew(pass_shared_state, pass_rect, pass_rect, child_pass_id, kInvalidResourceId, gfx::RectF(), - gfx::Size(), gfx::RectF(pass_rect), false); + gfx::Size(), false); render_pass_quad->SetFilters(filters, /*backdrop_filters=*/{}, /*backdrop_filter_bounds=*/std::nullopt, /*filters_scale=*/gfx::Vector2dF(1.0f, 1.0f), @@ -1794,7 +1789,6 @@ gfx::ScaleRect(gfx::RectF(sub_rect), 2.f / mask_rect.width(), 2.f / mask_rect.height()), // mask_uv_rect gfx::Size(mask_rect.size()), // mask_texture_size - gfx::RectF(sub_rect), // tex_coord_rect false); // force_anti_aliasing_off // White background behind the masked render pass. auto* white = root_pass->CreateAndAppendDrawQuad<SolidColorDrawQuad>(); @@ -1889,7 +1883,6 @@ gfx::ScaleRect(gfx::RectF(sub_rect), 2.f / mask_rect.width(), 2.f / mask_rect.height()), // mask_uv_rect gfx::Size(mask_rect.size()), // mask_texture_size - gfx::RectF(sub_rect), // tex_coord_rect false); // force_anti_aliasing_off // White background behind the masked render pass. auto* white = root_pass->CreateAndAppendDrawQuad<SolidColorDrawQuad>(); @@ -1971,7 +1964,6 @@ gfx::ScaleRect(gfx::RectF(viewport_rect), 1.f / mask_rect.width(), 1.f / mask_rect.height()), // mask_uv_rect gfx::Size(mask_rect.size()), // mask_texture_size - gfx::RectF(viewport_rect), // tex_coord_rect false); // force_anti_aliasing_off // White background behind the masked render pass. auto* white = root_pass->CreateAndAppendDrawQuad<SolidColorDrawQuad>(); @@ -2074,7 +2066,6 @@ gfx::ScaleRect(gfx::RectF(viewport_rect), 1.f / mask_rect.width(), 1.f / mask_rect.height()), // mask_uv_rect gfx::Size(mask_rect.size()), // mask_texture_size - gfx::RectF(viewport_rect), // tex_coord_rect false); // force_anti_aliasing_off // White background behind the masked render pass. auto* white = root_pass->CreateAndAppendDrawQuad<SolidColorDrawQuad>(); @@ -2193,7 +2184,6 @@ filter_pass_layer_rect_, filter_pass_id, mapped_mask_resource_id, mask_uv_rect, mask_texture_size, - gfx::RectF(), // tex_coord_rect false); // force_anti_aliasing_off filter_pass_quad->SetFilters( /*filters=*/{}, this->backdrop_filters_, @@ -2506,8 +2496,8 @@ pass_quad->SetAll(pass_shared_state, rect, rect, needs_blending, child_pass_id, kInvalidResourceId, gfx::RectF(), gfx::Size(), gfx::Vector2dF(1.0f, 1.0f), gfx::PointF(), - gfx::RectF(rect), force_anti_aliasing_off, - backdrop_filter_quality, intersects_damage_under, + force_anti_aliasing_off, backdrop_filter_quality, + intersects_damage_under, /*filters=*/cc::FilterOperations(), /*backdrop_filters=*/cc::FilterOperations(), /*backdrop_filter_bounds=*/std::nullopt); @@ -2678,8 +2668,7 @@ root_pass->CreateAndAppendDrawQuad<AggregatedRenderPassDrawQuad>(); child_pass_quad->SetNew(child_pass_shared_state, child_pass_rect, child_pass_rect, child_pass_id, kInvalidResourceId, - gfx::RectF(), gfx::Size(), - gfx::RectF(child_pass_rect), false); + gfx::RectF(), gfx::Size(), false); AggregatedRenderPassList pass_list; pass_list.push_back(std::move(child_pass)); @@ -4140,7 +4129,7 @@ root_pass->CreateAndAppendDrawQuad<AggregatedRenderPassDrawQuad>(); render_pass_quad->SetNew(pass_shared_state, pass_rect, pass_rect, child_pass_id, kInvalidResourceId, gfx::RectF(), - gfx::Size(), gfx::RectF(pass_rect), false); + gfx::Size(), false); // Add 60px blur to draw quad. render_pass_quad->SetFilters( /*filters=*/cc::FilterOperations(
diff --git a/components/viz/service/display/renderer_pixeltest_utils.cc b/components/viz/service/display/renderer_pixeltest_utils.cc index b8cae1ec..4aa3614 100644 --- a/components/viz/service/display/renderer_pixeltest_utils.cc +++ b/components/viz/service/display/renderer_pixeltest_utils.cc
@@ -146,15 +146,10 @@ cc::FilterOperations filters) { auto* quad = render_pass->CreateAndAppendDrawQuad<AggregatedRenderPassDrawQuad>(); - // The full `rect` is drawn (visible_rect == rect) but texture coords - // are relative to the underlying image. - gfx::RectF tex_coords{static_cast<float>(rect.width()), - static_cast<float>(rect.height())}; quad->SetNew(shared_state, rect, rect, pass_id, kInvalidResourceId, // mask_resource_id gfx::RectF(), // mask_uv_rect gfx::Size(), // mask_texture_size - tex_coords, // tex_coord_rect false); // force_anti_aliasing_off quad->SetFilters(filters, {}, // backdrop_filters std::nullopt, // backdrop_filter_bounds
diff --git a/components/viz/service/display/resolved_frame_data_unittest.cc b/components/viz/service/display/resolved_frame_data_unittest.cc index 412b871c..d188459 100644 --- a/components/viz/service/display/resolved_frame_data_unittest.cc +++ b/components/viz/service/display/resolved_frame_data_unittest.cc
@@ -87,7 +87,7 @@ auto* quad = render_pass->CreateAndAppendDrawQuad<CompositorRenderPassDrawQuad>(); quad->SetNew(sqs, kOutputRect, kOutputRect, render_pass_id, - kInvalidResourceId, gfx::RectF(), gfx::Size(), gfx::RectF(), + kInvalidResourceId, gfx::RectF(), gfx::Size(), /*force_anti_aliasing_off=*/false); }
diff --git a/components/viz/service/display/skia_renderer.cc b/components/viz/service/display/skia_renderer.cc index daaeb6d8..11d765a1 100644 --- a/components/viz/service/display/skia_renderer.cc +++ b/components/viz/service/display/skia_renderer.cc
@@ -2918,7 +2918,7 @@ // SkiaRenderer might've allocated a larger backing than our render // pass' requested size. overlay.uv_rect = - gfx::MapRect(overlay.rpdq->tex_coord_rect, + gfx::MapRect(overlay.rpdq->tex_coord_rect(), gfx::RectF(backing->size), gfx::RectF(1, 1)); if (overlay.rpdq->visible_rect != overlay.rpdq->rect) { @@ -3391,7 +3391,7 @@ SkSamplingOptions(SkFilterMode::kLinear, SkMipmapMode::kLinear); params->vis_tex_coords = cc::MathUtil::ScaleRectProportional( - quad->tex_coord_rect, gfx::RectF(quad->rect), params->visible_rect); + quad->tex_coord_rect(), gfx::RectF(quad->rect), params->visible_rect); gfx::RectF valid_texel_bounds(content_image->width(), content_image->height()); @@ -4107,7 +4107,7 @@ } params.vis_tex_coords = cc::MathUtil::ScaleRectProportional( - quad->tex_coord_rect, gfx::RectF(quad->rect), params.visible_rect); + quad->tex_coord_rect(), gfx::RectF(quad->rect), params.visible_rect); gfx::RectF valid_texel_bounds(content_image->width(), content_image->height());
diff --git a/components/viz/service/display/software_renderer.cc b/components/viz/service/display/software_renderer.cc index 2d195f0..d262c5b 100644 --- a/components/viz/service/display/software_renderer.cc +++ b/components/viz/service/display/software_renderer.cc
@@ -545,7 +545,7 @@ SkRect dest_rect = gfx::RectToSkRect(quad->rect); SkRect dest_visible_rect = gfx::RectToSkRect(quad->visible_rect); - SkRect content_rect = RectFToSkRect(quad->tex_coord_rect); + SkRect content_rect = RectFToSkRect(quad->tex_coord_rect()); if (source_bitmap && content_rect.isEmpty()) { // In case someone forgets to set it, we're treating an empty
diff --git a/components/viz/service/display/surface_aggregator.cc b/components/viz/service/display/surface_aggregator.cc index 17923d72..b5253a7 100644 --- a/components/viz/service/display/surface_aggregator.cc +++ b/components/viz/service/display/surface_aggregator.cc
@@ -1088,11 +1088,6 @@ surface_quad->visible_rect, inverse_extra_content_scale_x, inverse_extra_content_scale_y)); - // |tex_coord_rect| - A rectangle representing the bounds of the texture - // in the RenderPass's |quad_rect|. Not in content space, instead as an - // offset within |quad_rect|. - gfx::RectF tex_coord_rect = gfx::RectF(gfx::SizeF(quad_rect.size())); - // We can't produce content outside of |quad_rect|, so clip the visible // rect if necessary. quad_visible_rect.Intersect(quad_rect); @@ -1108,8 +1103,7 @@ dest_pass->CreateAndAppendDrawQuad<AggregatedRenderPassDrawQuad>(); quad->SetNew(shared_quad_state, quad_rect, quad_visible_rect, remapped_pass_id, kInvalidResourceId, gfx::RectF(), - gfx::Size(), tex_coord_rect, - /*force_anti_aliasing_off=*/false); + gfx::Size(), /*force_anti_aliasing_off=*/false); quad->SetFilters(resolved_root_pass.render_pass().filters, resolved_root_pass.render_pass().backdrop_filters, resolved_root_pass.render_pass().backdrop_filter_bounds, @@ -1311,7 +1305,6 @@ render_pass->CreateAndAppendDrawQuad<AggregatedRenderPassDrawQuad>(); quad->SetNew(shared_quad_state, current_output_rect, current_output_rect, quad_pass_id, kInvalidResourceId, gfx::RectF(), gfx::Size(), - gfx::RectF(current_output_rect), /*force_anti_aliasing_off=*/false); dest_pass_list_->push_back(std::move(render_pass)); }
diff --git a/components/viz/service/display/surface_aggregator_unittest.cc b/components/viz/service/display/surface_aggregator_unittest.cc index 5798de1..eadf13f 100644 --- a/components/viz/service/display/surface_aggregator_unittest.cc +++ b/components/viz/service/display/surface_aggregator_unittest.cc
@@ -448,7 +448,7 @@ quad->SetAll(shared_state, output_rect, output_rect, /*needs_blending=*/true, render_pass_id, kInvalidResourceId, gfx::RectF(), gfx::Size(), gfx::Vector2dF(1.0f, 1.0f), - gfx::PointF(), gfx::RectF(), + gfx::PointF(), /*force_anti_aliasing_off=*/false, /*backdrop_filter_quality=*/1.0f, intersects_damage_under); }
diff --git a/components/viz/service/frame_sinks/compositor_frame_sink_support.cc b/components/viz/service/frame_sinks/compositor_frame_sink_support.cc index 8a5a603..856f49a 100644 --- a/components/viz/service/frame_sinks/compositor_frame_sink_support.cc +++ b/components/viz/service/frame_sinks/compositor_frame_sink_support.cc
@@ -1359,6 +1359,16 @@ // If we don't have a sub target, capture everything in the frame. if (IsEntireTabCapture(sub_target)) { +#if BUILDFLAG(IS_ANDROID) + // On Android, the browser viewport includes the space used for browser + // controls so that scrolling can hide controls smoothly without the need to + // resize the viewport. However, for media capture scenarios (e.g. tab + // sharing), the desired capture area is just the web content viewport. + if (!frame.metadata.visible_viewport_size.IsEmpty()) { + out.render_pass_subrect = gfx::Rect(frame.metadata.visible_viewport_size); + return out; + } +#endif out.render_pass_subrect = gfx::Rect(out.root_render_pass_size); return out; } @@ -1612,8 +1622,7 @@ return; } - if (features::ShouldAckCOREarlyForViewTransition() && - !directive.maybe_cross_frame_sink() && + if (!directive.maybe_cross_frame_sink() && directive.delay_layer_tree_view_deletion()) { // Register the token for same-doc transitions to ensure // CopyOutputRequest can complete.
diff --git a/components/viz/service/frame_sinks/compositor_frame_sink_support_unittest.cc b/components/viz/service/frame_sinks/compositor_frame_sink_support_unittest.cc index dc3de9b..bec1b6f 100644 --- a/components/viz/service/frame_sinks/compositor_frame_sink_support_unittest.cc +++ b/components/viz/service/frame_sinks/compositor_frame_sink_support_unittest.cc
@@ -2407,6 +2407,30 @@ EXPECT_TRUE(props_with_frame->transform_to_root.IsIdentity()); } +#if BUILDFLAG(IS_ANDROID) +TEST_P(CompositorFrameSinkSupportTest, + GetRequestRegionProperties_EntireTabOverriddenByAndroidViewport) { + const SurfaceId surface_id(support_->frame_sink_id(), local_surface_id_); + constexpr gfx::Rect kViewportRect{0, 0, 10, 10}; + + auto frame = CompositorFrameBuilder() + .AddDefaultRenderPass() + .SetReferencedSurfaces({SurfaceRange(surface_id)}) + .Build(); + frame.metadata.visible_viewport_size = kViewportRect.size(); + + support_->SubmitCompositorFrame(local_surface_id_, std::move(frame)); + + // Passing in an empty sub-target triggers "Entire Tab Capture" logic. + const auto props = + support_->GetRequestRegionProperties(VideoCaptureSubTarget()); + + ASSERT_TRUE(props.has_value()); + EXPECT_EQ(kViewportRect, props->render_pass_subrect); + EXPECT_EQ(kDefaultSize, props->root_render_pass_size); +} +#endif + TEST_P(CompositorFrameSinkSupportTest, GetRequestRegionProperties_RenderPassWithSubtreeSize) { constexpr SubtreeCaptureId kSubtreeId(base::Token(0, 22u));
diff --git a/components/viz/service/input/render_input_router_support_base.cc b/components/viz/service/input/render_input_router_support_base.cc index 5e8c757..7787fd2 100644 --- a/components/viz/service/input/render_input_router_support_base.cc +++ b/components/viz/service/input/render_input_router_support_base.cc
@@ -107,7 +107,8 @@ if (!metadata) { return gfx::Size(); } - return metadata->visible_viewport_size; + return gfx::ScaleToFlooredSize(metadata->visible_viewport_size, + 1 / metadata->device_scale_factor); } void RenderInputRouterSupportBase::OnAutoscrollStart() {
diff --git a/components/viz/service/surfaces/surface.cc b/components/viz/service/surfaces/surface.cc index 46b2b30..aba9510 100644 --- a/components/viz/service/surfaces/surface.cc +++ b/components/viz/service/surfaces/surface.cc
@@ -316,24 +316,22 @@ pending_frame_data_.reset(); view_transition_dependencies_.clear(); - if (features::ShouldAckCOREarlyForViewTransition()) { - for (const auto& directive : frame.frame.metadata.transition_directives) { - const auto& token = directive.transition_token(); - // If there is no SurfaceAnimationManager for the `token` and an Animate - // directive has been issued, then previous frame is held up and has not - // performed Save directive yet for it's view transition. So add this - // token as dependency for new document's surface which needs to be - // resolved for activation. - if (directive.type() == - CompositorFrameTransitionDirective::Type::kAnimateRenderer && - !surface_manager_->FrameSinkManagerHasViewTransitionToken(token) && - directive.delay_layer_tree_view_deletion()) { - // Observe FrameSinkManager if we're not already observing. - if (!frame_sink_manager_observation_.IsObserving()) { - frame_sink_manager_observation_.Observe(surface_manager_); - } - view_transition_dependencies_.insert(token); + for (const auto& directive : frame.frame.metadata.transition_directives) { + const auto& token = directive.transition_token(); + // If there is no SurfaceAnimationManager for the `token` and an Animate + // directive has been issued, then previous frame is held up and has not + // performed Save directive yet for it's view transition. So add this + // token as dependency for new document's surface which needs to be + // resolved for activation. + if (directive.type() == + CompositorFrameTransitionDirective::Type::kAnimateRenderer && + !surface_manager_->FrameSinkManagerHasViewTransitionToken(token) && + directive.delay_layer_tree_view_deletion()) { + // Observe FrameSinkManager if we're not already observing. + if (!frame_sink_manager_observation_.IsObserving()) { + frame_sink_manager_observation_.Observe(surface_manager_); } + view_transition_dependencies_.insert(token); } } @@ -873,11 +871,9 @@ active_frame_data_->frame = std::move(frame); - if (features::ShouldAckCOREarlyForViewTransition()) { - // We need to recompute these as there can be undrawn surfaces as referenced - // surfaces for cross-doc view transitions on shared element replacement. - RecomputeActiveReferencedSurfaces(); - } + // We need to recompute these as there can be undrawn surfaces as referenced + // surfaces for cross-doc view transitions on shared element replacement. + RecomputeActiveReferencedSurfaces(); } const CompositorFrame& Surface::GetPendingFrame() {
diff --git a/components/viz/service/surfaces/surface_saved_frame.cc b/components/viz/service/surfaces/surface_saved_frame.cc index 67da76c..5cd6d95 100644 --- a/components/viz/service/surfaces/surface_saved_frame.cc +++ b/components/viz/service/surfaces/surface_saved_frame.cc
@@ -177,9 +177,7 @@ } // DispatchCopyDoneCallback early if that feature is enabled. - if ((features::ShouldAckCOREarlyForViewTransition() && - directive_.delay_layer_tree_view_deletion()) || - copy_request_count_ == 0) { + if (directive_.delay_layer_tree_view_deletion() || copy_request_count_ == 0) { DispatchCopyDoneCallback(); } @@ -187,15 +185,13 @@ // captured, as we will never receive a signal back from // NotifyCopyOfOutputComplete. // - // TODO(crbug.com/464502666): Refactor completion signals once - // ShouldAckCOREarlyForViewTransition becomes the default. + // TODO(crbug.com/464502666): Refactor completion signals. // // This will remove a benign race between DispatchCopyDoneCallback and // DispatchViewTransitionResourcesCaptured, which are sent on separate Mojo // pipes. // - // It will also resolve confusing behavior when - // kAckCopyOutputRequestEarlyForViewTransition is enabled, where + // It will also resolve confusing behavior, where // DispatchCopyDoneCallback is invoked at the start of the request rather than // at actual completion. if (copy_request_count_ == 0) { @@ -302,9 +298,7 @@ // Even if we early out, we update the count since we are no longer waiting // for this result. --copy_request_count_; - // Callback is run already when ShouldAckCOREarlyForViewTransition is enabled - if (!(features::ShouldAckCOREarlyForViewTransition() && - directive_.delay_layer_tree_view_deletion()) && + if (!directive_.delay_layer_tree_view_deletion() && copy_request_count_ == 0) { DispatchCopyDoneCallback(); }
diff --git a/components/viz/service/transitions/surface_animation_manager.cc b/components/viz/service/transitions/surface_animation_manager.cc index e42764e..9772f26e 100644 --- a/components/viz/service/transitions/surface_animation_manager.cc +++ b/components/viz/service/transitions/surface_animation_manager.cc
@@ -79,7 +79,6 @@ auto* render_pass_quad = target_render_pass ->CreateAndAppendDrawQuad<CompositorRenderPassDrawQuad>(); - gfx::RectF tex_coord_rect(gfx::Rect(shared_pass_output_rect.size())); render_pass_quad->SetNew( /*shared_quad_state=*/copied_quad_state, /*rect=*/shared_pass_output_rect, @@ -88,7 +87,6 @@ /*mask_resource_id=*/kInvalidResourceId, /*mask_uv_rect=*/gfx::RectF(), /*mask_texture_size=*/gfx::Size(), - /*tex_coord_rect=*/tex_coord_rect, /*force_anti_aliasing_off=*/false); } @@ -391,16 +389,14 @@ resolved_frame.render_pass_list.push_back(std::move(pass_copy)); } - if (features::ShouldAckCOREarlyForViewTransition()) { - // Add back the surface for old frame as reference surfaces to new - // `resolved_frame` metadata. - for (auto original_surface : original_surfaces) { - // For same document transitions, we can copy elements from same surface, - // but don't need to add itself to `referenced_surfaces`. - if (original_surface != surface->surface_id()) { - resolved_frame.metadata.referenced_surfaces.push_back( - SurfaceRange(original_surface)); - } + // Add back the surface for old frame as reference surfaces to new + // `resolved_frame` metadata. + for (auto original_surface : original_surfaces) { + // For same document transitions, we can copy elements from same surface, + // but don't need to add itself to `referenced_surfaces`. + if (original_surface != surface->surface_id()) { + resolved_frame.metadata.referenced_surfaces.push_back( + SurfaceRange(original_surface)); } }
diff --git a/components/viz/test/compositor_frame_helpers.cc b/components/viz/test/compositor_frame_helpers.cc index 44ccab4..1f0fab5a 100644 --- a/components/viz/test/compositor_frame_helpers.cc +++ b/components/viz/test/compositor_frame_helpers.cc
@@ -129,7 +129,7 @@ quad->SetAll( sqs, rect, visible_rect, params.needs_blending, id, kInvalidResourceId, gfx::RectF(), gfx::Size(), gfx::Vector2dF(1.0f, 1.0f), gfx::PointF(), - gfx::RectF(), params.force_anti_aliasing_off, + params.force_anti_aliasing_off, /*backdrop_filter_quality=*/1.0f, params.intersects_damage_under); return ThisRef();
diff --git a/components/webapps/browser/BUILD.gn b/components/webapps/browser/BUILD.gn index 124364b7eb..29c3803 100644 --- a/components/webapps/browser/BUILD.gn +++ b/components/webapps/browser/BUILD.gn
@@ -221,6 +221,7 @@ "install_result_code_unittest.cc", "installable/installable_evaluator_unittest.cc", "installable/installable_task_queue_unittest.cc", + "launch_queue/launch_queue_unittest.cc", "pwa_install_path_tracker_unittest.cc", "web_app_url_config_unittest.cc", "web_contents/web_app_url_loader_unittest.cc",
diff --git a/components/webapps/browser/launch_queue/launch_queue.cc b/components/webapps/browser/launch_queue/launch_queue.cc index b81de46e..79c8646 100644 --- a/components/webapps/browser/launch_queue/launch_queue.cc +++ b/components/webapps/browser/launch_queue/launch_queue.cc
@@ -136,7 +136,8 @@ } if (pending_navigation_) { - if (!delegate_->IsInScope(queue_.front(), handle->GetURL())) { + if (!handle->HasCommitted() || handle->IsErrorPage() || + !delegate_->IsInScope(queue_.front(), handle->GetURL())) { Reset(); return; }
diff --git a/components/webapps/browser/launch_queue/launch_queue_unittest.cc b/components/webapps/browser/launch_queue/launch_queue_unittest.cc new file mode 100644 index 0000000..a0cbb77 --- /dev/null +++ b/components/webapps/browser/launch_queue/launch_queue_unittest.cc
@@ -0,0 +1,206 @@ +// Copyright 2026 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "components/webapps/browser/launch_queue/launch_queue.h" + +#include <memory> +#include <utility> +#include <vector> + +#include "base/memory/raw_ptr.h" +#include "base/time/time.h" +#include "components/webapps/browser/launch_queue/launch_params.h" +#include "components/webapps/browser/launch_queue/launch_queue_delegate.h" +#include "content/public/browser/file_system_access_permission_context.h" +#include "content/public/browser/web_contents.h" +#include "content/public/test/navigation_simulator.h" +#include "content/public/test/test_renderer_host.h" +#include "mojo/public/cpp/bindings/associated_receiver.h" +#include "net/base/net_errors.h" +#include "testing/gmock/include/gmock/gmock.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "third_party/blink/public/common/associated_interfaces/associated_interface_provider.h" +#include "third_party/blink/public/mojom/file_system_access/file_system_access_directory_handle.mojom.h" +#include "third_party/blink/public/mojom/web_launch/web_launch.mojom.h" + +namespace webapps { + +class MockLaunchQueueDelegate : public LaunchQueueDelegate { + public: + MOCK_METHOD(bool, + IsInScope, + (const LaunchParams& launch_params, const GURL& current_url), + (const, override)); + MOCK_METHOD(content::PathInfo, + GetPathInfo, + (const base::FilePath& entry_path), + (const, override)); + MOCK_METHOD(bool, + IsValidLaunchParams, + (const LaunchParams& params), + (const, override)); +}; + +class FakeWebLaunchService : public blink::mojom::WebLaunchService { + public: + FakeWebLaunchService() = default; + ~FakeWebLaunchService() override = default; + + void Bind(mojo::ScopedInterfaceEndpointHandle handle) { + receiver_.reset(); + receiver_.Bind( + mojo::PendingAssociatedReceiver<blink::mojom::WebLaunchService>( + std::move(handle))); + } + + // blink::mojom::WebLaunchService: + void EnqueueLaunchParams( + const GURL& launch_url, + base::TimeTicks time_navigation_started_in_browser, + bool navigation_started, + std::vector<blink::mojom::FileSystemAccessEntryPtr> files) override { + launched_url_ = launch_url; + enqueue_called_ = true; + } + + bool enqueue_called() const { return enqueue_called_; } + const GURL& launched_url() const { return launched_url_; } + + void Reset() { + enqueue_called_ = false; + launched_url_ = GURL(); + } + + private: + mojo::AssociatedReceiver<blink::mojom::WebLaunchService> receiver_{this}; + bool enqueue_called_ = false; + GURL launched_url_; +}; + +class LaunchQueueTest : public content::RenderViewHostTestHarness { + public: + void SetUp() override { + content::RenderViewHostTestHarness::SetUp(); + auto delegate = + std::make_unique<testing::NiceMock<MockLaunchQueueDelegate>>(); + delegate_ = delegate.get(); + + ON_CALL(*delegate_, IsValidLaunchParams) + .WillByDefault(testing::Return(true)); + ON_CALL(*delegate_, IsInScope) + .WillByDefault( + [](const LaunchParams& params, const GURL& url) { return true; }); + + launch_queue_ = + std::make_unique<LaunchQueue>(web_contents(), std::move(delegate)); + + InitTestApi(web_contents()->GetPrimaryMainFrame()); + } + + void TearDown() override { + delegate_ = nullptr; + launch_queue_.reset(); + content::RenderViewHostTestHarness::TearDown(); + } + + void InitTestApi(content::RenderFrameHost* rfh) { + rfh->GetRemoteAssociatedInterfaces()->OverrideBinderForTesting( + blink::mojom::WebLaunchService::Name_, + base::BindRepeating(&FakeWebLaunchService::Bind, + base::Unretained(&fake_launch_service_))); + } + + protected: + LaunchParams CreateLaunchParams(const GURL& target_url, + bool started_new_navigation = true) { + LaunchParams params; + params.target_url = target_url; + params.started_new_navigation = started_new_navigation; + params.app_id = "test_app_id"; + return params; + } + + std::unique_ptr<LaunchQueue> launch_queue_; + raw_ptr<MockLaunchQueueDelegate> delegate_; + FakeWebLaunchService fake_launch_service_; +}; + +TEST_F(LaunchQueueTest, EnqueueAndCommit) { + GURL launch_url("https://example.com/launch"); + LaunchParams params = CreateLaunchParams(launch_url); + + launch_queue_->Enqueue(std::move(params)); + EXPECT_TRUE(launch_queue_->GetPendingLaunchAppId()); + + // Simulate successful navigation commit using NavigationSimulator. + content::NavigationSimulator::NavigateAndCommitFromBrowser(web_contents(), + launch_url); + + launch_queue_->FlushForTesting(); + + EXPECT_TRUE(fake_launch_service_.enqueue_called()); + EXPECT_EQ(fake_launch_service_.launched_url(), launch_url); + EXPECT_FALSE(launch_queue_->GetPendingLaunchAppId()); +} + +TEST_F(LaunchQueueTest, EnqueueAndAbort) { + GURL launch_url("https://example.com/launch"); + LaunchParams params = CreateLaunchParams(launch_url); + + launch_queue_->Enqueue(std::move(params)); + EXPECT_TRUE(launch_queue_->GetPendingLaunchAppId()); + + // Simulate aborted navigation (does not commit). + content::NavigationSimulator::NavigateAndFailFromBrowser( + web_contents(), launch_url, net::ERR_ABORTED); + + launch_queue_->FlushForTesting(); + + EXPECT_FALSE(fake_launch_service_.enqueue_called()); + EXPECT_FALSE( + launch_queue_->GetPendingLaunchAppId()); // Queue should be reset +} + +TEST_F(LaunchQueueTest, EnqueueAndError) { + GURL launch_url("https://example.com/launch"); + LaunchParams params = CreateLaunchParams(launch_url); + + launch_queue_->Enqueue(std::move(params)); + EXPECT_TRUE(launch_queue_->GetPendingLaunchAppId()); + + // Simulate navigation that commits an error page. + content::NavigationSimulator::NavigateAndFailFromBrowser( + web_contents(), launch_url, net::ERR_CONNECTION_RESET); + + launch_queue_->FlushForTesting(); + + EXPECT_FALSE(fake_launch_service_.enqueue_called()); + EXPECT_FALSE( + launch_queue_->GetPendingLaunchAppId()); // Queue should be reset +} + +TEST_F(LaunchQueueTest, EnqueueAndOutOfScope) { + GURL launch_url("https://example.com/launch"); + GURL out_of_scope_url("https://attacker.com/"); + LaunchParams params = CreateLaunchParams(launch_url); + + launch_queue_->Enqueue(std::move(params)); + EXPECT_TRUE(launch_queue_->GetPendingLaunchAppId()); + + // Delegate says it is out of scope. + EXPECT_CALL(*delegate_, IsInScope(testing::_, out_of_scope_url)) + .WillOnce(testing::Return(false)); + + // Simulate navigation to out of scope URL. + content::NavigationSimulator::NavigateAndCommitFromBrowser(web_contents(), + out_of_scope_url); + + launch_queue_->FlushForTesting(); + + EXPECT_FALSE(fake_launch_service_.enqueue_called()); + EXPECT_FALSE( + launch_queue_->GetPendingLaunchAppId()); // Queue should be reset +} + +} // namespace webapps
diff --git a/components/webauthn/ios/resources/passkey_controller.ts b/components/webauthn/ios/resources/passkey_controller.ts index 47760da..5df4b0b 100644 --- a/components/webauthn/ios/resources/passkey_controller.ts +++ b/components/webauthn/ios/resources/passkey_controller.ts
@@ -78,6 +78,11 @@ shouldHandleModalPasskeyRequests(); } +// Returns whether the PublicKeyCredential interface is defined. +function isPublicKeyCredentialDefined(): boolean { + return typeof PublicKeyCredential !== 'undefined'; +} + // Helper to generate the standard abort error. function getAbortError(): DOMException { return new DOMException('The request has been aborted.', 'AbortError'); @@ -123,31 +128,37 @@ private originalIsUVPAA: (() => Promise<boolean>)|undefined; constructor() { + // PublicKeyCredential can be undefined. + if (!isPublicKeyCredentialDefined()) { + return; + } + // Backup methods which may get overridden. - // TODO(crbug.com/483522384): PublicKeyCredential is sometimes undefined, - // ensure this workaround is sufficient. - if (typeof PublicKeyCredential !== 'undefined') { - if (PublicKeyCredential.isConditionalMediationAvailable) { - this.originalIsConditionalMediationAvailable = - PublicKeyCredential.isConditionalMediationAvailable.bind( - PublicKeyCredential); - } + if (PublicKeyCredential.isConditionalMediationAvailable) { + this.originalIsConditionalMediationAvailable = + PublicKeyCredential.isConditionalMediationAvailable.bind( + PublicKeyCredential); + } - if (PublicKeyCredential.getClientCapabilities) { - this.originalGetClientCapabilities = - PublicKeyCredential.getClientCapabilities.bind(PublicKeyCredential); - } + if (PublicKeyCredential.getClientCapabilities) { + this.originalGetClientCapabilities = + PublicKeyCredential.getClientCapabilities.bind(PublicKeyCredential); + } - if (PublicKeyCredential.isUserVerifyingPlatformAuthenticatorAvailable) { - this.originalIsUVPAA = - PublicKeyCredential.isUserVerifyingPlatformAuthenticatorAvailable - .bind(PublicKeyCredential); - } + if (PublicKeyCredential.isUserVerifyingPlatformAuthenticatorAvailable) { + this.originalIsUVPAA = + PublicKeyCredential.isUserVerifyingPlatformAuthenticatorAvailable + .bind(PublicKeyCredential); } } // Overrides PublicKeyCredential methods to expose the browser's capabilities. override(): void { + // PublicKeyCredential can be undefined. + if (!isPublicKeyCredentialDefined()) { + return; + } + // Only override PublicKeyCredential's behaviour when the browser is // handling passkey requests. if (shouldHandleConditionalPasskeyRequests() || @@ -715,13 +726,9 @@ // eslint-disable-next-line @typescript-eslint/explicit-member-accessibility public promise: Promise<PublicKeyCredential>; // Resolve function of the deferred promise. - // TODO(crbug.com/493884900): Fix members asserted as non-null. - /* eslint-disable-next-line no-restricted-syntax */ - private resolve!: ResolveFunction<PublicKeyCredential>; + private resolve: ResolveFunction<PublicKeyCredential> = () => {}; // Reject function of the deferred promise. - // TODO(crbug.com/493884900): Fix members asserted as non-null. - /* eslint-disable-next-line no-restricted-syntax */ - private reject!: RejectFunction; + private reject: RejectFunction = () => {}; // Unique ID for this deferred promise. // eslint-disable-next-line @typescript-eslint/explicit-member-accessibility public readonly id: string; @@ -1045,8 +1052,4 @@ gCrWeb.registerApi(passkey); // Override PublicKeyCredential's behaviour to expose browser capabilities. -// TODO(crbug.com/483522384): PublicKeyCredential is sometimes undefined, ensure -// this workaround is sufficient. -if (typeof PublicKeyCredential !== 'undefined') { - publicKeyCredentialOverrider.override(); -} +publicKeyCredentialOverrider.override();
diff --git a/components/webxr/android/arcore_install_helper.cc b/components/webxr/android/arcore_install_helper.cc index d87882b..b4a9ccab 100644 --- a/components/webxr/android/arcore_install_helper.cc +++ b/components/webxr/android/arcore_install_helper.cc
@@ -54,12 +54,12 @@ java_install_utils_); } - RunInstallFinishedCallback(false); + RunInstallFinishedCallback(content::XrInstallResult::kFailed); } void ArCoreInstallHelper::EnsureInstalled( const content::GlobalRenderFrameHostId& frame_id, - base::OnceCallback<void(bool)> install_callback) { + base::OnceCallback<void(content::XrInstallResult)> install_callback) { DVLOG(1) << __func__ << ": java_install_utils_.is_null()=" << java_install_utils_.is_null(); @@ -67,7 +67,7 @@ install_finished_callback_ = std::move(install_callback); if (java_install_utils_.is_null()) { - RunInstallFinishedCallback(false); + RunInstallFinishedCallback(content::XrInstallResult::kFailed); return; } @@ -78,9 +78,9 @@ return; } - // ARCore did not need to be installed/updated so mock out that its - // installation succeeded. - OnRequestInstallSupportedArCoreResult(nullptr, true); + // ARCore did not need to be installed/updated. + RunInstallFinishedCallback( + content::XrInstallResult::kSuccessAlreadyInstalled); } void ArCoreInstallHelper::ShowMessage( @@ -93,7 +93,7 @@ int button_text = -1; switch (availability) { case ArCoreAvailability::kUnsupportedDeviceNotCapable: { - RunInstallFinishedCallback(false); + RunInstallFinishedCallback(content::XrInstallResult::kFailed); return; // No need to process further } case ArCoreAvailability::kUnknownChecking: @@ -163,12 +163,15 @@ DVLOG(1) << __func__; // Nothing else to do, simply call the deferred callback. - RunInstallFinishedCallback(success); + RunInstallFinishedCallback(success + ? content::XrInstallResult::kSuccessInstalled + : content::XrInstallResult::kFailed); } -void ArCoreInstallHelper::RunInstallFinishedCallback(bool succeeded) { +void ArCoreInstallHelper::RunInstallFinishedCallback( + content::XrInstallResult result) { if (install_finished_callback_) { - std::move(install_finished_callback_).Run(succeeded); + std::move(install_finished_callback_).Run(result); } }
diff --git a/components/webxr/android/arcore_install_helper.h b/components/webxr/android/arcore_install_helper.h index b3c2982..df75942 100644 --- a/components/webxr/android/arcore_install_helper.h +++ b/components/webxr/android/arcore_install_helper.h
@@ -44,9 +44,9 @@ ArCoreInstallHelper& operator=(const ArCoreInstallHelper&) = delete; // content::XrInstallHelper implementation. - void EnsureInstalled( - const content::GlobalRenderFrameHostId& frame_id, - base::OnceCallback<void(bool)> install_callback) override; + void EnsureInstalled(const content::GlobalRenderFrameHostId& frame_id, + base::OnceCallback<void(content::XrInstallResult)> + install_callback) override; // Called from Java end. void OnRequestInstallSupportedArCoreResult(JNIEnv* env, bool success); @@ -56,9 +56,9 @@ void HandleMessagePrimaryAction( const content::GlobalRenderFrameHostId& frame_id); void HandleMessageDismissed(messages::DismissReason dismiss_reason); - void RunInstallFinishedCallback(bool succeeded); + void RunInstallFinishedCallback(content::XrInstallResult result); - base::OnceCallback<void(bool)> install_finished_callback_; + base::OnceCallback<void(content::XrInstallResult)> install_finished_callback_; base::android::ScopedJavaGlobalRef<jobject> java_install_utils_; std::unique_ptr<messages::MessageWrapper> message_;
diff --git a/content/browser/accessibility/web_contents_accessibility_android.cc b/content/browser/accessibility/web_contents_accessibility_android.cc index e45013b..aad839d 100644 --- a/content/browser/accessibility/web_contents_accessibility_android.cc +++ b/content/browser/accessibility/web_contents_accessibility_android.cc
@@ -416,11 +416,13 @@ // Move in the desired direction by the element type. int want_row_index = cur_row_index, want_col_index = cur_col_index; if (want_row) { - want_row_index += forwards ? 1 : -1; + want_row_index += + forwards ? cell_node->GetTableCellRowSpan().value_or(1) : -1; } if (want_col) { - want_col_index += forwards ? 1 : -1; + want_col_index += + forwards ? cell_node->GetTableCellColSpan().value_or(1) : -1; } if (want_col_bounds || want_table_bounds) {
diff --git a/content/browser/back_forward_cache_basics_browsertest.cc b/content/browser/back_forward_cache_basics_browsertest.cc index 8853b0f..a921d09a 100644 --- a/content/browser/back_forward_cache_basics_browsertest.cc +++ b/content/browser/back_forward_cache_basics_browsertest.cc
@@ -13,6 +13,7 @@ #include "content/common/content_navigation_policy.h" #include "content/common/features.h" #include "content/public/browser/render_frame_host.h" +#include "content/public/browser/security_principal.h" #include "content/public/browser/site_isolation_policy.h" #include "content/public/browser/web_contents.h" #include "content/public/common/url_constants.h" @@ -1541,7 +1542,8 @@ ->web_contents() ->GetPrimaryMainFrame() ->GetSiteInstance() - ->GetSiteURL()); + ->GetSecurityPrincipal() + .GetDeprecatedSiteURL()); EXPECT_EQ(net::OK, current_frame_host()->last_http_status_code()); RenderFrameDeletedObserver delete_rfh_a(current_frame_host());
diff --git a/content/browser/bad_message.h b/content/browser/bad_message.h index 3013f98..8d1e854 100644 --- a/content/browser/bad_message.h +++ b/content/browser/bad_message.h
@@ -375,6 +375,7 @@ RFHI_WEBMCP_DUPLICATE_SET_RECEIVER = 347, RFHI_WEBMCP_EXPOSED_NON_HTTPS_ORIGIN = 348, RFHI_SYNCHONOUS_COMMIT_ORIGIN_MISMATCH = 349, + RFHI_WEBMCP_INVALID_TOOL_OWNER = 350, // Please add new elements here. The naming convention is abbreviated class // name (e.g. RenderFrameHost becomes RFH) plus a unique description of the
diff --git a/content/browser/bluetooth/web_bluetooth_service_impl.cc b/content/browser/bluetooth/web_bluetooth_service_impl.cc index 9a8a4ff1..a075173 100644 --- a/content/browser/bluetooth/web_bluetooth_service_impl.cc +++ b/content/browser/bluetooth/web_bluetooth_service_impl.cc
@@ -1308,6 +1308,13 @@ return; } + if (BluetoothBlocklist::Get().IsExcludedFromReads( + query_result.characteristic->GetUUID())) { + RecordStartNotificationsOutcome(UMAGATTOperationOutcome::kBlocklisted); + std::move(callback).Run(blink::mojom::WebBluetoothResult::BLOCKLISTED_READ); + return; + } + BluetoothRemoteGattCharacteristic::Properties notify_or_indicate = query_result.characteristic->GetProperties() & (BluetoothRemoteGattCharacteristic::PROPERTY_NOTIFY |
diff --git a/content/browser/bluetooth/web_bluetooth_service_impl.h b/content/browser/bluetooth/web_bluetooth_service_impl.h index 3798fb0..c414992 100644 --- a/content/browser/bluetooth/web_bluetooth_service_impl.h +++ b/content/browser/bluetooth/web_bluetooth_service_impl.h
@@ -157,6 +157,8 @@ NoShowBluetoothScanningPromptInPrerendering); FRIEND_TEST_ALL_PREFIXES(WebBluetoothServiceImplTest, DeferredStartNotifySession); + FRIEND_TEST_ALL_PREFIXES(WebBluetoothServiceImplTest, + StartNotificationsBlocklisted); FRIEND_TEST_ALL_PREFIXES(WebBluetoothServiceImplTest, DeviceDisconnected); FRIEND_TEST_ALL_PREFIXES(WebBluetoothServiceImplTest, DeviceGattServicesDiscoveryTimeout);
diff --git a/content/browser/bluetooth/web_bluetooth_service_impl_unittest.cc b/content/browser/bluetooth/web_bluetooth_service_impl_unittest.cc index 309c147..42523de 100644 --- a/content/browser/bluetooth/web_bluetooth_service_impl_unittest.cc +++ b/content/browser/bluetooth/web_bluetooth_service_impl_unittest.cc
@@ -10,14 +10,15 @@ #include <vector> #include "base/memory/raw_ptr.h" -#include "base/test/scoped_feature_list.h" #include "base/task/single_thread_task_runner.h" #include "base/test/bind.h" #include "base/test/gmock_callback_support.h" #include "base/test/mock_callback.h" +#include "base/test/scoped_feature_list.h" #include "base/test/test_future.h" #include "content/browser/bluetooth/bluetooth_adapter_factory_wrapper.h" #include "content/browser/bluetooth/bluetooth_allowed_devices.h" +#include "content/browser/bluetooth/bluetooth_blocklist.h" #include "content/browser/bluetooth/web_bluetooth_pairing_manager.h" #include "content/public/browser/bluetooth_delegate.h" #include "content/public/common/content_client.h" @@ -1032,6 +1033,24 @@ } } +TEST_F(WebBluetoothServiceImplTest, StartNotificationsBlocklisted) { + RegisterTestCharacteristic(); + FakeBluetoothCharacteristic& test_characteristic = + battery_device_bundle().characteristic(); + + BluetoothBlocklist::Get().Add(test_characteristic.GetUUID(), + BluetoothBlocklist::Value::EXCLUDE_READS); + + base::test::TestFuture<WebBluetoothResult> future; + service_ptr_->RemoteCharacteristicStartNotifications( + test_characteristic.GetIdentifier(), + BindCharacteristicClientAndPassRemote(), future.GetCallback()); + + EXPECT_EQ(future.Get(), WebBluetoothResult::BLOCKLISTED_READ); + + BluetoothBlocklist::Get().ResetToDefaultValuesForTest(); +} + TEST_F(WebBluetoothServiceImplTest, DeviceGattServicesDiscoveryTimeout) { const auto battery_device_id = AddTestDevice(battery_device_bundle());
diff --git a/content/browser/browsing_instance.h b/content/browser/browsing_instance.h index 7193fe79..1635ece3 100644 --- a/content/browser/browsing_instance.h +++ b/content/browser/browsing_instance.h
@@ -144,8 +144,8 @@ // is ok with |url| sharing a process with other sites that do not require // a dedicated process. Note that setting this to true means that the // SiteInstanceImpl you get back may return "http://unisolated.invalid" for - // GetSiteURL() and lock_url() calls because the default instance is not - // bound to a single site. + // GetDeprecatedSiteURL() and lock_url() calls because the default instance is + // not bound to a single site. scoped_refptr<SiteInstanceImpl> GetSiteInstanceForURL( const UrlInfo& url_info, bool allow_default_instance);
diff --git a/content/browser/child_process_security_policy_unittest.cc b/content/browser/child_process_security_policy_unittest.cc index e367d3a..fc28fc9 100644 --- a/content/browser/child_process_security_policy_unittest.cc +++ b/content/browser/child_process_security_policy_unittest.cc
@@ -29,6 +29,7 @@ #include "content/common/features.h" #include "content/public/browser/browser_task_traits.h" #include "content/public/browser/browser_thread.h" +#include "content/public/browser/security_principal.h" #include "content/public/browser/site_isolation_policy.h" #include "content/public/common/bindings_policy.h" #include "content/public/common/content_client.h" @@ -3308,7 +3309,8 @@ if (ShouldUseDefaultSiteInstanceGroup()) { EXPECT_EQ(foo_instance->group(), foo_instance->DefaultSiteInstanceGroupForBrowsingInstance()); - EXPECT_EQ(foo_instance->GetSiteURL(), foo_url); + EXPECT_EQ(foo_instance->GetSecurityPrincipal().GetDeprecatedSiteURL(), + foo_url); } else { EXPECT_TRUE(foo_instance->IsDefaultSiteInstance()); EXPECT_EQ(foo_instance->GetSiteInfo(),
diff --git a/content/browser/file_system_access/file_system_access_manager_impl.cc b/content/browser/file_system_access/file_system_access_manager_impl.cc index 12dabda..d027b6ad 100644 --- a/content/browser/file_system_access/file_system_access_manager_impl.cc +++ b/content/browser/file_system_access/file_system_access_manager_impl.cc
@@ -239,10 +239,17 @@ } #endif + auto blocker = + web_contents->ForSecurityDropFullscreen(display::kInvalidDisplayId); + if (!blocker) { + std::move(callback).Run(file_system_access_error::FromStatus( + FileSystemAccessStatus::kOperationAborted), + {}); + return; + } + FileSystemChooser::ScopedObjects scoped_objects( - // Drop fullscreen mode so that the user sees the URL bar. - /*fullscreen_block=*/web_contents->ForSecurityDropFullscreen( - display::kInvalidDisplayId), + /*fullscreen_block=*/std::move(*blocker), // Maybe tuck the pip window so that it would not block the file picker // UI. /*pip_tucker=*/GetContentClient()
diff --git a/content/browser/indexed_db/indexed_db_context_impl.cc b/content/browser/indexed_db/indexed_db_context_impl.cc index 94d07a3..db7ad08 100644 --- a/content/browser/indexed_db/indexed_db_context_impl.cc +++ b/content/browser/indexed_db/indexed_db_context_impl.cc
@@ -16,6 +16,7 @@ #include <vector> #include "base/barrier_callback.h" +#include "base/barrier_closure.h" #include "base/check.h" #include "base/check_op.h" #include "base/containers/flat_map.h" @@ -853,24 +854,40 @@ // other callbacks) so that `ForceClose()` below doesn't mutate // `bucket_contexts_` while it's being iterated. weak_factory_.InvalidateWeakPtrs(); + + base::RepeatingClosure barrier; + if (shutdown_timer_) { + barrier = base::BarrierClosure( + bucket_contexts_.size(), base::BindOnce( + [](base::ElapsedTimer shutdown_timer) { + base::UmaHistogramTimes( + "IndexedDB.ContextShutdownDuration2", + shutdown_timer.Elapsed()); + }, + *shutdown_timer_)); + } + for (auto& [_, context] : bucket_contexts_) { - context.AsyncCall(&BucketContext::ForceClose).WithArgs(/*doom=*/false); + if (barrier) { + context.AsyncCall(&BucketContext::ForceClose) + .WithArgs(/*doom=*/false) + .Then(barrier); + } else { + context.AsyncCall(&BucketContext::ForceClose).WithArgs(/*doom=*/false); + } } bucket_contexts_.clear(); task_runner_limiters_.clear(); - - if (!in_memory()) { - base::UmaHistogramTimes("IndexedDB.ContextShutdownDuration", - base::TimeTicks::Now() - shutdown_start_time_); - } } void IndexedDBContextImpl::ShutdownOnIDBSequence( - base::TimeTicks start_time, + base::ElapsedTimer shutdown_timer, base::OnceClosure purge_origins) { DCHECK(idb_task_runner()->RunsTasksInCurrentSequence()); - shutdown_start_time_ = start_time; + if (!in_memory()) { + shutdown_timer_ = shutdown_timer; + } if (force_keep_session_state_ || origins_to_purge_on_shutdown_.empty() || in_memory()) { @@ -915,7 +932,7 @@ context_ptr->idb_task_runner()->PostTask( FROM_HERE, base::BindOnce(&IndexedDBContextImpl::ShutdownOnIDBSequence, - base::Unretained(context_ptr), base::TimeTicks::Now(), + base::Unretained(context_ptr), base::ElapsedTimer(), base::BindOnce(&IndexedDBContextImpl::PurgeOrigins, std::move(context)))); }
diff --git a/content/browser/indexed_db/indexed_db_context_impl.h b/content/browser/indexed_db/indexed_db_context_impl.h index 7a307b0..65336a76 100644 --- a/content/browser/indexed_db/indexed_db_context_impl.h +++ b/content/browser/indexed_db/indexed_db_context_impl.h
@@ -17,6 +17,7 @@ #include "base/gtest_prod_util.h" #include "base/memory/weak_ptr.h" #include "base/threading/sequence_bound.h" +#include "base/timer/elapsed_timer.h" #include "components/services/storage/privileged/mojom/indexed_db_client_state_checker.mojom.h" #include "components/services/storage/privileged/mojom/indexed_db_control.mojom.h" #include "components/services/storage/privileged/mojom/indexed_db_control_test.mojom.h" @@ -185,7 +186,7 @@ // Always run immediately before destruction. `purge_origins` owns `this` and // should be run only if it's necessary to delete data for some origins before // destruction of `this`. - void ShutdownOnIDBSequence(base::TimeTicks start_time, + void ShutdownOnIDBSequence(base::ElapsedTimer shutdown_timer, base::OnceClosure purge_origins); void PurgeOrigins(); @@ -355,9 +356,8 @@ // add it to a pending set and actually begin once the context is created. std::set<storage::BucketId> pending_bucket_recording_; - // When `Shutdown()` was called, or null if it's not been called. Used for - // UMA. - base::TimeTicks shutdown_start_time_; + // Timer started when `Shutdown()` was called. Used for UMA. + std::optional<base::ElapsedTimer> shutdown_timer_; // weak_factory_->GetWeakPtr() may be used on any thread, but the resulting // pointer must only be checked/used on idb_task_runner_.
diff --git a/content/browser/indexed_db/indexed_db_context_unittest.cc b/content/browser/indexed_db/indexed_db_context_unittest.cc index 64f1f6a..bf5a59a6 100644 --- a/content/browser/indexed_db/indexed_db_context_unittest.cc +++ b/content/browser/indexed_db/indexed_db_context_unittest.cc
@@ -10,6 +10,8 @@ #include "base/test/bind.h" #include "base/test/gmock_callback_support.h" #include "base/test/gmock_expected_support.h" +#include "base/test/metrics/histogram_tester.h" +#include "base/test/run_until.h" #include "base/test/test_future.h" #include "components/services/storage/public/cpp/buckets/bucket_info.h" #include "components/services/storage/public/cpp/buckets/bucket_locator.h" @@ -187,5 +189,43 @@ run_loop.Run(); } +TEST_F(IndexedDBContextTest, ShutdownDurationHistogramWithBucket) { + base::HistogramTester histogram_tester; + InitBucketContext(); + IndexedDBContextImpl::Shutdown(std::move(context_)); + ASSERT_TRUE(base::test::RunUntil([&]() { + return histogram_tester.GetAllSamples("IndexedDB.ContextShutdownDuration2") + .size() == 1u; + })); +} + +TEST_F(IndexedDBContextTest, ShutdownDurationHistogramWithoutBucket) { + base::HistogramTester histogram_tester; + + // `RunPostedTasks()` doesn't work after `Shutdown()`. + base::RunLoop loop; + auto runner = context_->idb_task_runner(); + IndexedDBContextImpl::Shutdown(std::move(context_)); + runner->PostTask(FROM_HERE, loop.QuitClosure()); + loop.Run(); + + histogram_tester.ExpectTotalCount("IndexedDB.ContextShutdownDuration2", 1); +} + +TEST_F(IndexedDBContextTest, ShutdownDurationHistogramNotRecordedForInMemory) { + base::HistogramTester histogram_tester; + SetUpInMemoryContext(); + InitBucketContext(); + + // `RunPostedTasks()` doesn't work after `Shutdown()`. + base::RunLoop loop; + auto runner = context_->idb_task_runner(); + IndexedDBContextImpl::Shutdown(std::move(context_)); + runner->PostTask(FROM_HERE, loop.QuitClosure()); + loop.Run(); + + histogram_tester.ExpectTotalCount("IndexedDB.ContextShutdownDuration2", 0); +} + } // namespace } // namespace content::indexed_db
diff --git a/content/browser/isolated_origin_browsertest.cc b/content/browser/isolated_origin_browsertest.cc index 456f8e0..6dfeee0 100644 --- a/content/browser/isolated_origin_browsertest.cc +++ b/content/browser/isolated_origin_browsertest.cc
@@ -33,6 +33,7 @@ #include "content/public/browser/browser_context.h" #include "content/public/browser/render_frame_host.h" #include "content/public/browser/render_process_host.h" +#include "content/public/browser/security_principal.h" #include "content/public/browser/site_isolation_policy.h" #include "content/public/browser/storage_partition_config.h" #include "content/public/browser/web_exposed_isolation_level.h" @@ -638,9 +639,10 @@ // Make sure the child (i.e. sub-origin) is not isolated. EXPECT_NE(root->current_frame_host()->GetSiteInstance(), child_frame_node->current_frame_host()->GetSiteInstance()); - EXPECT_EQ( - GURL("https://foo.com"), - child_frame_node->current_frame_host()->GetSiteInstance()->GetSiteURL()); + EXPECT_EQ(GURL("https://foo.com"), child_frame_node->current_frame_host() + ->GetSiteInstance() + ->GetSecurityPrincipal() + .GetDeprecatedSiteURL()); // The following test passes because IsIsolatedOrigin doesn't distinguish // between command-line isolation and opt-in isolation. EXPECT_TRUE(policy->IsIsolatedOrigin( @@ -1623,8 +1625,8 @@ .IsOriginKeyed()); EXPECT_EQ(AgentClusterKey::OACStatus::kSiteKeyedByDefault, new_prerender_site_instance_impl->GetSiteInfo().oac_status()); - EXPECT_TRUE(new_prerender_site_instance_impl->GetSiteURL() == - GURL("https://foo.com") || + EXPECT_TRUE(new_prerender_site_instance_impl->GetSecurityPrincipal() + .GetDeprecatedSiteURL() == GURL("https://foo.com") || new_prerender_site_instance_impl->IsDefaultSiteInstance()); } @@ -1753,9 +1755,10 @@ ->RequiresDedicatedProcess()); GURL expected_isolated_sub_origin = url::Origin::Create(isolated_suborigin_url).GetURL(); - EXPECT_EQ( - expected_isolated_sub_origin, - child_frame_node->current_frame_host()->GetSiteInstance()->GetSiteURL()); + EXPECT_EQ(expected_isolated_sub_origin, child_frame_node->current_frame_host() + ->GetSiteInstance() + ->GetSecurityPrincipal() + .GetDeprecatedSiteURL()); EXPECT_EQ(expected_isolated_suborigin_lock, ProcessLock::FromSiteInfo(child_frame_node->current_frame_host() ->GetSiteInstance() @@ -2212,7 +2215,7 @@ // The site URL for isolated.foo.com should be the full origin rather than // scheme and eTLD+1. EXPECT_EQ(https_server()->GetURL("isolated.foo.com", "/"), - isolated_instance->GetSiteURL()); + isolated_instance->GetSecurityPrincipal().GetDeprecatedSiteURL()); // Now use a renderer-initiated navigation to go to an unisolated origin, // www.foo.com. This should end up back in the `popup`'s process. @@ -2261,7 +2264,10 @@ https_server()->GetURL("isolated.bar.com", "/isolate_origin")); EXPECT_TRUE(NavigateToURLFromRenderer(web_contents(), second_isolated_url)); EXPECT_EQ(https_server()->GetURL("isolated.bar.com", "/"), - web_contents()->GetSiteInstance()->GetSiteURL()); + web_contents() + ->GetSiteInstance() + ->GetSecurityPrincipal() + .GetDeprecatedSiteURL()); EXPECT_NE(isolated_instance, web_contents()->GetSiteInstance()); EXPECT_NE(unisolated_instance, web_contents()->GetSiteInstance()); } @@ -2782,9 +2788,10 @@ // Both non-isolated subdomains are in the same SiteInstance. EXPECT_EQ(child_frame_node1->current_frame_host()->GetSiteInstance(), child_frame_node2->current_frame_host()->GetSiteInstance()); - EXPECT_EQ( - GURL("https://foo.com"), - child_frame_node1->current_frame_host()->GetSiteInstance()->GetSiteURL()); + EXPECT_EQ(GURL("https://foo.com"), child_frame_node1->current_frame_host() + ->GetSiteInstance() + ->GetSecurityPrincipal() + .GetDeprecatedSiteURL()); // The base-origin and the children are in different processes. EXPECT_NE( @@ -2924,9 +2931,14 @@ // Both SiteInstances should have the same site URL, because they have no // port. - EXPECT_EQ( - root->current_frame_host()->GetSiteInstance()->GetSiteURL(), - child_frame_node1->current_frame_host()->GetSiteInstance()->GetSiteURL()); + EXPECT_EQ(root->current_frame_host() + ->GetSiteInstance() + ->GetSecurityPrincipal() + .GetDeprecatedSiteURL(), + child_frame_node1->current_frame_host() + ->GetSiteInstance() + ->GetSecurityPrincipal() + .GetDeprecatedSiteURL()); EXPECT_NE(root->current_frame_host()->GetSiteInstance()->GetSiteInfo(), child_frame_node1->current_frame_host() ->GetSiteInstance() @@ -3559,8 +3571,9 @@ policy->GetProcessLock(sub_foo_process_id)); EXPECT_NE(foo_process_id, sub_foo_process_id); - EXPECT_NE(foo_site_instance->GetSiteURL(), - sub_foo_site_instance->GetSiteURL()); + EXPECT_NE( + foo_site_instance->GetSecurityPrincipal().GetDeprecatedSiteURL(), + sub_foo_site_instance->GetSecurityPrincipal().GetDeprecatedSiteURL()); // Now verify with a renderer-initiated navigation. GURL another_foo_url( @@ -3678,7 +3691,8 @@ // The site URL for isolated.foo.com should be the full origin rather than // scheme and eTLD+1. - EXPECT_EQ(GURL("http://isolated.foo.com/"), isolated_instance->GetSiteURL()); + EXPECT_EQ(GURL("http://isolated.foo.com/"), + isolated_instance->GetSecurityPrincipal().GetDeprecatedSiteURL()); // Now use a renderer-initiated navigation to go to an unisolated origin, // www.foo.com. This should end up back in the `popup`'s process. @@ -3722,8 +3736,10 @@ GURL second_isolated_url( embedded_test_server()->GetURL("isolated.bar.com", "/title3.html")); EXPECT_TRUE(NavigateToURLFromRenderer(web_contents(), second_isolated_url)); - EXPECT_EQ(GURL("http://isolated.bar.com/"), - web_contents()->GetSiteInstance()->GetSiteURL()); + EXPECT_EQ(GURL("http://isolated.bar.com/"), web_contents() + ->GetSiteInstance() + ->GetSecurityPrincipal() + .GetDeprecatedSiteURL()); EXPECT_NE(isolated_instance, web_contents()->GetSiteInstance()); EXPECT_NE(unisolated_instance, web_contents()->GetSiteInstance()); } @@ -3746,8 +3762,10 @@ popup->web_contents()->GetSiteInstance()); // The popup's site URL should match the full isolated origin. - EXPECT_EQ(GURL("http://isolated.foo.com/"), - popup->web_contents()->GetSiteInstance()->GetSiteURL()); + EXPECT_EQ(GURL("http://isolated.foo.com/"), popup->web_contents() + ->GetSiteInstance() + ->GetSecurityPrincipal() + .GetDeprecatedSiteURL()); // Now open a second popup from an isolated origin to a URL with an // unisolated origin and ensure that there was another process swap. @@ -3779,8 +3797,10 @@ EXPECT_NE(web_contents()->GetSiteInstance(), child->current_frame_host()->GetSiteInstance()); EXPECT_TRUE(child->current_frame_host()->IsCrossProcessSubframe()); - EXPECT_EQ(GURL("http://isolated.foo.com/"), - child->current_frame_host()->GetSiteInstance()->GetSiteURL()); + EXPECT_EQ(GURL("http://isolated.foo.com/"), child->current_frame_host() + ->GetSiteInstance() + ->GetSecurityPrincipal() + .GetDeprecatedSiteURL()); // Verify that the isolated frame's subframe (which starts out at a relative // path) is kept in the isolated parent's SiteInstance. @@ -3831,8 +3851,10 @@ EXPECT_NE(web_contents()->GetSiteInstance(), child->current_frame_host()->GetSiteInstance()); EXPECT_TRUE(child->current_frame_host()->IsCrossProcessSubframe()); - EXPECT_EQ(GURL("http://isolated.foo.com/"), - child->current_frame_host()->GetSiteInstance()->GetSiteURL()); + EXPECT_EQ(GURL("http://isolated.foo.com/"), child->current_frame_host() + ->GetSiteInstance() + ->GetSecurityPrincipal() + .GetDeprecatedSiteURL()); // Navigate the child frame cross-site, but to a non-isolated origin. Without // full site isolation, this should bring the subframe back into the main @@ -3893,8 +3915,10 @@ child->current_frame_host()->GetSiteInstance()->process_reuse_policy()); EXPECT_TRUE(child->current_frame_host()->IsCrossProcessSubframe()); - EXPECT_EQ(GURL("http://isolated.foo.com/"), - child->current_frame_host()->GetSiteInstance()->GetSiteURL()); + EXPECT_EQ(GURL("http://isolated.foo.com/"), child->current_frame_host() + ->GetSiteInstance() + ->GetSecurityPrincipal() + .GetDeprecatedSiteURL()); // The subframe's SiteInstance should still be different from second_shell's // SiteInstance, and they should be in separate BrowsingInstances. @@ -4527,8 +4551,10 @@ EXPECT_EQ(isolated_instance, child->current_frame_host()->GetSiteInstance()); EXPECT_FALSE(child->current_frame_host()->IsCrossProcessSubframe()); - EXPECT_EQ(GURL("http://isolated.foo.com/"), - child->current_frame_host()->GetSiteInstance()->GetSiteURL()); + EXPECT_EQ(GURL("http://isolated.foo.com/"), child->current_frame_host() + ->GetSiteInstance() + ->GetSecurityPrincipal() + .GetDeprecatedSiteURL()); // Now try navigating the main frame (renderer-initiated) to the isolated // origin's subdomain. This should not swap processes. @@ -5122,8 +5148,10 @@ child1->current_frame_host()->GetSiteInstance()); if (!SiteIsolationPolicy::IsErrorPageIsolationEnabled( /*in_main_frame=*/false)) { - EXPECT_EQ(GURL("http://isolated.foo.com/"), - child1->current_frame_host()->GetSiteInstance()->GetSiteURL()); + EXPECT_EQ(GURL("http://isolated.foo.com/"), child1->current_frame_host() + ->GetSiteInstance() + ->GetSecurityPrincipal() + .GetDeprecatedSiteURL()); } else { EXPECT_TRUE(child1->current_frame_host() ->GetSiteInstance() @@ -6461,8 +6489,10 @@ EXPECT_NE(web_contents()->GetSiteInstance(), child->current_frame_host()->GetSiteInstance()); EXPECT_TRUE(child->current_frame_host()->IsCrossProcessSubframe()); - EXPECT_EQ(GURL("http://isolated.foo.com/"), - child->current_frame_host()->GetSiteInstance()->GetSiteURL()); + EXPECT_EQ(GURL("http://isolated.foo.com/"), child->current_frame_host() + ->GetSiteInstance() + ->GetSecurityPrincipal() + .GetDeprecatedSiteURL()); // Verify that the isolated frame's subframe (which starts out at a relative // path) is kept in the isolated parent's SiteInstance.
diff --git a/content/browser/media/media_interface_proxy.cc b/content/browser/media/media_interface_proxy.cc index 900fe26..c1d2906 100644 --- a/content/browser/media/media_interface_proxy.cc +++ b/content/browser/media/media_interface_proxy.cc
@@ -24,6 +24,7 @@ #include "content/browser/renderer_host/render_frame_host_impl.h" #include "content/public/browser/media_service.h" #include "content/public/browser/render_frame_host.h" +#include "content/public/browser/security_principal.h" #include "content/public/browser/service_process_host.h" #include "content/public/browser/web_contents.h" #include "content/public/browser/web_contents_observer.h" @@ -531,7 +532,11 @@ // Passing an empty CdmType since it is not needed in this scenario. auto& mf_service = GetMediaFoundationService( media::CdmType(), render_frame_host().GetBrowserContext(), - render_frame_host().GetSiteInstance()->GetSiteURL(), cdm_path); + render_frame_host() + .GetSiteInstance() + ->GetSecurityPrincipal() + .GetDeprecatedSiteURL(), + cdm_path); // Passing an empty CdmType as MediaFoundation-based CDMs don't use CdmStorage // currently. @@ -595,7 +600,10 @@ DCHECK(!cdm_factory_map_.count(cdm_info.type)); auto* browser_context = render_frame_host().GetBrowserContext(); - auto& site = render_frame_host().GetSiteInstance()->GetSiteURL(); + auto& site = render_frame_host() + .GetSiteInstance() + ->GetSecurityPrincipal() + .GetDeprecatedSiteURL(); auto& cdm_service = GetCdmService(browser_context, site, cdm_info); mojo::Remote<media::mojom::CdmFactory> cdm_factory_remote;
diff --git a/content/browser/memory_coordinator/browser_memory_coordinator.cc b/content/browser/memory_coordinator/browser_memory_coordinator.cc index 2f9a568..09293e05 100644 --- a/content/browser/memory_coordinator/browser_memory_coordinator.cc +++ b/content/browser/memory_coordinator/browser_memory_coordinator.cc
@@ -92,14 +92,7 @@ #endif } -void BrowserMemoryCoordinator::NotifyReleaseMemoryForTesting() { - policy_manager_.NotifyReleaseMemoryForTesting(); -} -void BrowserMemoryCoordinator::NotifyUpdateMemoryLimitForTesting( - int percentage) { - policy_manager_.NotifyUpdateMemoryLimitForTesting(percentage); -} void BrowserMemoryCoordinator::OnHostDisconnected( ChildProcessId child_process_id) {
diff --git a/content/browser/memory_coordinator/browser_memory_coordinator.h b/content/browser/memory_coordinator/browser_memory_coordinator.h index 269fd77..a4951bc 100644 --- a/content/browser/memory_coordinator/browser_memory_coordinator.h +++ b/content/browser/memory_coordinator/browser_memory_coordinator.h
@@ -59,10 +59,6 @@ ChildProcessId child_process_id, mojo::PendingReceiver<mojom::ChildMemoryConsumerRegistryHost> receiver); - // For testing only. Notifies all registered consumer groups. - void NotifyReleaseMemoryForTesting(); - void NotifyUpdateMemoryLimitForTesting(int percentage); - private: void OnHostDisconnected(ChildProcessId child_process_id);
diff --git a/content/browser/memory_coordinator/memory_coordinator_browsertest.cc b/content/browser/memory_coordinator/memory_coordinator_browsertest.cc index 373bf160..7fff4f3 100644 --- a/content/browser/memory_coordinator/memory_coordinator_browsertest.cc +++ b/content/browser/memory_coordinator/memory_coordinator_browsertest.cc
@@ -7,6 +7,7 @@ #include "base/barrier_closure.h" #include "base/containers/flat_set.h" #include "base/hash/hash.h" +#include "base/memory_coordinator/mock_memory_consumer.h" #include "base/run_loop.h" #include "base/test/bind.h" #include "base/test/gmock_callback_support.h" @@ -20,6 +21,7 @@ #include "content/public/test/browser_test.h" #include "content/public/test/content_browser_test.h" #include "content/public/test/content_browser_test_utils.h" +#include "content/public/test/memory_coordinator_browsertest_util.h" #include "content/shell/browser/shell.h" #include "content/shell/common/memory_coordinator/memory_coordinator_test.mojom.h" #include "mojo/public/cpp/bindings/pending_receiver.h" @@ -195,4 +197,140 @@ } } +IN_PROC_BROWSER_TEST_F(MemoryCoordinatorBrowserTest, + ScopedMemoryLimitOverrideTest) { + MemoryCoordinatorPolicyManager& manager = + BrowserMemoryCoordinator::Get().policy_manager_for_testing(); + TestPolicy policy(manager); + MemoryCoordinatorPolicyRegistration registration(manager, policy); + + base::MemoryConsumerTraits traits( + base::MemoryConsumerTraits::EstimatedMemoryUsage::kSmall, + base::MemoryConsumerTraits::ReleaseMemoryCost::kRequiresTraversal, + base::MemoryConsumerTraits::InformationRetention::kLossless, + base::MemoryConsumerTraits::ExecutionType::kSynchronous, + base::MemoryConsumerTraits::ReleaseGCReferences::kYes); + + base::RegisteredMockMemoryConsumer consumer("Consumer", traits); + + // Policy requests 50%. + { + EXPECT_CALL(consumer, OnUpdateMemoryLimit()); + policy.UpdateConsumersWithFilter( + [](uint32_t consumer_id, + std::optional<base::MemoryConsumerTraits> traits, + ProcessType process_type, + ChildProcessId child_process_id) { return true; }, + /*percentage=*/50, /*release_memory=*/false); + EXPECT_EQ(consumer.memory_limit(), 50); + } + + // Scoped override requests 0%. + { + content::test::ScopedMemoryLimitOverride override("Consumer"); + EXPECT_EQ(consumer.memory_limit(), 50); + + EXPECT_CALL(consumer, OnUpdateMemoryLimit()); + override.SetLimit(0); + EXPECT_EQ(consumer.memory_limit(), 0); + + // Expect the limit to revert back to 50% upon scope exit (destruction). + EXPECT_CALL(consumer, OnUpdateMemoryLimit()); + } + EXPECT_EQ(consumer.memory_limit(), 50); + + // Test ClearLimit() and NotifyReleaseMemory() explicitly. + { + content::test::ScopedMemoryLimitOverride override("Consumer"); + EXPECT_CALL(consumer, OnUpdateMemoryLimit()); + override.SetLimit(10); + EXPECT_EQ(consumer.memory_limit(), 10); + + EXPECT_CALL(consumer, OnReleaseMemory()); + override.NotifyReleaseMemory(); + + EXPECT_CALL(consumer, OnUpdateMemoryLimit()); + override.ClearLimit(); + EXPECT_EQ(consumer.memory_limit(), 50); + } +} + +IN_PROC_BROWSER_TEST_F(MemoryCoordinatorBrowserTest, + ScopedMemoryLimitOverridePersistenceTest) { + MemoryCoordinatorPolicyManager& manager = + BrowserMemoryCoordinator::Get().policy_manager_for_testing(); + TestPolicy policy(manager); + MemoryCoordinatorPolicyRegistration registration(manager, policy); + + base::MemoryConsumerTraits traits( + base::MemoryConsumerTraits::EstimatedMemoryUsage::kSmall, + base::MemoryConsumerTraits::ReleaseMemoryCost::kRequiresTraversal, + base::MemoryConsumerTraits::InformationRetention::kLossless, + base::MemoryConsumerTraits::ExecutionType::kSynchronous, + base::MemoryConsumerTraits::ReleaseGCReferences::kYes); + + content::test::ScopedMemoryLimitOverride override("Consumer2"); + override.SetLimit(20); + + // Register consumer AFTER setting the override. + // Expect the limit to be applied immediately upon registration (during + // construction). + base::RegisteredMockMemoryConsumer consumer("Consumer2", traits); + EXPECT_EQ(consumer.memory_limit(), 20); +} + +IN_PROC_BROWSER_TEST_F(MemoryCoordinatorBrowserTest, + ScopedMemoryLimitOverrideIsolationTest) { + MemoryCoordinatorPolicyManager& manager = + BrowserMemoryCoordinator::Get().policy_manager_for_testing(); + TestPolicy policy(manager); + MemoryCoordinatorPolicyRegistration registration(manager, policy); + + base::MemoryConsumerTraits traits( + base::MemoryConsumerTraits::EstimatedMemoryUsage::kSmall, + base::MemoryConsumerTraits::ReleaseMemoryCost::kRequiresTraversal, + base::MemoryConsumerTraits::InformationRetention::kLossless, + base::MemoryConsumerTraits::ExecutionType::kSynchronous, + base::MemoryConsumerTraits::ReleaseGCReferences::kYes); + + base::RegisteredMockMemoryConsumer consumer1("Consumer1", traits); + base::RegisteredMockMemoryConsumer consumer2("Consumer2", traits); + + // Policy requests 50% for all. + { + EXPECT_CALL(consumer1, OnUpdateMemoryLimit()); + EXPECT_CALL(consumer2, OnUpdateMemoryLimit()); + policy.UpdateConsumersWithFilter( + [](uint32_t consumer_id, + std::optional<base::MemoryConsumerTraits> traits, + ProcessType process_type, + ChildProcessId child_process_id) { return true; }, + /*percentage=*/50, /*release_memory=*/false); + EXPECT_EQ(consumer1.memory_limit(), 50); + EXPECT_EQ(consumer2.memory_limit(), 50); + } + + // Override Consumer1 to 20%. Consumer2 should remain at 50%. + { + content::test::ScopedMemoryLimitOverride override("Consumer1"); + EXPECT_EQ(consumer1.memory_limit(), 50); + + EXPECT_CALL(consumer1, OnUpdateMemoryLimit()); + EXPECT_CALL(consumer2, OnUpdateMemoryLimit()).Times(0); + override.SetLimit(20); + EXPECT_EQ(consumer1.memory_limit(), 20); + EXPECT_EQ(consumer2.memory_limit(), 50); + + // Notify release for Consumer1. Consumer2 should not be notified. + EXPECT_CALL(consumer1, OnReleaseMemory()); + EXPECT_CALL(consumer2, OnReleaseMemory()).Times(0); + override.NotifyReleaseMemory(); + + // Revert override. + EXPECT_CALL(consumer1, OnUpdateMemoryLimit()); + } + EXPECT_EQ(consumer1.memory_limit(), 50); + EXPECT_EQ(consumer2.memory_limit(), 50); +} + } // namespace content
diff --git a/content/browser/permissions/permission_util.cc b/content/browser/permissions/permission_util.cc index c03cac1..23ae649 100644 --- a/content/browser/permissions/permission_util.cc +++ b/content/browser/permissions/permission_util.cc
@@ -142,7 +142,10 @@ const blink::mojom::PermissionDescriptorPtr& descriptor) { return descriptor->name == blink::mojom::PermissionName::VIDEO_CAPTURE || descriptor->name == blink::mojom::PermissionName::AUDIO_CAPTURE || - descriptor->name == blink::mojom::PermissionName::GEOLOCATION; + descriptor->name == blink::mojom::PermissionName::GEOLOCATION || + descriptor->name == blink::mojom::PermissionName::AR || + descriptor->name == blink::mojom::PermissionName::VR || + descriptor->name == blink::mojom::PermissionName::HAND_TRACKING; } bool PermissionUtil::IsEmbeddablePermission(
diff --git a/content/browser/renderer_host/compositor_impl_android.h b/content/browser/renderer_host/compositor_impl_android.h index 5fd62d4..ce64ca5 100644 --- a/content/browser/renderer_host/compositor_impl_android.h +++ b/content/browser/renderer_host/compositor_impl_android.h
@@ -19,7 +19,7 @@ #include "cc/paint/element_id.h" #include "cc/slim/layer_tree.h" #include "cc/slim/layer_tree_client.h" -#include "cc/trees/layer_tree_host_single_thread_client.h" +#include "cc/trees/layer_tree_host_single_thread_delegate.h" #include "cc/trees/paint_holding_commit_trigger.h" #include "cc/trees/paint_holding_reason.h" #include "components/viz/common/frame_sinks/begin_frame_source.h"
diff --git a/content/browser/renderer_host/cookie_browsertest.cc b/content/browser/renderer_host/cookie_browsertest.cc index b168a12..3098457 100644 --- a/content/browser/renderer_host/cookie_browsertest.cc +++ b/content/browser/renderer_host/cookie_browsertest.cc
@@ -23,6 +23,7 @@ #include "content/public/browser/browser_task_traits.h" #include "content/public/browser/browser_thread.h" #include "content/public/browser/render_process_host.h" +#include "content/public/browser/security_principal.h" #include "content/public/browser/site_isolation_policy.h" #include "content/public/browser/storage_partition.h" #include "content/public/common/content_client.h" @@ -229,16 +230,20 @@ WebContentsImpl* web_contents_http = static_cast<WebContentsImpl*>(shell()->web_contents()); if (AreStrictSiteInstancesEnabled()) { - EXPECT_EQ("http://a.test/", - web_contents_http->GetSiteInstance()->GetSiteURL().spec()); + EXPECT_EQ("http://a.test/", web_contents_http->GetSiteInstance() + ->GetSecurityPrincipal() + .GetDeprecatedSiteURL() + .spec()); // Create expected site url, including port if origin isolation is enabled. std::string expected_site_url = SiteIsolationPolicy::AreOriginKeyedProcessesEnabledByDefault( shell()->web_contents()->GetBrowserContext()) ? url::Origin::Create(https_url).GetURL().spec() : std::string("https://a.test/"); - EXPECT_EQ(expected_site_url, - web_contents_https->GetSiteInstance()->GetSiteURL().spec()); + EXPECT_EQ(expected_site_url, web_contents_https->GetSiteInstance() + ->GetSecurityPrincipal() + .GetDeprecatedSiteURL() + .spec()); } else { // Note: Both use the default SiteInstance because the URLs don't require // a dedicated process, but these default SiteInstances are not the same
diff --git a/content/browser/renderer_host/data_transfer_util.cc b/content/browser/renderer_host/data_transfer_util.cc index 42236d3d..e9939ea 100644 --- a/content/browser/renderer_host/data_transfer_util.cc +++ b/content/browser/renderer_host/data_transfer_util.cc
@@ -389,7 +389,11 @@ break; } case blink::mojom::DragItemDataView::Tag::kBinary: { - DCHECK(result.file_contents.empty()); + // DropData only supports a single file_contents entry. + // Skip additional binary items until multi-file support is added. + if (!result.file_contents.empty()) { + break; + } const blink::mojom::DragItemBinaryPtr& binary_item = item->get_binary(); base::span<const uint8_t> contents(binary_item->data);
diff --git a/content/browser/renderer_host/model_context_user_data.cc b/content/browser/renderer_host/model_context_user_data.cc index c9b6476..4cf6f5d 100644 --- a/content/browser/renderer_host/model_context_user_data.cc +++ b/content/browser/renderer_host/model_context_user_data.cc
@@ -5,7 +5,11 @@ #include "content/browser/renderer_host/model_context_user_data.h" #include "content/browser/bad_message.h" +#include "content/browser/renderer_host/frame_tree_node.h" +#include "content/browser/renderer_host/page_impl.h" #include "content/browser/renderer_host/render_frame_host_impl.h" +#include "content/browser/renderer_host/render_frame_host_manager.h" +#include "content/browser/site_instance_impl.h" #include "content/public/browser/browser_thread.h" #include "third_party/blink/public/common/features.h" #include "url/origin.h" @@ -48,8 +52,18 @@ // TODO(https://crbug.com/508285989): In the destructor, implement the implicit // unregistering of all `script_tools_` and invoking `NotifyToolChange()` on any -// relevant documents. -ModelContextUserData::~ModelContextUserData() = default; +// relevant documents. Right now, the destructor only supports the cancelling of +// pending tool execution in the calling renderer process, when the tool host's +// document is destroyed. +ModelContextUserData::~ModelContextUserData() { + auto& page = render_frame_host().GetPage(); + auto* page_data = ModelContextPageUserData::GetForPage(page); + if (page_data) { + auto& rfh_impl = static_cast<RenderFrameHostImpl&>(render_frame_host()); + page_data->CancelPendingScriptToolExecutionsForDocument( + rfh_impl.GetDocumentToken()); + } +} // static void ModelContextUserData::Bind( @@ -106,6 +120,19 @@ } } + // TOOD(https://crbug.com/509568047): Stop passing in a frame token and origin + // during tool registration. These values are obvious from context, and the + // renderer shouldn't need to pass them in and have the browser verify them. + // Instead, the browser should be setting them for the first time here, with + // no input from the renderer. + if (tool->tool_owner_frame_token != render_frame_host().GetFrameToken() || + tool->origin != render_frame_host().GetLastCommittedOrigin()) { + bad_message::ReceivedBadMessage( + render_frame_host().GetProcess(), + bad_message::RFHI_WEBMCP_INVALID_TOOL_OWNER); + return; + } + std::vector<url::Origin> exposed_origins = tool->exposed_origins; script_tools_.push_back(std::move(tool)); NotifyToolChange(exposed_origins); @@ -127,6 +154,19 @@ return; } + // Cancel all pending executions of the tool. For now this only include + // notifying the caller that tool execution has failed, but see the + // documentation above the + // `CancelPendingScriptToolExecutionsDueToUnregistration()` declaration about + // notifying the tool itself. + auto& page = render_frame_host().GetPage(); + auto* page_data = ModelContextPageUserData::GetForPage(page); + if (page_data) { + auto& rfh_impl = static_cast<RenderFrameHostImpl&>(render_frame_host()); + page_data->CancelPendingScriptToolExecutionsDueToUnregistration( + rfh_impl.GetDocumentToken(), name); + } + std::vector<url::Origin> exposed_origins = (*it)->exposed_origins; script_tools_.erase(it); @@ -147,6 +187,9 @@ std::vector<blink::mojom::ScriptToolPtr> all_tools; RenderFrameHost* main_frame = render_frame_host().GetMainFrame(); + SiteInstanceGroup* site_instance_group = + static_cast<SiteInstanceImpl*>(render_frame_host().GetSiteInstance()) + ->group(); main_frame->ForEachRenderFrameHostWithAction([&](RenderFrameHost* rfh) { if (rfh->GetMainFrame() != main_frame) { return RenderFrameHost::FrameIterationAction::kSkipChildren; @@ -166,7 +209,19 @@ for (const auto& t : local_tools) { if (IsScriptToolVisibleToOrigin(rfh->GetLastCommittedOrigin(), t->exposed_origins, caller_origin)) { - all_tools.push_back(t.Clone()); + blink::mojom::ScriptToolPtr cloned_tool = t.Clone(); + // Find the frame (it could be local or remote) that the caller can use + // to reference the `Window` hosting the tool. + // + // `token` will never be `nullopt`. + blink::FrameToken token = + *static_cast<RenderFrameHostImpl*>(rfh) + ->frame_tree_node() + ->render_manager() + ->GetFrameTokenForSiteInstanceGroup(site_instance_group); + + cloned_tool->tool_owner_frame_token = token; + all_tools.push_back(std::move(cloned_tool)); } } return RenderFrameHost::FrameIterationAction::kContinue; @@ -175,6 +230,123 @@ std::move(callback).Run(std::move(all_tools)); } +void ModelContextUserData::ExecuteRemoteScriptTool( + const blink::FrameToken& tool_owner_frame_token, + const url::Origin& expected_target_origin, + const std::string& name, + const std::string& input_arguments, + ExecuteRemoteScriptToolCallback callback) { + if (!IsWebMCPEnabled(render_frame_host())) { + bad_message::ReceivedBadMessage(render_frame_host().GetProcess(), + bad_message::RFHI_WEBMCP_NOT_ENABLED); + std::move(callback).Run(std::nullopt, false); + return; + } + + RenderFrameHostImpl* target_rfh = nullptr; + + if (tool_owner_frame_token.Is<blink::LocalFrameToken>()) { + target_rfh = static_cast<RenderFrameHostImpl*>( + RenderFrameHost::FromFrameToken(GlobalRenderFrameHostToken( + render_frame_host().GetProcess()->GetDeprecatedID(), + tool_owner_frame_token.GetAs<blink::LocalFrameToken>()))); + } else { + target_rfh = + static_cast<RenderFrameHostImpl*>(RenderFrameHost::FromPlaceholderToken( + render_frame_host().GetProcess()->GetDeprecatedID(), + tool_owner_frame_token.GetAs<blink::RemoteFrameToken>())); + } + + // Don't kill the renderer, since legitimate script can target a document that + // no longer exists, or simply target the *wrong* document. It can also target + // a document in another frame tree, but at the moment we do not support + // cross-frame-tree, same-browsing-context-group tool execution, hence the + // main-frame check below. + RenderFrameHost* main_frame = render_frame_host().GetMainFrame(); + if (!target_rfh || main_frame != target_rfh->GetMainFrame()) { + std::move(callback).Run(std::nullopt, false); + return; + } + + // Verify that `target_rfh`'s origin matches the origin that the tool invoker + // expects the tool to run in. This is necessary because it is possible that + // the `tool_owner_frame_token` points to a `RenderFrameProxyHost` whose frame + // tree node's `current_frame_host()` has changed since tool registration. + if (!target_rfh->GetLastCommittedOrigin().IsSameOriginWith( + expected_target_origin)) { + std::move(callback).Run(std::nullopt, false); + return; + } + + auto* target_data = ModelContextUserData::GetForCurrentDocument(target_rfh); + // Don't kill the renderer, since legitimate script can target the wrong + // document, including one that has never touched its `modelContext` accessor, + // and therefore does not have `target_data`. However, if our API ever moves + // to opaque "Tool" interface that internally encapsulates a tool host's frame + // token instead of making JavaScript pass it in via a `Window` argument, then + // we'd be able to terminate the renderer here, since it would not be possible + // for a legitimate renderer to supply a frame token targeting a frame host + // that exists, but does not have `target_data`. + if (!target_data) { + std::move(callback).Run(std::nullopt, false); + return; + } + + auto& tools = target_data->script_tools(); + auto it = std::find_if( + tools.begin(), tools.end(), + [&](const blink::mojom::ScriptToolPtr& t) { return t->name == name; }); + + // Don't kill the renderer, since legitimate script can provide the name of a + // tool that doesn't exist (including one that did exist, but got unregistered + // by the time the call to invoke it here). + if (it == tools.end()) { + std::move(callback).Run(std::nullopt, false); + return; + } + + // Don't kill the renderer here, since legitimate script can target a tool in + // another document that it might have been told about, but technically cannot + // access. + if (!IsScriptToolVisibleToOrigin( + target_rfh->GetLastCommittedOrigin(), (*it)->exposed_origins, + render_frame_host().GetLastCommittedOrigin())) { + std::move(callback).Run(std::nullopt, false); + return; + } + + // At this point, it is safe to invoke the tool in the target renderer pointed + // to by `target_data`. + // + // TODO(http://b/485810761): Right now `invocation_id` is only used to + // identify pending execution requests in the browser process. Plumb this up + // to the renderer for use by DevTools. + base::UnguessableToken invocation_id = base::UnguessableToken::Create(); + + ModelContextPageUserData* page_data = + ModelContextPageUserData::GetOrCreateForPage(target_rfh->GetPage()); + ModelContextPageUserData::PendingScriptToolExecution execution; + execution.caller_token = + static_cast<RenderFrameHostImpl&>(render_frame_host()).GetDocumentToken(); + execution.target_token = target_rfh->GetDocumentToken(); + execution.tool_name = name; + execution.callback = std::move(callback); + page_data->AddPendingScriptToolExecution(invocation_id, std::move(execution)); + + target_data->model_context_remote_->ExecuteScriptTool( + name, input_arguments, + base::BindOnce( + [](base::WeakPtr<ModelContextPageUserData> page_data, + base::UnguessableToken invocation_id, + const std::optional<std::string>& result, bool success) { + if (page_data) { + page_data->CompletePendingScriptToolExecution(invocation_id, + result, success); + } + }, + page_data->GetWeakPtr(), invocation_id)); +} + void ModelContextUserData::NotifyToolChange( const std::vector<url::Origin>& exposed_origins) { RenderFrameHost& rfh = render_frame_host(); @@ -206,4 +378,85 @@ }); } +PAGE_USER_DATA_KEY_IMPL(ModelContextPageUserData); + +ModelContextPageUserData::PendingScriptToolExecution:: + PendingScriptToolExecution() = default; +ModelContextPageUserData::PendingScriptToolExecution:: + PendingScriptToolExecution(PendingScriptToolExecution&&) = default; +ModelContextPageUserData::PendingScriptToolExecution& +ModelContextPageUserData::PendingScriptToolExecution::operator=( + PendingScriptToolExecution&&) = default; +ModelContextPageUserData::PendingScriptToolExecution:: + ~PendingScriptToolExecution() = default; + +ModelContextPageUserData::ModelContextPageUserData(Page& page) + : PageUserData<ModelContextPageUserData>(page) {} + +ModelContextPageUserData::~ModelContextPageUserData() = default; + +void ModelContextPageUserData::AddPendingScriptToolExecution( + const base::UnguessableToken& invocation_id, + PendingScriptToolExecution execution) { + pending_script_tool_executions_.emplace(invocation_id, std::move(execution)); +} + +void ModelContextPageUserData::CompletePendingScriptToolExecution( + const base::UnguessableToken& invocation_id, + const std::optional<std::string>& result, + bool success) { + // We are here because a tool just returned a result, and we should forward + // the result to the tool's invoker. But it's possible that no pending + // execution exists for this tool anymore. This can happen if the invoker + // document is destroyed before the response from the renderer hosting the + // tool comes back. + // + // Conversely, if the document *hosting* a tool gets destroyed before it + // responds with the result of the tool the associated pending execution is + // *also* removed from `this`, but the mojo receiver is terminated before this + // `CompletePendingScriptToolExecution()` callback is ever run, so we'll never + // end up here. + auto it = pending_script_tool_executions_.find(invocation_id); + if (it == pending_script_tool_executions_.end()) { + return; + } + + std::move(it->second.callback).Run(result, success); + pending_script_tool_executions_.erase(it); +} + +void ModelContextPageUserData::CancelPendingScriptToolExecutionsForDocument( + const blink::DocumentToken& document_token) { + for (auto it = pending_script_tool_executions_.begin(); + it != pending_script_tool_executions_.end();) { + if (it->second.caller_token == document_token || + it->second.target_token == document_token) { + std::move(it->second.callback).Run(std::nullopt, false); + it = pending_script_tool_executions_.erase(it); + } else { + ++it; + } + } +} + +void ModelContextPageUserData:: + CancelPendingScriptToolExecutionsDueToUnregistration( + const blink::DocumentToken& target_document_token, + const std::string& tool_name) { + for (auto it = pending_script_tool_executions_.begin(); + it != pending_script_tool_executions_.end();) { + if (it->second.target_token == target_document_token && + it->second.tool_name == tool_name) { + std::move(it->second.callback).Run(std::nullopt, false); + it = pending_script_tool_executions_.erase(it); + } else { + ++it; + } + } +} + +base::WeakPtr<ModelContextPageUserData> ModelContextPageUserData::GetWeakPtr() { + return weak_factory_.GetWeakPtr(); +} + } // namespace content
diff --git a/content/browser/renderer_host/model_context_user_data.h b/content/browser/renderer_host/model_context_user_data.h index 79c337a..b24125a 100644 --- a/content/browser/renderer_host/model_context_user_data.h +++ b/content/browser/renderer_host/model_context_user_data.h
@@ -5,14 +5,17 @@ #ifndef CONTENT_BROWSER_RENDERER_HOST_MODEL_CONTEXT_USER_DATA_H_ #define CONTENT_BROWSER_RENDERER_HOST_MODEL_CONTEXT_USER_DATA_H_ +#include <map> #include <vector> #include "base/memory/weak_ptr.h" #include "content/common/content_export.h" #include "content/public/browser/document_user_data.h" +#include "content/public/browser/page_user_data.h" #include "mojo/public/cpp/bindings/pending_receiver.h" #include "mojo/public/cpp/bindings/receiver_set.h" #include "mojo/public/cpp/bindings/remote.h" +#include "third_party/blink/public/common/tokens/tokens.h" #include "third_party/blink/public/mojom/content_extraction/script_tools.mojom.h" #include "url/origin.h" @@ -20,6 +23,93 @@ class RenderFrameHost; +// Associated with the Page, managing pending tool executions within a given +// frame tree. This must be `Page`-scoped instead of document-scoped, and +// therefore differs from `ModelContextUserData`, since tool executions are +// fundamentally cross-document, and it's easiest to have a centralized place to +// manage them. +// +// For example, if iframe A invokes a tool in iframe B, and iframe B navigates +// away during tool execution, the destructor needs to traverse the frame tree +// to locate the caller of the tool and run its pending callback, to signal to +// the caller's script that the tool execution has cancelled. The same must +// happen in the inverse, when the caller's script aborts its execution. To +// simplify the "finding" of tool caller and callee, we centralize all pending +// execution data here. +class CONTENT_EXPORT ModelContextPageUserData + : public PageUserData<ModelContextPageUserData> { + public: + struct PendingScriptToolExecution { + PendingScriptToolExecution(); + PendingScriptToolExecution(PendingScriptToolExecution&&); + PendingScriptToolExecution& operator=(PendingScriptToolExecution&&); + ~PendingScriptToolExecution(); + + blink::DocumentToken caller_token; + blink::DocumentToken target_token; + std::string tool_name; + blink::mojom::ModelContextHost::ExecuteRemoteScriptToolCallback callback; + }; + + explicit ModelContextPageUserData(Page& page); + ~ModelContextPageUserData() override; + + // When the `ExecuteRemoteScriptTool()` IPC is called on + // `blink.mojom.ModelContextHost` (i.e., `ModelContextUserData`), this method + // is called, and the callback to resolve/reject the Promise in the renderer + // is added to `pending_script_tool_executions_`. + void AddPendingScriptToolExecution( + const base::UnguessableToken& invocation_id, + PendingScriptToolExecution execution); + // When the browser process calls the `ExecuteScriptTool()` IPC on + // `blink.mojom.ModelContext` (i.e., `blink::ModelContext`), the callback + // signifying tool completion in the renderer calls this method. This method + // in turn takes the tool response, and forwards it back to the invoking + // renderer process, by running the response callback that was added in + // `AddPendingScriptToolExecution()` above. + void CompletePendingScriptToolExecution( + const base::UnguessableToken& invocation_id, + const std::optional<std::string>& result, + bool success); + // The below "cancellation" methods ensure that a failure to execute a tool to + // completion is propagated back to the calling renderer under certain + // circumstances. + // + // `CancelPendingScriptToolExecutionsForDocument()` removes all outstanding + // `PendingScriptToolExecution` structs associated with `document_token`, + // because the document is being destroyed. This means it serves two purposes: + // 1. It signals to the callers of all still-running tools in + // `document_token` that tool execution was cancelled because the tool + // owner is going away (by invoking the tool execution response + // callback); and + // 2. It simply cleans up all pending execution data for tools *called by* + // `document_token`, when tool callers gets destroyed. + // + // `CancelPendingScriptToolExecutionsDueToUnregistration()` is + // rather obviously more narrowly scoped, and is used to notify the caller of + // a still-running tool has been unregistered mid-execution. + // + // Note that there is no mechanism yet to signal from tool caller to tool + // executor that the caller has cancelled their invocation. See + // http://b/481899636 for this. + void CancelPendingScriptToolExecutionsForDocument( + const blink::DocumentToken& document_token); + void CancelPendingScriptToolExecutionsDueToUnregistration( + const blink::DocumentToken& target_document_token, + const std::string& tool_name); + + base::WeakPtr<ModelContextPageUserData> GetWeakPtr(); + + private: + friend class PageUserData<ModelContextPageUserData>; + PAGE_USER_DATA_KEY_DECL(); + + std::map<base::UnguessableToken, PendingScriptToolExecution> + pending_script_tool_executions_; + + base::WeakPtrFactory<ModelContextPageUserData> weak_factory_{this}; +}; + // `ModelContextUserData` tracks registered tools and handles communication with // `blink::ModelContext` It is created on-demand when the renderer first // accesses `navigator.modelContext`. If it does not exist for a frame, it means @@ -42,6 +132,12 @@ void RegisterScriptTool(blink::mojom::ScriptToolPtr tool) override; void UnregisterScriptTool(const std::string& name) override; void GetScriptTools(GetScriptToolsCallback callback) override; + void ExecuteRemoteScriptTool( + const blink::FrameToken& tool_owner_frame_token, + const url::Origin& expected_target_origin, + const std::string& name, + const std::string& input_arguments, + ExecuteRemoteScriptToolCallback callback) override; std::vector<blink::mojom::ScriptToolPtr>& script_tools() { return script_tools_;
diff --git a/content/browser/renderer_host/navigation_controller_impl_browsertest.cc b/content/browser/renderer_host/navigation_controller_impl_browsertest.cc index bb5ae34..df4a9f58 100644 --- a/content/browser/renderer_host/navigation_controller_impl_browsertest.cc +++ b/content/browser/renderer_host/navigation_controller_impl_browsertest.cc
@@ -57,6 +57,7 @@ #include "content/public/browser/navigation_details.h" #include "content/public/browser/render_frame_host.h" #include "content/public/browser/render_view_host.h" +#include "content/public/browser/security_principal.h" #include "content/public/browser/site_isolation_policy.h" #include "content/public/browser/web_contents.h" #include "content/public/browser/web_contents_delegate.h" @@ -2159,7 +2160,9 @@ // quickly. if (rfh->IsPendingDeletion()) { DLOG(INFO) << "Skipping pending delete RFH: " - << rfh->GetSiteInstance()->GetSiteURL(); + << rfh->GetSiteInstance() + ->GetSecurityPrincipal() + .GetDeprecatedSiteURL(); return; } @@ -14458,7 +14461,8 @@ EXPECT_EQ(GURL("http://bar.com"), root->child_at(0) ->current_frame_host() ->GetSiteInstance() - ->GetSiteURL()); + ->GetSecurityPrincipal() + .GetDeprecatedSiteURL()); } } @@ -18287,7 +18291,8 @@ EXPECT_NE( success_site_instance->GetOrCreateProcessForTesting()->GetDeprecatedID(), error_site_instance->GetProcess()->GetDeprecatedID()); - EXPECT_EQ(GURL(kUnreachableWebDataURL), error_site_instance->GetSiteURL()); + EXPECT_EQ(GURL(kUnreachableWebDataURL), + error_site_instance->GetSecurityPrincipal().GetDeprecatedSiteURL()); EXPECT_TRUE( error_site_instance->GetProcess()->GetProcessLock().is_error_page()); @@ -18465,7 +18470,8 @@ scoped_refptr<SiteInstance> error_site_instance = popup_main_frame->GetSiteInstance(); EXPECT_NE(original_site_instance, error_site_instance); - EXPECT_EQ(GURL(kUnreachableWebDataURL), error_site_instance->GetSiteURL()); + EXPECT_EQ(GURL(kUnreachableWebDataURL), + error_site_instance->GetSecurityPrincipal().GetDeprecatedSiteURL()); // The URL displayed in the URL bar is about:blank. EXPECT_EQ(GURL("about:blank"), popup_contents->GetVisibleURL()); @@ -23502,7 +23508,10 @@ shell()->web_contents()->GetPrimaryMainFrame()->GetLastCommittedOrigin(); EXPECT_EQ(origin_to_commit.value(), committed_origin); - GURL site_url = contents()->GetSiteInstance()->GetSiteURL(); + GURL site_url = contents() + ->GetSiteInstance() + ->GetSecurityPrincipal() + .GetDeprecatedSiteURL(); if (AreStrictSiteInstancesEnabled()) { EXPECT_EQ(site_url.spec(), "data:" + origin_to_commit->GetNonceForTesting()->ToString());
diff --git a/content/browser/renderer_host/navigation_controller_impl_unittest.cc b/content/browser/renderer_host/navigation_controller_impl_unittest.cc index bdbcb809..935d7468 100644 --- a/content/browser/renderer_host/navigation_controller_impl_unittest.cc +++ b/content/browser/renderer_host/navigation_controller_impl_unittest.cc
@@ -35,6 +35,7 @@ #include "content/common/frame.mojom.h" #include "content/public/browser/navigation_details.h" #include "content/public/browser/render_view_host.h" +#include "content/public/browser/security_principal.h" #include "content/public/browser/web_contents_delegate.h" #include "content/public/browser/web_contents_observer.h" #include "content/public/common/bindings_policy.h" @@ -2647,9 +2648,10 @@ EXPECT_EQ(0, our_controller.GetLastCommittedEntryIndex()); EXPECT_FALSE(our_controller.GetPendingEntry()); if (AreStrictSiteInstancesEnabled()) { - EXPECT_EQ( - url, - our_controller.GetLastCommittedEntry()->site_instance()->GetSiteURL()); + EXPECT_EQ(url, our_controller.GetLastCommittedEntry() + ->site_instance() + ->GetSecurityPrincipal() + .GetDeprecatedSiteURL()); } else { // Verify we get the default SiteInstance since |url| does not require a // dedicated process. @@ -2722,9 +2724,10 @@ EXPECT_EQ(0, our_controller.GetLastCommittedEntryIndex()); EXPECT_FALSE(our_controller.GetPendingEntry()); if (AreStrictSiteInstancesEnabled()) { - EXPECT_EQ( - url, - our_controller.GetLastCommittedEntry()->site_instance()->GetSiteURL()); + EXPECT_EQ(url, our_controller.GetLastCommittedEntry() + ->site_instance() + ->GetSecurityPrincipal() + .GetDeprecatedSiteURL()); } else { // Verify we get the default SiteInstance since |url| does not require a // dedicated process.
diff --git a/content/browser/renderer_host/navigation_request.cc b/content/browser/renderer_host/navigation_request.cc index 3f54969..04e9bb42 100644 --- a/content/browser/renderer_host/navigation_request.cc +++ b/content/browser/renderer_host/navigation_request.cc
@@ -140,6 +140,7 @@ #include "content/public/browser/reduce_accept_language_utils.h" #include "content/public/browser/render_view_host.h" #include "content/public/browser/runtime_feature_state/runtime_feature_state_document_data.h" +#include "content/public/browser/security_principal.h" #include "content/public/browser/site_isolation_policy.h" #include "content/public/browser/storage_partition.h" #include "content/public/browser/tracing_support.h" @@ -1810,7 +1811,8 @@ GetContentClient()->browser()->IsInitialWebUIURL( frame_tree_node_->current_frame_host() ->GetSiteInstance() - ->GetSiteURL()); + ->GetSecurityPrincipal() + .GetDeprecatedSiteURL()); CHECK(!current_rfh_is_initial_webui || IsInitialWebUINavigation()); if (IsInitialWebUINavigation()) { // Initial WebUI navigations must satisfy all these conditions
diff --git a/content/browser/renderer_host/navigation_request_browsertest.cc b/content/browser/renderer_host/navigation_request_browsertest.cc index 3d1e012..de38adf 100644 --- a/content/browser/renderer_host/navigation_request_browsertest.cc +++ b/content/browser/renderer_host/navigation_request_browsertest.cc
@@ -31,6 +31,7 @@ #include "content/public/browser/browser_thread.h" #include "content/public/browser/navigation_throttle.h" #include "content/public/browser/runtime_feature_state/runtime_feature_state_document_data.h" +#include "content/public/browser/security_principal.h" #include "content/public/browser/site_isolation_policy.h" #include "content/public/browser/web_contents.h" #include "content/public/browser/web_contents_observer.h" @@ -1952,11 +1953,15 @@ EXPECT_EQ(shell()->web_contents()->GetPrimaryMainFrame()->GetSiteInstance(), starting_site_instance); if (ShouldSkipEarlyCommitPendingForCrashedFrame()) { - EXPECT_EQ(GURL("http://a.com"), starting_site_instance->GetSiteURL()); + EXPECT_EQ( + GURL("http://a.com"), + starting_site_instance->GetSecurityPrincipal().GetDeprecatedSiteURL()); } else { // Because of the sad tab, this is actually the b.com SiteInstance, which // commits immediately after starting the navigation and has a process. - EXPECT_EQ(GURL("http://b.com"), starting_site_instance->GetSiteURL()); + EXPECT_EQ( + GURL("http://b.com"), + starting_site_instance->GetSecurityPrincipal().GetDeprecatedSiteURL()); } EXPECT_TRUE(starting_site_instance->HasProcess()); @@ -2365,7 +2370,8 @@ ->web_contents() ->GetPrimaryMainFrame() ->GetSiteInstance() - ->GetSiteURL()); + ->GetSecurityPrincipal() + .GetDeprecatedSiteURL()); } else { EXPECT_EQ( site_instance, @@ -2393,7 +2399,8 @@ ->web_contents() ->GetPrimaryMainFrame() ->GetSiteInstance() - ->GetSiteURL()); + ->GetSecurityPrincipal() + .GetDeprecatedSiteURL()); EXPECT_EQ(process_id, shell() ->web_contents() ->GetPrimaryMainFrame() @@ -2451,7 +2458,8 @@ ->web_contents() ->GetPrimaryMainFrame() ->GetSiteInstance() - ->GetSiteURL()); + ->GetSecurityPrincipal() + .GetDeprecatedSiteURL()); } } @@ -2523,7 +2531,8 @@ ->web_contents() ->GetPrimaryMainFrame() ->GetSiteInstance() - ->GetSiteURL()); + ->GetSecurityPrincipal() + .GetDeprecatedSiteURL()); } } } @@ -4381,9 +4390,11 @@ ASSERT_FALSE(NavigateToURL(shell(), url)); EXPECT_FALSE(observer.last_navigation_succeeded()); if (SiteIsolationPolicy::IsErrorPageIsolationEnabled(true)) { - EXPECT_EQ( - GURL(kUnreachableWebDataURL), - web_contents->GetPrimaryMainFrame()->GetSiteInstance()->GetSiteURL()); + EXPECT_EQ(GURL(kUnreachableWebDataURL), + web_contents->GetPrimaryMainFrame() + ->GetSiteInstance() + ->GetSecurityPrincipal() + .GetDeprecatedSiteURL()); } } @@ -4400,7 +4411,9 @@ } RenderFrameHostImpl* rfh = static_cast<RenderFrameHostImpl*>(web_contents->GetPrimaryMainFrame()); - EXPECT_NE(GURL(kUnreachableWebDataURL), rfh->GetSiteInstance()->GetSiteURL()); + EXPECT_NE( + GURL(kUnreachableWebDataURL), + rfh->GetSiteInstance()->GetSecurityPrincipal().GetDeprecatedSiteURL()); // Note that the error page's origin was opaque with a.com as the precursor. // This becomes the initiator origin for the about:blank navigation, and it @@ -4425,7 +4438,9 @@ // and an unassigned SiteInstance. See https://crbug.com/1426928. EXPECT_FALSE(rfh->GetProcess()->IsUnused()); if (AreAllSitesIsolatedForTesting()) { - EXPECT_EQ("http://a.com/", rfh->GetSiteInstance()->GetSiteURL()); + EXPECT_EQ( + "http://a.com/", + rfh->GetSiteInstance()->GetSecurityPrincipal().GetDeprecatedSiteURL()); EXPECT_TRUE(rfh->GetProcess()->GetProcessLock().IsLockedToSite()); EXPECT_EQ("http://a.com/", rfh->GetProcess()->GetProcessLock().site_url()); } else {
diff --git a/content/browser/renderer_host/render_frame_host_impl.cc b/content/browser/renderer_host/render_frame_host_impl.cc index f3f4fbf..88879ec 100644 --- a/content/browser/renderer_host/render_frame_host_impl.cc +++ b/content/browser/renderer_host/render_frame_host_impl.cc
@@ -16244,6 +16244,19 @@ IsInPrimaryMainFrame(), is_same_document_navigation, navigation_ukm_builder); + if (is_main_frame() && !is_same_document_navigation) { + // The previous document is already pending delete and can't change the + // related pages list. However, the new document may not know about all of + // the related pages that were created from the previous document, see + // crbug.com/457771782. As a result, we let the renderer know that there are + // other related pages. + bool has_other_related_pages = + GetSiteInstance() && + GetSiteInstance()->GetRelatedActiveContentsCount() > 1; + GetAssociatedLocalMainFrame()->NotifyRelatedPagesFinalized( + has_other_related_pages); + } + return true; }
diff --git a/content/browser/renderer_host/render_frame_host_manager.cc b/content/browser/renderer_host/render_frame_host_manager.cc index f1ac5a2..f7f5f7216 100644 --- a/content/browser/renderer_host/render_frame_host_manager.cc +++ b/content/browser/renderer_host/render_frame_host_manager.cc
@@ -76,6 +76,7 @@ #include "content/public/browser/render_process_host_observer.h" #include "content/public/browser/render_widget_host_iterator.h" #include "content/public/browser/render_widget_host_view.h" +#include "content/public/browser/security_principal.h" #include "content/public/browser/site_isolation_policy.h" #include "content/public/common/content_client.h" #include "content/public/common/content_features.h" @@ -1482,8 +1483,7 @@ // // This is well under the required shutdown time of the renderer process // which has security implications if exceeded (https://crbug.com/1177674). - if (features::ShouldAckCOREarlyForViewTransition() && - !old_render_frame_host->GetParentOrOuterDocument() && + if (!old_render_frame_host->GetParentOrOuterDocument() && view_transition_commit_info.HasViewTransitionResources() && view_transition_commit_info.delay_layer_tree_view_deletion) { view_transition_commit_info.view_transition_resources @@ -3640,7 +3640,8 @@ SiteInstanceImpl* parent_site_instance = frame_tree_node_->parent()->GetSiteInstance(); if (GetContentClient()->browser()->ShouldStayInParentProcessForNTP( - dest_url_info.url, parent_site_instance->GetSiteURL())) { + dest_url_info.url, parent_site_instance->GetSecurityPrincipal() + .GetDeprecatedSiteURL())) { // NTP is considered non-isolated. CHECK(!dest_url_info.IsIsolated()); AppendReason(reason,
diff --git a/content/browser/renderer_host/render_frame_host_manager_browsertest.cc b/content/browser/renderer_host/render_frame_host_manager_browsertest.cc index 672b606..0334508 100644 --- a/content/browser/renderer_host/render_frame_host_manager_browsertest.cc +++ b/content/browser/renderer_host/render_frame_host_manager_browsertest.cc
@@ -56,6 +56,7 @@ #include "content/public/browser/navigation_entry.h" #include "content/public/browser/reload_type.h" #include "content/public/browser/render_process_host.h" +#include "content/public/browser/security_principal.h" #include "content/public/browser/site_isolation_policy.h" #include "content/public/browser/web_contents.h" #include "content/public/browser/web_contents_observer.h" @@ -2016,7 +2017,8 @@ SiteInstance* blank_site_instance = shell()->web_contents()->GetPrimaryMainFrame()->GetSiteInstance(); EXPECT_EQ(shell()->web_contents()->GetLastCommittedURL(), GURL()); - EXPECT_EQ(blank_site_instance->GetSiteURL(), GURL()); + EXPECT_EQ(blank_site_instance->GetSecurityPrincipal().GetDeprecatedSiteURL(), + GURL()); rvh_observers.EnsureRVHGetsDestructed(blank_rvh); // Now navigate to the view-source URL and ensure we got a different @@ -4219,7 +4221,8 @@ ->GetPrimaryMainFrame(); SiteInstanceImpl* a_site_instance = rfh->GetSiteInstance(); if (AreStrictSiteInstancesEnabled()) { - EXPECT_EQ("http://a.com/", a_site_instance->GetSiteURL()); + EXPECT_EQ("http://a.com/", + a_site_instance->GetSecurityPrincipal().GetDeprecatedSiteURL()); } else { EXPECT_TRUE(a_site_instance->IsDefaultSiteInstance()); } @@ -4232,7 +4235,8 @@ SiteInstanceImpl* b_site_instance = static_cast<SiteInstanceImpl*>( rfh->child_at(0)->current_frame_host()->GetSiteInstance()); if (AreStrictSiteInstancesEnabled()) { - EXPECT_EQ("http://b.com/", b_site_instance->GetSiteURL()); + EXPECT_EQ("http://b.com/", + b_site_instance->GetSecurityPrincipal().GetDeprecatedSiteURL()); } else { EXPECT_TRUE(b_site_instance->IsDefaultSiteInstance()); } @@ -4309,8 +4313,12 @@ EXPECT_NE(site_instance, shell()->web_contents()->GetSiteInstance()); EXPECT_FALSE(site_instance->IsRelatedSiteInstance( shell()->web_contents()->GetSiteInstance())); - EXPECT_EQ(site_instance->GetSiteURL(), - shell()->web_contents()->GetSiteInstance()->GetSiteURL()); + EXPECT_EQ(site_instance->GetSecurityPrincipal().GetDeprecatedSiteURL(), + shell() + ->web_contents() + ->GetSiteInstance() + ->GetSecurityPrincipal() + .GetDeprecatedSiteURL()); } } @@ -4714,8 +4722,10 @@ ->GetPrimaryFrameTree() .root(); FrameTreeNode* child = root->child_at(0); - GURL child_site_url = - child->current_frame_host()->GetSiteInstance()->GetSiteURL(); + GURL child_site_url = child->current_frame_host() + ->GetSiteInstance() + ->GetSecurityPrincipal() + .GetDeprecatedSiteURL(); // Navigate the subframe to a URL that is cross-site from the main frame. { @@ -4728,8 +4738,10 @@ EXPECT_EQ(test_url, child->current_frame_host()->GetLastCommittedURL()); EXPECT_EQ(url::Origin::Create(test_url), child->current_frame_host()->GetLastCommittedOrigin()); - EXPECT_EQ(child_site_url, - child->current_frame_host()->GetSiteInstance()->GetSiteURL()); + EXPECT_EQ(child_site_url, child->current_frame_host() + ->GetSiteInstance() + ->GetSecurityPrincipal() + .GetDeprecatedSiteURL()); } // Reload the subframe while the network is down. @@ -4767,8 +4779,10 @@ EXPECT_EQ(test_url, child->current_frame_host()->GetLastCommittedURL()); EXPECT_EQ(url::Origin::Create(test_url), child->current_frame_host()->GetLastCommittedOrigin()); - EXPECT_EQ(child_site_url, - child->current_frame_host()->GetSiteInstance()->GetSiteURL()); + EXPECT_EQ(child_site_url, child->current_frame_host() + ->GetSiteInstance() + ->GetSecurityPrincipal() + .GetDeprecatedSiteURL()); } } @@ -4803,10 +4817,15 @@ .root(); FrameTreeNode* child1 = root->child_at(0); FrameTreeNode* child2 = root->child_at(1); - GURL a_site_url = root->current_frame_host()->GetSiteInstance()->GetSiteURL(); + GURL a_site_url = root->current_frame_host() + ->GetSiteInstance() + ->GetSecurityPrincipal() + .GetDeprecatedSiteURL(); EXPECT_EQ("a.com", a_site_url.GetHost()); - GURL b_site_url = - child2->current_frame_host()->GetSiteInstance()->GetSiteURL(); + GURL b_site_url = child2->current_frame_host() + ->GetSiteInstance() + ->GetSecurityPrincipal() + .GetDeprecatedSiteURL(); EXPECT_EQ("b.com", b_site_url.GetHost()); // Navigate the subframe to a cross-site URL, while blocking the request with @@ -4869,7 +4888,8 @@ SiteInstanceImpl* child1_site_instance = child1->current_frame_host()->GetSiteInstance(); - GURL c_site_url = child1_site_instance->GetSiteURL(); + GURL c_site_url = + child1_site_instance->GetSecurityPrincipal().GetDeprecatedSiteURL(); if (AreAllSitesIsolatedForTesting()) { EXPECT_EQ("c.com", c_site_url.GetHost()); EXPECT_EQ(test_url.GetHost(), c_site_url.GetHost()); @@ -5050,7 +5070,9 @@ } } - EXPECT_EQ(success_site_instance->GetSiteURL(), site_instance->GetSiteURL()); + EXPECT_EQ( + success_site_instance->GetSecurityPrincipal().GetDeprecatedSiteURL(), + site_instance->GetSecurityPrincipal().GetDeprecatedSiteURL()); EXPECT_NE(success_site_instance, site_instance); EXPECT_EQ(3, nav_controller.GetEntryCount()); @@ -5308,7 +5330,8 @@ } scoped_refptr<SiteInstance> webui_site_instance = shell()->web_contents()->GetPrimaryMainFrame()->GetSiteInstance(); - EXPECT_EQ(webui_url, webui_site_instance->GetSiteURL()); + EXPECT_EQ(webui_url, + webui_site_instance->GetSecurityPrincipal().GetDeprecatedSiteURL()); EXPECT_TRUE(ChildProcessSecurityPolicy::GetInstance()->HasWebUIBindings( webui_site_instance->GetProcess()->GetDeprecatedID()));
diff --git a/content/browser/renderer_host/render_frame_host_manager_unittest.cc b/content/browser/renderer_host/render_frame_host_manager_unittest.cc index b09b0862..ff3616c 100644 --- a/content/browser/renderer_host/render_frame_host_manager_unittest.cc +++ b/content/browser/renderer_host/render_frame_host_manager_unittest.cc
@@ -43,6 +43,7 @@ #include "content/public/browser/render_widget_host.h" #include "content/public/browser/render_widget_host_iterator.h" #include "content/public/browser/render_widget_host_observer.h" +#include "content/public/browser/security_principal.h" #include "content/public/browser/site_isolation_policy.h" #include "content/public/browser/web_contents_delegate.h" #include "content/public/browser/web_contents_observer.h" @@ -1170,7 +1171,9 @@ // try to re-use the SiteInstance/process for non Web UI things that may // get loaded in between. EXPECT_TRUE(host->GetSiteInstance()->HasSite()); - EXPECT_EQ(kUrl, host->GetSiteInstance()->GetSiteURL()); + EXPECT_EQ( + kUrl, + host->GetSiteInstance()->GetSecurityPrincipal().GetDeprecatedSiteURL()); // There will be a WebUI because GetFrameHostForNavigation was already called // twice.
diff --git a/content/browser/renderer_host/render_process_host_impl.cc b/content/browser/renderer_host/render_process_host_impl.cc index 96be9c5..0dad9d3 100644 --- a/content/browser/renderer_host/render_process_host_impl.cc +++ b/content/browser/renderer_host/render_process_host_impl.cc
@@ -158,6 +158,7 @@ #include "content/public/browser/render_widget_host.h" #include "content/public/browser/render_widget_host_iterator.h" #include "content/public/browser/resource_coordinator_service.h" +#include "content/public/browser/security_principal.h" #include "content/public/browser/site_isolation_policy.h" #include "content/public/browser/tracing_support.h" #include "content/public/browser/weak_document_ptr.h" @@ -1634,7 +1635,8 @@ #if !BUILDFLAG(IS_ANDROID) if (site_instance) { - const GURL& site_url = site_instance->GetSiteURL(); + const GURL& site_url = + site_instance->GetSecurityPrincipal().GetDeprecatedSiteURL(); if (GetContentClient()->browser()->IsTopChromeWebUIURL(site_url)) { flags |= RenderProcessFlags::kForTopChromeWebUI; } @@ -3475,7 +3477,7 @@ SpareRenderProcessHostManagerImpl::Get().PrepareForFutureRequests( site_instance->GetBrowserContext(), GetContentClient()->browser()->GetSpareRendererDelayForSiteURL( - site_instance->GetSiteURL())); + site_instance->GetSecurityPrincipal().GetDeprecatedSiteURL())); } // static @@ -5437,7 +5439,7 @@ spare_process_manager.PrepareForFutureRequests( browser_context, GetContentClient()->browser()->GetSpareRendererDelayForSiteURL( - site_instance->GetSiteURL())); + site_instance->GetSecurityPrincipal().GetDeprecatedSiteURL())); } if (is_unmatched_service_worker) {
diff --git a/content/browser/renderer_host/render_widget_host_view_aura.cc b/content/browser/renderer_host/render_widget_host_view_aura.cc index b6f9361..9903327 100644 --- a/content/browser/renderer_host/render_widget_host_view_aura.cc +++ b/content/browser/renderer_host/render_widget_host_view_aura.cc
@@ -16,6 +16,7 @@ #include "base/functional/callback_helpers.h" #include "base/logging.h" #include "base/memory/raw_ptr.h" +#include "base/memory/weak_auto_reset.h" #include "base/metrics/histogram_functions.h" #include "base/notimplemented.h" #include "base/strings/strcat.h" @@ -2235,7 +2236,13 @@ void RenderWidgetHostViewAura::OnBoundsChanged(const gfx::Rect& old_bounds, const gfx::Rect& new_bounds) { - base::AutoReset<bool> in_bounds_changed(&in_bounds_changed_, true); + // OnCaretBoundsChanged() below may call out to a third-party TSF IME on + // Windows, which can re-entrantly destroy `this`. Use WeakAutoReset so the + // unwind write does not land in freed memory (AutoReset::scoped_variable_ is + // RAW_PTR_EXCLUSION and not BRP-protected). + base::WeakAutoReset in_bounds_changed( + weak_ptr_factory_.GetWeakPtr(), + &RenderWidgetHostViewAura::in_bounds_changed_, true); // We care about this whenever RenderWidgetHostViewAura is not owned by a // WebContentsViewAura since changes to the Window's bounds need to be // messaged to the renderer. WebContentsViewAura invokes SetSize() or @@ -2244,7 +2251,12 @@ SetSize(new_bounds.size()); if (GetInputMethod()) { + auto weak_this = weak_ptr_factory_.GetWeakPtr(); GetInputMethod()->OnCaretBoundsChanged(this); + // `this` may have been deleted inside the IME callout. + if (!weak_this) { + return; + } UpdateInsetsWithVirtualKeyboardEnabled(); } }
diff --git a/content/browser/renderer_host/render_widget_host_view_aura_unittest.cc b/content/browser/renderer_host/render_widget_host_view_aura_unittest.cc index 0a69d55..feb608c4 100644 --- a/content/browser/renderer_host/render_widget_host_view_aura_unittest.cc +++ b/content/browser/renderer_host/render_widget_host_view_aura_unittest.cc
@@ -6861,6 +6861,84 @@ GetInputMethod()->RemoveObserver(this); } +// Mock InputMethod that runs a closure when OnCaretBoundsChanged is invoked. +// Simulates a Windows TSF IME whose ITextStoreACPSink::OnLayoutChange handler +// pumps the thread message queue, allowing a queued task to synchronously +// destroy the RenderWidgetHostViewAura while it is still inside +// OnBoundsChanged(). +class DestroyingMockInputMethod : public ui::MockInputMethod { + public: + DestroyingMockInputMethod() : ui::MockInputMethod(nullptr) {} + + void OnCaretBoundsChanged(const ui::TextInputClient* client) override { + ui::MockInputMethod::OnCaretBoundsChanged(client); + if (on_caret_bounds_changed_) { + std::move(on_caret_bounds_changed_).Run(); + } + } + + void set_on_caret_bounds_changed(base::OnceClosure closure) { + on_caret_bounds_changed_ = std::move(closure); + } + + private: + base::OnceClosure on_caret_bounds_changed_; +}; + +class RenderWidgetHostViewAuraOnBoundsChangedUAFTest + : public RenderWidgetHostViewAuraTest { + public: + void SetUp() override { + input_method_ = new DestroyingMockInputMethod(); + // Ownership is transferred to the WindowTreeHost via the InputMethod + // factory; see SetUpInputMethodForTesting(). + ui::SetUpInputMethodForTesting(input_method_); + SetUpEnvironment(); + } + + void TearDown() override { + input_method_ = nullptr; + RenderWidgetHostViewAuraTest::TearDown(); + } + + protected: + raw_ptr<DestroyingMockInputMethod> input_method_ = nullptr; +}; + +// RWHVA::OnBoundsChanged() constructs a base::AutoReset<bool> holding +// &in_bounds_changed_, then calls GetInputMethod()->OnCaretBoundsChanged(this). +// On Windows that reaches TSFTextStore::SendOnLayoutChange -> +// text_store_acp_sink_->OnLayoutChange(), a synchronous COM call into the +// active third-party IME. If the IME pumps messages and the view is destroyed +// re-entrantly, on unwind UpdateInsetsWithVirtualKeyboardEnabled() and +// ~AutoReset both touch freed memory. AutoReset::scoped_variable_ is +// RAW_PTR_EXCLUSION, so it is not MiraclePtr-protected: the ~AutoReset write +// lands in a freed (un-quarantined) slot. +TEST_F(RenderWidgetHostViewAuraOnBoundsChangedUAFTest, + DestroyDuringOnCaretBoundsChanged) { + InitViewForFrame(nullptr); + ParentHostView(view_, parent_view_); + // `view_` shares the root window (and thus the InputMethod) with + // `parent_view_`. + ASSERT_EQ(static_cast<ui::InputMethod*>(input_method_.get()), + GetInputMethod()); + + // Arrange for the view to be synchronously destroyed inside + // OnCaretBoundsChanged, simulating re-entrant destruction triggered by a + // TSF IME callout that pumps a queued window.close() / renderer-gone task. + FakeRenderWidgetHostViewAura* raw_view = view_.get(); + input_method_->set_on_caret_bounds_changed(base::BindLambdaForTesting([&]() { + widget_host_ = nullptr; + view_.ExtractAsDangling()->Destroy(); + })); + + // Under ASAN this triggers heap-use-after-free in + // UpdateInsetsWithVirtualKeyboardEnabled() (read of freed + // keyboard_occluded_bounds_) followed by a write-after-free in + // ~AutoReset<bool> to the freed in_bounds_changed_ slot. + raw_view->OnBoundsChanged(gfx::Rect(), gfx::Rect(0, 0, 100, 100)); +} + #if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_CHROMEOS) class MockVirtualKeyboardController final : public ui::VirtualKeyboardController {
diff --git a/content/browser/renderer_host/spare_render_process_host_manager_impl.cc b/content/browser/renderer_host/spare_render_process_host_manager_impl.cc index 784ef806..b7724b68 100644 --- a/content/browser/renderer_host/spare_render_process_host_manager_impl.cc +++ b/content/browser/renderer_host/spare_render_process_host_manager_impl.cc
@@ -22,6 +22,7 @@ #include "content/public/browser/browser_context.h" #include "content/public/browser/browser_main_runner.h" #include "content/public/browser/render_process_host.h" +#include "content/public/browser/security_principal.h" #include "content/public/common/content_client.h" #include "content/public/common/content_features.h" @@ -625,7 +626,7 @@ site_instance->GetSecurityPrincipal().IsGuest() #if !BUILDFLAG(IS_ANDROID) || GetContentClient()->browser()->IsTopChromeWebUIURL( - site_instance->GetSiteURL()) + site_instance->GetSecurityPrincipal().GetDeprecatedSiteURL()) #endif ) { action = SpareProcessMaybeTakeAction::kRefusedBySiteInstance;
diff --git a/content/browser/renderer_host/unassigned_site_instance_browsertest.cc b/content/browser/renderer_host/unassigned_site_instance_browsertest.cc index 8fa5ccca..64d8536 100644 --- a/content/browser/renderer_host/unassigned_site_instance_browsertest.cc +++ b/content/browser/renderer_host/unassigned_site_instance_browsertest.cc
@@ -17,6 +17,7 @@ #include "content/common/content_navigation_policy.h" #include "content/common/features.h" #include "content/public/browser/back_forward_cache.h" +#include "content/public/browser/security_principal.h" #include "content/public/browser/site_isolation_policy.h" #include "content/public/browser/web_exposed_isolation_level.h" #include "content/public/test/back_forward_cache_util.h" @@ -683,7 +684,7 @@ scoped_refptr<SiteInstanceImpl> instance1( web_contents()->GetPrimaryMainFrame()->GetSiteInstance()); RenderProcessHost* process1 = instance1->GetProcess(); - EXPECT_EQ(GURL(), instance1->GetSiteURL()); + EXPECT_EQ(GURL(), instance1->GetSecurityPrincipal().GetDeprecatedSiteURL()); // Navigate to page that uses up the site. It should reuse the previous // SiteInstance and set its site URL. @@ -692,7 +693,8 @@ web_contents()->GetPrimaryMainFrame()->GetSiteInstance()); EXPECT_TRUE(instance1->HasSite()); if (AreStrictSiteInstancesEnabled()) { - EXPECT_EQ(RegularUrlOriginMaybeWithPort(), instance1->GetSiteURL()); + EXPECT_EQ(RegularUrlOriginMaybeWithPort(), + instance1->GetSecurityPrincipal().GetDeprecatedSiteURL()); } else { EXPECT_TRUE(instance1->IsDefaultSiteInstance()); } @@ -709,7 +711,8 @@ EXPECT_NE(prev_entry_instance, instance1); EXPECT_NE(prev_entry_instance, nullptr); EXPECT_TRUE(prev_entry_instance->IsRelatedSiteInstance(instance1.get())); - EXPECT_EQ(GURL(), prev_entry_instance->GetSiteURL()); + EXPECT_EQ(GURL(), + prev_entry_instance->GetSecurityPrincipal().GetDeprecatedSiteURL()); // Navigate to bar.com, which destroys the previous RenderProcessHost. GURL other_regular_url( @@ -772,7 +775,8 @@ EXPECT_EQ(embedder_defined_unassigned_url(), web_contents()->GetLastCommittedURL()); EXPECT_NE(instance1, new_instance); - EXPECT_EQ(GURL(), new_instance->GetSiteURL()); + EXPECT_EQ(GURL(), + new_instance->GetSecurityPrincipal().GetDeprecatedSiteURL()); EXPECT_TRUE(new_instance->HasProcess()); // Because embedder_defined_unassigned_url does not set a site URL, it should @@ -908,9 +912,10 @@ RenderProcessHost* process2 = web_contents->GetPrimaryMainFrame()->GetProcess(); EXPECT_NE(process1, process2); - EXPECT_EQ( - RegularUrlOriginMaybeWithPort(), - web_contents->GetPrimaryMainFrame()->GetSiteInstance()->GetSiteURL()); + EXPECT_EQ(RegularUrlOriginMaybeWithPort(), web_contents->GetPrimaryMainFrame() + ->GetSiteInstance() + ->GetSecurityPrincipal() + .GetDeprecatedSiteURL()); EXPECT_EQ(ProcessLock::FromSiteInfo(SiteInfo::CreateForTesting( IsolationContext(browser_context), regular_url())), policy->GetProcessLock(process2->GetID()));
diff --git a/content/browser/renderer_host/view_transition_browsertest.cc b/content/browser/renderer_host/view_transition_browsertest.cc index d900138..a317999 100644 --- a/content/browser/renderer_host/view_transition_browsertest.cc +++ b/content/browser/renderer_host/view_transition_browsertest.cc
@@ -534,8 +534,7 @@ EnablePixelOutput(1.f); feature_list_.InitWithFeatures( /*enabled_features=*/ - {::features::kAckCopyOutputRequestEarlyForViewTransition, - blink::features::kDelayLayerTreeViewDeletionOnLocalSwap, + {blink::features::kDelayLayerTreeViewDeletionOnLocalSwap, ::features::kRenderDocument}, /*disabled_features=*/{}); }
diff --git a/content/browser/security_exploit_browsertest.cc b/content/browser/security_exploit_browsertest.cc index 1bd3908..8834540 100644 --- a/content/browser/security_exploit_browsertest.cc +++ b/content/browser/security_exploit_browsertest.cc
@@ -593,6 +593,90 @@ EXPECT_EQ(bad_message::RFHI_WEBMCP_NOT_ENABLED, kill_waiter.Wait()); } +// Test that calling `ExecuteRemoteScriptTool()` when the `WebMCP` base::Feature +// is disabled (but permissions policy is enabled) properly terminates the +// renderer process. +IN_PROC_BROWSER_TEST_F(SecurityExploitBrowserTestWebMCPDisabled, + ExecuteRemoteScriptToolWebMCPDisabled) { + GURL start_url(embedded_test_server()->GetURL("a.com", "/title1.html")); + EXPECT_TRUE(NavigateToURL(shell(), start_url)); + + RenderFrameHostImpl* rfh = static_cast<RenderFrameHostImpl*>( + shell()->web_contents()->GetPrimaryMainFrame()); + + EXPECT_TRUE( + rfh->IsFeatureEnabled(network::mojom::PermissionsPolicyFeature::kTools)); + + RenderProcessHostBadIpcMessageWaiter kill_waiter(rfh->GetProcess()); + + mojo::Remote<blink::mojom::ModelContextHost> script_tool_host; + rfh->BindModelContextHost(script_tool_host.BindNewPipeAndPassReceiver()); + + blink::FrameToken tool_owner_frame_token = rfh->GetFrameToken(); + + script_tool_host->ExecuteRemoteScriptTool( + tool_owner_frame_token, rfh->GetLastCommittedOrigin(), "test-tool", "[]", + base::BindOnce( + [](const std::optional<std::string>& result, bool success) {})); + EXPECT_EQ(bad_message::RFHI_WEBMCP_NOT_ENABLED, kill_waiter.Wait()); +} + +// Test that calling `RegisterScriptTool()` with an invalid +// `tool_owner_frame_token` properly terminates the renderer process. +IN_PROC_BROWSER_TEST_F(SecurityExploitBrowserTest, + RegisterScriptToolInvalidToolOwnerToken) { + GURL start_url(embedded_test_server()->GetURL("a.com", "/title1.html")); + EXPECT_TRUE(NavigateToURL(shell(), start_url)); + + RenderFrameHostImpl* rfh = static_cast<RenderFrameHostImpl*>( + shell()->web_contents()->GetPrimaryMainFrame()); + + EXPECT_TRUE( + rfh->IsFeatureEnabled(network::mojom::PermissionsPolicyFeature::kTools)); + + RenderProcessHostBadIpcMessageWaiter kill_waiter(rfh->GetProcess()); + + auto tool = blink::mojom::ScriptTool::New(); + tool->name = "test-tool"; + tool->description = "test description"; + tool->tool_owner_frame_token = blink::LocalFrameToken(); + tool->origin = rfh->GetLastCommittedOrigin(); + + mojo::Remote<blink::mojom::ModelContextHost> script_tool_host; + rfh->BindModelContextHost(script_tool_host.BindNewPipeAndPassReceiver()); + + script_tool_host->RegisterScriptTool(std::move(tool)); + EXPECT_EQ(bad_message::RFHI_WEBMCP_INVALID_TOOL_OWNER, kill_waiter.Wait()); +} + +// Test that calling `RegisterScriptTool()` with an invalid `origin` +// properly terminates the renderer process. +IN_PROC_BROWSER_TEST_F(SecurityExploitBrowserTest, + RegisterScriptToolInvalidToolOwnerOrigin) { + GURL start_url(embedded_test_server()->GetURL("a.com", "/title1.html")); + EXPECT_TRUE(NavigateToURL(shell(), start_url)); + + RenderFrameHostImpl* rfh = static_cast<RenderFrameHostImpl*>( + shell()->web_contents()->GetPrimaryMainFrame()); + + EXPECT_TRUE( + rfh->IsFeatureEnabled(network::mojom::PermissionsPolicyFeature::kTools)); + + RenderProcessHostBadIpcMessageWaiter kill_waiter(rfh->GetProcess()); + + auto tool = blink::mojom::ScriptTool::New(); + tool->name = "test-tool"; + tool->description = "test description"; + tool->tool_owner_frame_token = rfh->GetFrameToken(); + tool->origin = url::Origin::Create(GURL("https://b.com")); + + mojo::Remote<blink::mojom::ModelContextHost> script_tool_host; + rfh->BindModelContextHost(script_tool_host.BindNewPipeAndPassReceiver()); + + script_tool_host->RegisterScriptTool(std::move(tool)); + EXPECT_EQ(bad_message::RFHI_WEBMCP_INVALID_TOOL_OWNER, kill_waiter.Wait()); +} + // Test that calling `UnregisterScriptTool()` when the `tools` permission policy // is not enabled properly terminates the renderer process. IN_PROC_BROWSER_TEST_F(SecurityExploitBrowserTest, @@ -761,6 +845,8 @@ auto tool1 = blink::mojom::ScriptTool::New(); tool1->name = "test-tool"; tool1->description = "test description"; + tool1->tool_owner_frame_token = rfh->GetFrameToken(); + tool1->origin = rfh->GetLastCommittedOrigin(); mojo::Remote<blink::mojom::ModelContextHost> script_tool_host; rfh->BindModelContextHost(script_tool_host.BindNewPipeAndPassReceiver()); @@ -772,6 +858,8 @@ auto tool2 = blink::mojom::ScriptTool::New(); tool2->name = "test-tool"; // Duplicate name tool2->description = "test description"; + tool2->tool_owner_frame_token = rfh->GetFrameToken(); + tool2->origin = rfh->GetLastCommittedOrigin(); script_tool_host->RegisterScriptTool(std::move(tool2)); EXPECT_EQ(bad_message::RFHI_WEBMCP_REGISTER_DUPLICATE_TOOL_NAME,
diff --git a/content/browser/service_worker/service_worker_process_browsertest.cc b/content/browser/service_worker/service_worker_process_browsertest.cc index 04ed5d1..cffe097 100644 --- a/content/browser/service_worker/service_worker_process_browsertest.cc +++ b/content/browser/service_worker/service_worker_process_browsertest.cc
@@ -11,6 +11,7 @@ #include "content/browser/service_worker/service_worker_context_wrapper.h" #include "content/browser/web_contents/web_contents_impl.h" #include "content/public/browser/browser_context.h" +#include "content/public/browser/security_principal.h" #include "content/public/browser/storage_partition.h" #include "content/public/common/content_client.h" #include "content/public/common/content_features.h" @@ -217,7 +218,8 @@ EXPECT_EQ(web_contents()->GetLastCommittedURL(), empty_site_url); scoped_refptr<SiteInstanceImpl> site_instance = web_contents()->GetPrimaryMainFrame()->GetSiteInstance(); - EXPECT_EQ(GURL(), site_instance->GetSiteURL()); + EXPECT_EQ(GURL(), + site_instance->GetSecurityPrincipal().GetDeprecatedSiteURL()); ChildProcessId page_process_id = current_frame_host()->GetProcess()->GetID(); EXPECT_TRUE(page_process_id);
diff --git a/content/browser/service_worker/service_worker_process_manager_unittest.cc b/content/browser/service_worker/service_worker_process_manager_unittest.cc index 0f2eb63a..15258c5 100644 --- a/content/browser/service_worker/service_worker_process_manager_unittest.cc +++ b/content/browser/service_worker/service_worker_process_manager_unittest.cc
@@ -15,6 +15,7 @@ #include "content/browser/storage_partition_impl.h" #include "content/common/url_schemes.h" #include "content/public/browser/child_process_host.h" +#include "content/public/browser/security_principal.h" #include "content/public/common/content_features.h" #include "content/public/common/url_constants.h" #include "content/public/test/browser_task_environment.h" @@ -264,9 +265,10 @@ true /* can_use_existing_process */, AncestorFrameType::kNormalFrame, &process_info); EXPECT_EQ(blink::ServiceWorkerStatusCode::kOk, status); - EXPECT_EQ( - GURL("http://example.com"), - render_process_host_factory_->last_site_instance_used()->GetSiteURL()); + EXPECT_EQ(GURL("http://example.com"), + render_process_host_factory_->last_site_instance_used() + ->GetSecurityPrincipal() + .GetDeprecatedSiteURL()); EXPECT_FALSE(render_process_host_factory_->last_site_instance_used() ->GetSecurityPrincipal() .IsGuest());
diff --git a/content/browser/site_info.cc b/content/browser/site_info.cc index c423a1ec..daa2680 100644 --- a/content/browser/site_info.cc +++ b/content/browser/site_info.cc
@@ -459,6 +459,10 @@ return site_url_.SchemeIs(scheme); } +const GURL& SiteInfo::GetDeprecatedSiteURL() const { + return site_url(); +} + SiteInfo SiteInfo::GetNonOriginKeyedEquivalentForMetrics( const IsolationContext& isolation_context) const { SiteInfo non_oac_site_info(*this);
diff --git a/content/browser/site_info.h b/content/browser/site_info.h index 29a9f94..2e4bcae 100644 --- a/content/browser/site_info.h +++ b/content/browser/site_info.h
@@ -215,6 +215,7 @@ bool IsWebUI() const override; const StoragePartitionConfig& GetStoragePartitionConfig() const override; bool SchemeIs(std::string_view scheme) const override; + const GURL& GetDeprecatedSiteURL() const override; // This function returns a new SiteInfo which is equivalent to the original, // except that its AgentClusterKey is made site-keyed if it had been created @@ -222,27 +223,9 @@ SiteInfo GetNonOriginKeyedEquivalentForMetrics( const IsolationContext& isolation_context) const; - // Returns the site URL associated with all of the documents and workers in - // this principal, as described above. - // - // Compared to the AgentClusterKey, this URL might have been overridden from - // the actual URL of the content in cases that involve effective URLs such as - // hosted apps. The AgentClusterKey is always computed with the real URL, as - // it is a web spec concept and effective URLs are not part of the spec. - // - // NOTE: In most cases, code should be performing checks against the origin - // returned by |RenderFrameHost::GetLastCommittedOrigin()|. In contrast, the - // GURL returned by |site_url()| should not be considered authoritative - // because: - // - A SiteInstance can host pages from multiple sites if "site per process" - // is not enabled and the SiteInstance isn't hosting pages that require - // process isolation (e.g. WebUI or extensions). - // - Even with site per process, the site URL is not an origin: while often - // derived from the origin, it only contains the scheme and the eTLD + 1, - // i.e. an origin with the host "deeply.nested.subdomain.example.com" - // corresponds to a site URL with the host "example.com". - // - When origin isolation is in use, there may be multiple SiteInstance with - // the same site_url() but that differ in other properties. + // Additional non-virtual accessor to site_url_, which is ok to use from + // inside //content. See SecurityPrincipal::GetDeprecatedSiteURL for more + // info. const GURL& site_url() const { return site_url_; } // Returns the AgentClusterKey of the execution contexts within this SiteInfo.
diff --git a/content/browser/site_instance_impl.cc b/content/browser/site_instance_impl.cc index 97558363e..18f91809 100644 --- a/content/browser/site_instance_impl.cc +++ b/content/browser/site_instance_impl.cc
@@ -761,8 +761,8 @@ void SiteInstanceImpl::MaybeSetDefaultSiteInstanceGroup() { CHECK(ShouldUseDefaultSiteInstanceGroup()); if (!browsing_instance_->has_default_site_instance_group() && - CanBePlacedInDefaultSiteInstanceOrGroup(GetIsolationContext(), - GetSiteURL(), site_info_)) { + CanBePlacedInDefaultSiteInstanceOrGroup( + GetIsolationContext(), site_info_.site_url(), site_info_)) { CHECK(HasProcess()); CHECK(has_group()); browsing_instance_->set_default_site_instance_group( @@ -772,8 +772,8 @@ bool SiteInstanceImpl::CanPutSiteInstanceInDefaultGroup() { return ShouldUseDefaultSiteInstanceGroup() && - CanBePlacedInDefaultSiteInstanceOrGroup(GetIsolationContext(), - GetSiteURL(), site_info_); + CanBePlacedInDefaultSiteInstanceOrGroup( + GetIsolationContext(), site_info_.site_url(), site_info_); } SiteInstanceProcessAssignment @@ -781,10 +781,6 @@ return process_assignment_; } -const GURL& SiteInstanceImpl::GetSiteURL() const { - return site_info_.site_url(); -} - const SiteInfo& SiteInstanceImpl::GetSiteInfo() const { if (!has_site_) { // `site_info_` has not been set yet. The caller is reading default
diff --git a/content/browser/site_instance_impl.h b/content/browser/site_instance_impl.h index be1d3f8f..b63a4096 100644 --- a/content/browser/site_instance_impl.h +++ b/content/browser/site_instance_impl.h
@@ -173,7 +173,6 @@ SiteInstanceGroupId GetSiteInstanceGroupId() override; BrowserContext* GetBrowserContext() override; const SecurityPrincipal& GetSecurityPrincipal() const override; - const GURL& GetSiteURL() const override; scoped_refptr<SiteInstance> GetRelatedSiteInstance(const GURL& url) override; bool IsRelatedSiteInstance(const SiteInstance* instance) override; size_t GetRelatedActiveContentsCount() override;
diff --git a/content/browser/site_instance_impl_unittest.cc b/content/browser/site_instance_impl_unittest.cc index 520ca19..2d8529e 100644 --- a/content/browser/site_instance_impl_unittest.cc +++ b/content/browser/site_instance_impl_unittest.cc
@@ -35,6 +35,7 @@ #include "content/browser/webui/url_data_manager_backend.h" #include "content/browser/webui/web_ui_controller_factory_registry.h" #include "content/common/content_navigation_policy.h" +#include "content/public/browser/security_principal.h" #include "content/public/browser/site_isolation_policy.h" #include "content/public/browser/web_exposed_isolation_level.h" #include "content/public/browser/web_ui_controller.h" @@ -794,11 +795,13 @@ scoped_refptr<SiteInstanceImpl> instance(SiteInstanceImpl::Create(&context)); EXPECT_FALSE(instance->HasSite()); - EXPECT_TRUE(instance->GetSiteURL().is_empty()); + EXPECT_TRUE( + instance->GetSecurityPrincipal().GetDeprecatedSiteURL().is_empty()); instance->SetSite( UrlInfo::CreateForTesting(GURL("http://www.google.com/index.html"))); - EXPECT_EQ(GURL("http://google.com"), instance->GetSiteURL()); + EXPECT_EQ(GURL("http://google.com"), + instance->GetSecurityPrincipal().GetDeprecatedSiteURL()); EXPECT_TRUE(instance->HasSite()); @@ -1297,7 +1300,8 @@ SiteInstanceImpl::Create(browser_context.get())); EXPECT_FALSE(instance->HasSite()); - EXPECT_TRUE(instance->GetSiteURL().is_empty()); + EXPECT_TRUE( + instance->GetSecurityPrincipal().GetDeprecatedSiteURL().is_empty()); // Check prior to assigning a site or process to the instance, which is // expected to return false to allow the SiteInstance to be used for anything. @@ -1401,7 +1405,8 @@ SiteInstanceImpl::Create(browser_context.get())); EXPECT_FALSE(instance->HasSite()); - EXPECT_TRUE(instance->GetSiteURL().is_empty()); + EXPECT_TRUE( + instance->GetSecurityPrincipal().GetDeprecatedSiteURL().is_empty()); // Simulate navigating to a WebUI URL in a process that does not have WebUI // bindings. This already requires bypassing security checks. @@ -1443,7 +1448,8 @@ instance->SetSite(UrlInfo()); EXPECT_TRUE(instance->HasSite()); - EXPECT_TRUE(instance->GetSiteURL().is_empty()); + EXPECT_TRUE( + instance->GetSecurityPrincipal().GetDeprecatedSiteURL().is_empty()); instance->GetOrCreateProcessForTesting(); EXPECT_FALSE(RenderProcessHostImpl::GetSoleProcessHostForSite( @@ -2051,7 +2057,8 @@ if (AreStrictSiteInstancesEnabled()) { EXPECT_FALSE(instance1->IsDefaultSiteInstance()); - EXPECT_EQ(kNonIsolatedUrl, instance1->GetSiteURL()); + EXPECT_EQ(kNonIsolatedUrl, + instance1->GetSecurityPrincipal().GetDeprecatedSiteURL()); } else { EXPECT_TRUE(instance1->IsDefaultSiteInstance()); } @@ -2060,13 +2067,15 @@ EXPECT_TRUE(instance1->IsSameSiteWithURL(kNonIsolatedUrl)); EXPECT_FALSE(instance2->IsDefaultSiteInstance()); - EXPECT_EQ(kIsolatedUrl, instance2->GetSiteURL()); + EXPECT_EQ(kIsolatedUrl, + instance2->GetSecurityPrincipal().GetDeprecatedSiteURL()); EXPECT_TRUE(instance2->DoesSiteInfoForURLMatch( UrlInfo::CreateForTesting(kIsolatedUrl))); EXPECT_TRUE(instance2->IsSameSiteWithURL(kIsolatedUrl)); EXPECT_FALSE(instance3->IsDefaultSiteInstance()); - EXPECT_EQ(GURL("file:"), instance3->GetSiteURL()); + EXPECT_EQ(GURL("file:"), + instance3->GetSecurityPrincipal().GetDeprecatedSiteURL()); EXPECT_TRUE( instance3->DoesSiteInfoForURLMatch(UrlInfo::CreateForTesting(kFileUrl))); // Not same site because file URL's don't have a host. @@ -2085,7 +2094,8 @@ EXPECT_TRUE(instance5->HasSite()); if (AreStrictSiteInstancesEnabled()) { EXPECT_FALSE(instance5->IsDefaultSiteInstance()); - EXPECT_EQ("custom-standard://custom/", instance5->GetSiteURL()); + EXPECT_EQ("custom-standard://custom/", + instance5->GetSecurityPrincipal().GetDeprecatedSiteURL()); EXPECT_EQ("http://foo.com/", instance5->GetSiteInfo().GetProcessLockURL()); } else { EXPECT_TRUE(instance5->IsDefaultSiteInstance());
diff --git a/content/browser/site_per_process_browsertest.cc b/content/browser/site_per_process_browsertest.cc index 1e2a5062..ead18be 100644 --- a/content/browser/site_per_process_browsertest.cc +++ b/content/browser/site_per_process_browsertest.cc
@@ -105,6 +105,7 @@ #include "content/public/browser/javascript_dialog_manager.h" #include "content/public/browser/navigation_handle.h" #include "content/public/browser/render_process_host_priority_client.h" +#include "content/public/browser/security_principal.h" #include "content/public/browser/site_isolation_policy.h" #include "content/public/common/content_client.h" #include "content/public/common/content_features.h" @@ -5870,8 +5871,12 @@ auto* main_frame = shell()->web_contents()->GetPrimaryMainFrame(); auto* new_frame = new_shell->web_contents()->GetPrimaryMainFrame(); - GURL main_url = main_frame->GetSiteInstance()->GetSiteURL(); - GURL new_url = new_frame->GetSiteInstance()->GetSiteURL(); + GURL main_url = main_frame->GetSiteInstance() + ->GetSecurityPrincipal() + .GetDeprecatedSiteURL(); + GURL new_url = new_frame->GetSiteInstance() + ->GetSecurityPrincipal() + .GetDeprecatedSiteURL(); EXPECT_NE(new_frame->GetSiteInstance(), main_frame->GetSiteInstance()); // The site URL is the data scheme followed by a serialized nonce, which is @@ -6841,7 +6846,9 @@ : WebContentsObserver(web_contents) {} void DidStartNavigation(NavigationHandle* navigation_handle) override { DCHECK_EQ(GURL("http://b.com/"), - navigation_handle->GetStartingSiteInstance()->GetSiteURL()); + navigation_handle->GetStartingSiteInstance() + ->GetSecurityPrincipal() + .GetDeprecatedSiteURL()); } }; @@ -10185,8 +10192,10 @@ // In per-origin IsolatedSandboxedIframes mode, the server port is retained // in the site URL. GURL main_site(embedded_test_server()->GetURL("a.com", "/")); - EXPECT_EQ(main_site, - root->current_frame_host()->GetSiteInstance()->GetSiteURL()); + EXPECT_EQ(main_site, root->current_frame_host() + ->GetSiteInstance() + ->GetSecurityPrincipal() + .GetDeprecatedSiteURL()); } FrameTreeNode* child_node = root->child_at(0); @@ -10725,9 +10734,11 @@ child->current_frame_host()->GetSiteInstance()->process_reuse_policy()); EXPECT_TRUE(child->current_frame_host()->IsCrossProcessSubframe()); - EXPECT_EQ( - bar_url.GetHost(), - child->current_frame_host()->GetSiteInstance()->GetSiteURL().GetHost()); + EXPECT_EQ(bar_url.GetHost(), child->current_frame_host() + ->GetSiteInstance() + ->GetSecurityPrincipal() + .GetDeprecatedSiteURL() + .GetHost()); // The subframe's SiteInstance should still be different from second_shell's // SiteInstance, and they should be in separate BrowsingInstances. @@ -12438,9 +12449,14 @@ EXPECT_NE( subframe->current_frame_host()->GetSiteInstance()->GetProcess(), popup_subframe->current_frame_host()->GetSiteInstance()->GetProcess()); - EXPECT_NE( - subframe->current_frame_host()->GetSiteInstance()->GetSiteURL(), - popup_subframe->current_frame_host()->GetSiteInstance()->GetSiteURL()); + EXPECT_NE(subframe->current_frame_host() + ->GetSiteInstance() + ->GetSecurityPrincipal() + .GetDeprecatedSiteURL(), + popup_subframe->current_frame_host() + ->GetSiteInstance() + ->GetSecurityPrincipal() + .GetDeprecatedSiteURL()); } // Ensure that when a process is about to be destroyed after the last active
diff --git a/content/browser/web_contents/web_contents_impl.cc b/content/browser/web_contents/web_contents_impl.cc index 0ffb9c8..e5a78b6c 100644 --- a/content/browser/web_contents/web_contents_impl.cc +++ b/content/browser/web_contents/web_contents_impl.cc
@@ -5832,7 +5832,9 @@ // Only drop fullscreen on the specific destination display, if it is known. // This supports sites using cross-screen window management capabilities to // retain fullscreen and open a window on another screen. - ForSecurityDropFullscreen(display_id).RunAndReset(); + if (!ForSecurityDropFullscreen(display_id)) { + return nullptr; + } // The delegate can be null in tests. if (!delegate) { @@ -7403,10 +7405,12 @@ ExitFullscreenMode(will_cause_resize); } -base::ScopedClosureRunner WebContentsImpl::ForSecurityDropFullscreen( - int64_t display_id) { +std::optional<base::ScopedClosureRunner> +WebContentsImpl::ForSecurityDropFullscreen(int64_t display_id) { OPTIONAL_TRACE_EVENT1("content", "WebContentsImpl::ForSecurityDropFullscreen", "display_id", display_id); + base::WeakPtr<WebContentsImpl> weak_this = weak_factory_.GetWeakPtr(); + // Make WebContentses "related" to this instance exit HTML element fullscreen, // ignoring browser fullscreen and fullscreen-within-tab modes. This needs to // be done with two passes, because it is simple to walk _up_ the chain of @@ -7434,6 +7438,9 @@ } for (auto& fullscreen_contents : fullscreen_contents_list) { + if (!weak_this) { + return std::nullopt; + } if (!fullscreen_contents) { continue; } @@ -7446,6 +7453,10 @@ } } + if (!weak_this) { + return std::nullopt; + } + // Second, walk upstream from this WebContents, and drop the fullscreen of // all WebContentses that are in fullscreen. Block all the WebContentses in // the chain from entering fullscreen while the returned closure runner is @@ -7460,6 +7471,9 @@ } for (auto& opener : openers) { + if (!weak_this) { + return std::nullopt; + } if (!opener) { continue; } @@ -8247,11 +8261,9 @@ // Any new WebContents opened while this WebContents is in fullscreen can be // used to confuse the user, so drop fullscreen. - base::ScopedClosureRunner fullscreen_block = - ForSecurityDropFullscreen(/*display_id=*/display::kInvalidDisplayId); - // The new view source contents will be independent of this contents, so - // release the fullscreen block. - fullscreen_block.RunAndReset(); + if (!ForSecurityDropFullscreen(/*display_id=*/display::kInvalidDisplayId)) { + return; + } // We intentionally don't share the SiteInstance with the original frame so // that view source has a consistent process model and always ends up in a new @@ -8503,9 +8515,12 @@ // Any explicit focusing of another window while this WebContents is in // fullscreen can be used to confuse the user, so drop fullscreen. - base::ScopedClosureRunner fullscreen_block = + auto blocker = ForSecurityDropFullscreen(/*display_id=*/display::kInvalidDisplayId); - listener->SetFullscreenBlock(std::move(fullscreen_block)); + if (!blocker) { + return; + } + listener->SetFullscreenBlock(std::move(*blocker)); if (delegate_) { active_file_chooser_ = std::move(file_chooser); @@ -9080,14 +9095,17 @@ // Running a dialog causes an exit to webpage-initiated fullscreen. // http://crbug.com/728276 - base::ScopedClosureRunner fullscreen_block = + auto blocker = ForSecurityDropFullscreen(/*display_id=*/display::kInvalidDisplayId); + if (!blocker) { + return; + } auto callback = base::BindOnce( &WebContentsImpl::OnDialogClosed, weak_factory_.GetWeakPtr(), render_frame_host->GetProcess()->GetDeprecatedID(), render_frame_host->GetRoutingID(), std::move(response_callback), - std::move(fullscreen_block)); + std::move(*blocker)); std::vector<protocol::PageHandler*> page_handlers = protocol::PageHandler::EnabledForWebContents(this); @@ -9213,14 +9231,17 @@ // Running a dialog causes an exit to webpage-initiated fullscreen. // http://crbug.com/728276 - base::ScopedClosureRunner fullscreen_block = + auto blocker = ForSecurityDropFullscreen(/*display_id=*/display::kInvalidDisplayId); + if (!blocker) { + return; + } auto callback = base::BindOnce( &WebContentsImpl::OnDialogClosed, weak_factory_.GetWeakPtr(), render_frame_host->GetProcess()->GetDeprecatedID(), render_frame_host->GetRoutingID(), std::move(response_callback), - std::move(fullscreen_block)); + std::move(*blocker)); std::vector<protocol::PageHandler*> page_handlers = protocol::PageHandler::EnabledForWebContents(this); @@ -9317,9 +9338,12 @@ // Any explicit focusing of another window while this WebContents is in // fullscreen can be used to confuse the user, so drop fullscreen. - base::ScopedClosureRunner fullscreen_block = + auto blocker = ForSecurityDropFullscreen(/*display_id=*/display::kInvalidDisplayId); - listener->SetFullscreenBlock(std::move(fullscreen_block)); + if (!blocker) { + return; + } + listener->SetFullscreenBlock(std::move(*blocker)); if (delegate_) { active_file_chooser_ = std::move(file_chooser); @@ -9723,7 +9747,9 @@ // Only drop fullscreen on the specific destination display, which is known. // This supports sites using cross-screen window management capabilities to // retain fullscreen and place a window on another screen. - ForSecurityDropFullscreen(display_id).RunAndReset(); + if (!ForSecurityDropFullscreen(display_id)) { + return; + } delegate_->SetContentsBounds(this, bounds); } @@ -10429,11 +10455,9 @@ OPTIONAL_TRACE_EVENT0("content", "WebContentsImpl::DidCallFocus"); // Any explicit focusing of another window while this WebContents is in // fullscreen can be used to confuse the user, so drop fullscreen. - base::ScopedClosureRunner fullscreen_block = - ForSecurityDropFullscreen(/*display_id=*/display::kInvalidDisplayId); - // The other contents is independent of this contents, so release the - // fullscreen block. - fullscreen_block.RunAndReset(); + if (!ForSecurityDropFullscreen(/*display_id=*/display::kInvalidDisplayId)) { + return; + } } void WebContentsImpl::OnAdvanceFocus(RenderFrameHostImpl* source_rfh) {
diff --git a/content/browser/web_contents/web_contents_impl.h b/content/browser/web_contents/web_contents_impl.h index c2e0baa7..248b578 100644 --- a/content/browser/web_contents/web_contents_impl.h +++ b/content/browser/web_contents/web_contents_impl.h
@@ -619,8 +619,8 @@ bool IsFullscreen() override; bool ShouldShowStaleContentOnEviction() override; void ExitFullscreen(bool will_cause_resize) override; - [[nodiscard]] base::ScopedClosureRunner ForSecurityDropFullscreen( - int64_t display_id) override; + [[nodiscard]] std::optional<base::ScopedClosureRunner> + ForSecurityDropFullscreen(int64_t display_id) override; void ResumeLoadingCreatedWebContents() override; void SetIsOverlayContent(bool is_overlay_content) override; bool IsFocusedElementEditable() override;
diff --git a/content/browser/web_contents/web_contents_impl_browsertest.cc b/content/browser/web_contents/web_contents_impl_browsertest.cc index fccff6d..a4749d3 100644 --- a/content/browser/web_contents/web_contents_impl_browsertest.cc +++ b/content/browser/web_contents/web_contents_impl_browsertest.cc
@@ -4018,9 +4018,9 @@ // While the |fullscreen_block| is in scope, fullscreen should fail with an // error. - base::ScopedClosureRunner fullscreen_block = - web_contents->ForSecurityDropFullscreen( - /*display_id=*/display::kInvalidDisplayId); + auto blocker = web_contents->ForSecurityDropFullscreen( + /*display_id=*/display::kInvalidDisplayId); + ASSERT_TRUE(blocker.has_value()); EXPECT_TRUE(ExecJs(main_frame, "document.body.requestFullscreen();", EXECUTE_SCRIPT_NO_RESOLVE_PROMISES));
diff --git a/content/browser/web_contents/web_contents_impl_unittest.cc b/content/browser/web_contents/web_contents_impl_unittest.cc index 6607b450..87f856fa 100644 --- a/content/browser/web_contents/web_contents_impl_unittest.cc +++ b/content/browser/web_contents/web_contents_impl_unittest.cc
@@ -42,6 +42,7 @@ #include "content/public/browser/javascript_dialog_manager.h" #include "content/public/browser/navigation_details.h" #include "content/public/browser/render_widget_host_view.h" +#include "content/public/browser/security_principal.h" #include "content/public/browser/ssl_host_state_delegate.h" #include "content/public/browser/storage_partition.h" #include "content/public/browser/web_contents_delegate.h" @@ -888,7 +889,10 @@ EXPECT_EQ(native_url, contents()->GetLastCommittedURL()); EXPECT_EQ(native_url, contents()->GetVisibleURL()); EXPECT_EQ(orig_instance, contents()->GetSiteInstance()); - EXPECT_EQ(GURL(), contents()->GetSiteInstance()->GetSiteURL()); + EXPECT_EQ(GURL(), contents() + ->GetSiteInstance() + ->GetSecurityPrincipal() + .GetDeprecatedSiteURL()); EXPECT_FALSE(orig_instance->HasSite()); // Navigate to new site (should keep same site instance, but might change @@ -929,8 +933,11 @@ EXPECT_EQ(orig_instance, contents()->GetSiteInstance()); if (AreStrictSiteInstancesEnabled()) { - EXPECT_TRUE( - contents()->GetSiteInstance()->GetSiteURL().DomainIs("google.com")); + EXPECT_TRUE(contents() + ->GetSiteInstance() + ->GetSecurityPrincipal() + .GetDeprecatedSiteURL() + .DomainIs("google.com")); } else { // Verify that the empty SiteInstance gets converted into a default // SiteInstance because |url| does not require a dedicated process. @@ -990,7 +997,10 @@ navigation->Commit(); EXPECT_EQ(orig_instance, contents()->GetSiteInstance()); - EXPECT_EQ(GURL(), contents()->GetSiteInstance()->GetSiteURL()); + EXPECT_EQ(GURL(), contents() + ->GetSiteInstance() + ->GetSecurityPrincipal() + .GetDeprecatedSiteURL()); EXPECT_FALSE(orig_instance->HasSite()); // Navigate to a regular site and verify that the SiteInstance was kept.
diff --git a/content/browser/web_contents/web_contents_view_android.cc b/content/browser/web_contents/web_contents_view_android.cc index 24b9d275..840f472 100644 --- a/content/browser/web_contents/web_contents_view_android.cc +++ b/content/browser/web_contents/web_contents_view_android.cc
@@ -591,10 +591,12 @@ const gfx::PointF& screen_location) { // Android does not pass a valid location for ACTION_DRAG_STARTED, so do not // try to find GetRenderWidgetHostAtPointAsynchronously(). - DragEnteredCallback(location, screen_location, - static_cast<RenderWidgetHostViewBase*>( - web_contents_->GetRenderWidgetHostView()) - ->GetWeakPtr()); + auto* rwhv = web_contents_->GetRenderWidgetHostView(); + if (rwhv) { + DragEnteredCallback( + location, screen_location, + static_cast<RenderWidgetHostViewBase*>(rwhv)->GetWeakPtr()); + } } void WebContentsViewAndroid::DragEnteredCallback( @@ -646,13 +648,14 @@ } } - web_contents_->GetRenderWidgetHostAtPointAsynchronously( - static_cast<RenderWidgetHostViewBase*>( - web_contents_->GetRenderWidgetHostView()), - location, - base::BindOnce(&WebContentsViewAndroid::DragUpdatedCallback, - weak_ptr_factory_.GetWeakPtr(), location, - screen_location)); + auto* rwhv = web_contents_->GetRenderWidgetHostView(); + if (rwhv) { + web_contents_->GetRenderWidgetHostAtPointAsynchronously( + static_cast<RenderWidgetHostViewBase*>(rwhv), location, + base::BindOnce(&WebContentsViewAndroid::DragUpdatedCallback, + weak_ptr_factory_.GetWeakPtr(), location, + screen_location)); + } } void WebContentsViewAndroid::DragUpdatedCallback( @@ -672,8 +675,11 @@ if (target_rwh != current_target_rwh_for_drag_.get()) { if (current_target_rwh_for_drag_) { gfx::PointF transformed_leave_point = location; - static_cast<RenderWidgetHostViewBase*>( - web_contents_->GetRenderWidgetHostView()) + auto* rwhv = web_contents_->GetRenderWidgetHostView(); + if (!rwhv) { + return; + } + static_cast<RenderWidgetHostViewBase*>(rwhv) ->TransformPointToCoordSpaceForView( location, static_cast<RenderWidgetHostViewBase*>( @@ -701,13 +707,14 @@ void WebContentsViewAndroid::OnPerformDrop(const gfx::PointF& location, const gfx::PointF& screen_location) { - web_contents_->GetRenderWidgetHostAtPointAsynchronously( - static_cast<RenderWidgetHostViewBase*>( - web_contents_->GetRenderWidgetHostView()), - location, - base::BindOnce(&WebContentsViewAndroid::PerformDropCallback, - weak_ptr_factory_.GetWeakPtr(), location, - screen_location)); + auto* rwhv = web_contents_->GetRenderWidgetHostView(); + if (rwhv) { + web_contents_->GetRenderWidgetHostAtPointAsynchronously( + static_cast<RenderWidgetHostViewBase*>(rwhv), location, + base::BindOnce(&WebContentsViewAndroid::PerformDropCallback, + weak_ptr_factory_.GetWeakPtr(), location, + screen_location)); + } } void WebContentsViewAndroid::PerformDropCallback( @@ -788,7 +795,10 @@ web_contents_->GetDelegate()->TakeFocus(web_contents_, reverse)) { return; } - web_contents_->GetRenderWidgetHostView()->Focus(); + auto* rwhv = web_contents_->GetRenderWidgetHostView(); + if (rwhv) { + rwhv->Focus(); + } } int WebContentsViewAndroid::GetTopControlsHeight() const {
diff --git a/content/browser/webui/web_ui_security_browsertest.cc b/content/browser/webui/web_ui_security_browsertest.cc index 183b38ef..bf2f27c 100644 --- a/content/browser/webui/web_ui_security_browsertest.cc +++ b/content/browser/webui/web_ui_security_browsertest.cc
@@ -16,6 +16,7 @@ #include "content/browser/web_contents/web_contents_impl.h" #include "content/browser/webui/web_ui_controller_factory_registry.h" #include "content/common/content_navigation_policy.h" +#include "content/public/browser/security_principal.h" #include "content/public/browser/site_isolation_policy.h" #include "content/public/browser/url_data_source.h" #include "content/public/browser/web_ui.h" @@ -238,9 +239,11 @@ EXPECT_EQ(subframe_url, observer.last_committed_url()); EXPECT_EQ(root->current_frame_host()->GetSiteInstance(), root->child_at(0)->current_frame_host()->GetSiteInstance()); - EXPECT_EQ( - GetWebUIURL("web-ui"), - root->child_at(0)->current_frame_host()->GetSiteInstance()->GetSiteURL()); + EXPECT_EQ(GetWebUIURL("web-ui"), root->child_at(0) + ->current_frame_host() + ->GetSiteInstance() + ->GetSecurityPrincipal() + .GetDeprecatedSiteURL()); // The subframe should have its own WebUI object different from the parent // frame. @@ -288,8 +291,10 @@ EXPECT_EQ(url::Origin::Create(child_frame_url), child->current_frame_host()->GetLastCommittedOrigin()); } - EXPECT_EQ(GetWebUIURL("web-ui-subframe"), - child->current_frame_host()->GetSiteInstance()->GetSiteURL()); + EXPECT_EQ(GetWebUIURL("web-ui-subframe"), child->current_frame_host() + ->GetSiteInstance() + ->GetSecurityPrincipal() + .GetDeprecatedSiteURL()); EXPECT_NE(root->current_frame_host()->GetSiteInstance(), child->current_frame_host()->GetSiteInstance()); EXPECT_NE(root->current_frame_host()->GetProcess(), @@ -540,7 +545,10 @@ EXPECT_TRUE(root->current_frame_host()->GetEnabledBindings().empty()); if (SiteIsolationPolicy::IsErrorPageIsolationEnabled(true)) { - EXPECT_EQ(root->current_frame_host()->GetSiteInstance()->GetSiteURL(), + EXPECT_EQ(root->current_frame_host() + ->GetSiteInstance() + ->GetSecurityPrincipal() + .GetDeprecatedSiteURL(), GURL(kUnreachableWebDataURL)); }
diff --git a/content/browser/xr/service/browser_xr_runtime_impl.cc b/content/browser/xr/service/browser_xr_runtime_impl.cc index 5ea6411c..c8b538a 100644 --- a/content/browser/xr/service/browser_xr_runtime_impl.cc +++ b/content/browser/xr/service/browser_xr_runtime_impl.cc
@@ -149,7 +149,7 @@ } if (install_finished_callback_) { - std::move(install_finished_callback_).Run(false); + std::move(install_finished_callback_).Run(XrInstallResult::kFailed); } } @@ -404,12 +404,12 @@ void BrowserXRRuntimeImpl::EnsureInstalled( const content::GlobalRenderFrameHostId& frame_id, - base::OnceCallback<void(bool)> install_callback) { + base::OnceCallback<void(XrInstallResult)> install_callback) { DVLOG(2) << __func__; // If there's no install helper, then we can assume no install is needed. if (!install_helper_) { - std::move(install_callback).Run(true); + std::move(install_callback).Run(XrInstallResult::kSuccessAlreadyInstalled); return; } @@ -417,7 +417,7 @@ bool had_outstanding_callback = false; if (install_finished_callback_) { had_outstanding_callback = true; - std::move(install_finished_callback_).Run(false); + std::move(install_finished_callback_).Run(XrInstallResult::kFailed); } install_finished_callback_ = std::move(install_callback); @@ -432,10 +432,10 @@ weak_ptr_factory_.GetWeakPtr())); } -void BrowserXRRuntimeImpl::OnInstallFinished(bool succeeded) { +void BrowserXRRuntimeImpl::OnInstallFinished(XrInstallResult result) { DCHECK(install_finished_callback_); - std::move(install_finished_callback_).Run(succeeded); + std::move(install_finished_callback_).Run(result); } void BrowserXRRuntimeImpl::OnImmersiveSessionError() {
diff --git a/content/browser/xr/service/browser_xr_runtime_impl.h b/content/browser/xr/service/browser_xr_runtime_impl.h index dd875896..84ccbda 100644 --- a/content/browser/xr/service/browser_xr_runtime_impl.h +++ b/content/browser/xr/service/browser_xr_runtime_impl.h
@@ -73,8 +73,9 @@ device::mojom::XRRuntimeSessionOptionsPtr options, device::mojom::XRRuntime::RequestSessionCallback callback); - void EnsureInstalled(const content::GlobalRenderFrameHostId& frame_id, - base::OnceCallback<void(bool)> install_callback); + void EnsureInstalled( + const content::GlobalRenderFrameHostId& frame_id, + base::OnceCallback<void(XrInstallResult)> install_callback); VRServiceImpl* GetServiceWithActiveImmersiveSession() { return presenting_service_; } @@ -112,7 +113,7 @@ RequestSessionCallback callback, device::mojom::XRRuntimeSessionResultPtr session_result); void OnImmersiveSessionError(); - void OnInstallFinished(bool succeeded); + void OnInstallFinished(XrInstallResult result); void ShutdownRuntime(); device::mojom::XRDeviceId id_; @@ -133,7 +134,7 @@ std::unique_ptr<XrInstallHelper> install_helper_; std::unique_ptr<BrowserXRRuntime::Observer> runtime_observer_; std::unique_ptr<VrUiHost> vr_ui_host_; - base::OnceCallback<void(bool)> install_finished_callback_; + base::OnceCallback<void(XrInstallResult)> install_finished_callback_; bool has_pending_immersive_session_request_ = false; base::WeakPtrFactory<BrowserXRRuntimeImpl> weak_ptr_factory_{this};
diff --git a/content/browser/xr/service/vr_service_impl.cc b/content/browser/xr/service/vr_service_impl.cc index f6f78161..fdeeeba 100644 --- a/content/browser/xr/service/vr_service_impl.cc +++ b/content/browser/xr/service/vr_service_impl.cc
@@ -8,6 +8,7 @@ #include <utility> #include <vector> +#include "base/containers/to_vector.h" #include "base/dcheck_is_on.h" #include "base/feature_list.h" #include "base/functional/bind.h" @@ -18,6 +19,7 @@ #include "base/trace_event/common/trace_event_common.h" #include "build/build_config.h" #include "components/viz/common/surfaces/frame_sink_id.h" +#include "content/browser/permissions/permission_util.h" #include "content/browser/web_contents/web_contents_impl.h" #include "content/browser/xr/metrics/session_metrics_helper.h" #include "content/browser/xr/service/browser_xr_runtime_impl.h" @@ -574,19 +576,57 @@ void VRServiceImpl::DoRequestPermissions( const std::vector<blink::PermissionType> request_permissions, - base::OnceCallback<void(const std::vector<PermissionResult>&)> - result_callback) { + base::OnceCallback<void(const std::vector<blink::mojom::PermissionStatus>&, + bool)> result_callback) { PermissionController* permission_controller = GetWebContents()->GetBrowserContext()->GetPermissionController(); CHECK(permission_controller); + std::vector<blink::mojom::PermissionStatus> current_statuses; + current_statuses.reserve(request_permissions.size()); + for (auto permission_type : request_permissions) { + auto descriptor = + PermissionDescriptorUtil::CreatePermissionDescriptorForPermissionType( + permission_type); + + blink::mojom::PermissionStatus status; + if (PermissionUtil::IsDevicePermission(descriptor)) { + status = permission_controller->GetCombinedPermissionAndDeviceStatus( + std::move(descriptor), render_frame_host_); + } else { + status = permission_controller->GetPermissionStatusForCurrentDocument( + std::move(descriptor), render_frame_host_); + } + current_statuses.push_back(status); + } + + bool needs_prompt = + std::ranges::any_of(current_statuses, [](const auto& status) { + return status == blink::mojom::PermissionStatus::ASK; + }); + + // If we don't need to prompt the user, just return the results now. + if (!needs_prompt) { + std::move(result_callback).Run(current_statuses, /*needs_prompt=*/false); + return; + } + permission_controller->RequestPermissionsFromCurrentDocument( render_frame_host_, PermissionRequestDescription( PermissionDescriptorUtil:: CreatePermissionDescriptorForPermissionTypes(request_permissions), /*user_gesture=*/true), - std::move(result_callback)); + base::BindOnce( + [](base::OnceCallback<void( + const std::vector<blink::mojom::PermissionStatus>&, bool)> + callback, + bool needs_prompt, const std::vector<PermissionResult>& results) { + std::move(callback).Run( + base::ToVector(results, &PermissionResult::status), + needs_prompt); + }, + std::move(result_callback), needs_prompt)); } void VRServiceImpl::GetPermissionStatus(SessionRequestData request, @@ -611,18 +651,24 @@ void VRServiceImpl::OnPermissionResultsForMode( SessionRequestData request, const std::vector<blink::PermissionType>& permissions, - const std::vector<PermissionResult>& results) { + const std::vector<blink::mojom::PermissionStatus>& results, + bool needs_prompt) { DVLOG(2) << __func__ << ": permissions.size()=" << permissions.size(); DCHECK_EQ(permissions.size(), results.size()); - // Prolong the user activation since the user may have taken long enough to - // answer the permission prompts that the transient user activation expired. - // This is fine to do here, since we enforce that the activation existed prior - // to requesting permissions. - DVLOG(3) << __func__ << ": prolonging user activation, current status=" - << render_frame_host_->HasTransientUserActivation(); - render_frame_host_->NotifyUserActivation( - blink::mojom::UserActivationNotificationType::kInteraction); + if (needs_prompt) { + // Prolong the user activation since the user may have taken long enough to + // answer the permission prompts that the transient user activation expired. + // This is fine to do here, since we enforce that the activation existed + // prior to requesting permissions. + DVLOG(3) << __func__ << ": prolonging user activation, current status=" + << render_frame_host_->HasTransientUserActivation(); + render_frame_host_->NotifyUserActivation( + blink::mojom::UserActivationNotificationType::kInteraction); + } else { + DVLOG(3) << __func__ + << ": NOT prolonging user activation (no prompt shown)"; + } const XrPermissionResults permission_results(permissions, results); @@ -646,7 +692,7 @@ weak_ptr_factory_.GetWeakPtr(), std::move(request), permissions_for_features); if (permissions_for_features.empty()) { - std::move(result_callback).Run({}); + std::move(result_callback).Run({}, /*needs_prompt=*/false); return; } @@ -656,7 +702,22 @@ void VRServiceImpl::OnPermissionResultsForFeatures( SessionRequestData request, const std::vector<blink::PermissionType>& permissions, - const std::vector<PermissionResult>& results) { + const std::vector<blink::mojom::PermissionStatus>& results, + bool needs_prompt) { + if (needs_prompt) { + // Prolong the user activation since the user may have taken long enough to + // answer the permission prompts that the transient user activation expired. + // This is fine to do here, since we enforce that the activation existed + // prior to requesting permissions. + DVLOG(3) << __func__ << ": prolonging user activation, current status=" + << render_frame_host_->HasTransientUserActivation(); + render_frame_host_->NotifyUserActivation( + blink::mojom::UserActivationNotificationType::kInteraction); + } else { + DVLOG(3) << __func__ + << ": NOT prolonging user activation (no prompt shown)"; + } + const XrPermissionResults permission_results(permissions, results); std::unordered_set<device::mojom::XRSessionFeature> rejected_features; @@ -736,24 +797,29 @@ } void VRServiceImpl::OnInstallResult(SessionRequestData request, - bool install_succeeded) { - DVLOG(2) << __func__ << ": install_succeeded=" << install_succeeded; + XrInstallResult result) { + DVLOG(2) << __func__ << ": result=" << std::to_underlying(result); - if (!install_succeeded) { + if (result == XrInstallResult::kFailed) { RejectSession(std::move(request.callback), request.options->trace_id, device::mojom::RequestSessionError::RUNTIME_INSTALL_FAILURE, "Runtime installation failed."); return; } - // Prolong the user activation since the user may have taken long enough to - // install the runtime that the transient user activation expired. This is - // fine to do here, since we enforce that the activation existed prior to - // kicking off installation. - DVLOG(3) << __func__ << ": prolonging user activation, current status=" - << render_frame_host_->HasTransientUserActivation(); - render_frame_host_->NotifyUserActivation( - blink::mojom::UserActivationNotificationType::kInteraction); + if (result == XrInstallResult::kSuccessInstalled) { + // Prolong the user activation since the user may have taken long enough to + // install the runtime that the transient user activation expired. This is + // fine to do here, since we enforce that the activation existed prior to + // kicking off installation. + DVLOG(3) << __func__ << ": prolonging user activation, current status=" + << render_frame_host_->HasTransientUserActivation(); + render_frame_host_->NotifyUserActivation( + blink::mojom::UserActivationNotificationType::kInteraction); + } else { + DVLOG(3) << __func__ + << ": NOT prolonging user activation (no install UI shown)"; + } DoRequestSession(std::move(request)); }
diff --git a/content/browser/xr/service/vr_service_impl.h b/content/browser/xr/service/vr_service_impl.h index fb224d3..04a89e2 100644 --- a/content/browser/xr/service/vr_service_impl.h +++ b/content/browser/xr/service/vr_service_impl.h
@@ -16,6 +16,7 @@ #include "content/common/content_export.h" #include "content/public/browser/permission_result.h" #include "content/public/browser/web_contents_observer.h" +#include "content/public/browser/xr_install_helper.h" #include "device/vr/public/mojom/isolated_xr_service.mojom-forward.h" #include "device/vr/public/mojom/vr_service.mojom.h" #include "device/vr/public/mojom/xr_device.mojom.h" @@ -144,7 +145,8 @@ void DoRequestPermissions( const std::vector<blink::PermissionType> request_permissions, - base::OnceCallback<void(const std::vector<PermissionResult>&)> + base::OnceCallback< + void(const std::vector<blink::mojom::PermissionStatus>&, bool)> result_callback); // The following steps are ordered in the general flow for "RequestSession" @@ -158,16 +160,18 @@ void OnPermissionResultsForMode( SessionRequestData request, const std::vector<blink::PermissionType>& permissions, - const std::vector<PermissionResult>& results); + const std::vector<blink::mojom::PermissionStatus>& results, + bool needs_prompt); void OnPermissionResultsForFeatures( SessionRequestData request, const std::vector<blink::PermissionType>& permissions, - const std::vector<PermissionResult>& results); + const std::vector<blink::mojom::PermissionStatus>& results, + bool needs_prompt); void EnsureRuntimeInstalled(SessionRequestData request, BrowserXRRuntimeImpl* runtime); - void OnInstallResult(SessionRequestData request_data, bool install_succeeded); + void OnInstallResult(SessionRequestData request_data, XrInstallResult result); void DoRequestSession(SessionRequestData request);
diff --git a/content/browser/xr/service/xr_permission_results.cc b/content/browser/xr/service/xr_permission_results.cc index bffe823..9c5c25c 100644 --- a/content/browser/xr/service/xr_permission_results.cc +++ b/content/browser/xr/service/xr_permission_results.cc
@@ -16,15 +16,15 @@ // on |permissions| and |permission_statuses|. Those 2 vectors must have equal // length, and the permission status for permission at `permissions[i]` is // assumed to be in `permission_statuses[i]`. -base::flat_map<blink::PermissionType, content::PermissionResult> +base::flat_map<blink::PermissionType, blink::mojom::PermissionStatus> CreatePermissionTypeToStatusMap( const std::vector<blink::PermissionType>& permissions, - const std::vector<content::PermissionResult>& permission_results) { - DCHECK_EQ(permissions.size(), permission_results.size()); + const std::vector<blink::mojom::PermissionStatus>& permission_statuses) { + DCHECK_EQ(permissions.size(), permission_statuses.size()); - base::flat_map<blink::PermissionType, content::PermissionResult> result; + base::flat_map<blink::PermissionType, blink::mojom::PermissionStatus> result; for (size_t i = 0; i < permissions.size(); ++i) { - result[permissions[i]] = content::PermissionResult(permission_results[i]); + result[permissions[i]] = permission_statuses[i]; } return result; } @@ -35,10 +35,10 @@ XrPermissionResults::XrPermissionResults( const std::vector<blink::PermissionType>& permission_types, - const std::vector<PermissionResult>& permission_results) + const std::vector<blink::mojom::PermissionStatus>& permission_statuses) : permission_type_to_status_( CreatePermissionTypeToStatusMap(permission_types, - permission_results)) {} + permission_statuses)) {} XrPermissionResults::~XrPermissionResults() = default; @@ -68,7 +68,7 @@ return false; } - return permission_type_to_status_.at(permission_type).status == + return permission_type_to_status_.at(permission_type) == blink::mojom::PermissionStatus::GRANTED; }
diff --git a/content/browser/xr/service/xr_permission_results.h b/content/browser/xr/service/xr_permission_results.h index ab933cc..b93cf0b 100644 --- a/content/browser/xr/service/xr_permission_results.h +++ b/content/browser/xr/service/xr_permission_results.h
@@ -23,7 +23,7 @@ public: XrPermissionResults( const std::vector<blink::PermissionType>& permission_types, - const std::vector<PermissionResult>& permission_results); + const std::vector<blink::mojom::PermissionStatus>& permission_statuses); ~XrPermissionResults(); // Checks if |permission_type_to_status| contains permissions necessary to @@ -41,7 +41,7 @@ device::mojom::XRSessionFeature feature); private: - const base::flat_map<blink::PermissionType, PermissionResult> + const base::flat_map<blink::PermissionType, blink::mojom::PermissionStatus> permission_type_to_status_; bool HasPermissionsFor(blink::PermissionType permission_type) const;
diff --git a/content/common/BUILD.gn b/content/common/BUILD.gn index 6294792..2fc5cf1 100644 --- a/content/common/BUILD.gn +++ b/content/common/BUILD.gn
@@ -69,8 +69,6 @@ "background_fetch/background_fetch_types.cc", "background_fetch/background_fetch_types.h", "color_parser.cc", - "common_param_traits.cc", - "common_param_traits.h", "content_constants_internal.h", "content_export.h", "content_navigation_policy.cc", @@ -534,8 +532,6 @@ ] traits_public_deps = [ - # NOTE: These dependencies are here to satisfy gn check because - # common_param_traits_macros.h include their headers. # Although the mojo bindings target is configured to allow direct circular # includes from //content/common and //content/public/common, this isn't a # transitive allowance, so those targets' own public_deps aren't included in
diff --git a/content/common/common_param_traits.cc b/content/common/common_param_traits.cc deleted file mode 100644 index ef954cc..0000000 --- a/content/common/common_param_traits.cc +++ /dev/null
@@ -1,20 +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. - -// Get basic type definitions. -#include "content/common/common_param_traits.h" - -// Generate param traits write methods. -#include "ipc/param_traits_write_macros.h" -namespace IPC { -#undef CONTENT_COMMON_COMMON_PARAM_TRAITS_MACROS_H_ -#include "content/common/common_param_traits_macros.h" -} // namespace IPC - -// Generate param traits read methods. -#include "ipc/param_traits_read_macros.h" -namespace IPC { -#undef CONTENT_COMMON_COMMON_PARAM_TRAITS_MACROS_H_ -#include "content/common/common_param_traits_macros.h" -} // namespace IPC
diff --git a/content/common/common_param_traits.h b/content/common/common_param_traits.h deleted file mode 100644 index 26e3af4..0000000 --- a/content/common/common_param_traits.h +++ /dev/null
@@ -1,13 +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 CONTENT_COMMON_COMMON_PARAM_TRAITS_H_ -#define CONTENT_COMMON_COMMON_PARAM_TRAITS_H_ - -// This file provides declarations of IPC serialization macros that are used -// in more than one IPC message file. - -#include "content/common/common_param_traits_macros.h" - -#endif // CONTENT_COMMON_COMMON_PARAM_TRAITS_H_
diff --git a/content/common/common_param_traits_macros.h b/content/common/common_param_traits_macros.h deleted file mode 100644 index be94718..0000000 --- a/content/common/common_param_traits_macros.h +++ /dev/null
@@ -1,32 +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. - -// Singly or Multiply-included shared traits file depending on circumstances. -// This allows the use of IPC serialization macros in more than one IPC message -// file. -#ifndef CONTENT_COMMON_COMMON_PARAM_TRAITS_MACROS_H_ -#define CONTENT_COMMON_COMMON_PARAM_TRAITS_MACROS_H_ - -#include "cc/trees/browser_controls_params.h" -#include "content/common/content_export.h" -#include "ipc/param_traits_macros.h" -#include "ipc/param_traits_utils.h" -#include "services/device/public/mojom/screen_orientation_lock_types.mojom-shared.h" -#include "third_party/blink/public/common/widget/device_emulation_params.h" -#include "third_party/blink/public/common/widget/visual_properties.h" - -#undef IPC_MESSAGE_EXPORT -#define IPC_MESSAGE_EXPORT CONTENT_EXPORT - -// Traits for VisualProperties. -IPC_ENUM_TRAITS_MAX_VALUE(blink::mojom::EmulatedScreenType, - blink::mojom::EmulatedScreenType::kMaxValue) - -IPC_ENUM_TRAITS_MAX_VALUE(device::mojom::ScreenOrientationLockType, - device::mojom::ScreenOrientationLockType::kMaxValue) - -IPC_ENUM_TRAITS_MAX_VALUE(display::mojom::ScreenOrientation, - display::mojom::ScreenOrientation::kMaxValue) - -#endif // CONTENT_COMMON_COMMON_PARAM_TRAITS_MACROS_H_
diff --git a/content/common/memory_coordinator/memory_consumer_registry.cc b/content/common/memory_coordinator/memory_consumer_registry.cc index ef4125ca..5361e78 100644 --- a/content/common/memory_coordinator/memory_consumer_registry.cc +++ b/content/common/memory_coordinator/memory_consumer_registry.cc
@@ -45,7 +45,7 @@ // Ensure the added consumer is up to date with the current memory limit // applied to this consumer group. if (memory_limit_ != base::MemoryConsumer::kDefaultMemoryLimit) { - consumer.UpdateMemoryLimit(memory_limit_); + consumer.UpdateMemoryLimitNoNotification(memory_limit_); } }
diff --git a/content/common/memory_coordinator/memory_consumer_registry_unittest.cc b/content/common/memory_coordinator/memory_consumer_registry_unittest.cc index 7f6e3c6..357486c 100644 --- a/content/common/memory_coordinator/memory_consumer_registry_unittest.cc +++ b/content/common/memory_coordinator/memory_consumer_registry_unittest.cc
@@ -133,8 +133,8 @@ entries().front().host->UpdateConsumers({{kConsumerId, kNewLimit, false}}); EXPECT_EQ(consumer1.memory_limit(), kNewLimit); - // New consumer should inherit limit - EXPECT_CALL(consumer2, OnUpdateMemoryLimit()); + // New consumer should inherit limit without calling OnUpdateMemoryLimit + EXPECT_CALL(consumer2, OnUpdateMemoryLimit()).Times(0); registry().AddMemoryConsumer(kConsumerName, kTestTraits1, &consumer2); EXPECT_EQ(consumer2.memory_limit(), kNewLimit);
diff --git a/content/common/memory_coordinator/memory_coordinator_policy_manager.cc b/content/common/memory_coordinator/memory_coordinator_policy_manager.cc index 236aca9..b482a69 100644 --- a/content/common/memory_coordinator/memory_coordinator_policy_manager.cc +++ b/content/common/memory_coordinator/memory_coordinator_policy_manager.cc
@@ -62,7 +62,26 @@ return new_limit; } +std::optional<int> +MemoryCoordinatorPolicyManager::GroupState::SetOverrideLimitForTesting( + std::optional<int> percentage) { + if (override_limit_ == percentage) { + return std::nullopt; + } + override_limit_ = percentage; + int new_limit = RecomputeMemoryLimit(); + if (new_limit == current_limit_) { + return std::nullopt; + } + current_limit_ = new_limit; + return new_limit; +} + int MemoryCoordinatorPolicyManager::GroupState::RecomputeMemoryLimit() const { + if (override_limit_) { + return *override_limit_; + } + // The aggregate limit is the product of all policy limits. // For example, if policy A requests 80% and policy B requests 50%, the // aggregate limit is 40% (0.8 * 0.5 = 0.4). @@ -196,6 +215,17 @@ std::make_unique<GroupState>(consumer_name, traits, process_type)); CHECK(inserted); + // Apply any pending override for this consumer that was set before + // registration. + auto it = memory_limit_overrides_.find(consumer_name); + if (it != memory_limit_overrides_.end()) { + auto& group_state = host_state.groups[consumer_id]; + if (std::optional<int> new_limit = + group_state->SetOverrideLimitForTesting(it->second)) { + host_state.host->UpdateConsumers({{consumer_id, *new_limit, false}}); + } + } + for (auto& observer : observers_) { observer.OnConsumerGroupAdded(consumer_id, consumer_name, traits, process_type, child_process_id); @@ -311,12 +341,18 @@ } } -void MemoryCoordinatorPolicyManager::NotifyReleaseMemoryForTesting() { - for (auto const& [_, host_state] : hosts_) { +void MemoryCoordinatorPolicyManager::ApplyMemoryLimitOverrideForTesting( + std::string_view consumer_name, + int percentage) { + for (auto const& [child_id, host_state] : hosts_) { std::vector<MemoryConsumerUpdate> updates; - updates.reserve(host_state->groups.size()); for (auto const& [consumer_id, group_state] : host_state->groups) { - updates.push_back({consumer_id, std::nullopt, true}); + if (group_state->consumer_name() == consumer_name) { + if (std::optional<int> new_limit = + group_state->SetOverrideLimitForTesting(percentage)) { + updates.push_back({consumer_id, *new_limit, false}); + } + } } if (!updates.empty()) { host_state->host->UpdateConsumers(std::move(updates)); @@ -324,15 +360,54 @@ } } -void MemoryCoordinatorPolicyManager::NotifyUpdateMemoryLimitForTesting( +void MemoryCoordinatorPolicyManager::AddMemoryLimitOverrideForTesting( + std::string_view consumer_name, int percentage) { - for (auto const& [_, host_state] : hosts_) { + auto [it, inserted] = + memory_limit_overrides_.try_emplace(consumer_name, percentage); + CHECK(inserted); + + ApplyMemoryLimitOverrideForTesting(consumer_name, percentage); +} + +void MemoryCoordinatorPolicyManager::UpdateMemoryLimitOverrideForTesting( + std::string_view consumer_name, + int percentage) { + auto it = memory_limit_overrides_.find(consumer_name); + CHECK(it != memory_limit_overrides_.end()); + it->second = percentage; + + ApplyMemoryLimitOverrideForTesting(consumer_name, percentage); +} + +void MemoryCoordinatorPolicyManager::ClearMemoryLimitOverrideForTesting( + std::string_view consumer_name) { + size_t removed = memory_limit_overrides_.erase(consumer_name); + CHECK_EQ(removed, 1u); + + for (auto const& [child_id, host_state] : hosts_) { std::vector<MemoryConsumerUpdate> updates; - updates.reserve(host_state->groups.size()); for (auto const& [consumer_id, group_state] : host_state->groups) { - if (percentage != group_state->current_limit()) { - group_state->SetCurrentLimitForTesting(percentage); - updates.push_back({consumer_id, percentage, false}); + if (group_state->consumer_name() == consumer_name) { + if (std::optional<int> new_limit = + group_state->SetOverrideLimitForTesting(std::nullopt)) { + updates.push_back({consumer_id, *new_limit, false}); + } + } + } + if (!updates.empty()) { + host_state->host->UpdateConsumers(std::move(updates)); + } + } +} + +void MemoryCoordinatorPolicyManager::NotifyReleaseMemoryForTesting( + std::string_view consumer_name) { + for (auto const& [child_id, host_state] : hosts_) { + std::vector<MemoryConsumerUpdate> updates; + for (auto const& [consumer_id, group_state] : host_state->groups) { + if (group_state->consumer_name() == consumer_name) { + updates.push_back({consumer_id, std::nullopt, true}); } } if (!updates.empty()) {
diff --git a/content/common/memory_coordinator/memory_coordinator_policy_manager.h b/content/common/memory_coordinator/memory_coordinator_policy_manager.h index 3186e79..c28006d 100644 --- a/content/common/memory_coordinator/memory_coordinator_policy_manager.h +++ b/content/common/memory_coordinator/memory_coordinator_policy_manager.h
@@ -138,9 +138,25 @@ std::optional<int> percentage, bool release_memory); - // For testing only. Notifies all registered consumer groups. - void NotifyReleaseMemoryForTesting(); - void NotifyUpdateMemoryLimitForTesting(int percentage); + // Testing utilities --------------------------------------------------------- + + // Adds a memory limit override for the consumer named `consumer_name`. + // This override takes precedence over any limits calculated by policies. + // Fails a CHECK if an override already exists for this consumer. + void AddMemoryLimitOverrideForTesting(std::string_view consumer_name, + int percentage); + + // Updates an existing memory limit override for the consumer named + // `consumer_name`. Fails a CHECK if an override does not exist for this + // consumer. + void UpdateMemoryLimitOverrideForTesting(std::string_view consumer_name, + int percentage); + + // Clears the memory limit override for the consumer named `consumer_name`. + void ClearMemoryLimitOverrideForTesting(std::string_view consumer_name); + + // Simulates a memory release request for the consumer named `consumer_name`. + void NotifyReleaseMemoryForTesting(std::string_view consumer_name); private: class GroupState { @@ -161,10 +177,12 @@ ProcessType process_type() const { return process_type_; } int current_limit() const { return current_limit_; } - void SetCurrentLimitForTesting(int limit) { current_limit_ = limit; } + // Sets a memory limit override for testing. Returns the new effective + // limit if it changed. + std::optional<int> SetOverrideLimitForTesting( + std::optional<int> percentage); private: - // Computes the memory limit based on existing policies. int RecomputeMemoryLimit() const; const std::string consumer_name_; @@ -176,6 +194,9 @@ // The last memory limit that was applied to this group. int current_limit_ = base::MemoryConsumer::kDefaultMemoryLimit; + + // The memory limit override set for testing. + std::optional<int> override_limit_; }; struct HostState { @@ -193,6 +214,11 @@ ChildProcessId child_process_id, std::vector<MemoryConsumerUpdate> updates); + // Applies the memory limit override to all registered consumers with the + // given name. + void ApplyMemoryLimitOverrideForTesting(std::string_view consumer_name, + int percentage); + base::ObserverList<Observer> observers_; #if BUILDFLAG(ENABLE_MEMORY_COORDINATOR_INTERNALS) @@ -202,6 +228,11 @@ base::flat_set<MemoryCoordinatorPolicy*> policies_; absl::flat_hash_map<ChildProcessId, std::unique_ptr<HostState>> hosts_; + + // Overrides for specific consumers. These take precedence over limits + // calculated by policies. + base::flat_map<std::string /* consumer_name */, int, std::less<>> + memory_limit_overrides_; }; } // namespace content
diff --git a/content/common/memory_coordinator/memory_coordinator_policy_manager_unittest.cc b/content/common/memory_coordinator/memory_coordinator_policy_manager_unittest.cc index 7273473..fd8d5af 100644 --- a/content/common/memory_coordinator/memory_coordinator_policy_manager_unittest.cc +++ b/content/common/memory_coordinator/memory_coordinator_policy_manager_unittest.cc
@@ -19,6 +19,7 @@ #include "content/common/buildflags.h" #include "content/common/memory_coordinator/memory_consumer_group_host.h" #include "content/common/memory_coordinator/memory_coordinator_policy.h" +#include "content/public/test/memory_coordinator_browsertest_util.h" #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" @@ -225,48 +226,86 @@ policy_manager().RemoveMemoryConsumerGroupHost(kChildId); } -TEST_F(MemoryCoordinatorPolicyManagerTest, TestHelpers) { - MockMemoryConsumerGroupHost host1; - MockMemoryConsumerGroupHost host2; +TEST_F(MemoryCoordinatorPolicyManagerTest, SetMemoryLimitOverride) { + MockMemoryConsumerGroupHost host; + const ChildProcessId kChildId(1); + policy_manager().AddMemoryConsumerGroupHost(kChildId, &host); - const char kConsumerName1[] = "consumer1"; - const uint32_t kConsumerId1 = base::PersistentHash(kConsumerName1); - const char kConsumerName2[] = "consumer2"; - const uint32_t kConsumerId2 = base::PersistentHash(kConsumerName2); - const ChildProcessId kChildId1(1); - const ChildProcessId kChildId2(2); + static constexpr char kConsumerName[] = "consumer"; + const uint32_t kConsumerId = base::PersistentHash(kConsumerName); - policy_manager().AddMemoryConsumerGroupHost(kChildId1, &host1); - policy_manager().AddMemoryConsumerGroupHost(kChildId2, &host2); - - policy_manager().OnConsumerGroupAdded(kConsumerId1, kConsumerName1, + policy_manager().OnConsumerGroupAdded(kConsumerId, kConsumerName, kTestTraits1, PROCESS_TYPE_RENDERER, - kChildId1); - policy_manager().OnConsumerGroupAdded(kConsumerId2, kConsumerName2, - kTestTraits1, PROCESS_TYPE_RENDERER, - kChildId2); + kChildId); - EXPECT_CALL(host1, UpdateConsumers(ElementsAre( - MemoryConsumerUpdate{kConsumerId1, 42, false}))); - EXPECT_CALL(host2, UpdateConsumers(ElementsAre( - MemoryConsumerUpdate{kConsumerId2, 42, false}))); - policy_manager().NotifyUpdateMemoryLimitForTesting(42); - Mock::VerifyAndClearExpectations(&host1); - Mock::VerifyAndClearExpectations(&host2); + // Add override. + EXPECT_CALL(host, UpdateConsumers(ElementsAre( + MemoryConsumerUpdate{kConsumerId, 42, false}))); + policy_manager().AddMemoryLimitOverrideForTesting(kConsumerName, 42); + Mock::VerifyAndClearExpectations(&host); - EXPECT_CALL(host1, UpdateConsumers(ElementsAre(MemoryConsumerUpdate{ - kConsumerId1, std::nullopt, true}))); - EXPECT_CALL(host2, UpdateConsumers(ElementsAre(MemoryConsumerUpdate{ - kConsumerId2, std::nullopt, true}))); - policy_manager().NotifyReleaseMemoryForTesting(); - Mock::VerifyAndClearExpectations(&host1); - Mock::VerifyAndClearExpectations(&host2); + // Update override. + EXPECT_CALL(host, UpdateConsumers(ElementsAre( + MemoryConsumerUpdate{kConsumerId, 24, false}))); + policy_manager().UpdateMemoryLimitOverrideForTesting(kConsumerName, 24); + Mock::VerifyAndClearExpectations(&host); + + // Clear override. Reverts to default (100%). + EXPECT_CALL(host, UpdateConsumers(ElementsAre( + MemoryConsumerUpdate{kConsumerId, 100, false}))); + policy_manager().ClearMemoryLimitOverrideForTesting(kConsumerName); + Mock::VerifyAndClearExpectations(&host); // Clean up. - policy_manager().OnConsumerGroupRemoved(kConsumerId1, kChildId1); - policy_manager().OnConsumerGroupRemoved(kConsumerId2, kChildId2); - policy_manager().RemoveMemoryConsumerGroupHost(kChildId1); - policy_manager().RemoveMemoryConsumerGroupHost(kChildId2); + policy_manager().OnConsumerGroupRemoved(kConsumerId, kChildId); + policy_manager().RemoveMemoryConsumerGroupHost(kChildId); +} + +TEST_F(MemoryCoordinatorPolicyManagerTest, SetMemoryLimitOverride_Persistence) { + MockMemoryConsumerGroupHost host; + const ChildProcessId kChildId(1); + policy_manager().AddMemoryConsumerGroupHost(kChildId, &host); + + static constexpr char kConsumerName[] = "consumer"; + const uint32_t kConsumerId = base::PersistentHash(kConsumerName); + + // Set override BEFORE adding consumer. + policy_manager().AddMemoryLimitOverrideForTesting(kConsumerName, 42); + + // Adding consumer should immediately apply override. + EXPECT_CALL(host, UpdateConsumers(ElementsAre( + MemoryConsumerUpdate{kConsumerId, 42, false}))); + policy_manager().OnConsumerGroupAdded(kConsumerId, kConsumerName, + kTestTraits1, PROCESS_TYPE_RENDERER, + kChildId); + Mock::VerifyAndClearExpectations(&host); + + // Clean up. + policy_manager().ClearMemoryLimitOverrideForTesting(kConsumerName); + policy_manager().OnConsumerGroupRemoved(kConsumerId, kChildId); + policy_manager().RemoveMemoryConsumerGroupHost(kChildId); +} + +TEST_F(MemoryCoordinatorPolicyManagerTest, NotifyReleaseMemory) { + MockMemoryConsumerGroupHost host; + const ChildProcessId kChildId(1); + policy_manager().AddMemoryConsumerGroupHost(kChildId, &host); + + static constexpr char kConsumerName[] = "consumer"; + const uint32_t kConsumerId = base::PersistentHash(kConsumerName); + + policy_manager().OnConsumerGroupAdded(kConsumerId, kConsumerName, + kTestTraits1, PROCESS_TYPE_RENDERER, + kChildId); + + EXPECT_CALL(host, UpdateConsumers(ElementsAre(MemoryConsumerUpdate{ + kConsumerId, std::nullopt, true}))); + policy_manager().NotifyReleaseMemoryForTesting(kConsumerName); + Mock::VerifyAndClearExpectations(&host); + + // Clean up. + policy_manager().OnConsumerGroupRemoved(kConsumerId, kChildId); + policy_manager().RemoveMemoryConsumerGroupHost(kChildId); } TEST_F(MemoryCoordinatorPolicyManagerTest, UpdateConsumers_MultipleProcesses) {
diff --git a/content/public/android/java/src/org/chromium/content/browser/selection/SelectionPopupControllerImpl.java b/content/public/android/java/src/org/chromium/content/browser/selection/SelectionPopupControllerImpl.java index 26da903..925dd5ef 100644 --- a/content/public/android/java/src/org/chromium/content/browser/selection/SelectionPopupControllerImpl.java +++ b/content/public/android/java/src/org/chromium/content/browser/selection/SelectionPopupControllerImpl.java
@@ -17,6 +17,7 @@ import android.graphics.Rect; import android.os.Build; import android.os.Handler; +import android.os.SystemClock; import android.text.TextUtils; import android.view.ActionMode; import android.view.HapticFeedbackConstants; @@ -199,6 +200,7 @@ private boolean mUnselectAllOnDismiss; private String mLastSelectedText; private int mLastSelectionOffset; + private long mShowMenuStartTimeMs; private boolean mIsInHandleDragging; // Tracks whether a touch selection is currently active. @@ -534,6 +536,7 @@ int sourceType, RenderFrameHost renderFrameHost, MenuModelBridge menuModelBridge) { + mShowMenuStartTimeMs = SystemClock.elapsedRealtime(); mMenuModelBridge = menuModelBridge; RecordHistogram.recordEnumeratedHistogram( "Android.ShowSelectionMenuSourceType", sourceType, MenuSourceType.MAX_VALUE); @@ -605,6 +608,16 @@ createAndShowDropdownMenu(); break; } + if (mShowMenuStartTimeMs > 0) { + long duration = SystemClock.elapsedRealtime() - mShowMenuStartTimeMs; + recordShowMenuTime(duration); + mShowMenuStartTimeMs = 0; + } + } + + private void recordShowMenuTime(long durationMs) { + RecordHistogram.recordCustomTimesHistogram( + "Android.SelectionMenu.TimeToShowMenu", durationMs, 1, 2000, 50); } /**
diff --git a/content/public/android/javatests/src/org/chromium/content/browser/accessibility/WebContentsAccessibilityTest.java b/content/public/android/javatests/src/org/chromium/content/browser/accessibility/WebContentsAccessibilityTest.java index 52f517c..6892e75 100644 --- a/content/public/android/javatests/src/org/chromium/content/browser/accessibility/WebContentsAccessibilityTest.java +++ b/content/public/android/javatests/src/org/chromium/content/browser/accessibility/WebContentsAccessibilityTest.java
@@ -117,6 +117,7 @@ import androidx.core.view.accessibility.AccessibilityNodeInfoCompat; import androidx.test.filters.LargeTest; +import androidx.test.filters.MediumTest; import androidx.test.filters.SmallTest; import com.google.common.truth.Expect; @@ -327,6 +328,10 @@ mActivityTestRule.focusNode(virtualViewId); } + private boolean isNodeAccessibilityFocused(int virtualViewId) { + return createAccessibilityNodeInfo(virtualViewId).isAccessibilityFocused(); + } + /** * Set focus to the node and wait until a selection event is received. * @@ -3822,10 +3827,58 @@ } /** - * Tests that TalkBack's Browse Mode table navigation (e.g., Ctrl + Alt + Arrows) - * correctly delegates focus to the interactive widget inside a gridcell. - * This ensures the inner link receives focus and can be activated, rather - * than focus getting trapped on the non-interactive cell wrapper. + * Focuses {@code fromVirtualViewId}. Performs an action and checks that focus does not change. + * + * @param fromVirtualViewId The view to focus initially. + * @param action The {@link AccessibilityActionCompat} action to perform. + * @param htmlElementString The {@link + * AccessibilityNodeInfo#ACTION_ARGUMENT_HTML_ELEMENT_STRING}. + */ + private void assertAccessibilityFocusDoesNotMove( + int fromVirtualViewId, + AccessibilityNodeInfoCompat.AccessibilityActionCompat action, + String htmlElementString) + throws Throwable { + mActivityTestRule.focusNode(fromVirtualViewId); + + Bundle bundle = new Bundle(); + bundle.putString(ACTION_ARGUMENT_HTML_ELEMENT_STRING, htmlElementString); + Assert.assertFalse(performActionOnUiThread(fromVirtualViewId, action, bundle)); + } + + /** + * Focuses {@code fromVirtualViewId}. Performs an action and checks that the expected view is + * focused. + * + * @param fromVirtualViewId The view to focus initially. + * @param action The {@link AccessibilityActionCompat} action to perform. + * @param htmlElementString The {@link + * AccessibilityNodeInfo#ACTION_ARGUMENT_HTML_ELEMENT_STRING}. + * @param expectedNewFocusedVirtualViewId The view which is expected to gain focus. + */ + private void assertAccessibilityFocusMoves( + int fromVirtualViewId, + AccessibilityNodeInfoCompat.AccessibilityActionCompat action, + String htmlElementString, + int expectedNewFocusedVirtualViewId) + throws Throwable { + mActivityTestRule.focusNode(fromVirtualViewId); + + Bundle bundle = new Bundle(); + bundle.putString(ACTION_ARGUMENT_HTML_ELEMENT_STRING, htmlElementString); + Assert.assertTrue( + performActionOnUiThread( + fromVirtualViewId, + action, + bundle, + () -> isNodeAccessibilityFocused(expectedNewFocusedVirtualViewId))); + } + + /** + * Tests that TalkBack's Browse Mode table navigation (e.g., Ctrl + Alt + Arrows) correctly + * delegates focus to the interactive widget inside a gridcell. This ensures the inner link + * receives focus and can be activated, rather than focus getting trapped on the non-interactive + * cell wrapper. */ @Test @SmallTest @@ -3842,38 +3895,202 @@ // Find the inner link nodes int vvid1 = waitForNodeMatching(sViewIdResourceNameMatcher, "link1"); int vvid2 = waitForNodeMatching(sViewIdResourceNameMatcher, "link2"); - AccessibilityNodeInfoCompat mNodeInfo1 = createAccessibilityNodeInfo(vvid1); - AccessibilityNodeInfoCompat mNodeInfo2 = createAccessibilityNodeInfo(vvid2); - Assert.assertNotNull(NODE_TIMEOUT_ERROR, mNodeInfo1); - Assert.assertNotNull(NODE_TIMEOUT_ERROR, mNodeInfo2); - - // Focus the first link to act as our starting point for navigation. - Assert.assertTrue( - performActionOnUiThread( - vvid1, - ACTION_ACCESSIBILITY_FOCUS, - null, - () -> createAccessibilityNodeInfo(vvid1).isAccessibilityFocused())); - - // Simulate table navigation - Bundle bundle = new Bundle(); - bundle.putString(ACTION_ARGUMENT_HTML_ELEMENT_STRING, "COLUMN"); + AccessibilityNodeInfoCompat nodeInfo1 = createAccessibilityNodeInfo(vvid1); + AccessibilityNodeInfoCompat nodeInfo2 = createAccessibilityNodeInfo(vvid2); + Assert.assertNotNull(NODE_TIMEOUT_ERROR, nodeInfo1); + Assert.assertNotNull(NODE_TIMEOUT_ERROR, nodeInfo2); // Assert that the action successfully delegates focus to the inner link of the next cell, // rather than the gridcell wrapper. - Assert.assertTrue( - performActionOnUiThread( - vvid1, - ACTION_NEXT_HTML_ELEMENT, - bundle, - () -> createAccessibilityNodeInfo(vvid2).isAccessibilityFocused())); + assertAccessibilityFocusMoves( + /* fromVirtualViewId= */ vvid1, + ACTION_NEXT_HTML_ELEMENT, + "COLUMN", + /* expectedNewFocusedVirtualViewId= */ vvid2); // Update nodes and verify results. mActivityTestRule.sendEndOfTestSignal(); - mNodeInfo1 = createAccessibilityNodeInfo(vvid1); - mNodeInfo2 = createAccessibilityNodeInfo(vvid2); - Assert.assertFalse(PERFORM_ACTION_ERROR, mNodeInfo1.isAccessibilityFocused()); - Assert.assertTrue(PERFORM_ACTION_ERROR, mNodeInfo2.isAccessibilityFocused()); + Assert.assertFalse(PERFORM_ACTION_ERROR, isNodeAccessibilityFocused(vvid1)); + Assert.assertTrue(PERFORM_ACTION_ERROR, isNodeAccessibilityFocused(vvid2)); + } + + @Test + @MediumTest + public void testPerformAction_nextHtmlElement_tableNavigation() throws Throwable { + setupTestWithHTML( + "<table role='table'>" + + " <tr>" + + " <td id='topleft'>topleft</td>" + + " <td id='topright'>topright</td>" + + " </tr>" + + " <tr>" + + " <td id='bottomleft' tabindex='0'>bottomleft</td>" + + " <td id='bottomright' tabindex='0'>bottomright</td>" + + " </tr>" + + "</table>"); + + int topLeftVirtualViewId = waitForNodeMatching(sViewIdResourceNameMatcher, "topleft"); + int topRightVirtualViewId = waitForNodeMatching(sViewIdResourceNameMatcher, "topright"); + int bottomLeftVirtualViewId = waitForNodeMatching(sViewIdResourceNameMatcher, "bottomleft"); + int bottomRightVirtualViewId = + waitForNodeMatching(sViewIdResourceNameMatcher, "bottomright"); + + // Check navigation where top-left cell initially has focus. + assertAccessibilityFocusMoves( + topLeftVirtualViewId, ACTION_NEXT_HTML_ELEMENT, "COLUMN", topRightVirtualViewId); + assertAccessibilityFocusMoves( + topLeftVirtualViewId, ACTION_NEXT_HTML_ELEMENT, "ROW", bottomLeftVirtualViewId); + + // Check navigation where bottom-right cell initially has focus. + assertAccessibilityFocusDoesNotMove( + bottomRightVirtualViewId, ACTION_NEXT_HTML_ELEMENT, "COLUMN"); + assertAccessibilityFocusDoesNotMove( + bottomRightVirtualViewId, ACTION_NEXT_HTML_ELEMENT, "ROW"); + } + + @Test + @MediumTest + public void testPerformAction_previousHtmlElement_tableNavigation() throws Throwable { + setupTestWithHTML( + "<table role='table'>" + + " <tr>" + + " <td id='topleft'>topleft</td>" + + " <td id='topright'>topright</td>" + + " </tr>" + + " <tr>" + + " <td id='bottomleft'>bottomleft</td>" + + " <td id='bottomright'>bottomright</td>" + + " </tr>" + + "</table>"); + + int topLeftVirtualViewId = waitForNodeMatching(sViewIdResourceNameMatcher, "topleft"); + int topRightVirtualViewId = waitForNodeMatching(sViewIdResourceNameMatcher, "topright"); + int bottomLeftVirtualViewId = waitForNodeMatching(sViewIdResourceNameMatcher, "bottomleft"); + int bottomRightVirtualViewId = + waitForNodeMatching(sViewIdResourceNameMatcher, "bottomright"); + + // Check navigation where top-left cell initially has focus. + assertAccessibilityFocusDoesNotMove( + topLeftVirtualViewId, ACTION_PREVIOUS_HTML_ELEMENT, "COLUMN"); + assertAccessibilityFocusDoesNotMove( + topLeftVirtualViewId, ACTION_PREVIOUS_HTML_ELEMENT, "ROW"); + + // Check navigation where bottom-right cell initially has focus. + assertAccessibilityFocusMoves( + bottomRightVirtualViewId, + ACTION_PREVIOUS_HTML_ELEMENT, + "COLUMN", + bottomLeftVirtualViewId); + assertAccessibilityFocusMoves( + bottomRightVirtualViewId, + ACTION_PREVIOUS_HTML_ELEMENT, + "ROW", + topRightVirtualViewId); + } + + /** Tests that navigating to the next row/column takes the column span into account. */ + @Test + @MediumTest + public void testPerformAction_nextHtmlElement_tableNavigationWithColspan() throws Throwable { + // Build table with cell which spans multiple columns. + setupTestWithHTML( + "<table role='table'>" + + " <tr>" + + " <td colspan=2 id='topleft'>topleft</td>" + + " <td colspan=2 id='topright'>topright</td>" + + " </tr>" + + " <tr>" + + " <td colspan=2 id='bottomleft'>bottomleft</td>" + + " <td id='bottomcenter'>bottomcenter</td>" + + " <td id='bottomright'>bottomright</td>" + + " </tr>" + + "</table>"); + + int topLeftVirtualViewId = waitForNodeMatching(sViewIdResourceNameMatcher, "topleft"); + int topRightVirtualViewId = waitForNodeMatching(sViewIdResourceNameMatcher, "topright"); + int bottomLeftVirtualViewId = waitForNodeMatching(sViewIdResourceNameMatcher, "bottomleft"); + int bottomCenterVirtualViewId = + waitForNodeMatching(sViewIdResourceNameMatcher, "bottomcenter"); + + assertAccessibilityFocusMoves( + topLeftVirtualViewId, ACTION_NEXT_HTML_ELEMENT, "COLUMN", topRightVirtualViewId); + + assertAccessibilityFocusDoesNotMove( + topRightVirtualViewId, ACTION_NEXT_HTML_ELEMENT, "COLUMN"); + assertAccessibilityFocusMoves( + topRightVirtualViewId, ACTION_NEXT_HTML_ELEMENT, "ROW", bottomCenterVirtualViewId); + + assertAccessibilityFocusMoves( + bottomLeftVirtualViewId, + ACTION_NEXT_HTML_ELEMENT, + "COLUMN", + bottomCenterVirtualViewId); + } + + /** Tests that navigating to the previous row/column takes the column span into account. */ + @Test + @MediumTest + public void testPerformAction_previousHtmlElement_tableNavigationWithColspan() + throws Throwable { + // Build table with cell which spans multiple columns. + setupTestWithHTML( + "<table role='table'>" + + " <tr>" + + " <td colspan=2 id='topleft'>topleft</td>" + + " <td colspan=2 id='topright'>topright</td>" + + " </tr>" + + " <tr>" + + " <td colspan=2 id='bottomleft'>bottomleft</td>" + + " <td id='bottomcenter'>bottomcenter</td>" + + " <td id='bottomright'>bottomright</td>" + + " </tr>" + + "</table>"); + + int topLeftVirtualViewId = waitForNodeMatching(sViewIdResourceNameMatcher, "topleft"); + int topRightVirtualViewId = waitForNodeMatching(sViewIdResourceNameMatcher, "topright"); + int bottomRightVirtualViewId = + waitForNodeMatching(sViewIdResourceNameMatcher, "bottomright"); + + assertAccessibilityFocusMoves( + topRightVirtualViewId, + ACTION_PREVIOUS_HTML_ELEMENT, + "COLUMN", + topLeftVirtualViewId); + + assertAccessibilityFocusMoves( + bottomRightVirtualViewId, + ACTION_PREVIOUS_HTML_ELEMENT, + "ROW", + topRightVirtualViewId); + } + + /** Tests that navigating to the next row/column takes the row span into account. */ + @Test + @MediumTest + public void testPerformAction_nextHtmlElement_tableNavigationWithRowspan() throws Throwable { + // Build table with cell which spans 2 rows. + setupTestWithHTML( + "<table role='table'>" + + " <tr>" + + " <td id='topleft'>topleft</td>" + + " <td rowspan=2 id='right'>right</td>" + + " </tr>" + + " <tr>" + + " <td id='bottomleft'>bottomleft</td>" + + " </tr>" + + "</table>"); + + int topLeftVirtualViewId = waitForNodeMatching(sViewIdResourceNameMatcher, "topleft"); + int bottomLeftVirtualViewId = waitForNodeMatching(sViewIdResourceNameMatcher, "bottomleft"); + int rightVirtualViewId = waitForNodeMatching(sViewIdResourceNameMatcher, "right"); + + assertAccessibilityFocusMoves( + topLeftVirtualViewId, ACTION_NEXT_HTML_ELEMENT, "COLUMN", rightVirtualViewId); + + assertAccessibilityFocusMoves( + bottomLeftVirtualViewId, ACTION_NEXT_HTML_ELEMENT, "COLUMN", rightVirtualViewId); + + assertAccessibilityFocusDoesNotMove(rightVirtualViewId, ACTION_NEXT_HTML_ELEMENT, "ROW"); } // ------------------ Misc tests that cannot be done as tree/event tests ------------------ //
diff --git a/content/public/android/junit/src/org/chromium/content/browser/selection/SelectionPopupControllerTest.java b/content/public/android/junit/src/org/chromium/content/browser/selection/SelectionPopupControllerTest.java index fab75909..56256dc 100644 --- a/content/public/android/junit/src/org/chromium/content/browser/selection/SelectionPopupControllerTest.java +++ b/content/public/android/junit/src/org/chromium/content/browser/selection/SelectionPopupControllerTest.java
@@ -64,6 +64,7 @@ import org.chromium.base.test.BaseRobolectricTestRunner; import org.chromium.base.test.util.Feature; import org.chromium.base.test.util.Features; +import org.chromium.base.test.util.HistogramWatcher; import org.chromium.build.annotations.Nullable; import org.chromium.content.browser.GestureListenerManagerImpl; import org.chromium.content.browser.PopupController; @@ -1179,6 +1180,38 @@ return resolveInfo; } + @Test + @Feature({"TextInput"}) + public void testShowSelectionMenuLatencyMetric() { + HistogramWatcher histogramWatcher = + HistogramWatcher.newSingleRecordWatcher("Android.SelectionMenu.TimeToShowMenu"); + + showSelectionMenu( + mController, + AMPHITHEATRE_FULL, + /* selectionStartOffset= */ 0, + MenuSourceType.LONG_PRESS); + + histogramWatcher.assertExpected(); + } + + @Test + @Feature({"TextInput"}) + public void testShowSelectionMenuLatencyMetricTablet() { + setDropdownMenuFeatureEnabled(true); + HistogramWatcher histogramWatcher = + HistogramWatcher.newSingleRecordWatcher("Android.SelectionMenu.TimeToShowMenu"); + + // MOUSE source type triggers dropdown which uses tablet path if enabled + showSelectionMenu( + mController, + AMPHITHEATRE_FULL, + /* selectionStartOffset= */ 0, + MenuSourceType.MOUSE); + + histogramWatcher.assertExpected(); + } + // Result generated by long press "Amphitheatre" in "1600 Amphitheatre Parkway". private SelectionClient.Result resultForAmphitheatre() { SelectionClient.Result result = new SelectionClient.Result();
diff --git a/content/public/browser/security_principal.h b/content/public/browser/security_principal.h index f093c77..d750dff 100644 --- a/content/public/browser/security_principal.h +++ b/content/public/browser/security_principal.h
@@ -5,7 +5,10 @@ #ifndef CONTENT_PUBLIC_BROWSER_SECURITY_PRINCIPAL_H_ #define CONTENT_PUBLIC_BROWSER_SECURITY_PRINCIPAL_H_ +#include <string_view> + #include "content/common/content_export.h" +#include "url/gurl.h" namespace content { @@ -63,6 +66,46 @@ // chrome-extension://<extension-id>/, so SchemeIs("chrome-extension") // returns true and SchemeIs("https") returns false. virtual bool SchemeIs(std::string_view scheme) const = 0; + + // Returns the site URL associated with all of the documents and workers in + // this principal, as described above. + // + // Compared to the content-internal AgentClusterKey, this URL might have been + // overridden from the actual URL of the content in cases that involve + // effective URLs such as hosted apps. The AgentClusterKey is always computed + // with the real URL, as it is a web spec concept and effective URLs are not + // part of the spec. + // + // NOTE: In most cases, code should be performing checks against the origin + // returned by |RenderFrameHost::GetLastCommittedOrigin()|. In contrast, the + // GURL returned by |GetDeprecatedSiteURL()| should not be considered + // authoritative because: + // - A SiteInstance can host pages from multiple sites if site isolation + // is not enabled (e.g., on Android) and the SiteInstance isn't hosting + // pages that require process isolation (e.g. WebUI or extensions). + // - With site isolation but not origin isolation, the site URL is not an + // origin: while often derived from the origin, it only contains the scheme + // and the eTLD + 1, i.e. an origin with the host + // "deeply.nested.subdomain.example.com" corresponds to a site URL with the + // host "example.com". + // - When origin isolation is in use, there may be multiple SiteInstances + // with the same GetDeprecatedSiteURL() but that differ in other + // SecurityPrincipal properties, such as whether the principal is for PDF + // or sandboxed content. + // + // DEPRECATED: Prefer RenderFrameHost::GetLastCommittedOrigin() or more + // specific SecurityPrincipal methods (e.g., SchemeIs()) over inspecting the + // site URL directly. This method is provided for callers that have not yet + // been migrated away from the site URL. + // Cases like hosted apps should keep using the site URL for now for + // resolving effective URLs. We expect no new use cases of effective URLs to + // arise, and for most use cases of effective URLs to go away over time + // (for example, hosted apps are deprecated and will be removed). + // More SecurityPrincipal properties will be added over time, which should + // cover more Site URL use cases. If there's a property that's missing on + // SecurityPrincipal and that's needed for a new feature, reach out to + // //content/OWNERS to discuss how to add it. + virtual const GURL& GetDeprecatedSiteURL() const = 0; }; } // namespace content
diff --git a/content/public/browser/site_instance.h b/content/public/browser/site_instance.h index 0c680a1..2ce0203 100644 --- a/content/public/browser/site_instance.h +++ b/content/public/browser/site_instance.h
@@ -169,22 +169,6 @@ // this SiteInstance. virtual const SecurityPrincipal& GetSecurityPrincipal() const = 0; - // Get the web site that this SiteInstance is rendering pages for. This - // includes the scheme and registered domain, but not the port. - // - // NOTE: In most cases, code should be performing checks against the origin - // returned by |RenderFrameHost::GetLastCommittedOrigin()|. In contrast, the - // GURL returned by |GetSiteURL()| should not be considered authoritative - // because: - // - a SiteInstance can host pages from multiple sites if "site per process" - // is not enabled and the SiteInstance isn't hosting pages that require - // process isolation (e.g. WebUI or extensions) - // - even with site per process, the site URL is not an origin: while often - // derived from the origin, it only contains the scheme and the eTLD + 1, - // i.e. an origin with the host "deeply.nested.subdomain.example.com" - // corresponds to a site URL with the host "example.com". - virtual const GURL& GetSiteURL() const = 0; - // Gets a SiteInstance for the given URL that shares the current // BrowsingInstance, creating a new SiteInstance if necessary. This ensures // that a BrowsingInstance only has one SiteInstance per site, so that pages
diff --git a/content/public/browser/web_contents.h b/content/public/browser/web_contents.h index e69bf31..1cc8f74 100644 --- a/content/public/browser/web_contents.h +++ b/content/public/browser/web_contents.h
@@ -179,6 +179,7 @@ public: using UniqueToken = base::TokenType<class WebContentsTokenTag>; + struct CONTENT_EXPORT CreateParams { explicit CreateParams( BrowserContext* context, @@ -1485,21 +1486,27 @@ // confusion if taken while in fullscreen. If this WebContents or any outer // WebContents is in fullscreen, drop it. // - // Returns a ScopedClosureRunner, and for the lifetime of that closure, this - // (and other related) WebContentses will not enter fullscreen. If the action - // should cause a one-time dropping of fullscreen (e.g. a UI element not - // attached to the WebContents), invoke RunAndReset() on the returned - // base::ScopedClosureRunner to release the fullscreen block immediately. - // Otherwise, if the action should cause fullscreen to be prohibited for a - // span of time (e.g. a UI element attached to the WebContents), keep the - // closure alive for that duration. + // Returns a ScopedClosureRunner (wrapped in std::optional), and for the + // lifetime of that closure, this (and other related) WebContentses will not + // enter fullscreen. If the action should cause a one-time dropping of + // fullscreen (e.g. a UI element not attached to the WebContents), the block + // can be released immediately by letting the returned optional go out of + // scope (e.g. `if (!ForSecurityDropFullscreen(...)) return;`), which + // automatically runs the closure upon temporary destruction. Alternatively, + // invoke RunAndReset() on the returned runner. Otherwise, if the action + // should cause fullscreen to be prohibited for a span of time (e.g. a UI + // element attached to the WebContents), keep the closure alive for that + // duration. + // + // If `this` WebContents is destroyed during the call (e.g. if exiting + // fullscreen triggers destruction), returns `std::nullopt`. // // If |display_id| is valid, only WebContentses on that specific screen will // exit fullscreen; the scoped prohibition will still apply to all displays. // This supports sites using cross-screen window placement capabilities to // retain fullscreen and open or place a window on another screen. - [[nodiscard]] virtual base::ScopedClosureRunner ForSecurityDropFullscreen( - int64_t display_id) = 0; + [[nodiscard]] virtual std::optional<base::ScopedClosureRunner> + ForSecurityDropFullscreen(int64_t display_id) = 0; // Unblocks requests from renderer for a newly created window. This is // used in showCreatedWindow() or sometimes later in cases where
diff --git a/content/public/browser/xr_install_helper.h b/content/public/browser/xr_install_helper.h index 5633f6a..6c9130134 100644 --- a/content/public/browser/xr_install_helper.h +++ b/content/public/browser/xr_install_helper.h
@@ -12,6 +12,13 @@ struct GlobalRenderFrameHostId; +// The result of a WebXR runtime installation check. +enum class XrInstallResult { + kSuccessAlreadyInstalled, + kSuccessInstalled, + kFailed, +}; + // Interface class to provide the opportunity for runtimes to ensure that any // necessary installation steps that need to occur from within the browser // process are kicked off. This is acquired via the |XrInstallHelperFactory|. @@ -33,7 +40,7 @@ // successfully installed (or verified to already be installed). virtual void EnsureInstalled( const content::GlobalRenderFrameHostId& frame_id, - base::OnceCallback<void(bool installed)> install_callback) = 0; + base::OnceCallback<void(XrInstallResult)> install_callback) = 0; protected: XrInstallHelper() = default;
diff --git a/content/public/test/memory_coordinator_browsertest_util.cc b/content/public/test/memory_coordinator_browsertest_util.cc index b867463..666467b 100644 --- a/content/public/test/memory_coordinator_browsertest_util.cc +++ b/content/public/test/memory_coordinator_browsertest_util.cc
@@ -5,15 +5,44 @@ #include "content/public/test/memory_coordinator_browsertest_util.h" #include "content/browser/memory_coordinator/browser_memory_coordinator.h" +#include "content/common/memory_coordinator/memory_coordinator_policy_manager.h" namespace content::test { -void NotifyReleaseMemory() { - BrowserMemoryCoordinator::Get().NotifyReleaseMemoryForTesting(); +ScopedMemoryLimitOverride::ScopedMemoryLimitOverride( + std::string_view consumer_name) + : consumer_name_(consumer_name) {} + +ScopedMemoryLimitOverride::~ScopedMemoryLimitOverride() { + ClearLimit(); } -void NotifyUpdateMemoryLimit(int percentage) { - BrowserMemoryCoordinator::Get().NotifyUpdateMemoryLimitForTesting(percentage); +void ScopedMemoryLimitOverride::SetLimit(int percentage) { + if (!limit_.has_value()) { + BrowserMemoryCoordinator::Get() + .policy_manager_for_testing() + .AddMemoryLimitOverrideForTesting(consumer_name_, percentage); + } else { + BrowserMemoryCoordinator::Get() + .policy_manager_for_testing() + .UpdateMemoryLimitOverrideForTesting(consumer_name_, percentage); + } + limit_ = percentage; +} + +void ScopedMemoryLimitOverride::ClearLimit() { + if (limit_.has_value()) { + BrowserMemoryCoordinator::Get() + .policy_manager_for_testing() + .ClearMemoryLimitOverrideForTesting(consumer_name_); + limit_.reset(); + } +} + +void ScopedMemoryLimitOverride::NotifyReleaseMemory() { + BrowserMemoryCoordinator::Get() + .policy_manager_for_testing() + .NotifyReleaseMemoryForTesting(consumer_name_); } } // namespace content::test
diff --git a/content/public/test/memory_coordinator_browsertest_util.h b/content/public/test/memory_coordinator_browsertest_util.h index 26cff2d..1e8da7d 100644 --- a/content/public/test/memory_coordinator_browsertest_util.h +++ b/content/public/test/memory_coordinator_browsertest_util.h
@@ -5,18 +5,36 @@ #ifndef CONTENT_PUBLIC_TEST_MEMORY_COORDINATOR_BROWSERTEST_UTIL_H_ #define CONTENT_PUBLIC_TEST_MEMORY_COORDINATOR_BROWSERTEST_UTIL_H_ +#include <memory> +#include <optional> +#include <string> +#include <string_view> + +#include "content/common/content_export.h" + namespace content::test { -// Note: For both of these functions, all MemoryConsumers, including those -// registered in child processes, will be invoked. The call will be synchronous -// iff the MemoryConsumer is registered on the main thread of the browser -// process. In other cases, the call will be asynchronous. +// A scoped object to override the memory limit of a specific consumer for +// tests. This can be used by tests outside of content/ (e.g., in chrome/). +// Note: This class does not support nested overrides for the same consumer +// name. +class ScopedMemoryLimitOverride { + public: + explicit ScopedMemoryLimitOverride(std::string_view consumer_name); + ~ScopedMemoryLimitOverride(); -// Calls `ReleaseMemory()` on all registered MemoryConsumers. -void NotifyReleaseMemory(); + void SetLimit(int percentage); + void ClearLimit(); + void NotifyReleaseMemory(); -// Calls `UpdateMemoryLimit(percentage) on all registered MemoryConsumers. -void NotifyUpdateMemoryLimit(int percentage); + ScopedMemoryLimitOverride(const ScopedMemoryLimitOverride&) = delete; + ScopedMemoryLimitOverride& operator=(const ScopedMemoryLimitOverride&) = + delete; + + private: + const std::string consumer_name_; + std::optional<int> limit_; +}; } // namespace content::test
diff --git a/content/renderer/memory_coordinator/last_resort_gc_policy_unittest.cc b/content/renderer/memory_coordinator/last_resort_gc_policy_unittest.cc index 0383470..4c1cb83 100644 --- a/content/renderer/memory_coordinator/last_resort_gc_policy_unittest.cc +++ b/content/renderer/memory_coordinator/last_resort_gc_policy_unittest.cc
@@ -144,14 +144,13 @@ // Create the consumer AFTER the last resort GC event. base::MockMemoryConsumer consumer; - // It should immediately receive the limit that was set (0% limit) upon - // registration. - EXPECT_CALL(consumer, OnUpdateMemoryLimit()).WillOnce([&]() { - EXPECT_EQ(consumer.memory_limit(), 0); - }); + // It should inherit the limit that was set (0% limit) upon registration + // without OnUpdateMemoryLimit notification. + EXPECT_CALL(consumer, OnUpdateMemoryLimit()).Times(0); base::MemoryConsumerRegistration registration( "Consumer", kTraitsWithReleaseGC, &consumer); + EXPECT_EQ(consumer.memory_limit(), 0); } } // namespace content
diff --git a/content/shell/browser/shell_content_browser_client.cc b/content/shell/browser/shell_content_browser_client.cc index f0d82838..05cf993 100644 --- a/content/shell/browser/shell_content_browser_client.cc +++ b/content/shell/browser/shell_content_browser_client.cc
@@ -53,6 +53,7 @@ #include "content/public/browser/network_service_instance.h" #include "content/public/browser/page_navigator.h" #include "content/public/browser/render_process_host.h" +#include "content/public/browser/security_principal.h" #include "content/public/browser/storage_partition.h" #include "content/public/browser/web_contents.h" #include "content/public/browser/web_contents_view_delegate.h" @@ -217,9 +218,11 @@ mojo::PendingReceiver<media::mojom::MediaFoundationPreferences> receiver) { // Passing in a NullCallback since we don't have MediaFoundationServiceMonitor // in content. - MediaFoundationPreferencesImpl::Create( - frame_host->GetSiteInstance()->GetSiteURL(), base::NullCallback(), - std::move(receiver)); + MediaFoundationPreferencesImpl::Create(frame_host->GetSiteInstance() + ->GetSecurityPrincipal() + .GetDeprecatedSiteURL(), + base::NullCallback(), + std::move(receiver)); } #endif // BUILDFLAG(IS_WIN)
diff --git a/content/shell/renderer/memory_coordinator/memory_coordinator_test_impl.cc b/content/shell/renderer/memory_coordinator/memory_coordinator_test_impl.cc index 24a4180..ba38106 100644 --- a/content/shell/renderer/memory_coordinator/memory_coordinator_test_impl.cc +++ b/content/shell/renderer/memory_coordinator/memory_coordinator_test_impl.cc
@@ -49,6 +49,10 @@ registration_(name, traits, this) { client_.set_disconnect_handler(base::BindOnce( &TestMemoryConsumer::OnConnectionError, base::Unretained(this))); + // Ensure any already assigned memory limit is honored. + if (memory_limit() != base::MemoryConsumer::kDefaultMemoryLimit) { + OnUpdateMemoryLimit(); + } } MemoryCoordinatorTestImpl::TestMemoryConsumer::~TestMemoryConsumer() = default;
diff --git a/content/shell/renderer/memory_coordinator/memory_coordinator_test_impl.h b/content/shell/renderer/memory_coordinator/memory_coordinator_test_impl.h index f7e3c79..cf5015f6 100644 --- a/content/shell/renderer/memory_coordinator/memory_coordinator_test_impl.h +++ b/content/shell/renderer/memory_coordinator/memory_coordinator_test_impl.h
@@ -40,7 +40,7 @@ mojo::PendingRemote<mojom::MemoryCoordinatorTestClient> client) override; private: - class TestMemoryConsumer : public base::MemoryConsumer { + class TestMemoryConsumer final : public base::MemoryConsumer { public: TestMemoryConsumer( MemoryCoordinatorTestImpl* parent,
diff --git a/content/test/content_browser_test_utils_internal.cc b/content/test/content_browser_test_utils_internal.cc index 43c80261..cd226cfa 100644 --- a/content/test/content_browser_test_utils_internal.cc +++ b/content/test/content_browser_test_utils_internal.cc
@@ -39,6 +39,7 @@ #include "content/public/browser/file_select_listener.h" #include "content/public/browser/navigation_handle.h" #include "content/public/browser/render_frame_host.h" +#include "content/public/browser/security_principal.h" #include "content/public/browser/site_isolation_policy.h" #include "content/public/browser/web_contents.h" #include "content/public/test/browser_test_utils.h" @@ -419,7 +420,9 @@ SiteInstanceImpl* site_instance = static_cast<SiteInstanceImpl*>(legend_entry.second); std::string description = - GetUrlWithoutPort(site_instance->GetSiteURL()).spec(); + GetUrlWithoutPort( + site_instance->GetSecurityPrincipal().GetDeprecatedSiteURL()) + .spec(); // data: URLs have site URLs of the form data:nonce, where the nonce is an // UnguessableToken. Make these deterministic for testing by using the
diff --git a/content/test/data/accessibility/html/input-date-with-popup-open-expected-auralinux.txt b/content/test/data/accessibility/html/input-date-with-popup-open-expected-auralinux.txt index 4c31833..99a2e93 100644 --- a/content/test/data/accessibility/html/input-date-with-popup-open-expected-auralinux.txt +++ b/content/test/data/accessibility/html/input-date-with-popup-open-expected-auralinux.txt
@@ -14,7 +14,7 @@ ++++++++++[section] ++++++++++++[section] ++++++++++++++[section] -++++++++++++++++[push button] name='Show month selection panel' +++++++++++++++++[push button] name='September 2008' description='Show month selection panel' description:Show month selection panel description-from:aria-description ++++++++++++++++++[static] name='September 2008' ++++++++++++++++++[image] ++++++++++++++[push button] name='Show previous month' description='Show previous month' description-from:tooltip
diff --git a/content/test/data/accessibility/html/input-date-with-popup-open-expected-blink.txt b/content/test/data/accessibility/html/input-date-with-popup-open-expected-blink.txt index 9dfefc9..a8114a79 100644 --- a/content/test/data/accessibility/html/input-date-with-popup-open-expected-blink.txt +++ b/content/test/data/accessibility/html/input-date-with-popup-open-expected-blink.txt
@@ -26,7 +26,7 @@ ++++++++++++++++genericContainer ++++++++++++++++++genericContainer ignored ++++++++++++++++++++genericContainer -++++++++++++++++++++++button name='Show month selection panel' +++++++++++++++++++++++button description='Show month selection panel' descriptionFrom=ariaDescription ++++++++++++++++++++++++staticText ++++++++++++++++++++++++++inlineTextBox ++++++++++++++++++++++++image
diff --git a/content/test/data/accessibility/html/input-date-with-popup-open-expected-fuchsia.txt b/content/test/data/accessibility/html/input-date-with-popup-open-expected-fuchsia.txt index 218e2ac..c85a534 100644 --- a/content/test/data/accessibility/html/input-date-with-popup-open-expected-fuchsia.txt +++ b/content/test/data/accessibility/html/input-date-with-popup-open-expected-fuchsia.txt
@@ -26,7 +26,7 @@ ++++++++++++++++UNKNOWN ++++++++++++++++++UNKNOWN hidden ++++++++++++++++++++UNKNOWN -++++++++++++++++++++++BUTTON focusable label='Show month selection panel' actions='{DEFAULT}' +++++++++++++++++++++++BUTTON focusable label='September 2008' actions='{DEFAULT}' secondary_label='Show month selection panel' ++++++++++++++++++++++++STATIC_TEXT label='September 2008' ++++++++++++++++++++++++++UNKNOWN label='September 2008' ++++++++++++++++++++++++IMAGE
diff --git a/content/test/data/accessibility/html/input-date-with-popup-open-expected-mac.txt b/content/test/data/accessibility/html/input-date-with-popup-open-expected-mac.txt index f8b9489..0a27726e 100644 --- a/content/test/data/accessibility/html/input-date-with-popup-open-expected-mac.txt +++ b/content/test/data/accessibility/html/input-date-with-popup-open-expected-mac.txt
@@ -14,7 +14,7 @@ ++++++++++AXGroup AXRoleDescription='group' ++++++++++++AXGroup AXRoleDescription='group' ++++++++++++++AXGroup AXRoleDescription='group' -++++++++++++++++AXButton AXDescription='Show month selection panel' AXRoleDescription='button' +++++++++++++++++AXButton AXRoleDescription='button' AXTitle='September 2008' ++++++++++++++++++AXStaticText AXRoleDescription='text' AXValue='September 2008' ++++++++++++++++++AXImage AXRoleDescription='image' ++++++++++++++AXButton AXDescription='Show previous month' AXRoleDescription='button'
diff --git a/content/test/data/accessibility/html/input-date-with-popup-open-expected-uia-win.txt b/content/test/data/accessibility/html/input-date-with-popup-open-expected-uia-win.txt index d6dcbcac..569cb58c 100644 --- a/content/test/data/accessibility/html/input-date-with-popup-open-expected-uia-win.txt +++ b/content/test/data/accessibility/html/input-date-with-popup-open-expected-uia-win.txt
@@ -15,7 +15,7 @@ ++++++++++++Group IsControlElement=false ++++++++++++++Group IsControlElement=false ++++++++++++++++Group IsControlElement=false -++++++++++++++++++Button Name='Show month selection panel' +++++++++++++++++++Button Name='September 2008' ++++++++++++++++++++Text Name='September 2008' IsControlElement=false ++++++++++++++++++++Image ++++++++++++++++Button Name='Show previous month'
diff --git a/content/test/data/accessibility/html/input-date-with-popup-open-expected-win.txt b/content/test/data/accessibility/html/input-date-with-popup-open-expected-win.txt index d0a98cd5..664c820 100644 --- a/content/test/data/accessibility/html/input-date-with-popup-open-expected-win.txt +++ b/content/test/data/accessibility/html/input-date-with-popup-open-expected-win.txt
@@ -14,7 +14,7 @@ ++++++++++IA2_ROLE_SECTION ia2_hypertext='<obj0>' ++++++++++++IA2_ROLE_SECTION ia2_hypertext='<obj0><obj1><obj2><obj3>' ++++++++++++++IA2_ROLE_SECTION ia2_hypertext='<obj0>' -++++++++++++++++ROLE_SYSTEM_PUSHBUTTON name='Show month selection panel' FOCUSABLE +++++++++++++++++ROLE_SYSTEM_PUSHBUTTON FOCUSABLE description:Show month selection panel description-from:aria-description description='Show month selection panel' ++++++++++++++++++ROLE_SYSTEM_STATICTEXT ++++++++++++++++++ROLE_SYSTEM_GRAPHIC READONLY ++++++++++++++ROLE_SYSTEM_PUSHBUTTON name='Show previous month' FOCUSABLE description-from:tooltip ia2_hypertext='<obj0>' description='Show previous month'
diff --git a/content/test/data/accessibility/html/input-date-with-popup-open-multiple-expected-auralinux.txt b/content/test/data/accessibility/html/input-date-with-popup-open-multiple-expected-auralinux.txt index 1437168..5aa51ba 100644 --- a/content/test/data/accessibility/html/input-date-with-popup-open-multiple-expected-auralinux.txt +++ b/content/test/data/accessibility/html/input-date-with-popup-open-multiple-expected-auralinux.txt
@@ -32,7 +32,7 @@ ++++++++++[section] ++++++++++++[section] ++++++++++++++[section] -++++++++++++++++[push button] name='Show month selection panel' +++++++++++++++++[push button] name='September 2008' description='Show month selection panel' description:Show month selection panel description-from:aria-description ++++++++++++++++++[static] name='September 2008' ++++++++++++++++++[image] ++++++++++++++[push button] name='Show previous month' description='Show previous month' description-from:tooltip
diff --git a/content/test/data/accessibility/html/input-date-with-popup-open-multiple-expected-blink.txt b/content/test/data/accessibility/html/input-date-with-popup-open-multiple-expected-blink.txt index 66c7dcf..af1bec0 100644 --- a/content/test/data/accessibility/html/input-date-with-popup-open-multiple-expected-blink.txt +++ b/content/test/data/accessibility/html/input-date-with-popup-open-multiple-expected-blink.txt
@@ -62,9 +62,9 @@ ++++++++++++++++genericContainer ++++++++++++++++++genericContainer ignored ++++++++++++++++++++genericContainer -++++++++++++++++++++++button name='Show month selection panel' -++++++++++++++++++++++++staticText -++++++++++++++++++++++++++inlineTextBox +++++++++++++++++++++++button description='Show month selection panel' name='September 2008' descriptionFrom=ariaDescription +++++++++++++++++++++++++staticText name='September 2008' +++++++++++++++++++++++++++inlineTextBox name='September 2008' ++++++++++++++++++++++++image ++++++++++++++++++++button description='Show previous month' name='Show previous month' descriptionFrom=title ++++++++++++++++++++++image
diff --git a/content/test/data/accessibility/html/input-date-with-popup-open-multiple-expected-mac.txt b/content/test/data/accessibility/html/input-date-with-popup-open-multiple-expected-mac.txt index 535b25f..a361847 100644 --- a/content/test/data/accessibility/html/input-date-with-popup-open-multiple-expected-mac.txt +++ b/content/test/data/accessibility/html/input-date-with-popup-open-multiple-expected-mac.txt
@@ -32,7 +32,7 @@ ++++++++++AXGroup AXRoleDescription='group' ++++++++++++AXGroup AXRoleDescription='group' ++++++++++++++AXGroup AXRoleDescription='group' -++++++++++++++++AXButton AXDescription='Show month selection panel' AXRoleDescription='button' +++++++++++++++++AXButton AXRoleDescription='button' AXTitle='September 2008' ++++++++++++++++++AXStaticText AXRoleDescription='text' AXValue='September 2008' ++++++++++++++++++AXImage AXRoleDescription='image' ++++++++++++++AXButton AXDescription='Show previous month' AXRoleDescription='button'
diff --git a/content/test/data/accessibility/html/input-date-with-popup-open-multiple-for-win-expected-blink.txt b/content/test/data/accessibility/html/input-date-with-popup-open-multiple-for-win-expected-blink.txt index 477b3de..e6c1e5b 100644 --- a/content/test/data/accessibility/html/input-date-with-popup-open-multiple-for-win-expected-blink.txt +++ b/content/test/data/accessibility/html/input-date-with-popup-open-multiple-for-win-expected-blink.txt
@@ -62,9 +62,9 @@ ++++++++++++++++genericContainer ++++++++++++++++++genericContainer ignored ++++++++++++++++++++genericContainer -++++++++++++++++++++++button name='Show month selection panel' -++++++++++++++++++++++++staticText -++++++++++++++++++++++++++inlineTextBox +++++++++++++++++++++++button description='Show month selection panel' name='September 2008' descriptionFrom=ariaDescription +++++++++++++++++++++++++staticText name='September 2008' +++++++++++++++++++++++++++inlineTextBox name='September 2008' ++++++++++++++++++++++++image ++++++++++++++++++++button description='Show previous month' name='Show previous month' descriptionFrom=title ++++++++++++++++++++++image
diff --git a/content/test/data/accessibility/html/input-date-with-popup-open-multiple-for-win-expected-uia-win.txt b/content/test/data/accessibility/html/input-date-with-popup-open-multiple-for-win-expected-uia-win.txt index 45b7ba1..b1e9ded 100644 --- a/content/test/data/accessibility/html/input-date-with-popup-open-multiple-for-win-expected-uia-win.txt +++ b/content/test/data/accessibility/html/input-date-with-popup-open-multiple-for-win-expected-uia-win.txt
@@ -33,8 +33,8 @@ ++++++++++++Group IsControlElement=false ++++++++++++++Group IsControlElement=false ++++++++++++++++Group IsControlElement=false -++++++++++++++++++Button Name='Show month selection panel' -++++++++++++++++++++Text IsControlElement=false +++++++++++++++++++Button Name='September 2008' +++++++++++++++++++++Text Name='September 2008' IsControlElement=false ++++++++++++++++++++Image ++++++++++++++++Button Name='Show previous month' ++++++++++++++++++Image
diff --git a/content/test/data/accessibility/html/input-date-with-popup-open-multiple-for-win-expected-win.txt b/content/test/data/accessibility/html/input-date-with-popup-open-multiple-for-win-expected-win.txt index 4030f42d..ecf160d 100644 --- a/content/test/data/accessibility/html/input-date-with-popup-open-multiple-for-win-expected-win.txt +++ b/content/test/data/accessibility/html/input-date-with-popup-open-multiple-for-win-expected-win.txt
@@ -32,8 +32,8 @@ ++++++++++IA2_ROLE_SECTION ++++++++++++IA2_ROLE_SECTION ++++++++++++++IA2_ROLE_SECTION -++++++++++++++++ROLE_SYSTEM_PUSHBUTTON name='Show month selection panel' FOCUSABLE -++++++++++++++++++ROLE_SYSTEM_STATICTEXT +++++++++++++++++ROLE_SYSTEM_PUSHBUTTON name='September 2008' FOCUSABLE description:Show month selection panel description-from:aria-description description='Show month selection panel' +++++++++++++++++++ROLE_SYSTEM_STATICTEXT name='September 2008' ++++++++++++++++++ROLE_SYSTEM_GRAPHIC READONLY ++++++++++++++ROLE_SYSTEM_PUSHBUTTON name='Show previous month' FOCUSABLE description-from:tooltip description='Show previous month' ++++++++++++++++ROLE_SYSTEM_GRAPHIC READONLY
diff --git a/content/test/data/accessibility/html/input-date-with-popup-open-multiple-for-win.html b/content/test/data/accessibility/html/input-date-with-popup-open-multiple-for-win.html index 8663151..c14fb0c 100644 --- a/content/test/data/accessibility/html/input-date-with-popup-open-multiple-for-win.html +++ b/content/test/data/accessibility/html/input-date-with-popup-open-multiple-for-win.html
@@ -5,14 +5,6 @@ @UIA-WIN-ALLOW:LocalizedControlType='date picker' @DEFAULT-ACTION-ON:Show date picker,Third date picker @WAIT-FOR:Sunday - -Windows 7 formats dates differently than Windows 10 (September, 2008 in Windows -7 vs September 2008 in Windows 10), so skip the name attribute in this case -so this test can pass on both platforms. - -@BLINK-DENY:name='September* -@WIN-DENY:name='September* -@UIA-WIN-DENY:Name='September* --> <!DOCTYPE html> <html>
diff --git a/content/test/data/accessibility/html/input-date-with-popup-open-multiple.html b/content/test/data/accessibility/html/input-date-with-popup-open-multiple.html index 5403b94..c14fb0c 100644 --- a/content/test/data/accessibility/html/input-date-with-popup-open-multiple.html +++ b/content/test/data/accessibility/html/input-date-with-popup-open-multiple.html
@@ -5,15 +5,6 @@ @UIA-WIN-ALLOW:LocalizedControlType='date picker' @DEFAULT-ACTION-ON:Show date picker,Third date picker @WAIT-FOR:Sunday - -Windows 7 formats dates differently than Windows 10 (September, 2008 in Windows -7 vs September 2008 in Windows 10), so skip the name attribute in this case -so this test can pass on both platforms. - -@BLINK-DENY:name='September* -@WIN-DENY:name='September* -@UIA-WIN-DENY:Name='September* - --> <!DOCTYPE html> <html>
diff --git a/content/test/gpu/gpu_tests/test_expectations/webgl_conformance_expectations.txt b/content/test/gpu/gpu_tests/test_expectations/webgl_conformance_expectations.txt index b3fc46dd..f670c58 100644 --- a/content/test/gpu/gpu_tests/test_expectations/webgl_conformance_expectations.txt +++ b/content/test/gpu/gpu_tests/test_expectations/webgl_conformance_expectations.txt
@@ -487,29 +487,6 @@ crbug.com/844311 [ angle-opengl mac ] conformance/glsl/misc/fragcolor-fragdata-invariant.html [ Failure ] -## Mac Swangle ## -# These are failing very often -crbug.com/504989192 [ mac angle-swiftshader ] conformance/context/context-eviction-with-garbage-collection.html [ Failure ] -crbug.com/504989192 [ mac angle-swiftshader ] conformance/context/context-release-upon-reload.html [ Failure ] -crbug.com/504989192 [ mac angle-swiftshader ] conformance/context/premultiplyalpha-test.html [ Failure ] -crbug.com/504989192 [ mac angle-swiftshader ] conformance/rendering/framebuffer-switch.html [ Failure ] -crbug.com/504989192 [ mac angle-swiftshader ] conformance/state/gl-enable-enum-test.html [ Failure ] -crbug.com/504989192 [ mac angle-swiftshader ] conformance/textures/misc/exif-orientation.html [ Failure ] -crbug.com/504989192 [ mac angle-swiftshader ] conformance/textures/webgl_canvas/tex-2d-rgb-rgb-unsigned_short_5_6_5.html [ Failure ] -crbug.com/504989192 [ mac angle-swiftshader ] conformance/textures/webgl_canvas/tex-2d-rgba-rgba-unsigned_byte.html [ Failure ] -crbug.com/504989192 [ mac angle-swiftshader ] conformance/textures/webgl_canvas/tex-2d-rgba-rgba-unsigned_short_4_4_4_4.html [ Failure ] -crbug.com/504989192 [ mac angle-swiftshader ] conformance/uniforms/gl-get-uniform-location-errors.html [ Failure ] -crbug.com/504989192 [ mac angle-swiftshader ] conformance/uniforms/gl-uniform-arrays.html [ Failure ] -crbug.com/504989192 [ mac angle-swiftshader ] conformance/uniforms/gl-uniform-bool.html [ Failure ] -crbug.com/504989192 [ mac angle-swiftshader ] conformance/context/context-type-test.html [ Failure ] -# These are failing less often -crbug.com/504989192 [ mac angle-swiftshader ] conformance/rendering/draw-arrays-out-of-bounds.html [ RetryOnFailure ] -crbug.com/504989192 [ mac angle-swiftshader ] conformance/textures/webgl_canvas/tex-2d-rgba-rgba-unsigned_short_5_5_5_1.html [ RetryOnFailure ] -crbug.com/504989192 [ mac angle-swiftshader ] conformance/typedarrays/array-large-array-tests.html [ RetryOnFailure ] -crbug.com/504989192 [ mac angle-swiftshader ] conformance/typedarrays/data-view-crash.html [ RetryOnFailure ] -crbug.com/504989192 [ mac angle-swiftshader ] conformance/typedarrays/data-view-test.html [ RetryOnFailure ] -crbug.com/504989192 [ mac angle-swiftshader ] conformance/typedarrays/typed-arrays-in-workers.html [ RetryOnFailure ] - ## Mac AMD failures ## # TODO(kbr): uncomment the following expectation after test has @@ -544,6 +521,8 @@ ## Linux / OpenGL / GTX 1660 ## crbug.com/498282277 [ linux angle-opengl nvidia-0x2184 ] conformance/textures/canvas/tex-2d-rgba-rgba-unsigned_byte.html [ RetryOnFailure ] +crbug.com/510410317 [ linux nvidia passthrough ] conformance/offscreencanvas/offscreencanvas-transfer-image-bitmap.html [ RetryOnFailure ] + #################### # Android failures # ####################
diff --git a/content/test/web_contents_observer_consistency_checker.cc b/content/test/web_contents_observer_consistency_checker.cc index f0a652c8..b0e9ab72c 100644 --- a/content/test/web_contents_observer_consistency_checker.cc +++ b/content/test/web_contents_observer_consistency_checker.cc
@@ -22,6 +22,7 @@ #include "content/public/browser/render_frame_host.h" #include "content/public/browser/render_process_host.h" #include "content/public/browser/render_widget_host.h" +#include "content/public/browser/security_principal.h" #include "content/public/browser/site_instance.h" #include "content/public/browser/web_contents.h" #include "content/public/browser/web_contents_observer.h" @@ -414,10 +415,14 @@ std::string WebContentsObserverConsistencyChecker::Format( RenderFrameHost* render_frame_host) { - return base::StringPrintf( - "(%d, %d -> %s)", render_frame_host->GetProcess()->GetDeprecatedID(), - render_frame_host->GetRoutingID(), - render_frame_host->GetSiteInstance()->GetSiteURL().spec().c_str()); + return base::StringPrintf("(%d, %d -> %s)", + render_frame_host->GetProcess()->GetDeprecatedID(), + render_frame_host->GetRoutingID(), + render_frame_host->GetSiteInstance() + ->GetSecurityPrincipal() + .GetDeprecatedSiteURL() + .spec() + .c_str()); } bool WebContentsObserverConsistencyChecker::NavigationIsOngoing(
diff --git a/content/web_test/renderer/web_frame_test_proxy.cc b/content/web_test/renderer/web_frame_test_proxy.cc index 32b5e71..1b49612 100644 --- a/content/web_test/renderer/web_frame_test_proxy.cc +++ b/content/web_test/renderer/web_frame_test_proxy.cc
@@ -694,7 +694,7 @@ case ax::mojom::Event::kEndOfTest: case ax::mojom::Event::kEnabledChanged: case ax::mojom::Event::kFocusAfterMenuClose: - case ax::mojom::Event::kFocusContext: + case ax::mojom::Event::kFocusContextDeprecated: case ax::mojom::Event::kHitTestResult: case ax::mojom::Event::kImageFrameUpdated: case ax::mojom::Event::kLiveRegionCreated:
diff --git a/device/vr/public/mojom/test/device_config.h b/device/vr/public/mojom/test/device_config.h index 052b7c7..51805ee 100644 --- a/device/vr/public/mojom/test/device_config.h +++ b/device/vr/public/mojom/test/device_config.h
@@ -10,11 +10,10 @@ namespace device { struct DeviceConfig { - float interpupillary_distance; - std::array<float, 4> - viewport_left; // raw projection left {left, right, top, bottom} - std::array<float, 4> - viewport_right; // raw projection right {left, right, top, bottom} + float interpupillary_distance = 0.0f; + // Both viewports are in the form of {left, right, top, bottom} FOVs. + std::array<float, 4> viewport_left = {0.0f, 0.0f, 0.0f, 0.0f}; + std::array<float, 4> viewport_right = {0.0f, 0.0f, 0.0f, 0.0f}; }; } // namespace device
diff --git a/docs/security/faq.md b/docs/security/faq.md index 65a8d2a1f..5d9f324 100644 --- a/docs/security/faq.md +++ b/docs/security/faq.md
@@ -23,7 +23,8 @@ unnecessary risk for users of widely-used open source libraries. All critical, high, and medium severity bugs are visible only to the security team and to the engineers directly involved in fixing them. Low-severity security bugs may be -visible to all project contributors after an initial triage phase. +visible to all project contributors after an initial triage phase. Low severity +bugs that are not being actively worked on may be made public after four weeks. <a name="TOC-Can-you-please-un-hide-old-security-bugs-"></a> ### Can you please un-hide old security bugs? @@ -1425,4 +1426,4 @@ <a name="TOC-What-is-the-security-model-for-Split-View-"></a> ### What's the security model for Split View? -See our [Split View Security FAQ](https://chromium.googlesource.com/chromium/src/+/main/chrome/browser/ui/tabs/docs/split_view_security_faq.md). \ No newline at end of file +See our [Split View Security FAQ](https://chromium.googlesource.com/chromium/src/+/main/chrome/browser/ui/tabs/docs/split_view_security_faq.md).
diff --git a/docs/security/life-of-a-security-issue.md b/docs/security/life-of-a-security-issue.md index e6151e6..66e9a68 100644 --- a/docs/security/life-of-a-security-issue.md +++ b/docs/security/life-of-a-security-issue.md
@@ -59,8 +59,9 @@ ## 2. Triage bug -After the bug is filed, a [security shepherd](shepherd.md) will evaluate the -report. The shepherd does several tasks: +After the bug is filed, a [security shepherd](shepherd.md) (with help from some +robots) will evaluate the report. The sheepdog robot, and the shepherd do +several tasks: - Ensure the bug has the necessary information for reproduction - Assess the bug's [severity](severity-guidelines.md) @@ -72,12 +73,18 @@ ## 3. Assign bug -The primary job of the shepherd is to route valid and actionable reports of -security bugs to the Chromium developer who is best poised to fix the issue. +The primary job of the triage rotation is to route valid and actionable reports +of security bugs to the Chromium developer who is best poised to fix the issue. After the issue is assigned, there may be discussion between the developer(s) involved, members of the security team, and the original reporter. +Low Severity (S3) issues may not be assigned but instead enter the default +triage mechanisms for Chrome. To enable this, they are made visible internally +soon after being marked Low, and made public after approximately four weeks. +This allows anyone with the right knowledge and skills to find and fix the +issue. + ## 4. Author and land a CL on `main` The developer will author a fix and a regression test for the security issue @@ -168,3 +175,6 @@ after the issue is marked *Fixed*, security automation opens the bug for public disclosure. At that time, the reporter can consider their obligations under coordinated disclosure to be fulfilled. + +Low Severity (S3) issues may be made public after approximately four weeks if +a developer is not actively investigating the issue.
diff --git a/docs/website b/docs/website index 81a7ea9..5a23c51 160000 --- a/docs/website +++ b/docs/website
@@ -1 +1 @@ -Subproject commit 81a7ea93897de0f405c0105c06e94b2c7d8f9b34 +Subproject commit 5a23c51d744c6e0483d985769143f1b6ecd8b2b8
diff --git a/extensions/browser/BUILD.gn b/extensions/browser/BUILD.gn index 0faaea1..6997c67a 100644 --- a/extensions/browser/BUILD.gn +++ b/extensions/browser/BUILD.gn
@@ -381,6 +381,10 @@ "extension_system.h", "extension_system_provider.cc", "extension_system_provider.h", + "extension_user_activation_service.cc", + "extension_user_activation_service.h", + "extension_user_activation_service_factory.cc", + "extension_user_activation_service_factory.h", "extension_user_script_loader.cc", "extension_user_script_loader.h", "extension_util.cc", @@ -1101,6 +1105,7 @@ "extension_pref_value_map_unittest.cc", "extension_registrar_unittest.cc", "extension_registry_unittest.cc", + "extension_user_activation_service_unittest.cc", "extension_util_unittest.cc", "file_highlighter_unittest.cc", "file_reader_unittest.cc", @@ -1210,7 +1215,6 @@ deps += [ "//components/storage_monitor:test_support", "//extensions/browser/api/declarative_net_request/filter_list_converter:unit_tests", - "//extensions/shell:app_shell_lib", ] if (!is_android) { @@ -1265,11 +1269,11 @@ "//chromeos/dbus/power", "//components/account_id", "//components/feedback", + "//components/feedback/content:factory", "//components/prefs", "//components/prefs:test_support", "//components/user_manager", "//components/user_manager:test_support", - "//extensions/shell:app_shell_lib", "//google_apis", ] }
diff --git a/extensions/browser/api/declarative_net_request/ruleset_manager.cc b/extensions/browser/api/declarative_net_request/ruleset_manager.cc index 9d4b5fc..874b61a 100644 --- a/extensions/browser/api/declarative_net_request/ruleset_manager.cc +++ b/extensions/browser/api/declarative_net_request/ruleset_manager.cc
@@ -55,8 +55,10 @@ void NotifyRequestWithheld(const ExtensionId& extension_id, const WebRequestInfo& request) { DCHECK(ExtensionsAPIClient::Get()); + // TODO(crbug.com/379869738): Remove GetUnsafeValue. ExtensionsAPIClient::Get()->NotifyWebRequestWithheld( - request.render_process_id, request.frame_routing_id, extension_id); + request.global_id.child_id.GetUnsafeValue(), + request.global_id.frame_routing_id, extension_id); } // Helper to log the time taken in RulesetManager::EvaluateRequestInternal.
diff --git a/extensions/browser/api/feedback_private/feedback_private_api_unittest_base_chromeos.cc b/extensions/browser/api/feedback_private/feedback_private_api_unittest_base_chromeos.cc index afcb1c4a..25d362e 100644 --- a/extensions/browser/api/feedback_private/feedback_private_api_unittest_base_chromeos.cc +++ b/extensions/browser/api/feedback_private/feedback_private_api_unittest_base_chromeos.cc
@@ -10,14 +10,15 @@ #include "base/functional/callback_helpers.h" #include "base/location.h" #include "base/task/single_thread_task_runner.h" +#include "components/feedback/content/feedback_uploader_factory.h" #include "components/feedback/system_logs/system_logs_source.h" #include "components/keyed_service/core/keyed_service.h" #include "extensions/browser/api/api_resource_manager.h" +#include "extensions/browser/api/extensions_api_client.h" +#include "extensions/browser/api/feedback_private/feedback_private_delegate.h" #include "extensions/browser/api/feedback_private/log_source_access_manager.h" #include "extensions/browser/api/feedback_private/log_source_resource.h" #include "extensions/common/api/feedback_private.h" -#include "extensions/shell/browser/api/feedback_private/shell_feedback_private_delegate.h" -#include "extensions/shell/browser/shell_extensions_api_client.h" namespace extensions { @@ -87,7 +88,7 @@ int call_count_ = 0; }; -class TestFeedbackPrivateDelegate : public ShellFeedbackPrivateDelegate { +class TestFeedbackPrivateDelegate : public FeedbackPrivateDelegate { public: TestFeedbackPrivateDelegate() = default; @@ -97,13 +98,47 @@ ~TestFeedbackPrivateDelegate() override = default; + // FeedbackPrivateDelegate: + base::DictValue GetStrings(content::BrowserContext* context, + bool from_crash) const override { + return {}; + } + + void FetchSystemInformation( + content::BrowserContext* context, + system_logs::SysLogsFetcherCallback callback) const override {} + std::unique_ptr<system_logs::SystemLogsSource> CreateSingleLogSource( api::feedback_private::LogSource source_type) const override { return std::make_unique<TestSingleLogSource>(source_type); } + + void FetchExtraLogs(scoped_refptr<feedback::FeedbackData> feedback_data, + FetchExtraLogsCallback callback) const override {} + + api::feedback_private::LandingPageType GetLandingPageType( + const feedback::FeedbackData& feedback_data) const override { + return api::feedback_private::LandingPageType::kNoLandingPage; + } + + std::string GetSignedInUserEmail( + content::BrowserContext* context) const override { + return ""; + } + + void NotifyFeedbackDelayed() const override {} + + feedback::FeedbackUploader* GetFeedbackUploaderForContext( + content::BrowserContext* context) const override { + return feedback::FeedbackUploaderFactory::GetForBrowserContext(context); + } + + void OpenFeedback( + content::BrowserContext* context, + api::feedback_private::FeedbackSource source) const override {} }; -class TestExtensionsAPIClient : public ShellExtensionsAPIClient { +class TestExtensionsAPIClient : public ExtensionsAPIClient { public: TestExtensionsAPIClient() = default; @@ -112,7 +147,7 @@ ~TestExtensionsAPIClient() override = default; - // ShellExtensionsApiClient implementation: + // ExtensionsAPIClient implementation: FeedbackPrivateDelegate* GetFeedbackPrivateDelegate() override { if (!feedback_private_delegate_) { feedback_private_delegate_ =
diff --git a/extensions/browser/api/feedback_private/feedback_service_unittest.cc b/extensions/browser/api/feedback_private/feedback_service_unittest.cc index 3c1fe3ae..7b58ba4 100644 --- a/extensions/browser/api/feedback_private/feedback_service_unittest.cc +++ b/extensions/browser/api/feedback_private/feedback_service_unittest.cc
@@ -16,9 +16,10 @@ #include "components/feedback/feedback_data.h" #include "components/feedback/feedback_report.h" #include "content/public/test/test_utils.h" +#include "extensions/browser/api/feedback_private/feedback_private_delegate.h" #include "extensions/browser/api/feedback_private/mock_feedback_service.h" #include "extensions/browser/api_unittest.h" -#include "extensions/shell/browser/api/feedback_private/shell_feedback_private_delegate.h" +#include "extensions/common/api/feedback_private.h" #include "services/network/public/cpp/shared_url_loader_factory.h" #include "services/network/public/cpp/weak_wrapper_shared_url_loader_factory.h" #include "services/network/test/test_url_loader_factory.h" @@ -89,7 +90,7 @@ base::WeakPtrFactory<MockFeedbackUploader> weak_ptr_factory_{this}; }; -class MockFeedbackPrivateDelegate : public ShellFeedbackPrivateDelegate { +class MockFeedbackPrivateDelegate : public FeedbackPrivateDelegate { public: MockFeedbackPrivateDelegate() { ON_CALL(*this, FetchSystemInformation) @@ -112,16 +113,41 @@ ~MockFeedbackPrivateDelegate() override = default; + MOCK_METHOD(base::DictValue, + GetStrings, + (content::BrowserContext*, bool), + (const, override)); MOCK_METHOD(void, FetchSystemInformation, (content::BrowserContext*, system_logs::SysLogsFetcherCallback), (const, override)); #if BUILDFLAG(IS_CHROMEOS) + MOCK_METHOD(std::unique_ptr<system_logs::SystemLogsSource>, + CreateSingleLogSource, + (api::feedback_private::LogSource), + (const, override)); MOCK_METHOD(void, FetchExtraLogs, (scoped_refptr<feedback::FeedbackData>, FetchExtraLogsCallback), (const, override)); + MOCK_METHOD(api::feedback_private::LandingPageType, + GetLandingPageType, + (const feedback::FeedbackData&), + (const, override)); #endif // BUILDFLAG(IS_CHROMEOS) + MOCK_METHOD(std::string, + GetSignedInUserEmail, + (content::BrowserContext*), + (const, override)); + MOCK_METHOD(void, NotifyFeedbackDelayed, (), (const, override)); + MOCK_METHOD(feedback::FeedbackUploader*, + GetFeedbackUploaderForContext, + (content::BrowserContext*), + (const, override)); + MOCK_METHOD(void, + OpenFeedback, + (content::BrowserContext*, api::feedback_private::FeedbackSource), + (const, override)); }; #if BUILDFLAG(IS_CHROMEOS) @@ -332,9 +358,9 @@ base::MockCallback<SendFeedbackCallback> mock_callback; EXPECT_CALL(mock_callback, Run(true)); - auto shell_delegate = std::make_unique<ShellFeedbackPrivateDelegate>(); + auto feedback_delegate = std::make_unique<MockFeedbackPrivateDelegate>(); auto feedback_service = base::MakeRefCounted<FeedbackService>( - browser_context(), shell_delegate.get()); + browser_context(), feedback_delegate.get()); RunUntilFeedbackIsSent(feedback_service, params, mock_callback.Get()); }
diff --git a/extensions/browser/api/guest_view/web_view/web_view_internal_api.cc b/extensions/browser/api/guest_view/web_view/web_view_internal_api.cc index fc1edfa..05bb70bf 100644 --- a/extensions/browser/api/guest_view/web_view/web_view_internal_api.cc +++ b/extensions/browser/api/guest_view/web_view/web_view_internal_api.cc
@@ -25,6 +25,7 @@ #include "content/public/browser/browser_context.h" #include "content/public/browser/render_frame_host.h" #include "content/public/browser/render_view_host.h" +#include "content/public/browser/security_principal.h" #include "content/public/browser/web_contents.h" #include "content/public/common/stop_find_action.h" #include "extensions/browser/extension_api_frame_id_map.h" @@ -111,7 +112,9 @@ } if (embedder_rfh && embedder_rfh->GetMainFrame()->GetWebUI()) { - const GURL& url = embedder_rfh->GetSiteInstance()->GetSiteURL(); + const GURL& url = embedder_rfh->GetSiteInstance() + ->GetSecurityPrincipal() + .GetDeprecatedSiteURL(); return extensions::mojom::HostID( extensions::mojom::HostID::HostType::kWebUi, url.spec()); } @@ -637,8 +640,11 @@ if (!params->instance_id) return RespondNow(Error(kViewInstanceIdError)); - GURL owner_base_url( - render_frame_host()->GetSiteInstance()->GetSiteURL().GetWithEmptyPath()); + GURL owner_base_url(render_frame_host() + ->GetSiteInstance() + ->GetSecurityPrincipal() + .GetDeprecatedSiteURL() + .GetWithEmptyPath()); std::optional<extensions::mojom::HostID> host_id = GenerateHostIDFromEmbedder(extension(), render_frame_host()); if (!host_id) {
diff --git a/extensions/browser/api/web_request/extension_web_request_event_router.cc b/extensions/browser/api/web_request/extension_web_request_event_router.cc index e2a4c79e..a645c16d 100644 --- a/extensions/browser/api/web_request/extension_web_request_event_router.cc +++ b/extensions/browser/api/web_request/extension_web_request_event_router.cc
@@ -252,14 +252,13 @@ // |context|. bool IsRequestFromExtension(const WebRequestInfo& request, content::BrowserContext* context) { - if (request.render_process_id == -1) { + if (!request.global_id.child_id) { return false; } - // TODO(crbug.com/379869738) Remove FromUnsafeValue. const Extension* extension = ProcessMap::Get(context)->GetEnabledExtensionByProcessID( - content::ChildProcessId::FromUnsafeValue(request.render_process_id)); + request.global_id.child_id); return extension && !extension->is_hosted_app(); } @@ -616,7 +615,7 @@ // Initialize as an inactive lazy listener. EventListener::ID listener_id(context, extension_id, *sub_event_name, - /*render_process_id=*/-1, + content::ChildProcessId(), /*web_view_instance_id=*/0, /*worker_thread_id=*/kMainThreadId, /*service_worker_version_id=*/ @@ -988,7 +987,7 @@ content::BrowserContext* browser_context, const ExtensionId& extension_id, const std::string& sub_event_name, - int render_process_id, + content::ChildProcessId render_process_id, int web_view_instance_id, int worker_thread_id, int64_t service_worker_version_id) @@ -1760,7 +1759,7 @@ for (const EventListener::ID& id : *listener_ids) { // Look for the event listener in the different listener sources. - bool is_active = id.render_process_id != -1; + bool is_active = !id.render_process_id.is_null(); Listeners* on_the_record_listeners = is_active ? &active_listeners : &inactive_listeners; Listeners* cross_listeners = @@ -1829,12 +1828,13 @@ // extension listener (as can happen if the extension fails to re-register // the event listener synchronously). If this happens, we treat the event // as handled so as to not block indefinitely. + // TODO(crbug.com/379869738): Remove GetUnsafeValue. event->cannot_dispatch_callback = base::BindRepeating( &WebRequestEventRouter::OnEventHandled, weak_ptr_factory_.GetWeakPtr(), id.browser_context, id.extension_id, - event_name, id.sub_event_name, request_id, id.render_process_id, - id.web_view_instance_id, id.worker_thread_id, - id.service_worker_version_id, nullptr); + event_name, id.sub_event_name, request_id, + id.render_process_id.GetUnsafeValue(), id.web_view_instance_id, + id.worker_thread_id, id.service_worker_version_id, nullptr); EventRouter::Get(id.browser_context) ->DispatchEventToExtension(id.extension_id, std::move(event)); } @@ -1854,9 +1854,11 @@ std::unique_ptr<EventResponse> response) { BrowserContextData& context_data = data_[GetBrowserContextID(browser_context)]; - EventListener::ID id(browser_context, extension_id, sub_event_name, - render_process_id, web_view_instance_id, - worker_thread_id, service_worker_version_id); + // TODO(crbug.com/379869738): Remove FromUnsafeValue. + EventListener::ID id( + browser_context, extension_id, sub_event_name, + content::ChildProcessId::FromUnsafeValue(render_process_id), + web_view_instance_id, worker_thread_id, service_worker_version_id); EventListener* listener = nullptr; // Check if the "handled" event was for an inactive listener (indicated by @@ -1913,9 +1915,12 @@ } BrowserContextID browser_context_id = GetBrowserContextID(browser_context); - EventListener::ID id(browser_context, extension_id, sub_event_name, - render_process_id, web_view_instance_id, - worker_thread_id, service_worker_version_id); + + // TODO(crbug.com/379869738): Remove FromUnsafeValue. + EventListener::ID id( + browser_context, extension_id, sub_event_name, + content::ChildProcessId::FromUnsafeValue(render_process_id), + web_view_instance_id, worker_thread_id, service_worker_version_id); if (is_lazy && FindEventListenerBySubEventName(browser_context_id, extension_id, event_name, sub_event_name)) { @@ -2071,8 +2076,7 @@ bool listener_matches = extension_id == id.extension_id && sub_event_name == id.sub_event_name && - (!render_process_id || - render_process_id->GetUnsafeValue() == id.render_process_id) && + (!render_process_id || render_process_id == id.render_process_id) && (!worker_thread_id || worker_thread_id == id.worker_thread_id) && (!service_worker_version_id || service_worker_version_id == id.service_worker_version_id); @@ -2182,7 +2186,7 @@ listener->id.worker_thread_id = kMainThreadId; listener->id.service_worker_version_id = blink::mojom::kInvalidServiceWorkerVersionId; - listener->id.render_process_id = -1; + listener->id.render_process_id = content::ChildProcessId(); data.inactive_listeners[event_name].push_back(std::move(listener)); } else if (update_type == ListenerUpdateType::kRemove) { // Service worker listeners should always be removed via @@ -2314,7 +2318,7 @@ void WebRequestEventRouter::RemoveWebViewEventListeners( content::BrowserContext* browser_context, - int render_process_id, + content::ChildProcessId render_process_id, int web_view_instance_id) { DCHECK_CURRENTLY_ON(BrowserThread::UI); @@ -2745,8 +2749,7 @@ } if (request.is_web_view) { - if (listener.id.render_process_id != - request.web_view_embedder_process_id.value() || + if (listener.id.render_process_id != request.web_view_embedder_process_id || listener.id.web_view_instance_id != request.web_view_instance_id) { return false; } @@ -2757,7 +2760,7 @@ // Filter requests from other extensions / apps. This does not work for // content scripts, or extension pages in non-extension processes. if (is_request_from_extension && - listener.id.render_process_id != request.render_process_id) { + listener.id.render_process_id != request.global_id.child_id) { return false; } @@ -2793,9 +2796,10 @@ if (access != PermissionsData::PageAccess::kAllowed) { if (access == PermissionsData::PageAccess::kWithheld) { DCHECK(ExtensionsAPIClient::Get()); + // TODO(crbug.com/379869738): Remove GetUnsafeValue. ExtensionsAPIClient::Get()->NotifyWebRequestWithheld( - request.render_process_id, request.frame_routing_id, - listener.id.extension_id); + request.global_id.child_id.GetUnsafeValue(), + request.global_id.frame_routing_id, listener.id.extension_id); } return false;
diff --git a/extensions/browser/api/web_request/extension_web_request_event_router.h b/extensions/browser/api/web_request/extension_web_request_event_router.h index 32116da..9a29ff2 100644 --- a/extensions/browser/api/web_request/extension_web_request_event_router.h +++ b/extensions/browser/api/web_request/extension_web_request_event_router.h
@@ -304,7 +304,7 @@ // Removes the listeners for a given <webview>. void RemoveWebViewEventListeners(content::BrowserContext* browser_context, - int render_process_id, + content::ChildProcessId render_process_id, int web_view_instance_id); // Called when an incognito browser_context is created or destroyed. When @@ -422,7 +422,7 @@ ID(content::BrowserContext* browser_context, const ExtensionId& extension_id, const std::string& sub_event_name, - int render_process_id, + content::ChildProcessId render_process_id, int web_view_instance_id, int worker_thread_id, int64_t service_worker_version_id); @@ -436,7 +436,7 @@ ExtensionId extension_id; std::string sub_event_name; // In the case of a webview, this is the process ID of the embedder. - int render_process_id; + content::ChildProcessId render_process_id; int web_view_instance_id; // The worker_thread_id and service_worker_version_id members are only // meaningful for event listeners for ServiceWorker events. Otherwise,
diff --git a/extensions/browser/api/web_request/web_request_event_details.cc b/extensions/browser/api/web_request/web_request_event_details.cc index 326de18..231b7e2d 100644 --- a/extensions/browser/api/web_request/web_request_event_details.cc +++ b/extensions/browser/api/web_request/web_request_event_details.cc
@@ -119,7 +119,8 @@ ToString(request.frame_data.document_lifecycle)); } initiator_ = request.initiator; - render_process_id_ = request.render_process_id; + // TODO(crbug.com/379869738): Remove GetUnsafeValue. + render_process_id_ = request.global_id.child_id.GetUnsafeValue(); } WebRequestEventDetails::~WebRequestEventDetails() = default;
diff --git a/extensions/browser/api/web_request/web_request_info.cc b/extensions/browser/api/web_request/web_request_info.cc index 9b5cf55..61ef7ee 100644 --- a/extensions/browser/api/web_request/web_request_info.cc +++ b/extensions/browser/api/web_request/web_request_info.cc
@@ -176,8 +176,7 @@ WebRequestInfoInitParams::WebRequestInfoInitParams( uint64_t request_id, - int render_process_id, - int frame_routing_id, + content::GlobalRenderFrameHostId global_id, std::unique_ptr<ExtensionNavigationUIData> navigation_ui_data, const network::ResourceRequest& request, bool is_download, @@ -186,8 +185,7 @@ std::optional<int64_t> navigation_id) : id(request_id), url(request.url), - render_process_id(render_process_id), - frame_routing_id(frame_routing_id), + global_id(global_id), method(request.method), is_navigation_request(!!navigation_ui_data), initiator(request.request_initiator), @@ -229,12 +227,14 @@ } frame_data = navigation_ui_data->frame_data(); parent_routing_id = navigation_ui_data->parent_routing_id(); - } else if (frame_routing_id != IPC::mojom::kRoutingIdNone) { + } else if (global_id.frame_routing_id != IPC::mojom::kRoutingIdNone) { #if BUILDFLAG(ENABLE_GUEST_VIEW) // Grab any WebView-related information if relevant. WebViewRendererState::WebViewInfo web_view_info; + // TODO(crbug.com/379869738): Remove GetUnsafeValue. if (WebViewRendererState::GetInstance()->GetInfo( - render_process_id, frame_routing_id, &web_view_info)) { + global_id.child_id.GetUnsafeValue(), global_id.frame_routing_id, + &web_view_info)) { is_web_view = true; web_view_instance_id = web_view_info.instance_id; web_view_rules_registry_id = web_view_info.rules_registry_id; @@ -242,8 +242,7 @@ } #endif - parent_routing_id = - content::GlobalRenderFrameHostId(render_process_id, frame_routing_id); + parent_routing_id = global_id; // For subresource loads we attempt to resolve the FrameData immediately. frame_data = ExtensionApiFrameIdMap::Get()->GetFrameData(parent_routing_id); @@ -253,8 +252,7 @@ WebRequestInfo::WebRequestInfo(WebRequestInfoInitParams params) : id(params.id), url(std::move(params.url)), - render_process_id(params.render_process_id), - frame_routing_id(params.frame_routing_id), + global_id(params.global_id), method(std::move(params.method)), is_navigation_request(params.is_navigation_request), initiator(std::move(params.initiator)),
diff --git a/extensions/browser/api/web_request/web_request_info.h b/extensions/browser/api/web_request/web_request_info.h index 1f03e63..47a1ebe 100644 --- a/extensions/browser/api/web_request/web_request_info.h +++ b/extensions/browser/api/web_request/web_request_info.h
@@ -44,8 +44,7 @@ // URLLoaderFactory interface. WebRequestInfoInitParams( uint64_t request_id, - int render_process_id, - int frame_routing_id, + content::GlobalRenderFrameHostId global_id, std::unique_ptr<ExtensionNavigationUIData> navigation_ui_data, const network::ResourceRequest& request, bool is_download, @@ -63,8 +62,7 @@ uint64_t id = 0; GURL url; - int render_process_id = -1; - int frame_routing_id = IPC::mojom::kRoutingIdNone; + content::GlobalRenderFrameHostId global_id; std::string method; bool is_navigation_request = false; std::optional<url::Origin> initiator; @@ -129,13 +127,9 @@ // The URL of the request. const GURL url; - // The ID of the render process which initiated the request, or -1 of not - // applicable (i.e. if initiated by the browser). - const int render_process_id; - - // The frame routing ID of the frame which initiated this request, or - // IPC::mojom::kRoutingIdNone if the request was not initiated by a frame. - const int frame_routing_id = IPC::mojom::kRoutingIdNone; + // The ID and frame routing ID of the render process which initiated the + // request, or invalid if not applicable (i.e. if initiated by the browser). + const content::GlobalRenderFrameHostId global_id; // The HTTP method used for the request, if applicable. const std::string method;
diff --git a/extensions/browser/api/web_request/web_request_info_unittest.cc b/extensions/browser/api/web_request/web_request_info_unittest.cc index 5bd661035..7f74639 100644 --- a/extensions/browser/api/web_request/web_request_info_unittest.cc +++ b/extensions/browser/api/web_request/web_request_info_unittest.cc
@@ -32,7 +32,7 @@ request.request_body->AppendFileRange(base::FilePath::FromASCII(kFilePath), 0, std::numeric_limits<uint64_t>::max(), base::Time()); - WebRequestInfo info(WebRequestInfoInitParams(0, 0, 0, nullptr, request, false, + WebRequestInfo info(WebRequestInfoInitParams(0, {}, nullptr, request, false, false, false, std::nullopt)); ASSERT_TRUE(info.request_body_data); base::Value* value = info.request_body_data->Find(
diff --git a/extensions/browser/api/web_request/web_request_permissions.cc b/extensions/browser/api/web_request/web_request_permissions.cc index 2e39d53..9c67616 100644 --- a/extensions/browser/api/web_request/web_request_permissions.cc +++ b/extensions/browser/api/web_request/web_request_permissions.cc
@@ -257,7 +257,15 @@ return false; } - bool is_request_from_browser = request.render_process_id == -1; + // TODO(crbug.com/379869738): Remove GetUnsafeValue once there is a better way + // to identify prefetch requests from the browser. Changing this to the + // correct code of `is_null()` breaks functionality as the magic value 0 is + // actually used for prefetches, even though it's usually used by the browser + // process. When uses are correctly ported to content::ChildProcessId we + // should be able to fix this. See also + // ChromeExtensionsAPIClient::ShouldHideBrowserNetworkRequest. + bool is_request_from_browser = + request.global_id.child_id.GetUnsafeValue() == -1; if (is_request_from_browser) { // Browser initiated service worker script requests (e.g., for update check) @@ -292,11 +300,9 @@ } // Hide requests from the Chrome WebStore App. - // TODO(crbug.com/379869738) Remove FromUnsafeValue. if (!is_request_from_browser && - permission_helper->process_map()->Contains( - extensions::kWebStoreAppId, content::ChildProcessId::FromUnsafeValue( - request.render_process_id))) { + permission_helper->process_map()->Contains(extensions::kWebStoreAppId, + request.global_id.child_id)) { return true; } @@ -313,10 +319,11 @@ const GURL& url = request.url; + // TODO(crbug.com/379869738): Remove GetUnsafeValue. bool is_request_from_webui_renderer = !is_request_from_browser && content::ChildProcessSecurityPolicy::GetInstance()->HasWebUIBindings( - request.render_process_id); + request.global_id.child_id.GetUnsafeValue()); if (is_request_from_webui_renderer) { #if DCHECK_IS_ON()
diff --git a/extensions/browser/api/web_request/web_request_permissions_unittest.cc b/extensions/browser/api/web_request/web_request_permissions_unittest.cc index 908635b..70f6aeb 100644 --- a/extensions/browser/api/web_request/web_request_permissions_unittest.cc +++ b/extensions/browser/api/web_request/web_request_permissions_unittest.cc
@@ -171,17 +171,17 @@ } cases.insert(cases.end(), additional_cases.begin(), additional_cases.end()); - const int kRendererProcessId = 1; - const int kBrowserProcessId = -1; + const content::ChildProcessId kRendererProcessId(1); + const content::ChildProcessId kBrowserProcessId; // Returns a WebRequestInfoInitParams instance constructed as per the given // parameters. auto create_request_params = [](const GURL& url, WebRequestResourceType web_request_type, - int render_process_id) { + content::ChildProcessId render_process_id) { WebRequestInfoInitParams request; request.url = url; - request.render_process_id = render_process_id; + request.global_id.child_id = render_process_id; request.web_request_type = web_request_type; request.is_navigation_request = web_request_type == WebRequestResourceType::MAIN_FRAME || @@ -264,11 +264,9 @@ // If the origin is labeled by the WebStoreAppId, it becomes protected. { - const int kWebstoreProcessId = 42; - // TODO(crbug.com/379869738) Remove FromUnsafeValue. + const content::ChildProcessId kWebstoreProcessId(42); ProcessMap::Get(browser_context()) - ->Insert(extensions::kWebStoreAppId, - content::ChildProcessId::FromUnsafeValue(kWebstoreProcessId)); + ->Insert(extensions::kWebStoreAppId, kWebstoreProcessId); WebRequestInfo sensitive_request_info(create_request_params( non_sensitive_url, WebRequestResourceType::SCRIPT, kWebstoreProcessId)); EXPECT_TRUE(WebRequestPermissions::HideRequest(permission_helper, @@ -314,7 +312,7 @@ WebRequestResourceType type) { WebRequestInfoInitParams request; request.url = url; - request.render_process_id = 1; + request.global_id.child_id = content::ChildProcessId(1); request.web_request_type = type; request.initiator = url::Origin::Create(GURL("chrome-untrusted://test/")); @@ -353,7 +351,7 @@ auto create_sub_frame_navigation_request = [](const GURL& url) { WebRequestInfoInitParams request; request.url = url; - request.render_process_id = 1; + request.global_id.child_id = content::ChildProcessId(1); request.web_request_type = WebRequestResourceType::SUB_FRAME; request.is_navigation_request = true; request.initiator = url::Origin::Create(GURL("chrome-untrusted://test/")); @@ -376,7 +374,7 @@ auto create_main_frame_request_info = [](const GURL& url) { WebRequestInfoInitParams request; request.url = url; - request.render_process_id = 1; + request.global_id.child_id = content::ChildProcessId(1); request.web_request_type = WebRequestResourceType::MAIN_FRAME; request.is_navigation_request = true; request.initiator = url::Origin::Create(GURL("chrome-untrusted://test/"));
diff --git a/extensions/browser/api/web_request/web_request_proxying_url_loader_factory.cc b/extensions/browser/api/web_request/web_request_proxying_url_loader_factory.cc index 97752eef..b9f35dd 100644 --- a/extensions/browser/api/web_request/web_request_proxying_url_loader_factory.cc +++ b/extensions/browser/api/web_request/web_request_proxying_url_loader_factory.cc
@@ -267,8 +267,11 @@ // https://developer.chrome.com/extensions/webRequest#event-onBeforeRequest. network::ResourceRequest request_for_info = request_; request_for_info.request_initiator = original_initiator_; + // TODO(crbug.com/379869738): Port render_process_id_ to ChildProcessId. info_.emplace(WebRequestInfoInitParams( - request_id_, factory_->render_process_id_, frame_routing_id_, + request_id_, + content::GlobalRenderFrameHostId(factory_->render_process_id_, + frame_routing_id_), factory_->navigation_ui_data_ ? factory_->navigation_ui_data_->DeepCopy() : nullptr, request_for_info, factory_->IsForDownload(),
diff --git a/extensions/browser/api/web_request/web_request_proxying_websocket.cc b/extensions/browser/api/web_request/web_request_proxying_websocket.cc index b96bc18..c560578 100644 --- a/extensions/browser/api/web_request/web_request_proxying_websocket.cc +++ b/extensions/browser/api/web_request/web_request_proxying_websocket.cc
@@ -76,10 +76,10 @@ response_(network::mojom::URLResponseHead::New()), has_extra_headers_(has_extra_headers), has_security_info_(has_security_info), + // TODO(crbug.com/379869738): Port process_id to ChildProcessId. info_(WebRequestInfoInitParams( request_id_generator->Generate(IPC::mojom::kRoutingIdNone, 0), - process_id, - render_frame_id, + content::GlobalRenderFrameHostId(process_id, render_frame_id), nullptr, request, /*is_download=*/false,
diff --git a/extensions/browser/api/web_request/web_request_proxying_webtransport.cc b/extensions/browser/api/web_request/web_request_proxying_webtransport.cc index c245f52..282f7d9 100644 --- a/extensions/browser/api/web_request/web_request_proxying_webtransport.cc +++ b/extensions/browser/api/web_request/web_request_proxying_webtransport.cc
@@ -301,15 +301,16 @@ request.url = url; request.request_initiator = initiator_origin; - const int process_id = render_process_host.GetDeprecatedID(); + const content::ChildProcessId process_id = render_process_host.GetID(); - WebRequestInfoInitParams params = - WebRequestInfoInitParams(request_id, process_id, frame_routing_id, - /*navigation_ui_data=*/nullptr, request, - /*is_download=*/false, - /*is_async=*/true, - /*is_service_worker_script=*/false, - /*navigation_id=*/std::nullopt); + WebRequestInfoInitParams params = WebRequestInfoInitParams( + request_id, + content::GlobalRenderFrameHostId(process_id, frame_routing_id), + /*navigation_ui_data=*/nullptr, request, + /*is_download=*/false, + /*is_async=*/true, + /*is_service_worker_script=*/false, + /*navigation_id=*/std::nullopt); params.web_request_type = WebRequestResourceType::WEB_TRANSPORT; auto proxy = std::make_unique<WebTransportHandshakeProxy>(
diff --git a/extensions/browser/core_browser_context_keyed_service_factories.cc b/extensions/browser/core_browser_context_keyed_service_factories.cc index ba8fb1a..524c6d30 100644 --- a/extensions/browser/core_browser_context_keyed_service_factories.cc +++ b/extensions/browser/core_browser_context_keyed_service_factories.cc
@@ -17,6 +17,7 @@ #include "extensions/browser/extension_prefs_helper_factory.h" #include "extensions/browser/extension_protocols.h" #include "extensions/browser/extension_registrar_factory.h" +#include "extensions/browser/extension_user_activation_service_factory.h" #include "extensions/browser/image_loader_factory.h" #include "extensions/browser/message_tracker.h" #include "extensions/browser/pending_extension_manager_factory.h" @@ -57,6 +58,7 @@ ExtensionFunction::EnsureShutdownNotifierFactoryBuilt(); ExtensionPrefsFactory::GetInstance(); ExtensionNavigationRegistry::GetFactoryInstance(); + ExtensionUserActivationServiceFactory::GetInstance(); ExtensionPrefsHelperFactory::GetInstance(); ExtensionRegistrarFactory::GetInstance(); ImageLoaderFactory::GetInstance();
diff --git a/extensions/browser/event_router.cc b/extensions/browser/event_router.cc index 5e5685f5..c1e3885 100644 --- a/extensions/browser/event_router.cc +++ b/extensions/browser/event_router.cc
@@ -38,6 +38,7 @@ #include "extensions/browser/events/event_dispatch_helper.h" #include "extensions/browser/extension_host.h" #include "extensions/browser/extension_prefs.h" +#include "extensions/browser/extension_user_activation_service.h" #include "extensions/browser/extension_util.h" #include "extensions/browser/extensions_browser_client.h" #include "extensions/browser/process_manager.h" @@ -188,6 +189,12 @@ base::ListValue event_args, mojom::EventDispatcher::DispatchEventCallback callback) { CHECK(observed_process_set_.contains(rph)); + + if (params->is_user_gesture && + params->host_id->type == mojom::HostID::HostType::kExtensions) { + ExtensionUserActivationService::Get(browser_context_) + ->NotifyUserActivation(params->host_id->id); + } int worker_thread_id = params->worker_thread_id; mojo::AssociatedRemote<mojom::EventDispatcher>& dispatcher = rph_dispatcher_map_[rph][worker_thread_id]; @@ -1247,8 +1254,9 @@ return; } - for (TestObserver& observer : test_observers_) + for (TestObserver& observer : test_observers_) { observer.OnWillDispatchEvent(*event); + } EventDispatchHelper::DispatchEvent( *browser_context_, listeners_, @@ -1259,23 +1267,22 @@ restrict_to_extension_id, restrict_to_url, std::move(event)); } -void EventRouter::DispatchEventToProcess( - const ExtensionId& extension_id, - const GURL& listener_url, - RenderProcessHost* process, - int64_t service_worker_version_id, - int worker_thread_id, - std::unique_ptr<Event> event, - bool did_enqueue) { +void EventRouter::DispatchEventToProcess(const ExtensionId& extension_id, + const GURL& listener_url, + RenderProcessHost* process, + int64_t service_worker_version_id, + int worker_thread_id, + std::unique_ptr<Event> event, + bool did_enqueue) { BrowserContext* listener_context = process->GetBrowserContext(); ProcessMap* process_map = ProcessMap::Get(listener_context); // NOTE: |extension| being NULL does not necessarily imply that this event // shouldn't be dispatched. Events can be dispatched to WebUI and webviews as // well. It all depends on what GetMostLikelyContextType returns. - const Extension* extension = - ExtensionRegistry::Get(browser_context_)->enabled_extensions().GetByID( - extension_id); + const Extension* extension = ExtensionRegistry::Get(browser_context_) + ->enabled_extensions() + .GetByID(extension_id); if (!extension && !extension_id.empty()) { // Trying to dispatch an event to an extension that doesn't exist. The @@ -1489,9 +1496,10 @@ // TODO(mpcomplete): We should never get this message unless // HasLazyBackgroundPage is true. Find out why we're getting it anyway. if (host->extension() && - BackgroundInfo::HasLazyBackgroundPage(host->extension())) + BackgroundInfo::HasLazyBackgroundPage(host->extension())) { pm->DecrementLazyKeepaliveCount(host->extension(), Activity::EVENT, event_name); + } } bool EventRouter::HasRegisteredEvents(const ExtensionId& extension_id) const {
diff --git a/extensions/browser/event_router_factory.cc b/extensions/browser/event_router_factory.cc index d1b5b5c8..f8cbf2bb 100644 --- a/extensions/browser/event_router_factory.cc +++ b/extensions/browser/event_router_factory.cc
@@ -10,6 +10,7 @@ #include "extensions/browser/extension_prefs.h" #include "extensions/browser/extension_prefs_factory.h" #include "extensions/browser/extension_registry_factory.h" +#include "extensions/browser/extension_user_activation_service_factory.h" #include "extensions/browser/extensions_browser_client.h" #include "extensions/browser/process_manager_factory.h" @@ -35,6 +36,7 @@ BrowserContextDependencyManager::GetInstance()) { DependsOn(ExtensionRegistryFactory::GetInstance()); DependsOn(ExtensionPrefsFactory::GetInstance()); + DependsOn(ExtensionUserActivationServiceFactory::GetInstance()); DependsOn(ProcessManagerFactory::GetInstance()); }
diff --git a/extensions/browser/extension_function_dispatcher.cc b/extensions/browser/extension_function_dispatcher.cc index 453f2838..a15ee6841 100644 --- a/extensions/browser/extension_function_dispatcher.cc +++ b/extensions/browser/extension_function_dispatcher.cc
@@ -29,6 +29,7 @@ #include "content/public/browser/render_frame_host.h" #include "content/public/browser/render_process_host.h" #include "content/public/browser/render_process_host_observer.h" +#include "content/public/browser/security_principal.h" #include "content/public/browser/service_worker_context.h" #include "content/public/browser/web_contents.h" #include "content/public/browser/web_contents_observer.h" @@ -40,6 +41,7 @@ #include "extensions/browser/extension_function_registry.h" #include "extensions/browser/extension_registry.h" #include "extensions/browser/extension_system.h" +#include "extensions/browser/extension_user_activation_service.h" #include "extensions/browser/extension_util.h" #include "extensions/browser/extensions_browser_client.h" #include "extensions/browser/process_manager.h" @@ -159,8 +161,7 @@ content::BrowserContext* browser_context) : browser_context_(browser_context), delegate_(nullptr) {} -ExtensionFunctionDispatcher::~ExtensionFunctionDispatcher() { -} +ExtensionFunctionDispatcher::~ExtensionFunctionDispatcher() = default; void ExtensionFunctionDispatcher::Dispatch( mojom::RequestParamsPtr params, @@ -173,9 +174,11 @@ ExtensionIdForTracing(params->extension_id)); ScopedRequestParamsCrashKeys request_params_crash_keys(*params); - SCOPED_CRASH_KEY_STRING256( - "extensions", "frame.GetSiteInstance()", - frame.GetSiteInstance()->GetSiteURL().possibly_invalid_spec()); + SCOPED_CRASH_KEY_STRING256("extensions", "frame.GetSiteInstance()", + frame.GetSiteInstance() + ->GetSecurityPrincipal() + .GetDeprecatedSiteURL() + .possibly_invalid_spec()); if (auto bad_message_code = ValidateRequest(*params, &frame, process)) { // Kill the renderer if it's an invalid request. @@ -344,9 +347,8 @@ return; } - if (extension && - ExtensionsBrowserClient::Get()->CanExtensionCrossIncognito( - extension, browser_context_)) { + if (extension && ExtensionsBrowserClient::Get()->CanExtensionCrossIncognito( + extension, browser_context_)) { function->set_include_incognito_information(true); } @@ -574,6 +576,12 @@ function->set_source_url(*render_frame_host_url); } + if (params_without_args.user_gesture && + !params_without_args.extension_id.empty()) { + ExtensionUserActivationService::Get(browser_context_) + ->NotifyUserActivation(params_without_args.extension_id); + } + function->set_has_callback(params_without_args.has_callback); function->set_user_gesture(params_without_args.user_gesture); function->set_extension(extension);
diff --git a/extensions/browser/extension_navigation_throttle.cc b/extensions/browser/extension_navigation_throttle.cc index 6b4ab4a..2731b64 100644 --- a/extensions/browser/extension_navigation_throttle.cc +++ b/extensions/browser/extension_navigation_throttle.cc
@@ -242,7 +242,10 @@ // https://crbug.com/40091207. bool current_frame_is_extension_process = !!registry->enabled_extensions().GetExtensionOrAppByURL( - navigation_handle()->GetStartingSiteInstance()->GetSiteURL()); + navigation_handle() + ->GetStartingSiteInstance() + ->GetSecurityPrincipal() + .GetDeprecatedSiteURL()); if (!url_has_extension_scheme && !current_frame_is_extension_process) { // Relax this restriction for apps that use <webview>. See
diff --git a/extensions/browser/extension_user_activation_service.cc b/extensions/browser/extension_user_activation_service.cc new file mode 100644 index 0000000..dd9873e --- /dev/null +++ b/extensions/browser/extension_user_activation_service.cc
@@ -0,0 +1,41 @@ +// Copyright 2026 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "extensions/browser/extension_user_activation_service.h" + +#include "base/functional/bind.h" +#include "extensions/browser/extension_user_activation_service_factory.h" +#include "third_party/blink/public/common/frame/user_activation_state.h" + +namespace extensions { + +ExtensionUserActivationService::ExtensionUserActivationService() = default; + +ExtensionUserActivationService::~ExtensionUserActivationService() = default; + +// static +ExtensionUserActivationService* ExtensionUserActivationService::Get( + content::BrowserContext* context) { + return ExtensionUserActivationServiceFactory::GetForBrowserContext(context); +} + +void ExtensionUserActivationService::NotifyUserActivation( + const ExtensionId& extension_id) { + user_activation_timers_[extension_id].Start( + FROM_HERE, blink::kActivationLifespan, + base::BindOnce(&ExtensionUserActivationService::RemoveActivation, + base::Unretained(this), extension_id)); +} + +bool ExtensionUserActivationService::HasTransientActivation( + const ExtensionId& extension_id) const { + return user_activation_timers_.contains(extension_id); +} + +void ExtensionUserActivationService::RemoveActivation( + const ExtensionId& extension_id) { + user_activation_timers_.erase(extension_id); +} + +} // namespace extensions
diff --git a/extensions/browser/extension_user_activation_service.h b/extensions/browser/extension_user_activation_service.h new file mode 100644 index 0000000..73b88db --- /dev/null +++ b/extensions/browser/extension_user_activation_service.h
@@ -0,0 +1,51 @@ +// Copyright 2026 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef EXTENSIONS_BROWSER_EXTENSION_USER_ACTIVATION_SERVICE_H_ +#define EXTENSIONS_BROWSER_EXTENSION_USER_ACTIVATION_SERVICE_H_ + +#include <map> + +#include "base/timer/timer.h" +#include "components/keyed_service/core/keyed_service.h" +#include "extensions/common/extension_id.h" + +namespace content { +class BrowserContext; +} + +namespace extensions { + +// Tracks transient user activation state for extensions. +// This service is notified when an extension is activated by a user gesture +// (such as during API calls or event dispatching) and maintains that activation +// state for a short duration. +class ExtensionUserActivationService : public KeyedService { + public: + ExtensionUserActivationService(); + ~ExtensionUserActivationService() override; + + ExtensionUserActivationService(const ExtensionUserActivationService&) = + delete; + ExtensionUserActivationService& operator=( + const ExtensionUserActivationService&) = delete; + + static ExtensionUserActivationService* Get(content::BrowserContext* context); + + // Notifies the service that the extension was activated by a user gesture. + void NotifyUserActivation(const ExtensionId& extension_id); + + // Returns true if the extension has a transient activation (i.e. it was + // activated by a user gesture within the last 5 seconds). + bool HasTransientActivation(const ExtensionId& extension_id) const; + + private: + void RemoveActivation(const ExtensionId& extension_id); + + std::map<ExtensionId, base::OneShotTimer> user_activation_timers_; +}; + +} // namespace extensions + +#endif // EXTENSIONS_BROWSER_EXTENSION_USER_ACTIVATION_SERVICE_H_
diff --git a/extensions/browser/extension_user_activation_service_factory.cc b/extensions/browser/extension_user_activation_service_factory.cc new file mode 100644 index 0000000..e7fa8aa --- /dev/null +++ b/extensions/browser/extension_user_activation_service_factory.cc
@@ -0,0 +1,48 @@ +// Copyright 2026 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "extensions/browser/extension_user_activation_service_factory.h" + +#include "components/keyed_service/content/browser_context_dependency_manager.h" +#include "extensions/browser/extension_user_activation_service.h" +#include "extensions/browser/extensions_browser_client.h" + +namespace extensions { + +// static +ExtensionUserActivationService* +ExtensionUserActivationServiceFactory::GetForBrowserContext( + content::BrowserContext* context) { + return static_cast<ExtensionUserActivationService*>( + GetInstance()->GetServiceForBrowserContext(context, true)); +} + +// static +ExtensionUserActivationServiceFactory* +ExtensionUserActivationServiceFactory::GetInstance() { + static base::NoDestructor<ExtensionUserActivationServiceFactory> instance; + return instance.get(); +} + +ExtensionUserActivationServiceFactory::ExtensionUserActivationServiceFactory() + : BrowserContextKeyedServiceFactory( + "ExtensionUserActivationService", + BrowserContextDependencyManager::GetInstance()) {} + +ExtensionUserActivationServiceFactory:: + ~ExtensionUserActivationServiceFactory() = default; + +std::unique_ptr<KeyedService> +ExtensionUserActivationServiceFactory::BuildServiceInstanceForBrowserContext( + content::BrowserContext* context) const { + return std::make_unique<ExtensionUserActivationService>(); +} + +content::BrowserContext* +ExtensionUserActivationServiceFactory::GetBrowserContextToUse( + content::BrowserContext* context) const { + return ExtensionsBrowserClient::Get()->GetContextOwnInstance(context); +} + +} // namespace extensions
diff --git a/extensions/browser/extension_user_activation_service_factory.h b/extensions/browser/extension_user_activation_service_factory.h new file mode 100644 index 0000000..4650b61 --- /dev/null +++ b/extensions/browser/extension_user_activation_service_factory.h
@@ -0,0 +1,42 @@ +// Copyright 2026 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef EXTENSIONS_BROWSER_EXTENSION_USER_ACTIVATION_SERVICE_FACTORY_H_ +#define EXTENSIONS_BROWSER_EXTENSION_USER_ACTIVATION_SERVICE_FACTORY_H_ + +#include "base/no_destructor.h" +#include "components/keyed_service/content/browser_context_keyed_service_factory.h" + +namespace extensions { + +class ExtensionUserActivationService; + +class ExtensionUserActivationServiceFactory + : public BrowserContextKeyedServiceFactory { + public: + ExtensionUserActivationServiceFactory( + const ExtensionUserActivationServiceFactory&) = delete; + ExtensionUserActivationServiceFactory& operator=( + const ExtensionUserActivationServiceFactory&) = delete; + + static ExtensionUserActivationService* GetForBrowserContext( + content::BrowserContext* context); + static ExtensionUserActivationServiceFactory* GetInstance(); + + private: + friend base::NoDestructor<ExtensionUserActivationServiceFactory>; + + ExtensionUserActivationServiceFactory(); + ~ExtensionUserActivationServiceFactory() override; + + // BrowserContextKeyedServiceFactory: + std::unique_ptr<KeyedService> BuildServiceInstanceForBrowserContext( + content::BrowserContext* context) const override; + content::BrowserContext* GetBrowserContextToUse( + content::BrowserContext* context) const override; +}; + +} // namespace extensions + +#endif // EXTENSIONS_BROWSER_EXTENSION_USER_ACTIVATION_SERVICE_FACTORY_H_
diff --git a/extensions/browser/extension_user_activation_service_unittest.cc b/extensions/browser/extension_user_activation_service_unittest.cc new file mode 100644 index 0000000..6281d14a --- /dev/null +++ b/extensions/browser/extension_user_activation_service_unittest.cc
@@ -0,0 +1,96 @@ +// Copyright 2026 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "extensions/browser/extension_user_activation_service.h" + +#include "base/test/task_environment.h" +#include "content/public/test/browser_task_environment.h" +#include "content/public/test/test_browser_context.h" +#include "extensions/browser/extensions_test.h" +#include "extensions/common/extension_id.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace extensions { + +class ExtensionUserActivationServiceTest : public ExtensionsTest { + public: + ExtensionUserActivationServiceTest() + : ExtensionsTest(content::BrowserTaskEnvironment::TimeSource::MOCK_TIME) { + } + ~ExtensionUserActivationServiceTest() override = default; + + void SetUp() override { + ExtensionsTest::SetUp(); + service_ = std::make_unique<ExtensionUserActivationService>(); + } + + void TearDown() override { + service_.reset(); + ExtensionsTest::TearDown(); + } + + protected: + ExtensionUserActivationService* service() { return service_.get(); } + + private: + std::unique_ptr<ExtensionUserActivationService> service_; +}; + +TEST_F(ExtensionUserActivationServiceTest, TransientActivation) { + const ExtensionId kExtensionId = std::string("foo"); + + EXPECT_FALSE(service()->HasTransientActivation(kExtensionId)); + + service()->NotifyUserActivation(kExtensionId); + EXPECT_TRUE(service()->HasTransientActivation(kExtensionId)); + + // Advance time by 3 seconds. The activation should still be valid. + task_environment()->FastForwardBy(base::Seconds(3)); + EXPECT_TRUE(service()->HasTransientActivation(kExtensionId)); + + // Advance time by 3 more seconds. The activation should expire. + task_environment()->FastForwardBy(base::Seconds(3)); + EXPECT_FALSE(service()->HasTransientActivation(kExtensionId)); +} + +TEST_F(ExtensionUserActivationServiceTest, + TransientActivation_MultipleGestures) { + const ExtensionId kExtensionId = std::string("foo"); + + EXPECT_FALSE(service()->HasTransientActivation(kExtensionId)); + + // First gesture. + service()->NotifyUserActivation(kExtensionId); + EXPECT_TRUE(service()->HasTransientActivation(kExtensionId)); + + // Advance time by 3 seconds. The activation should still be valid. + task_environment()->FastForwardBy(base::Seconds(3)); + EXPECT_TRUE(service()->HasTransientActivation(kExtensionId)); + + // Second gesture. Should reset the timer. + service()->NotifyUserActivation(kExtensionId); + + // Advance time by 3 more seconds. + task_environment()->FastForwardBy(base::Seconds(3)); + EXPECT_TRUE(service()->HasTransientActivation(kExtensionId)); +} + +TEST_F(ExtensionUserActivationServiceTest, + TransientActivation_MultipleExtensions) { + const ExtensionId kExtensionId1 = std::string("foo"); + const ExtensionId kExtensionId2 = std::string("bar"); + + EXPECT_FALSE(service()->HasTransientActivation(kExtensionId1)); + EXPECT_FALSE(service()->HasTransientActivation(kExtensionId2)); + + service()->NotifyUserActivation(kExtensionId1); + EXPECT_TRUE(service()->HasTransientActivation(kExtensionId1)); + EXPECT_FALSE(service()->HasTransientActivation(kExtensionId2)); + + task_environment()->FastForwardBy(base::Seconds(6)); + EXPECT_FALSE(service()->HasTransientActivation(kExtensionId1)); + EXPECT_FALSE(service()->HasTransientActivation(kExtensionId2)); +} + +} // namespace extensions
diff --git a/extensions/browser/extension_util.cc b/extensions/browser/extension_util.cc index 95b20cc..81c2658 100644 --- a/extensions/browser/extension_util.cc +++ b/extensions/browser/extension_util.cc
@@ -391,7 +391,8 @@ // Navigating to a disabled (or uninstalled or not-yet-installed) extension // will set the site URL to chrome-extension://invalid. - ExtensionId maybe_extension_id = site_instance.GetSiteURL().GetHost(); + ExtensionId maybe_extension_id = + site_instance.GetSecurityPrincipal().GetDeprecatedSiteURL().GetHost(); if (maybe_extension_id == "invalid") { return ExtensionId(); } @@ -413,7 +414,7 @@ return std::string(); } - return site_instance->GetSiteURL().GetHost(); + return site_instance->GetSecurityPrincipal().GetDeprecatedSiteURL().GetHost(); } bool CanRendererHostExtensionOrigin(int render_process_id,
diff --git a/extensions/browser/extensions_browser_client.cc b/extensions/browser/extensions_browser_client.cc index 22ddd8fe..66ccf5e 100644 --- a/extensions/browser/extensions_browser_client.cc +++ b/extensions/browser/extensions_browser_client.cc
@@ -235,7 +235,8 @@ bool in_memory, base::OnceCallback<void(std::optional<content::StoragePartitionConfig>)> callback) { - const GURL& owner_site_url = owner_site_instance->GetSiteURL(); + const GURL& owner_site_url = + owner_site_instance->GetSecurityPrincipal().GetDeprecatedSiteURL(); auto partition_config = content::StoragePartitionConfig::Create( browser_context, owner_site_url.GetHost(), partition_name, in_memory);
diff --git a/extensions/browser/guest_view/web_view/web_view_guest.cc b/extensions/browser/guest_view/web_view/web_view_guest.cc index 6deb733..c1f8ae8 100644 --- a/extensions/browser/guest_view/web_view/web_view_guest.cc +++ b/extensions/browser/guest_view/web_view/web_view_guest.cc
@@ -366,9 +366,7 @@ // Clean up web request event listeners for the WebView. WebRequestEventRouter::Get(browser_context) - // TODO(crbug.com/379869738): remove GetUnsafeValue - ->RemoveWebViewEventListeners(browser_context, - embedder_process_id.GetUnsafeValue(), + ->RemoveWebViewEventListeners(browser_context, embedder_process_id, view_instance_id); // Clean up content scripts for the WebView.
diff --git a/extensions/common/api/automation.webidl b/extensions/common/api/automation.webidl index 3cbec3f..27e3c92 100644 --- a/extensions/common/api/automation.webidl +++ b/extensions/common/api/automation.webidl
@@ -46,7 +46,7 @@ "focus", "focusAfterMenuClose", "focusChanged", - "focusContext", + "focusContextDeprecated", "grabbedChanged", "grammarMarkerChanged", "haspopupChanged",
diff --git a/extensions/common/extension_features.cc b/extensions/common/extension_features.cc index 62aefcd..78a11e49 100644 --- a/extensions/common/extension_features.cc +++ b/extensions/common/extension_features.cc
@@ -57,10 +57,20 @@ "glic_require_consent_for_invoke", false); -const base::FeatureParam<bool> kGlicOpenNewTabInForegroundParam( - &kApiGlicAccessFromGoogleWebpage, - "glic_open_new_tab_in_foreground", - true); +const base::FeatureParam<GlicOpenNewTabDisposition>::Option + kGlicOpenNewTabDispositionOptions[] = { + {GlicOpenNewTabDisposition::kForeground, + kGlicOpenNewTabDispositionForeground}, + {GlicOpenNewTabDisposition::kBackground, + kGlicOpenNewTabDispositionBackground}, + {GlicOpenNewTabDisposition::kForegroundIfNotConsented, + kGlicOpenNewTabDispositionForegroundIfNotConsented}}; + +const base::FeatureParam<GlicOpenNewTabDisposition> + kGlicOpenNewTabDispositionParam{&kApiGlicAccessFromGoogleWebpage, + "glic_open_new_tab_disposition", + GlicOpenNewTabDisposition::kForeground, + &kGlicOpenNewTabDispositionOptions}; BASE_FEATURE(kApiProxyOverrideRulesPrivate, base::FEATURE_ENABLED_BY_DEFAULT);
diff --git a/extensions/common/extension_features.h b/extensions/common/extension_features.h index 07e00d9..7cea8e0 100644 --- a/extensions/common/extension_features.h +++ b/extensions/common/extension_features.h
@@ -81,7 +81,21 @@ extern const base::FeatureParam<std::string> kProdPromptEndpointUrlParam; extern const base::FeatureParam<std::string> kGlicInvokeApiOAuth2ScopeParam; extern const base::FeatureParam<bool> kGlicRequireConsentForInvokeParam; -extern const base::FeatureParam<bool> kGlicOpenNewTabInForegroundParam; + +enum class GlicOpenNewTabDisposition { + kForeground, // Always open in foreground. + kBackground, // Always open in background. + kForegroundIfNotConsented, // Open in foreground if user has not consented, + // else in background. +}; +extern const base::FeatureParam<GlicOpenNewTabDisposition> + kGlicOpenNewTabDispositionParam; + +// String constants for GlicOpenNewTabDisposition. +inline constexpr char kGlicOpenNewTabDispositionForeground[] = "foreground"; +inline constexpr char kGlicOpenNewTabDispositionBackground[] = "background"; +inline constexpr char kGlicOpenNewTabDispositionForegroundIfNotConsented[] = + "foreground_if_not_consented"; // Controls the availability of the new `proxyOverrideRulesPrivate` API. BASE_DECLARE_FEATURE(kApiProxyOverrideRulesPrivate);
diff --git a/extensions/shell/browser/shell_content_browser_client.cc b/extensions/shell/browser/shell_content_browser_client.cc index ea6d552..fbf8ade 100644 --- a/extensions/shell/browser/shell_content_browser_client.cc +++ b/extensions/shell/browser/shell_content_browser_client.cc
@@ -379,7 +379,7 @@ ExtensionRegistry* registry = ExtensionRegistry::Get(site_instance->GetBrowserContext()); return registry->enabled_extensions().GetExtensionOrAppByURL( - site_instance->GetSiteURL()); + site_instance->GetSecurityPrincipal().GetDeprecatedSiteURL()); } } // namespace extensions
diff --git a/gpu/command_buffer/service/dawn_platform.cc b/gpu/command_buffer/service/dawn_platform.cc index 623f1a0..f6795b8 100644 --- a/gpu/command_buffer/service/dawn_platform.cc +++ b/gpu/command_buffer/service/dawn_platform.cc
@@ -360,6 +360,8 @@ case dawn::platform::Features::kWebGPUDecomposeUniformBuffers: return base::FeatureList::IsEnabled( features::kWebGPUDecomposeUniformBuffers); + case dawn::platform::Features::kWebGPUUseHLSL2021: + return base::FeatureList::IsEnabled(features::kWebGPUUseHLSL2021); default: return false; }
diff --git a/gpu/command_buffer/service/shared_image/compound_image_backing.cc b/gpu/command_buffer/service/shared_image/compound_image_backing.cc index 4f70b38b6..45c1821 100644 --- a/gpu/command_buffer/service/shared_image/compound_image_backing.cc +++ b/gpu/command_buffer/service/shared_image/compound_image_backing.cc
@@ -1122,38 +1122,42 @@ RepresentationAccessMode mode, SharedImageAccessStream stream) { AutoLock auto_lock(this); - ElementHolder* access_element = GetElement(backing); - if (!access_element) { - LOG(ERROR) << "Backing (" << backing->GetName() - << ") not in the element list"; - return; - } - // If this element already has the latest content, we're good for read access. - if (access_element->content_id_ == latest_content_id_) { + // Identify if this backing is a permanent element of the container or a + // transient one allocated for a specific representation. + ElementHolder* access_element = GetElement(backing); + + // FAST PATH: If this is a permanent element and it already has the latest + // content, we can skip synchronization for read access. + if (access_element && access_element->content_id_ == latest_content_id_) { if (mode == RepresentationAccessMode::kWrite) { - // For write access, this backing is about to become the new latest. + // For write access, this backing will become the new "source of truth". ++latest_content_id_; access_element->content_id_ = latest_content_id_; } return; } - // This backing is stale. We need to find the element which has the latest - // content and copy from it. + // STALE OR TRANSIENT PATH: Either this permanent backing is out-of-date, or + // it's a transient backing that needs to be initialized. We must find the + // permanent element that currently holds the latest content and copy from it. ElementHolder* latest_content_element = GetElementWithLatestContent(); bool updated_backing = false; bool copy_succeeded = false; + if (latest_content_element) { + // Copy data from the latest permanent backing into the current backing + // (which could be another permanent backing or a transient one). copy_succeeded = copy_manager_->CopyImage( /*src_backing=*/latest_content_element->GetBacking(), - /*dst_backing=*/access_element->GetBacking()); + /*dst_backing=*/backing); + if (copy_succeeded) { updated_backing = true; - // Propagate the clear rect from the source backing to the destination - // backing as well as all the other child backings and - // CompoundImageBacking. + // When we sync data, we also sync the logical clear state. Propagate the + // clear rect from the source to the destination, all child backings, and + // the container itself. const gfx::Rect src_cleared_rect = latest_content_element->GetBacking()->ClearedRect(); SetClearedRectInternal(src_cleared_rect); @@ -1165,8 +1169,7 @@ } else { LOG(ERROR) << "Failed to copy from " << latest_content_element->GetBacking()->GetName() << " to " - << access_element->GetBacking()->GetName() - << ". Backing can be using stale data"; + << backing->GetName() << ". Backing can be using stale data"; } UMA_HISTOGRAM_BOOLEAN("GPU.CompoundImageBacking.ContentSync.Success", @@ -1176,7 +1179,7 @@ latest_content_element->GetBacking()->GetType()); UMA_HISTOGRAM_ENUMERATION( "GPU.CompoundImageBacking.ContentSync.DestBackingType", - access_element->GetBacking()->GetType()); + backing->GetType()); UMA_HISTOGRAM_ENUMERATION("GPU.CompoundImageBacking.ContentSync.Reason", mode == RepresentationAccessMode::kRead ? ContentSyncReason::kRead @@ -1188,16 +1191,19 @@ static_cast<int32_t>(static_cast<uint32_t>(this->usage()))); } - // Update content IDs. In case of write, we are updating the - // |latest_content_id_| as well as marking the |access_element| as having - // latest content irrespective of above copy failures since write will likely - // overwrite all of the previous content. Although not necessarily true for - // partial writes. For read, we only mark the |access_element| as having - // latest content if the copy succeeded. + // UPDATE VERSIONING: + // 1. For WRITE access: Increment the global version. If this is a permanent + // element, track that it now holds this latest version. Transient backings + // don't track their own version locally as they are destroyed after use, + // but their write will be synced back to permanent elements in EndAccess. + // 2. For READ access: If the copy succeeded, mark this permanent element as + // being up-to-date with the latest version. if (mode == RepresentationAccessMode::kWrite) { ++latest_content_id_; - access_element->content_id_ = latest_content_id_; - } else if (updated_backing) { + if (access_element) { + access_element->content_id_ = latest_content_id_; + } + } else if (updated_backing && access_element) { access_element->content_id_ = latest_content_id_; } } @@ -1207,34 +1213,31 @@ AutoLock auto_lock(this); CHECK(backing); + bool is_transient_backing = !GetElement(backing); + // If the last access was a write and an underlying backing was accessed, // propagate its cleared rect to the compound backing if it's different. if (mode == RepresentationAccessMode::kWrite) { auto cleared_rect = backing->ClearedRect(); if (cleared_rect != ClearedRect()) { - SetClearedRectInternal(cleared_rect); + // For transient backings, only update if it's more cleared to avoid + // overwriting CSI with stale state. + if (!is_transient_backing || cleared_rect.Contains(ClearedRect())) { + SetClearedRectInternal(cleared_rect); + } } // When is_thread_safe() is true, multiple threads can access the backings. - // If a GL backing was written to, we proactively sync its content to all - // other backings. This is required to support multithreading scenarios - // where GL access happens on the GPU main thread and skia or other kind of - // access happens on a different thread. Note that this is only enabled when - // kUseDynamicBackingAllocations is enabled where CompoundImageBacking can - // dynamically allocate GL backing on a different thread during runtime. - // When dynamic allocations are not enabled, existing backings already - // handles the proactive sync where needed (eg: - // WrappedGraphiteTextureBacking via - // GLTexturePassthroughFallbackImageRepresentation). Note that this solution - // will not work if we start with GLTextureImageBacking and than want to - // reallocate on a different thread where we can't access GL texture. We - // will need to handle that case with another solution. - // Also note that we are copying back to all the backings here, even to - // those that will never be read. This can be a performance bottleneck when - // there are multiple backings. + // If a backing was written to, we proactively sync its content to all other + // backings. This is especially critical for transient backings (which are + // not thread-safe but used in a thread-safe container): we must copy their + // latest content to the permanent backings before the transient backing is + // destroyed. This ensures that subsequent accesses on other threads will + // see the updated data. Note that we are copying back to all the backings + // here, even to those that will never be read. This can be a performance + // bottleneck when there are multiple backings. if (base::FeatureList::IsEnabled(features::kUseDynamicBackingAllocations) && - is_thread_safe() && - (backing->GetType() == SharedImageBackingType::kGLTexture)) { + is_thread_safe() && is_transient_backing) { for (auto& element : elements_) { auto* dst_backing = element.GetBacking(); if (dst_backing && dst_backing != backing && @@ -1791,7 +1794,7 @@ CompoundImageBacking::ElementHolder* CompoundImageBacking::GetElement( const SharedImageBacking* backing) { for (auto& element : elements_) { - if (element.GetBacking() == backing) { + if (element.backing.get() == backing) { return &element; } } @@ -1856,14 +1859,13 @@ }); if (gpu_backing_factory) { - ElementHolder element; - element.access_streams.Put(stream); + std::unique_ptr<SharedImageBacking> new_backing; CreateBackingFromBackingFactory(gpu_backing_factory->GetWeakPtr(), - debug_label(), usage, element.backing); - if (element.backing) { + debug_label(), usage, new_backing); + if (new_backing) { UMA_HISTOGRAM_ENUMERATION( "GPU.CompoundImageBacking.DynamicAllocation.BackingType", - element.backing->GetType()); + new_backing->GetType()); UMA_HISTOGRAM_ENUMERATION( "GPU.CompoundImageBacking.DynamicAllocation.AccessStream", stream); UMA_HISTOGRAM_SPARSE( @@ -1871,6 +1873,23 @@ "InitialSharedImageUsage", static_cast<int32_t>(static_cast<uint32_t>(this->usage()))); + // If the CSI container is thread-safe, we treat newly created backings + // as transient if they are not thread-safe. They will be owned by the + // representation and destroyed after use. This ensures that a + // non-thread-safe backing allocated on one thread doesn't persist in + // the thread-safe container, which could lead to race conditions if + // accessed from another thread later. + if (is_thread_safe() && !new_backing->is_thread_safe()) { + out_transient_backing = std::move(new_backing); + return out_transient_backing.get(); + } + + // Else we treat the backing as non-transient and add it to the list of + // alive elements. This is done when either the container is not + // thread-safe or the new backing itself is thread-safe. + ElementHolder element; + element.access_streams.Put(stream); + element.backing = std::move(new_backing); elements_.push_back(std::move(element)); if (elements_.size() > max_elements_allocated_) { max_elements_allocated_ = elements_.size();
diff --git a/gpu/command_buffer/service/shared_image/compound_image_backing.h b/gpu/command_buffer/service/shared_image/compound_image_backing.h index 094614f..5a04afc 100644 --- a/gpu/command_buffer/service/shared_image/compound_image_backing.h +++ b/gpu/command_buffer/service/shared_image/compound_image_backing.h
@@ -135,14 +135,17 @@ // |elements_| vector, from concurrent access during Produce*() calls. // // 3. Underlying Backings thread safety: The underlying backings/elements -// currently themselves may or may not be thread-safe. Future changes will make -// the underlying backings thread-safety needs more robust. The dynamically -// allocated backings are currently never thread safe which is an issue and will -// be addressed in future. Note that indiscriminately making all backings -// thread-safe (especially for internal images like Render Passes which are -// typically only accessed on the display compositor thread, such as Android RT -// for WebView) could lead to performance regressions. Future refinements -// should account for these single-threaded but high-traffic use cases. +// themselves may or may not be thread-safe. When the container is thread-safe +// (e.g. for DrDC or WebView), any dynamically allocated backing that is not +// thread-safe is treated as "transient." It is owned by the representation and +// destroyed after use, rather than being added to the container's permanent +// elements. This ensures that non-thread-safe backings are never accessed +// concurrently across threads. Upon completion of access, any writes to a +// transient backing are synchronized back to the container's permanent +// backings. Note that indiscriminately making all backings thread-safe +// (especially for internal images like Render Passes) could lead to performance +// regressions, so this transient approach provides safety without unnecessary +// overhead. class GPU_GLES2_EXPORT CompoundImageBacking : public ClearTrackingSharedImageBacking { public:
diff --git a/gpu/config/gpu_finch_features.cc b/gpu/config/gpu_finch_features.cc index 29f61656..6e2fe17 100644 --- a/gpu/config/gpu_finch_features.cc +++ b/gpu/config/gpu_finch_features.cc
@@ -262,6 +262,8 @@ BASE_FEATURE(kWebGPUDecomposeUniformBuffers, base::FEATURE_ENABLED_BY_DEFAULT); +BASE_FEATURE(kWebGPUUseHLSL2021, base::FEATURE_DISABLED_BY_DEFAULT); + #if BUILDFLAG(IS_ANDROID) // Blocklists meant for DrDc.
diff --git a/gpu/config/gpu_finch_features.h b/gpu/config/gpu_finch_features.h index e103798..52ab8e7 100644 --- a/gpu/config/gpu_finch_features.h +++ b/gpu/config/gpu_finch_features.h
@@ -118,6 +118,7 @@ GPU_CONFIG_EXPORT BASE_DECLARE_FEATURE(kWebGPUAndroidOpenGLES); GPU_CONFIG_EXPORT BASE_DECLARE_FEATURE(kWebGPUUseSpirv14); GPU_CONFIG_EXPORT BASE_DECLARE_FEATURE(kWebGPUDecomposeUniformBuffers); +GPU_CONFIG_EXPORT BASE_DECLARE_FEATURE(kWebGPUUseHLSL2021); #if BUILDFLAG(IS_WIN) GPU_CONFIG_EXPORT BASE_DECLARE_FEATURE(kWebGPUQualcommWindows); #endif
diff --git a/gpu/ipc/common/vulkan_ycbcr_info_mojom_traits.h b/gpu/ipc/common/vulkan_ycbcr_info_mojom_traits.h index e349c8bd..0048aef 100644 --- a/gpu/ipc/common/vulkan_ycbcr_info_mojom_traits.h +++ b/gpu/ipc/common/vulkan_ycbcr_info_mojom_traits.h
@@ -51,6 +51,22 @@ out->suggested_xchroma_offset = data.suggested_xchroma_offset(); out->suggested_ychroma_offset = data.suggested_ychroma_offset(); out->format_features = data.format_features(); + + // Values from Vulkan definitions, because we can't easy depend on vulkan + // here. + // https://source.chromium.org/chromium/chromium/src/+/main:third_party/vulkan-headers/src/include/vulkan/vulkan_core.h;drc=f6a6f7ab165cedbfa2a7d0c93fe27a2d01ce09c8;l=5438 + const uint32_t VK_SAMPLER_YCBCR_MODEL_CONVERSION_YCBCR_2020 = 4; + const uint32_t VK_CHROMA_LOCATION_MIDPOINT = 1; + const uint32_t VK_SAMPLER_YCBCR_RANGE_ITU_NARROW = 1; + + if (out->suggested_ycbcr_model > + VK_SAMPLER_YCBCR_MODEL_CONVERSION_YCBCR_2020 || + out->suggested_ycbcr_range > VK_SAMPLER_YCBCR_RANGE_ITU_NARROW || + out->suggested_xchroma_offset > VK_CHROMA_LOCATION_MIDPOINT || + out->suggested_ychroma_offset > VK_CHROMA_LOCATION_MIDPOINT) { + return false; + } + return true; } };
diff --git a/gpu/vulkan/vulkan_util.cc b/gpu/vulkan/vulkan_util.cc index ea1c3ad..9ea19ad2 100644 --- a/gpu/vulkan/vulkan_util.cc +++ b/gpu/vulkan/vulkan_util.cc
@@ -105,88 +105,8 @@ return false; } -BASE_FEATURE(kVulkanV2, base::FEATURE_ENABLED_BY_DEFAULT); -BASE_FEATURE(kVulkanV3, base::FEATURE_DISABLED_BY_DEFAULT); - -bool IsDeviceBlockedByFeatureParams(const GPUInfo& gpu_info, - const base::Feature* feature) { - const base::FeatureParam<std::string> kBlockListByHardware{ - feature, "BlockListByHardware", ""}; - - const base::FeatureParam<std::string> kBlockListByBrand{ - feature, "BlockListByBrand", ""}; - - const base::FeatureParam<std::string> kBlockListByDevice{ - feature, "BlockListByDevice", ""}; - - const base::FeatureParam<std::string> kBlockListByAndroidBuildId{ - feature, "BlockListByAndroidBuildId", ""}; - - const base::FeatureParam<std::string> kBlockListByManufacturer{ - feature, "BlockListByManufacturer", ""}; - - const base::FeatureParam<std::string> kBlockListByModel{ - feature, "BlockListByModel", ""}; - - const base::FeatureParam<std::string> kBlockListByBoard{ - feature, "BlockListByBoard", ""}; - - const base::FeatureParam<std::string> kBlockListByAndroidBuildFP{ - feature, "BlockListByAndroidBuildFP", ""}; - - const base::FeatureParam<std::string> kBlockListByGLDriver{ - feature, "BlockListByGLDriver", ""}; - - const base::FeatureParam<std::string> kBlockListByGLRenderer{ - feature, "BlockListByGLRenderer", ""}; - - // Check block list against build info. - if (IsDeviceBlocked(base::android::android_info::hardware(), - kBlockListByHardware.Get())) { - return true; - } - if (IsDeviceBlocked(base::android::android_info::brand(), - kBlockListByBrand.Get())) { - return true; - } - if (IsDeviceBlocked(base::android::android_info::device(), - kBlockListByDevice.Get())) { - return true; - } - if (IsDeviceBlocked(base::android::android_info::android_build_id(), - kBlockListByAndroidBuildId.Get())) { - return true; - } - if (IsDeviceBlocked(base::android::android_info::manufacturer(), - kBlockListByManufacturer.Get())) { - return true; - } - if (IsDeviceBlocked(base::android::android_info::model(), - kBlockListByModel.Get())) { - return true; - } - if (IsDeviceBlocked(base::android::android_info::board(), - kBlockListByBoard.Get())) { - return true; - } - if (IsDeviceBlocked(base::android::android_info::android_build_fp(), - kBlockListByAndroidBuildFP.Get())) { - return true; - } - - if (IsDeviceBlocked(gpu_info.gl_renderer, kBlockListByGLRenderer.Get())) { - return true; - } - - if (IsDeviceBlocked(gpu_info.gpu.driver_version, - kBlockListByGLDriver.Get())) { - return true; - } - - return false; -} - -bool IsVulkanV2Allowed() { +// Everything that passed 2022 deQP tests. +bool HasMinDeqpLevelForMediaTek() { // We require at least android V deqp test to pass for v2. constexpr int32_t kVulkanDEQPAndroidV = 0x7e80301; if (base::android::device_info::vulkan_deqp_level() < kVulkanDEQPAndroidV) { @@ -195,122 +115,6 @@ return true; } - -bool IsVulkanV2Enabled(const GPUInfo& gpu_info, - std::string_view experiment_arm) { - if (!IsVulkanV2Allowed()) { - return false; - } - - if (!base::FeatureList::IsEnabled(kVulkanV2)) { - return false; - } - - if (IsDeviceBlockedByFeatureParams(gpu_info, &kVulkanV2)) { - return false; - } - - const base::FeatureParam<std::string> kBlockListByExperimentArm{ - &kVulkanV2, "BlockListByExperimentArm", ""}; - - if (IsDeviceBlocked(experiment_arm, kBlockListByExperimentArm.Get())) { - return false; - } - - return true; -} - -bool ShouldBypassMediatekBlock(const GPUInfo& gpu_info) { - return IsVulkanV2Enabled(gpu_info, "Mediatek"); -} - -// Everything except MediaTek. -bool IsVulkanV1EnabledForMali(const GPUInfo& gpu_info) { - // https://crbug.com/1183702 - if (IsDeviceBlocked(gpu_info.gl_renderer, "*Mali-G?? M*")) { - return false; - } - return true; -} - -// Everything that passed 2022 deQP tests. -bool IsVulkanV2EnabledForMali( - const GPUInfo& gpu_info, - const VulkanPhysicalDeviceProperties& device_properties) { - // Mali-G57 have problems initializing Vulkan even with 2022 deQP tests - // passed, devices that init successfully show performance regression. - if (base::StartsWith(device_properties.device_name, "Mali-G57")) { - return false; - } - - // For V2 we MediaTek is allowed. - return ShouldBypassMediatekBlock(gpu_info); -} - -// Only Adreno 630 with drivers newer than 444.0 -bool IsVulkanV1EnabledForAdreno( - const GPUInfo& gpu_info, - const VulkanPhysicalDeviceProperties& device_properties) { - // https://crbug.com/1246857 - if (IsDeviceBlocked(gpu_info.gpu.driver_version, - "324.0|331.0|334.0|378.0|415.0|420.0|444.0")) { - return false; - } - - // https:://crbug.com/1165783: Performance is not yet as good as GL. - return device_properties.device_name == std::string_view("Adreno (TM) 630"); -} - -bool IsVulkanV2EnabledForAdreno( - const GPUInfo& gpu_info, - const VulkanPhysicalDeviceProperties& device_properties) { - // Adreno shows regression even with 2022 deQP tests. - return false; -} - -// Adreno 610+ and drivers 502+. -bool IsVulkanV3EnabledForAdreno( - const GPUInfo& gpu_info, - const VulkanPhysicalDeviceProperties& device_properties) { - // If IsVulkanV2Allowed(), this device is part of VulkanV2 finch and we should - // not make decision again. This is to prevent VulkanV2 control group to get - // Vulkan enabled by getting into VulkanV3 enabled group. - if (IsVulkanV2Allowed()) { - return false; - } - - std::vector<const char*> slow_gpus_for_v3 = { - "Adreno (TM) 2??", - "Adreno (TM) 3??", - "Adreno (TM) 4??", - "Adreno (TM) 5??", - }; - - const bool is_slow_gpu_for_v3 = - std::ranges::any_of(slow_gpus_for_v3, [&](const char* pattern) { - return base::MatchPattern(device_properties.device_name, pattern); - }); - - if (is_slow_gpu_for_v3) { - return false; - } - - constexpr uint32_t kMinVersion = 0x801F6000; // 502.0 - if (device_properties.driver_version < kMinVersion) { - return false; - } - - if (!base::FeatureList::IsEnabled(kVulkanV3)) { - return false; - } - - if (IsDeviceBlockedByFeatureParams(gpu_info, &kVulkanV3)) { - return false; - } - - return true; -} - #endif } // namespace @@ -475,11 +279,11 @@ return true; #endif #else // BUILDFLAG(IS_ANDROID) - if (base::FeatureList::IsEnabled(features::kSkipVulkanBlocklist)) { + if (base::FeatureList::IsEnabled(features::kSkipVulkanBlocklist)) { return true; } - if (IsBlockedByBuildInfo() && !ShouldBypassMediatekBlock(gpu_info)) { + if (IsBlockedByBuildInfo() && !HasMinDeqpLevelForMediaTek()) { return false; } @@ -523,14 +327,29 @@ return false; } - return IsVulkanV1EnabledForMali(gpu_info) || - IsVulkanV2EnabledForMali(gpu_info, device_properties); + // Allow remaining Mali GPUs that aren't MediaTek. https://crbug.com/1183702 + if (!IsDeviceBlocked(gpu_info.gl_renderer, "*Mali-G?? M*")) { + return true; + } + + // MediaTek Mali-G57 has problems initializing Vulkan even with 2022 deQP + // tests passed, devices that init successfully show performance regression. + if (device_name == "G57") { + return false; + } + + // For MediaTek allow everything that passed 2022 deQP tests. + return HasMinDeqpLevelForMediaTek(); } if (device_properties.vendor_id == kVendorQualcomm) { - return IsVulkanV1EnabledForAdreno(gpu_info, device_properties) || - IsVulkanV2EnabledForAdreno(gpu_info, device_properties) || - IsVulkanV3EnabledForAdreno(gpu_info, device_properties); + // Only Adreno 630 with drivers newer than 444.0. This was launched for + // Pixel 3 in the original Vulkan launch but otherwise Vulkan hasn't + // performan as well as GL. https:://crbug.com/1165783 + return device_properties.device_name == + std::string_view("Adreno (TM) 630") && + !IsDeviceBlocked(gpu_info.gpu.driver_version, + "324.0|331.0|334.0|378.0|415.0|420.0|444.0"); } if (device_properties.vendor_id == kVendorImagination) {
diff --git a/infra/config/generated/builders/ci/GPU FYI Linux Builder/targets/chromium.gpu.fyi.json b/infra/config/generated/builders/ci/GPU FYI Linux Builder/targets/chromium.gpu.fyi.json index 8b7098e2..3bfb5649 100644 --- a/infra/config/generated/builders/ci/GPU FYI Linux Builder/targets/chromium.gpu.fyi.json +++ b/infra/config/generated/builders/ci/GPU FYI Linux Builder/targets/chromium.gpu.fyi.json
@@ -121,283 +121,10 @@ ] }, "Linux FYI Experimental Release (AMD RX 5500XT)": { - "gtest_tests": [ - { - "args": [ - "--no-xvfb" - ], - "merge": { - "script": "//testing/merge_scripts/standard_isolated_script_merge.py" - }, - "module_name": "//third_party/angle/src/tests:angle_unittests", - "module_scheme": "gtest", - "name": "angle_unittests", - "swarming": { - "containment_type": "AUTO", - "dimensions": { - "display_attached": "1", - "display_server": "x11", - "gpu": "1002:7340-25.2.8", - "os": "Ubuntu-24.04", - "pool": "chromium.tests.gpu" - }, - "expiration": 21600, - "hard_timeout": 1800, - "io_timeout": 1800, - "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" - }, - "test": "angle_unittests", - "test_id_prefix": "ninja://third_party/angle/src/tests:angle_unittests/", - "use_isolated_scripts_api": true - }, - { - "args": [ - "--use-cmd-decoder=passthrough", - "--use-gl=angle", - "--use-gpu-in-tests", - "--no-xvfb" - ], - "merge": { - "script": "//testing/merge_scripts/standard_gtest_merge.py" - }, - "module_name": "//gpu:gl_tests", - "module_scheme": "gtest", - "name": "gl_tests_passthrough", - "swarming": { - "containment_type": "AUTO", - "dimensions": { - "display_attached": "1", - "display_server": "x11", - "gpu": "1002:7340-25.2.8", - "os": "Ubuntu-24.04", - "pool": "chromium.tests.gpu" - }, - "expiration": 21600, - "hard_timeout": 1800, - "io_timeout": 1800, - "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com", - "shards": 2 - }, - "test": "gl_tests", - "test_id_prefix": "ninja://gpu:gl_tests/" - }, - { - "args": [ - "--use-gpu-in-tests", - "--no-xvfb", - "--git-revision=${got_revision}" - ], - "merge": { - "script": "//testing/merge_scripts/standard_gtest_merge.py" - }, - "module_name": "//ui/gl:gl_unittests", - "module_scheme": "gtest", - "name": "gl_unittests", - "precommit_args": [ - "--gerrit-issue=${patch_issue}", - "--gerrit-patchset=${patch_set}", - "--buildbucket-id=${buildbucket_build_id}" - ], - "swarming": { - "containment_type": "AUTO", - "dimensions": { - "display_attached": "1", - "display_server": "x11", - "gpu": "1002:7340-25.2.8", - "os": "Ubuntu-24.04", - "pool": "chromium.tests.gpu" - }, - "expiration": 21600, - "hard_timeout": 1800, - "io_timeout": 1800, - "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" - }, - "test": "gl_unittests", - "test_id_prefix": "ninja://ui/gl:gl_unittests/" - }, - { - "args": [ - "--enable-gpu", - "--use-gpu-in-tests", - "--gtest_filter=*MappableBufferTest*", - "--no-xvfb" - ], - "merge": { - "script": "//testing/merge_scripts/standard_gtest_merge.py" - }, - "module_name": "//gpu:gpu_unittests", - "module_scheme": "gtest", - "name": "mappable_buffer_tests", - "swarming": { - "containment_type": "AUTO", - "dimensions": { - "display_attached": "1", - "display_server": "x11", - "gpu": "1002:7340-25.2.8", - "os": "Ubuntu-24.04", - "pool": "chromium.tests.gpu" - }, - "expiration": 21600, - "hard_timeout": 1800, - "io_timeout": 1800, - "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" - }, - "test": "gpu_unittests", - "test_id_prefix": "ninja://gpu:gpu_unittests/" - }, - { - "args": [ - "--enable-gpu", - "--test-launcher-bot-mode", - "--test-launcher-jobs=1", - "--gtest_filter=TabCaptureApiPixelTest.EndToEnd*", - "--no-xvfb" - ], - "merge": { - "script": "//testing/merge_scripts/standard_gtest_merge.py" - }, - "module_name": "//chrome/test:browser_tests", - "module_scheme": "gtest", - "name": "tab_capture_end2end_tests", - "swarming": { - "containment_type": "AUTO", - "dimensions": { - "display_attached": "1", - "display_server": "x11", - "gpu": "1002:7340-25.2.8", - "os": "Ubuntu-24.04", - "pool": "chromium.tests.gpu" - }, - "expiration": 21600, - "hard_timeout": 1800, - "io_timeout": 1800, - "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" - }, - "test": "browser_tests", - "test_id_prefix": "ninja://chrome/test:browser_tests/" - }, - { - "args": [ - "--use-gpu-in-tests", - "--no-xvfb" - ], - "merge": { - "script": "//testing/merge_scripts/standard_gtest_merge.py" - }, - "module_name": "//gpu/vulkan:vulkan_tests", - "module_scheme": "gtest", - "name": "vulkan_tests", - "swarming": { - "containment_type": "AUTO", - "dimensions": { - "display_attached": "1", - "display_server": "x11", - "gpu": "1002:7340-25.2.8", - "os": "Ubuntu-24.04", - "pool": "chromium.tests.gpu" - }, - "expiration": 21600, - "hard_timeout": 1800, - "io_timeout": 1800, - "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" - }, - "test": "vulkan_tests", - "test_id_prefix": "ninja://gpu/vulkan:vulkan_tests/" - } - ], "isolated_scripts": [ { "args": [ - "context_lost", - "--show-stdout", - "--browser=release", - "--passthrough", - "-v", - "--stable-jobs", - "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc --use-cmd-decoder=passthrough --use-gl=angle", - "--enforce-browser-version", - "--jobs=4" - ], - "merge": { - "script": "//testing/merge_scripts/standard_isolated_script_merge.py" - }, - "module_name": "//chrome/test:telemetry_gpu_integration_test", - "module_scheme": "flat", - "name": "context_lost_passthrough_tests", - "resultdb": { - "enable": true, - "has_native_resultdb_integration": true - }, - "swarming": { - "containment_type": "AUTO", - "dimensions": { - "display_attached": "1", - "display_server": "x11", - "gpu": "1002:7340-25.2.8", - "os": "Ubuntu-24.04", - "pool": "chromium.tests.gpu" - }, - "expiration": 21600, - "hard_timeout": 1800, - "idempotent": false, - "io_timeout": 1800, - "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" - }, - "test": "telemetry_gpu_integration_test", - "test_id_prefix": "ninja://chrome/test:telemetry_gpu_integration_test/" - }, - { - "args": [ - "expected_color", - "--show-stdout", - "--browser=release", - "--passthrough", - "-v", - "--stable-jobs", - "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc --use-cmd-decoder=passthrough --use-gl=angle", - "--enforce-browser-version", - "--git-revision=${got_revision}", - "--dont-restore-color-profile-after-test", - "--test-machine-name", - "${buildername}", - "--jobs=4" - ], - "merge": { - "script": "//testing/merge_scripts/standard_isolated_script_merge.py" - }, - "module_name": "//chrome/test:telemetry_gpu_integration_test", - "module_scheme": "flat", - "name": "expected_color_pixel_passthrough_test", - "precommit_args": [ - "--gerrit-issue=${patch_issue}", - "--gerrit-patchset=${patch_set}", - "--buildbucket-id=${buildbucket_build_id}" - ], - "resultdb": { - "enable": true, - "has_native_resultdb_integration": true - }, - "swarming": { - "containment_type": "AUTO", - "dimensions": { - "display_attached": "1", - "display_server": "x11", - "gpu": "1002:7340-25.2.8", - "os": "Ubuntu-24.04", - "pool": "chromium.tests.gpu" - }, - "expiration": 21600, - "hard_timeout": 1800, - "idempotent": false, - "io_timeout": 1800, - "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" - }, - "test": "telemetry_gpu_integration_test", - "test_id_prefix": "ninja://chrome/test:telemetry_gpu_integration_test/" - }, - { - "args": [ - "gpu_process", + "noop_sleep", "--show-stdout", "--browser=release", "--passthrough", @@ -412,11 +139,7 @@ }, "module_name": "//chrome/test:telemetry_gpu_integration_test", "module_scheme": "flat", - "name": "gpu_process_launch_tests", - "resultdb": { - "enable": true, - "has_native_resultdb_integration": true - }, + "name": "noop_sleep_tests", "swarming": { "containment_type": "AUTO", "dimensions": { @@ -434,345 +157,6 @@ }, "test": "telemetry_gpu_integration_test", "test_id_prefix": "ninja://chrome/test:telemetry_gpu_integration_test/" - }, - { - "args": [ - "hardware_accelerated_feature", - "--show-stdout", - "--browser=release", - "--passthrough", - "-v", - "--stable-jobs", - "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc", - "--enforce-browser-version", - "--jobs=4" - ], - "merge": { - "script": "//testing/merge_scripts/standard_isolated_script_merge.py" - }, - "module_name": "//chrome/test:telemetry_gpu_integration_test", - "module_scheme": "flat", - "name": "hardware_accelerated_feature_tests", - "resultdb": { - "enable": true, - "has_native_resultdb_integration": true - }, - "swarming": { - "containment_type": "AUTO", - "dimensions": { - "display_attached": "1", - "display_server": "x11", - "gpu": "1002:7340-25.2.8", - "os": "Ubuntu-24.04", - "pool": "chromium.tests.gpu" - }, - "expiration": 21600, - "hard_timeout": 1800, - "idempotent": false, - "io_timeout": 1800, - "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" - }, - "test": "telemetry_gpu_integration_test", - "test_id_prefix": "ninja://chrome/test:telemetry_gpu_integration_test/" - }, - { - "args": [ - "info_collection", - "--show-stdout", - "--browser=release", - "--passthrough", - "-v", - "--stable-jobs", - "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc --force_high_performance_gpu", - "--enforce-browser-version", - "--expected-vendor-id", - "1002", - "--expected-device-id", - "7340", - "--jobs=4" - ], - "merge": { - "script": "//testing/merge_scripts/standard_isolated_script_merge.py" - }, - "module_name": "//chrome/test:telemetry_gpu_integration_test", - "module_scheme": "flat", - "name": "info_collection_tests", - "resultdb": { - "enable": true, - "has_native_resultdb_integration": true - }, - "swarming": { - "containment_type": "AUTO", - "dimensions": { - "display_attached": "1", - "display_server": "x11", - "gpu": "1002:7340-25.2.8", - "os": "Ubuntu-24.04", - "pool": "chromium.tests.gpu" - }, - "expiration": 21600, - "hard_timeout": 1800, - "idempotent": false, - "io_timeout": 1800, - "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" - }, - "test": "telemetry_gpu_integration_test", - "test_id_prefix": "ninja://chrome/test:telemetry_gpu_integration_test/" - }, - { - "args": [ - "pixel", - "--show-stdout", - "--browser=release", - "--passthrough", - "-v", - "--stable-jobs", - "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc --use-cmd-decoder=passthrough --use-gl=angle", - "--enforce-browser-version", - "--git-revision=${got_revision}", - "--dont-restore-color-profile-after-test", - "--test-machine-name", - "${buildername}", - "--jobs=4" - ], - "merge": { - "script": "//testing/merge_scripts/standard_isolated_script_merge.py" - }, - "module_name": "//chrome/test:telemetry_gpu_integration_test", - "module_scheme": "flat", - "name": "pixel_skia_gold_passthrough_test", - "precommit_args": [ - "--gerrit-issue=${patch_issue}", - "--gerrit-patchset=${patch_set}", - "--buildbucket-id=${buildbucket_build_id}" - ], - "resultdb": { - "enable": true, - "has_native_resultdb_integration": true - }, - "swarming": { - "containment_type": "AUTO", - "dimensions": { - "display_attached": "1", - "display_server": "x11", - "gpu": "1002:7340-25.2.8", - "os": "Ubuntu-24.04", - "pool": "chromium.tests.gpu" - }, - "expiration": 21600, - "hard_timeout": 1800, - "idempotent": false, - "io_timeout": 1800, - "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" - }, - "test": "telemetry_gpu_integration_test", - "test_id_prefix": "ninja://chrome/test:telemetry_gpu_integration_test/" - }, - { - "args": [ - "screenshot_sync", - "--show-stdout", - "--browser=release", - "--passthrough", - "-v", - "--stable-jobs", - "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc --use-cmd-decoder=passthrough --use-gl=angle", - "--enforce-browser-version", - "--dont-restore-color-profile-after-test", - "--jobs=4" - ], - "merge": { - "script": "//testing/merge_scripts/standard_isolated_script_merge.py" - }, - "module_name": "//chrome/test:telemetry_gpu_integration_test", - "module_scheme": "flat", - "name": "screenshot_sync_passthrough_tests", - "resultdb": { - "enable": true, - "has_native_resultdb_integration": true - }, - "swarming": { - "containment_type": "AUTO", - "dimensions": { - "display_attached": "1", - "display_server": "x11", - "gpu": "1002:7340-25.2.8", - "os": "Ubuntu-24.04", - "pool": "chromium.tests.gpu" - }, - "expiration": 21600, - "hard_timeout": 1800, - "idempotent": false, - "io_timeout": 1800, - "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" - }, - "test": "telemetry_gpu_integration_test", - "test_id_prefix": "ninja://chrome/test:telemetry_gpu_integration_test/" - }, - { - "args": [ - "trace_test", - "--show-stdout", - "--browser=release", - "--passthrough", - "-v", - "--stable-jobs", - "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc", - "--enforce-browser-version", - "--jobs=4" - ], - "merge": { - "script": "//testing/merge_scripts/standard_isolated_script_merge.py" - }, - "module_name": "//chrome/test:telemetry_gpu_integration_test", - "module_scheme": "flat", - "name": "trace_test", - "resultdb": { - "enable": true, - "has_native_resultdb_integration": true - }, - "swarming": { - "containment_type": "AUTO", - "dimensions": { - "display_attached": "1", - "display_server": "x11", - "gpu": "1002:7340-25.2.8", - "os": "Ubuntu-24.04", - "pool": "chromium.tests.gpu" - }, - "expiration": 21600, - "hard_timeout": 1800, - "idempotent": false, - "io_timeout": 1800, - "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" - }, - "test": "telemetry_gpu_integration_test", - "test_id_prefix": "ninja://chrome/test:telemetry_gpu_integration_test/" - }, - { - "args": [ - "webcodecs", - "--show-stdout", - "--browser=release", - "--passthrough", - "-v", - "--stable-jobs", - "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc", - "--enforce-browser-version", - "--jobs=4" - ], - "merge": { - "script": "//testing/merge_scripts/standard_isolated_script_merge.py" - }, - "module_name": "//chrome/test:telemetry_gpu_integration_test", - "module_scheme": "flat", - "name": "webcodecs_tests", - "resultdb": { - "enable": true, - "has_native_resultdb_integration": true - }, - "swarming": { - "containment_type": "AUTO", - "dimensions": { - "display_attached": "1", - "display_server": "x11", - "gpu": "1002:7340-25.2.8", - "os": "Ubuntu-24.04", - "pool": "chromium.tests.gpu" - }, - "expiration": 21600, - "hard_timeout": 1800, - "idempotent": false, - "io_timeout": 1800, - "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" - }, - "test": "telemetry_gpu_integration_test", - "test_id_prefix": "ninja://chrome/test:telemetry_gpu_integration_test/" - }, - { - "args": [ - "webgl2_conformance", - "--show-stdout", - "--browser=release", - "--passthrough", - "-v", - "--stable-jobs", - "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc --use-cmd-decoder=passthrough --use-gl=angle --use-angle=gl --force_high_performance_gpu", - "--enforce-browser-version", - "--webgl-conformance-version=2.0.1", - "--read-abbreviated-json-results-from=../../content/test/data/gpu/webgl2_conformance_linux_runtimes.json", - "--jobs=4" - ], - "merge": { - "script": "//testing/merge_scripts/standard_isolated_script_merge.py" - }, - "module_name": "//chrome/test:telemetry_gpu_integration_test", - "module_scheme": "flat", - "name": "webgl2_conformance_gl_passthrough_tests", - "resultdb": { - "enable": true, - "has_native_resultdb_integration": true - }, - "swarming": { - "containment_type": "AUTO", - "dimensions": { - "display_attached": "1", - "display_server": "x11", - "gpu": "1002:7340-25.2.8", - "os": "Ubuntu-24.04", - "pool": "chromium.tests.gpu" - }, - "expiration": 21600, - "hard_timeout": 1800, - "idempotent": false, - "io_timeout": 1800, - "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com", - "shards": 5 - }, - "test": "telemetry_gpu_integration_test", - "test_id_prefix": "ninja://chrome/test:telemetry_gpu_integration_test/" - }, - { - "args": [ - "webgl1_conformance", - "--show-stdout", - "--browser=release", - "--passthrough", - "-v", - "--stable-jobs", - "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc --use-cmd-decoder=passthrough --use-gl=angle --use-angle=gl --force_high_performance_gpu", - "--enforce-browser-version", - "--read-abbreviated-json-results-from=../../content/test/data/gpu/webgl1_conformance_linux_runtimes.json", - "--jobs=4" - ], - "merge": { - "script": "//testing/merge_scripts/standard_isolated_script_merge.py" - }, - "module_name": "//chrome/test:telemetry_gpu_integration_test", - "module_scheme": "flat", - "name": "webgl_conformance_gl_passthrough_tests", - "resultdb": { - "enable": true, - "has_native_resultdb_integration": true - }, - "swarming": { - "containment_type": "AUTO", - "dimensions": { - "display_attached": "1", - "display_server": "x11", - "gpu": "1002:7340-25.2.8", - "os": "Ubuntu-24.04", - "pool": "chromium.tests.gpu" - }, - "expiration": 21600, - "hard_timeout": 1800, - "idempotent": false, - "io_timeout": 1800, - "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com", - "shards": 2 - }, - "test": "telemetry_gpu_integration_test", - "test_id_prefix": "ninja://chrome/test:telemetry_gpu_integration_test/" } ] }, @@ -912,8 +296,8 @@ "dimensions": { "display_attached": "1", "display_server": "x11", - "gpu": "1002:7340-23.2.1|1002:7340-25.2.8", - "os": "Ubuntu-22.04|Ubuntu-24.04", + "gpu": "1002:7340-25.2.8", + "os": "Ubuntu-24.04", "pool": "chromium.tests.gpu" }, "hard_timeout": 1800, @@ -942,8 +326,8 @@ "dimensions": { "display_attached": "1", "display_server": "x11", - "gpu": "1002:7340-23.2.1|1002:7340-25.2.8", - "os": "Ubuntu-22.04|Ubuntu-24.04", + "gpu": "1002:7340-25.2.8", + "os": "Ubuntu-24.04", "pool": "chromium.tests.gpu" }, "hard_timeout": 1800, @@ -976,8 +360,8 @@ "dimensions": { "display_attached": "1", "display_server": "x11", - "gpu": "1002:7340-23.2.1|1002:7340-25.2.8", - "os": "Ubuntu-22.04|Ubuntu-24.04", + "gpu": "1002:7340-25.2.8", + "os": "Ubuntu-24.04", "pool": "chromium.tests.gpu" }, "hard_timeout": 1800, @@ -1005,8 +389,8 @@ "dimensions": { "display_attached": "1", "display_server": "x11", - "gpu": "1002:7340-23.2.1|1002:7340-25.2.8", - "os": "Ubuntu-22.04|Ubuntu-24.04", + "gpu": "1002:7340-25.2.8", + "os": "Ubuntu-24.04", "pool": "chromium.tests.gpu" }, "hard_timeout": 1800, @@ -1035,8 +419,8 @@ "dimensions": { "display_attached": "1", "display_server": "x11", - "gpu": "1002:7340-23.2.1|1002:7340-25.2.8", - "os": "Ubuntu-22.04|Ubuntu-24.04", + "gpu": "1002:7340-25.2.8", + "os": "Ubuntu-24.04", "pool": "chromium.tests.gpu" }, "hard_timeout": 1800, @@ -1062,8 +446,8 @@ "dimensions": { "display_attached": "1", "display_server": "x11", - "gpu": "1002:7340-23.2.1|1002:7340-25.2.8", - "os": "Ubuntu-22.04|Ubuntu-24.04", + "gpu": "1002:7340-25.2.8", + "os": "Ubuntu-24.04", "pool": "chromium.tests.gpu" }, "hard_timeout": 1800, @@ -1102,8 +486,8 @@ "dimensions": { "display_attached": "1", "display_server": "x11", - "gpu": "1002:7340-23.2.1|1002:7340-25.2.8", - "os": "Ubuntu-22.04|Ubuntu-24.04", + "gpu": "1002:7340-25.2.8", + "os": "Ubuntu-24.04", "pool": "chromium.tests.gpu" }, "hard_timeout": 1800, @@ -1150,8 +534,8 @@ "dimensions": { "display_attached": "1", "display_server": "x11", - "gpu": "1002:7340-23.2.1|1002:7340-25.2.8", - "os": "Ubuntu-22.04|Ubuntu-24.04", + "gpu": "1002:7340-25.2.8", + "os": "Ubuntu-24.04", "pool": "chromium.tests.gpu" }, "hard_timeout": 1800, @@ -1189,8 +573,8 @@ "dimensions": { "display_attached": "1", "display_server": "x11", - "gpu": "1002:7340-23.2.1|1002:7340-25.2.8", - "os": "Ubuntu-22.04|Ubuntu-24.04", + "gpu": "1002:7340-25.2.8", + "os": "Ubuntu-24.04", "pool": "chromium.tests.gpu" }, "hard_timeout": 1800, @@ -1228,8 +612,8 @@ "dimensions": { "display_attached": "1", "display_server": "x11", - "gpu": "1002:7340-23.2.1|1002:7340-25.2.8", - "os": "Ubuntu-22.04|Ubuntu-24.04", + "gpu": "1002:7340-25.2.8", + "os": "Ubuntu-24.04", "pool": "chromium.tests.gpu" }, "hard_timeout": 1800, @@ -1271,8 +655,8 @@ "dimensions": { "display_attached": "1", "display_server": "x11", - "gpu": "1002:7340-23.2.1|1002:7340-25.2.8", - "os": "Ubuntu-22.04|Ubuntu-24.04", + "gpu": "1002:7340-25.2.8", + "os": "Ubuntu-24.04", "pool": "chromium.tests.gpu" }, "hard_timeout": 1800, @@ -1319,8 +703,8 @@ "dimensions": { "display_attached": "1", "display_server": "x11", - "gpu": "1002:7340-23.2.1|1002:7340-25.2.8", - "os": "Ubuntu-22.04|Ubuntu-24.04", + "gpu": "1002:7340-25.2.8", + "os": "Ubuntu-24.04", "pool": "chromium.tests.gpu" }, "hard_timeout": 1800, @@ -1359,8 +743,8 @@ "dimensions": { "display_attached": "1", "display_server": "x11", - "gpu": "1002:7340-23.2.1|1002:7340-25.2.8", - "os": "Ubuntu-22.04|Ubuntu-24.04", + "gpu": "1002:7340-25.2.8", + "os": "Ubuntu-24.04", "pool": "chromium.tests.gpu" }, "hard_timeout": 1800, @@ -1398,8 +782,8 @@ "dimensions": { "display_attached": "1", "display_server": "x11", - "gpu": "1002:7340-23.2.1|1002:7340-25.2.8", - "os": "Ubuntu-22.04|Ubuntu-24.04", + "gpu": "1002:7340-25.2.8", + "os": "Ubuntu-24.04", "pool": "chromium.tests.gpu" }, "hard_timeout": 1800, @@ -1437,8 +821,8 @@ "dimensions": { "display_attached": "1", "display_server": "x11", - "gpu": "1002:7340-23.2.1|1002:7340-25.2.8", - "os": "Ubuntu-22.04|Ubuntu-24.04", + "gpu": "1002:7340-25.2.8", + "os": "Ubuntu-24.04", "pool": "chromium.tests.gpu" }, "hard_timeout": 1800, @@ -1478,8 +862,8 @@ "dimensions": { "display_attached": "1", "display_server": "x11", - "gpu": "1002:7340-23.2.1|1002:7340-25.2.8", - "os": "Ubuntu-22.04|Ubuntu-24.04", + "gpu": "1002:7340-25.2.8", + "os": "Ubuntu-24.04", "pool": "chromium.tests.gpu" }, "hard_timeout": 1800, @@ -1519,8 +903,8 @@ "dimensions": { "display_attached": "1", "display_server": "x11", - "gpu": "1002:7340-23.2.1|1002:7340-25.2.8", - "os": "Ubuntu-22.04|Ubuntu-24.04", + "gpu": "1002:7340-25.2.8", + "os": "Ubuntu-24.04", "pool": "chromium.tests.gpu" }, "hard_timeout": 1800,
diff --git a/infra/config/generated/builders/ci/GPU FYI Linux Wayland Builder/targets/chromium.gpu.fyi.json b/infra/config/generated/builders/ci/GPU FYI Linux Wayland Builder/targets/chromium.gpu.fyi.json index d6ac674..2bcc5444 100644 --- a/infra/config/generated/builders/ci/GPU FYI Linux Wayland Builder/targets/chromium.gpu.fyi.json +++ b/infra/config/generated/builders/ci/GPU FYI Linux Wayland Builder/targets/chromium.gpu.fyi.json
@@ -23,8 +23,8 @@ "dimensions": { "display_attached": "1", "display_server": "x11", - "gpu": "1002:7340-23.2.1|1002:7340-25.2.8", - "os": "Ubuntu-22.04|Ubuntu-24.04", + "gpu": "1002:7340-25.2.8", + "os": "Ubuntu-24.04", "pool": "chromium.tests.gpu" }, "hard_timeout": 1800, @@ -63,8 +63,8 @@ "dimensions": { "display_attached": "1", "display_server": "x11", - "gpu": "1002:7340-23.2.1|1002:7340-25.2.8", - "os": "Ubuntu-22.04|Ubuntu-24.04", + "gpu": "1002:7340-25.2.8", + "os": "Ubuntu-24.04", "pool": "chromium.tests.gpu" }, "hard_timeout": 1800,
diff --git "a/infra/config/generated/builders/ci/Linux FYI Experimental Release \050AMD RX 5500XT\051/targets/chromium.gpu.fyi.json" "b/infra/config/generated/builders/ci/Linux FYI Experimental Release \050AMD RX 5500XT\051/targets/chromium.gpu.fyi.json" index 8083947..ec4568c 100644 --- "a/infra/config/generated/builders/ci/Linux FYI Experimental Release \050AMD RX 5500XT\051/targets/chromium.gpu.fyi.json" +++ "b/infra/config/generated/builders/ci/Linux FYI Experimental Release \050AMD RX 5500XT\051/targets/chromium.gpu.fyi.json"
@@ -1,282 +1,9 @@ { "Linux FYI Experimental Release (AMD RX 5500XT)": { - "gtest_tests": [ - { - "args": [ - "--no-xvfb" - ], - "merge": { - "script": "//testing/merge_scripts/standard_isolated_script_merge.py" - }, - "module_name": "//third_party/angle/src/tests:angle_unittests", - "module_scheme": "gtest", - "name": "angle_unittests", - "swarming": { - "containment_type": "AUTO", - "dimensions": { - "display_attached": "1", - "display_server": "x11", - "gpu": "1002:7340-25.2.8", - "os": "Ubuntu-24.04", - "pool": "chromium.tests.gpu" - }, - "expiration": 21600, - "hard_timeout": 1800, - "io_timeout": 1800, - "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" - }, - "test": "angle_unittests", - "test_id_prefix": "ninja://third_party/angle/src/tests:angle_unittests/", - "use_isolated_scripts_api": true - }, - { - "args": [ - "--use-cmd-decoder=passthrough", - "--use-gl=angle", - "--use-gpu-in-tests", - "--no-xvfb" - ], - "merge": { - "script": "//testing/merge_scripts/standard_gtest_merge.py" - }, - "module_name": "//gpu:gl_tests", - "module_scheme": "gtest", - "name": "gl_tests_passthrough", - "swarming": { - "containment_type": "AUTO", - "dimensions": { - "display_attached": "1", - "display_server": "x11", - "gpu": "1002:7340-25.2.8", - "os": "Ubuntu-24.04", - "pool": "chromium.tests.gpu" - }, - "expiration": 21600, - "hard_timeout": 1800, - "io_timeout": 1800, - "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com", - "shards": 2 - }, - "test": "gl_tests", - "test_id_prefix": "ninja://gpu:gl_tests/" - }, - { - "args": [ - "--use-gpu-in-tests", - "--no-xvfb", - "--git-revision=${got_revision}" - ], - "merge": { - "script": "//testing/merge_scripts/standard_gtest_merge.py" - }, - "module_name": "//ui/gl:gl_unittests", - "module_scheme": "gtest", - "name": "gl_unittests", - "precommit_args": [ - "--gerrit-issue=${patch_issue}", - "--gerrit-patchset=${patch_set}", - "--buildbucket-id=${buildbucket_build_id}" - ], - "swarming": { - "containment_type": "AUTO", - "dimensions": { - "display_attached": "1", - "display_server": "x11", - "gpu": "1002:7340-25.2.8", - "os": "Ubuntu-24.04", - "pool": "chromium.tests.gpu" - }, - "expiration": 21600, - "hard_timeout": 1800, - "io_timeout": 1800, - "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" - }, - "test": "gl_unittests", - "test_id_prefix": "ninja://ui/gl:gl_unittests/" - }, - { - "args": [ - "--enable-gpu", - "--use-gpu-in-tests", - "--gtest_filter=*MappableBufferTest*", - "--no-xvfb" - ], - "merge": { - "script": "//testing/merge_scripts/standard_gtest_merge.py" - }, - "module_name": "//gpu:gpu_unittests", - "module_scheme": "gtest", - "name": "mappable_buffer_tests", - "swarming": { - "containment_type": "AUTO", - "dimensions": { - "display_attached": "1", - "display_server": "x11", - "gpu": "1002:7340-25.2.8", - "os": "Ubuntu-24.04", - "pool": "chromium.tests.gpu" - }, - "expiration": 21600, - "hard_timeout": 1800, - "io_timeout": 1800, - "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" - }, - "test": "gpu_unittests", - "test_id_prefix": "ninja://gpu:gpu_unittests/" - }, - { - "args": [ - "--enable-gpu", - "--test-launcher-bot-mode", - "--test-launcher-jobs=1", - "--gtest_filter=TabCaptureApiPixelTest.EndToEnd*", - "--no-xvfb" - ], - "merge": { - "script": "//testing/merge_scripts/standard_gtest_merge.py" - }, - "module_name": "//chrome/test:browser_tests", - "module_scheme": "gtest", - "name": "tab_capture_end2end_tests", - "swarming": { - "containment_type": "AUTO", - "dimensions": { - "display_attached": "1", - "display_server": "x11", - "gpu": "1002:7340-25.2.8", - "os": "Ubuntu-24.04", - "pool": "chromium.tests.gpu" - }, - "expiration": 21600, - "hard_timeout": 1800, - "io_timeout": 1800, - "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" - }, - "test": "browser_tests", - "test_id_prefix": "ninja://chrome/test:browser_tests/" - }, - { - "args": [ - "--use-gpu-in-tests", - "--no-xvfb" - ], - "merge": { - "script": "//testing/merge_scripts/standard_gtest_merge.py" - }, - "module_name": "//gpu/vulkan:vulkan_tests", - "module_scheme": "gtest", - "name": "vulkan_tests", - "swarming": { - "containment_type": "AUTO", - "dimensions": { - "display_attached": "1", - "display_server": "x11", - "gpu": "1002:7340-25.2.8", - "os": "Ubuntu-24.04", - "pool": "chromium.tests.gpu" - }, - "expiration": 21600, - "hard_timeout": 1800, - "io_timeout": 1800, - "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" - }, - "test": "vulkan_tests", - "test_id_prefix": "ninja://gpu/vulkan:vulkan_tests/" - } - ], "isolated_scripts": [ { "args": [ - "context_lost", - "--show-stdout", - "--browser=release", - "--passthrough", - "-v", - "--stable-jobs", - "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc --use-cmd-decoder=passthrough --use-gl=angle", - "--enforce-browser-version", - "--jobs=4" - ], - "merge": { - "script": "//testing/merge_scripts/standard_isolated_script_merge.py" - }, - "module_name": "//chrome/test:telemetry_gpu_integration_test", - "module_scheme": "flat", - "name": "context_lost_passthrough_tests", - "resultdb": { - "enable": true, - "has_native_resultdb_integration": true - }, - "swarming": { - "containment_type": "AUTO", - "dimensions": { - "display_attached": "1", - "display_server": "x11", - "gpu": "1002:7340-25.2.8", - "os": "Ubuntu-24.04", - "pool": "chromium.tests.gpu" - }, - "expiration": 21600, - "hard_timeout": 1800, - "idempotent": false, - "io_timeout": 1800, - "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" - }, - "test": "telemetry_gpu_integration_test", - "test_id_prefix": "ninja://chrome/test:telemetry_gpu_integration_test/" - }, - { - "args": [ - "expected_color", - "--show-stdout", - "--browser=release", - "--passthrough", - "-v", - "--stable-jobs", - "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc --use-cmd-decoder=passthrough --use-gl=angle", - "--enforce-browser-version", - "--git-revision=${got_revision}", - "--dont-restore-color-profile-after-test", - "--test-machine-name", - "${buildername}", - "--jobs=4" - ], - "merge": { - "script": "//testing/merge_scripts/standard_isolated_script_merge.py" - }, - "module_name": "//chrome/test:telemetry_gpu_integration_test", - "module_scheme": "flat", - "name": "expected_color_pixel_passthrough_test", - "precommit_args": [ - "--gerrit-issue=${patch_issue}", - "--gerrit-patchset=${patch_set}", - "--buildbucket-id=${buildbucket_build_id}" - ], - "resultdb": { - "enable": true, - "has_native_resultdb_integration": true - }, - "swarming": { - "containment_type": "AUTO", - "dimensions": { - "display_attached": "1", - "display_server": "x11", - "gpu": "1002:7340-25.2.8", - "os": "Ubuntu-24.04", - "pool": "chromium.tests.gpu" - }, - "expiration": 21600, - "hard_timeout": 1800, - "idempotent": false, - "io_timeout": 1800, - "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" - }, - "test": "telemetry_gpu_integration_test", - "test_id_prefix": "ninja://chrome/test:telemetry_gpu_integration_test/" - }, - { - "args": [ - "gpu_process", + "noop_sleep", "--show-stdout", "--browser=release", "--passthrough", @@ -291,11 +18,7 @@ }, "module_name": "//chrome/test:telemetry_gpu_integration_test", "module_scheme": "flat", - "name": "gpu_process_launch_tests", - "resultdb": { - "enable": true, - "has_native_resultdb_integration": true - }, + "name": "noop_sleep_tests", "swarming": { "containment_type": "AUTO", "dimensions": { @@ -313,345 +36,6 @@ }, "test": "telemetry_gpu_integration_test", "test_id_prefix": "ninja://chrome/test:telemetry_gpu_integration_test/" - }, - { - "args": [ - "hardware_accelerated_feature", - "--show-stdout", - "--browser=release", - "--passthrough", - "-v", - "--stable-jobs", - "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc", - "--enforce-browser-version", - "--jobs=4" - ], - "merge": { - "script": "//testing/merge_scripts/standard_isolated_script_merge.py" - }, - "module_name": "//chrome/test:telemetry_gpu_integration_test", - "module_scheme": "flat", - "name": "hardware_accelerated_feature_tests", - "resultdb": { - "enable": true, - "has_native_resultdb_integration": true - }, - "swarming": { - "containment_type": "AUTO", - "dimensions": { - "display_attached": "1", - "display_server": "x11", - "gpu": "1002:7340-25.2.8", - "os": "Ubuntu-24.04", - "pool": "chromium.tests.gpu" - }, - "expiration": 21600, - "hard_timeout": 1800, - "idempotent": false, - "io_timeout": 1800, - "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" - }, - "test": "telemetry_gpu_integration_test", - "test_id_prefix": "ninja://chrome/test:telemetry_gpu_integration_test/" - }, - { - "args": [ - "info_collection", - "--show-stdout", - "--browser=release", - "--passthrough", - "-v", - "--stable-jobs", - "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc --force_high_performance_gpu", - "--enforce-browser-version", - "--expected-vendor-id", - "1002", - "--expected-device-id", - "7340", - "--jobs=4" - ], - "merge": { - "script": "//testing/merge_scripts/standard_isolated_script_merge.py" - }, - "module_name": "//chrome/test:telemetry_gpu_integration_test", - "module_scheme": "flat", - "name": "info_collection_tests", - "resultdb": { - "enable": true, - "has_native_resultdb_integration": true - }, - "swarming": { - "containment_type": "AUTO", - "dimensions": { - "display_attached": "1", - "display_server": "x11", - "gpu": "1002:7340-25.2.8", - "os": "Ubuntu-24.04", - "pool": "chromium.tests.gpu" - }, - "expiration": 21600, - "hard_timeout": 1800, - "idempotent": false, - "io_timeout": 1800, - "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" - }, - "test": "telemetry_gpu_integration_test", - "test_id_prefix": "ninja://chrome/test:telemetry_gpu_integration_test/" - }, - { - "args": [ - "pixel", - "--show-stdout", - "--browser=release", - "--passthrough", - "-v", - "--stable-jobs", - "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc --use-cmd-decoder=passthrough --use-gl=angle", - "--enforce-browser-version", - "--git-revision=${got_revision}", - "--dont-restore-color-profile-after-test", - "--test-machine-name", - "${buildername}", - "--jobs=4" - ], - "merge": { - "script": "//testing/merge_scripts/standard_isolated_script_merge.py" - }, - "module_name": "//chrome/test:telemetry_gpu_integration_test", - "module_scheme": "flat", - "name": "pixel_skia_gold_passthrough_test", - "precommit_args": [ - "--gerrit-issue=${patch_issue}", - "--gerrit-patchset=${patch_set}", - "--buildbucket-id=${buildbucket_build_id}" - ], - "resultdb": { - "enable": true, - "has_native_resultdb_integration": true - }, - "swarming": { - "containment_type": "AUTO", - "dimensions": { - "display_attached": "1", - "display_server": "x11", - "gpu": "1002:7340-25.2.8", - "os": "Ubuntu-24.04", - "pool": "chromium.tests.gpu" - }, - "expiration": 21600, - "hard_timeout": 1800, - "idempotent": false, - "io_timeout": 1800, - "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" - }, - "test": "telemetry_gpu_integration_test", - "test_id_prefix": "ninja://chrome/test:telemetry_gpu_integration_test/" - }, - { - "args": [ - "screenshot_sync", - "--show-stdout", - "--browser=release", - "--passthrough", - "-v", - "--stable-jobs", - "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc --use-cmd-decoder=passthrough --use-gl=angle", - "--enforce-browser-version", - "--dont-restore-color-profile-after-test", - "--jobs=4" - ], - "merge": { - "script": "//testing/merge_scripts/standard_isolated_script_merge.py" - }, - "module_name": "//chrome/test:telemetry_gpu_integration_test", - "module_scheme": "flat", - "name": "screenshot_sync_passthrough_tests", - "resultdb": { - "enable": true, - "has_native_resultdb_integration": true - }, - "swarming": { - "containment_type": "AUTO", - "dimensions": { - "display_attached": "1", - "display_server": "x11", - "gpu": "1002:7340-25.2.8", - "os": "Ubuntu-24.04", - "pool": "chromium.tests.gpu" - }, - "expiration": 21600, - "hard_timeout": 1800, - "idempotent": false, - "io_timeout": 1800, - "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" - }, - "test": "telemetry_gpu_integration_test", - "test_id_prefix": "ninja://chrome/test:telemetry_gpu_integration_test/" - }, - { - "args": [ - "trace_test", - "--show-stdout", - "--browser=release", - "--passthrough", - "-v", - "--stable-jobs", - "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc", - "--enforce-browser-version", - "--jobs=4" - ], - "merge": { - "script": "//testing/merge_scripts/standard_isolated_script_merge.py" - }, - "module_name": "//chrome/test:telemetry_gpu_integration_test", - "module_scheme": "flat", - "name": "trace_test", - "resultdb": { - "enable": true, - "has_native_resultdb_integration": true - }, - "swarming": { - "containment_type": "AUTO", - "dimensions": { - "display_attached": "1", - "display_server": "x11", - "gpu": "1002:7340-25.2.8", - "os": "Ubuntu-24.04", - "pool": "chromium.tests.gpu" - }, - "expiration": 21600, - "hard_timeout": 1800, - "idempotent": false, - "io_timeout": 1800, - "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" - }, - "test": "telemetry_gpu_integration_test", - "test_id_prefix": "ninja://chrome/test:telemetry_gpu_integration_test/" - }, - { - "args": [ - "webcodecs", - "--show-stdout", - "--browser=release", - "--passthrough", - "-v", - "--stable-jobs", - "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc", - "--enforce-browser-version", - "--jobs=4" - ], - "merge": { - "script": "//testing/merge_scripts/standard_isolated_script_merge.py" - }, - "module_name": "//chrome/test:telemetry_gpu_integration_test", - "module_scheme": "flat", - "name": "webcodecs_tests", - "resultdb": { - "enable": true, - "has_native_resultdb_integration": true - }, - "swarming": { - "containment_type": "AUTO", - "dimensions": { - "display_attached": "1", - "display_server": "x11", - "gpu": "1002:7340-25.2.8", - "os": "Ubuntu-24.04", - "pool": "chromium.tests.gpu" - }, - "expiration": 21600, - "hard_timeout": 1800, - "idempotent": false, - "io_timeout": 1800, - "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" - }, - "test": "telemetry_gpu_integration_test", - "test_id_prefix": "ninja://chrome/test:telemetry_gpu_integration_test/" - }, - { - "args": [ - "webgl2_conformance", - "--show-stdout", - "--browser=release", - "--passthrough", - "-v", - "--stable-jobs", - "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc --use-cmd-decoder=passthrough --use-gl=angle --use-angle=gl --force_high_performance_gpu", - "--enforce-browser-version", - "--webgl-conformance-version=2.0.1", - "--read-abbreviated-json-results-from=../../content/test/data/gpu/webgl2_conformance_linux_runtimes.json", - "--jobs=4" - ], - "merge": { - "script": "//testing/merge_scripts/standard_isolated_script_merge.py" - }, - "module_name": "//chrome/test:telemetry_gpu_integration_test", - "module_scheme": "flat", - "name": "webgl2_conformance_gl_passthrough_tests", - "resultdb": { - "enable": true, - "has_native_resultdb_integration": true - }, - "swarming": { - "containment_type": "AUTO", - "dimensions": { - "display_attached": "1", - "display_server": "x11", - "gpu": "1002:7340-25.2.8", - "os": "Ubuntu-24.04", - "pool": "chromium.tests.gpu" - }, - "expiration": 21600, - "hard_timeout": 1800, - "idempotent": false, - "io_timeout": 1800, - "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com", - "shards": 5 - }, - "test": "telemetry_gpu_integration_test", - "test_id_prefix": "ninja://chrome/test:telemetry_gpu_integration_test/" - }, - { - "args": [ - "webgl1_conformance", - "--show-stdout", - "--browser=release", - "--passthrough", - "-v", - "--stable-jobs", - "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc --use-cmd-decoder=passthrough --use-gl=angle --use-angle=gl --force_high_performance_gpu", - "--enforce-browser-version", - "--read-abbreviated-json-results-from=../../content/test/data/gpu/webgl1_conformance_linux_runtimes.json", - "--jobs=4" - ], - "merge": { - "script": "//testing/merge_scripts/standard_isolated_script_merge.py" - }, - "module_name": "//chrome/test:telemetry_gpu_integration_test", - "module_scheme": "flat", - "name": "webgl_conformance_gl_passthrough_tests", - "resultdb": { - "enable": true, - "has_native_resultdb_integration": true - }, - "swarming": { - "containment_type": "AUTO", - "dimensions": { - "display_attached": "1", - "display_server": "x11", - "gpu": "1002:7340-25.2.8", - "os": "Ubuntu-24.04", - "pool": "chromium.tests.gpu" - }, - "expiration": 21600, - "hard_timeout": 1800, - "idempotent": false, - "io_timeout": 1800, - "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com", - "shards": 2 - }, - "test": "telemetry_gpu_integration_test", - "test_id_prefix": "ninja://chrome/test:telemetry_gpu_integration_test/" } ] }
diff --git "a/infra/config/generated/builders/ci/Linux FYI Release \050AMD RX 5500 XT\051/targets/chromium.gpu.fyi.json" "b/infra/config/generated/builders/ci/Linux FYI Release \050AMD RX 5500 XT\051/targets/chromium.gpu.fyi.json" index 46b8a409..1e574fb 100644 --- "a/infra/config/generated/builders/ci/Linux FYI Release \050AMD RX 5500 XT\051/targets/chromium.gpu.fyi.json" +++ "b/infra/config/generated/builders/ci/Linux FYI Release \050AMD RX 5500 XT\051/targets/chromium.gpu.fyi.json"
@@ -16,8 +16,8 @@ "dimensions": { "display_attached": "1", "display_server": "x11", - "gpu": "1002:7340-23.2.1|1002:7340-25.2.8", - "os": "Ubuntu-22.04|Ubuntu-24.04", + "gpu": "1002:7340-25.2.8", + "os": "Ubuntu-24.04", "pool": "chromium.tests.gpu" }, "hard_timeout": 1800, @@ -46,8 +46,8 @@ "dimensions": { "display_attached": "1", "display_server": "x11", - "gpu": "1002:7340-23.2.1|1002:7340-25.2.8", - "os": "Ubuntu-22.04|Ubuntu-24.04", + "gpu": "1002:7340-25.2.8", + "os": "Ubuntu-24.04", "pool": "chromium.tests.gpu" }, "hard_timeout": 1800, @@ -80,8 +80,8 @@ "dimensions": { "display_attached": "1", "display_server": "x11", - "gpu": "1002:7340-23.2.1|1002:7340-25.2.8", - "os": "Ubuntu-22.04|Ubuntu-24.04", + "gpu": "1002:7340-25.2.8", + "os": "Ubuntu-24.04", "pool": "chromium.tests.gpu" }, "hard_timeout": 1800, @@ -109,8 +109,8 @@ "dimensions": { "display_attached": "1", "display_server": "x11", - "gpu": "1002:7340-23.2.1|1002:7340-25.2.8", - "os": "Ubuntu-22.04|Ubuntu-24.04", + "gpu": "1002:7340-25.2.8", + "os": "Ubuntu-24.04", "pool": "chromium.tests.gpu" }, "hard_timeout": 1800, @@ -139,8 +139,8 @@ "dimensions": { "display_attached": "1", "display_server": "x11", - "gpu": "1002:7340-23.2.1|1002:7340-25.2.8", - "os": "Ubuntu-22.04|Ubuntu-24.04", + "gpu": "1002:7340-25.2.8", + "os": "Ubuntu-24.04", "pool": "chromium.tests.gpu" }, "hard_timeout": 1800, @@ -166,8 +166,8 @@ "dimensions": { "display_attached": "1", "display_server": "x11", - "gpu": "1002:7340-23.2.1|1002:7340-25.2.8", - "os": "Ubuntu-22.04|Ubuntu-24.04", + "gpu": "1002:7340-25.2.8", + "os": "Ubuntu-24.04", "pool": "chromium.tests.gpu" }, "hard_timeout": 1800, @@ -206,8 +206,8 @@ "dimensions": { "display_attached": "1", "display_server": "x11", - "gpu": "1002:7340-23.2.1|1002:7340-25.2.8", - "os": "Ubuntu-22.04|Ubuntu-24.04", + "gpu": "1002:7340-25.2.8", + "os": "Ubuntu-24.04", "pool": "chromium.tests.gpu" }, "hard_timeout": 1800, @@ -254,8 +254,8 @@ "dimensions": { "display_attached": "1", "display_server": "x11", - "gpu": "1002:7340-23.2.1|1002:7340-25.2.8", - "os": "Ubuntu-22.04|Ubuntu-24.04", + "gpu": "1002:7340-25.2.8", + "os": "Ubuntu-24.04", "pool": "chromium.tests.gpu" }, "hard_timeout": 1800, @@ -293,8 +293,8 @@ "dimensions": { "display_attached": "1", "display_server": "x11", - "gpu": "1002:7340-23.2.1|1002:7340-25.2.8", - "os": "Ubuntu-22.04|Ubuntu-24.04", + "gpu": "1002:7340-25.2.8", + "os": "Ubuntu-24.04", "pool": "chromium.tests.gpu" }, "hard_timeout": 1800, @@ -332,8 +332,8 @@ "dimensions": { "display_attached": "1", "display_server": "x11", - "gpu": "1002:7340-23.2.1|1002:7340-25.2.8", - "os": "Ubuntu-22.04|Ubuntu-24.04", + "gpu": "1002:7340-25.2.8", + "os": "Ubuntu-24.04", "pool": "chromium.tests.gpu" }, "hard_timeout": 1800, @@ -375,8 +375,8 @@ "dimensions": { "display_attached": "1", "display_server": "x11", - "gpu": "1002:7340-23.2.1|1002:7340-25.2.8", - "os": "Ubuntu-22.04|Ubuntu-24.04", + "gpu": "1002:7340-25.2.8", + "os": "Ubuntu-24.04", "pool": "chromium.tests.gpu" }, "hard_timeout": 1800, @@ -423,8 +423,8 @@ "dimensions": { "display_attached": "1", "display_server": "x11", - "gpu": "1002:7340-23.2.1|1002:7340-25.2.8", - "os": "Ubuntu-22.04|Ubuntu-24.04", + "gpu": "1002:7340-25.2.8", + "os": "Ubuntu-24.04", "pool": "chromium.tests.gpu" }, "hard_timeout": 1800, @@ -463,8 +463,8 @@ "dimensions": { "display_attached": "1", "display_server": "x11", - "gpu": "1002:7340-23.2.1|1002:7340-25.2.8", - "os": "Ubuntu-22.04|Ubuntu-24.04", + "gpu": "1002:7340-25.2.8", + "os": "Ubuntu-24.04", "pool": "chromium.tests.gpu" }, "hard_timeout": 1800, @@ -502,8 +502,8 @@ "dimensions": { "display_attached": "1", "display_server": "x11", - "gpu": "1002:7340-23.2.1|1002:7340-25.2.8", - "os": "Ubuntu-22.04|Ubuntu-24.04", + "gpu": "1002:7340-25.2.8", + "os": "Ubuntu-24.04", "pool": "chromium.tests.gpu" }, "hard_timeout": 1800, @@ -541,8 +541,8 @@ "dimensions": { "display_attached": "1", "display_server": "x11", - "gpu": "1002:7340-23.2.1|1002:7340-25.2.8", - "os": "Ubuntu-22.04|Ubuntu-24.04", + "gpu": "1002:7340-25.2.8", + "os": "Ubuntu-24.04", "pool": "chromium.tests.gpu" }, "hard_timeout": 1800, @@ -582,8 +582,8 @@ "dimensions": { "display_attached": "1", "display_server": "x11", - "gpu": "1002:7340-23.2.1|1002:7340-25.2.8", - "os": "Ubuntu-22.04|Ubuntu-24.04", + "gpu": "1002:7340-25.2.8", + "os": "Ubuntu-24.04", "pool": "chromium.tests.gpu" }, "hard_timeout": 1800, @@ -623,8 +623,8 @@ "dimensions": { "display_attached": "1", "display_server": "x11", - "gpu": "1002:7340-23.2.1|1002:7340-25.2.8", - "os": "Ubuntu-22.04|Ubuntu-24.04", + "gpu": "1002:7340-25.2.8", + "os": "Ubuntu-24.04", "pool": "chromium.tests.gpu" }, "hard_timeout": 1800,
diff --git "a/infra/config/generated/builders/ci/Linux Wayland FYI Release \050AMD\051/targets/chromium.gpu.fyi.json" "b/infra/config/generated/builders/ci/Linux Wayland FYI Release \050AMD\051/targets/chromium.gpu.fyi.json" index 63793c42..40e70ce0 100644 --- "a/infra/config/generated/builders/ci/Linux Wayland FYI Release \050AMD\051/targets/chromium.gpu.fyi.json" +++ "b/infra/config/generated/builders/ci/Linux Wayland FYI Release \050AMD\051/targets/chromium.gpu.fyi.json"
@@ -23,8 +23,8 @@ "dimensions": { "display_attached": "1", "display_server": "x11", - "gpu": "1002:7340-23.2.1|1002:7340-25.2.8", - "os": "Ubuntu-22.04|Ubuntu-24.04", + "gpu": "1002:7340-25.2.8", + "os": "Ubuntu-24.04", "pool": "chromium.tests.gpu" }, "hard_timeout": 1800, @@ -63,8 +63,8 @@ "dimensions": { "display_attached": "1", "display_server": "x11", - "gpu": "1002:7340-23.2.1|1002:7340-25.2.8", - "os": "Ubuntu-22.04|Ubuntu-24.04", + "gpu": "1002:7340-25.2.8", + "os": "Ubuntu-24.04", "pool": "chromium.tests.gpu" }, "hard_timeout": 1800,
diff --git a/infra/config/generated/builders/ci/android-17-x64-fyi-rel/targets/chromium.android.fyi.json b/infra/config/generated/builders/ci/android-17-x64-fyi-rel/targets/chromium.android.fyi.json index 657e0088..4e15b56b 100644 --- a/infra/config/generated/builders/ci/android-17-x64-fyi-rel/targets/chromium.android.fyi.json +++ b/infra/config/generated/builders/ci/android-17-x64-fyi-rel/targets/chromium.android.fyi.json
@@ -3789,48 +3789,6 @@ }, { "args": [ - "--platform=android", - "--avd-config=../../tools/android/avd/proto/android_37_google_apis_ps16k_x64.textpb" - ], - "description": "Run with android_37_google_apis_ps16k_x64", - "merge": { - "script": "//testing/merge_scripts/standard_isolated_script_merge.py" - }, - "module_name": "//content/shell:content_shell_crash_test", - "module_scheme": "single", - "name": "content_shell_crash_test", - "resultdb": { - "enable": true, - "has_native_resultdb_integration": true, - "result_format": "single" - }, - "swarming": { - "dimensions": { - "cores": "8", - "cpu": "x86-64", - "device_os": null, - "device_type": null, - "os": "Ubuntu-22.04", - "pool": "chromium.tests.avd" - }, - "named_caches": [ - { - "name": "android_37_google_apis_ps16k_x64", - "path": ".android_emulator/android_37_google_apis_ps16k_x64" - } - ], - "optional_dimensions": { - "60": { - "caches": "android_37_google_apis_ps16k_x64" - } - }, - "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" - }, - "test": "content_shell_crash_test", - "test_id_prefix": "ninja://content/shell:content_shell_crash_test/" - }, - { - "args": [ "--avd-config=../../tools/android/avd/proto/android_37_google_apis_ps16k_x64.textpb" ], "description": "Run with android_37_google_apis_ps16k_x64",
diff --git a/infra/config/generated/builders/ci/android-canary-x64-fyi-rel/targets/chromium.android.fyi.json b/infra/config/generated/builders/ci/android-canary-x64-fyi-rel/targets/chromium.android.fyi.json index 47795cc0..6235079 100644 --- a/infra/config/generated/builders/ci/android-canary-x64-fyi-rel/targets/chromium.android.fyi.json +++ b/infra/config/generated/builders/ci/android-canary-x64-fyi-rel/targets/chromium.android.fyi.json
@@ -3788,48 +3788,6 @@ }, { "args": [ - "--platform=android", - "--avd-config=../../tools/android/avd/proto/android_canary_google_apis_x64.textpb" - ], - "description": "Run with android_canary_google_apis_x64", - "merge": { - "script": "//testing/merge_scripts/standard_isolated_script_merge.py" - }, - "module_name": "//content/shell:content_shell_crash_test", - "module_scheme": "single", - "name": "content_shell_crash_test", - "resultdb": { - "enable": true, - "has_native_resultdb_integration": true, - "result_format": "single" - }, - "swarming": { - "dimensions": { - "cores": "8", - "cpu": "x86-64", - "device_os": null, - "device_type": null, - "os": "Ubuntu-22.04", - "pool": "chromium.tests.avd" - }, - "named_caches": [ - { - "name": "android_canary_google_apis_x64", - "path": ".android_emulator/android_canary_google_apis_x64" - } - ], - "optional_dimensions": { - "60": { - "caches": "android_canary_google_apis_x64" - } - }, - "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" - }, - "test": "content_shell_crash_test", - "test_id_prefix": "ninja://content/shell:content_shell_crash_test/" - }, - { - "args": [ "--avd-config=../../tools/android/avd/proto/android_canary_google_apis_x64.textpb" ], "description": "Run with android_canary_google_apis_x64",
diff --git a/infra/config/generated/builders/ci/linux-arm64-rel-fyi/targets/chromium.fyi.json b/infra/config/generated/builders/ci/linux-arm64-rel-fyi/targets/chromium.fyi.json index 852867ae..e9394ee1 100644 --- a/infra/config/generated/builders/ci/linux-arm64-rel-fyi/targets/chromium.fyi.json +++ b/infra/config/generated/builders/ci/linux-arm64-rel-fyi/targets/chromium.fyi.json
@@ -239,6 +239,9 @@ "test_id_prefix": "ninja://third_party/boringssl:boringssl_ssl_tests/" }, { + "args": [ + "--test-launcher-filter-file=../../testing/buildbot/filters/linux-arm64-rel-fyi.browser_tests.filter" + ], "merge": { "script": "//testing/merge_scripts/standard_gtest_merge.py" }, @@ -415,6 +418,9 @@ "test_id_prefix": "ninja://ui/compositor:compositor_unittests/" }, { + "args": [ + "--test-launcher-filter-file=../../testing/buildbot/filters/linux-arm64-rel-fyi.content_browsertests.filter" + ], "merge": { "script": "//testing/merge_scripts/standard_gtest_merge.py" }, @@ -797,6 +803,9 @@ "test_id_prefix": "ninja://headless:headless_unittests/" }, { + "args": [ + "--test-launcher-filter-file=../../testing/buildbot/filters/linux-arm64-rel-fyi.interactive_ui_tests.filter" + ], "merge": { "script": "//testing/merge_scripts/standard_gtest_merge.py" },
diff --git a/infra/config/generated/builders/ci/linux-arm64-wayland-rel-fyi/targets/chromium.fyi.json b/infra/config/generated/builders/ci/linux-arm64-wayland-rel-fyi/targets/chromium.fyi.json index e10b7197..47014a2 100644 --- a/infra/config/generated/builders/ci/linux-arm64-wayland-rel-fyi/targets/chromium.fyi.json +++ b/infra/config/generated/builders/ci/linux-arm64-wayland-rel-fyi/targets/chromium.fyi.json
@@ -7,7 +7,7 @@ "--use-mutter", "--ozone-platform=wayland", "--mutter-display=1280x800", - "--test-launcher-filter-file=../../testing/buildbot/filters/ozone-linux.browser_tests_mutter.filter" + "--test-launcher-filter-file=../../testing/buildbot/filters/ozone-linux.browser_tests_mutter.filter;../../testing/buildbot/filters/linux-arm64-wayland-rel-fyi.browser_tests.filter" ], "merge": { "script": "//testing/merge_scripts/standard_gtest_merge.py" @@ -60,7 +60,7 @@ "--no-xvfb", "--use-mutter", "--ozone-platform=wayland", - "--test-launcher-filter-file=../../testing/buildbot/filters/ozone-linux.interactive_ui_tests_mutter.filter" + "--test-launcher-filter-file=../../testing/buildbot/filters/ozone-linux.interactive_ui_tests_mutter.filter;../../testing/buildbot/filters/linux-arm64-wayland-rel-fyi.interactive_ui_tests.filter" ], "merge": { "script": "//testing/merge_scripts/standard_gtest_merge.py"
diff --git a/infra/config/generated/builders/try/android-17-x64-fyi-rel/targets/chromium.android.fyi.json b/infra/config/generated/builders/try/android-17-x64-fyi-rel/targets/chromium.android.fyi.json index 657e0088..4e15b56b 100644 --- a/infra/config/generated/builders/try/android-17-x64-fyi-rel/targets/chromium.android.fyi.json +++ b/infra/config/generated/builders/try/android-17-x64-fyi-rel/targets/chromium.android.fyi.json
@@ -3789,48 +3789,6 @@ }, { "args": [ - "--platform=android", - "--avd-config=../../tools/android/avd/proto/android_37_google_apis_ps16k_x64.textpb" - ], - "description": "Run with android_37_google_apis_ps16k_x64", - "merge": { - "script": "//testing/merge_scripts/standard_isolated_script_merge.py" - }, - "module_name": "//content/shell:content_shell_crash_test", - "module_scheme": "single", - "name": "content_shell_crash_test", - "resultdb": { - "enable": true, - "has_native_resultdb_integration": true, - "result_format": "single" - }, - "swarming": { - "dimensions": { - "cores": "8", - "cpu": "x86-64", - "device_os": null, - "device_type": null, - "os": "Ubuntu-22.04", - "pool": "chromium.tests.avd" - }, - "named_caches": [ - { - "name": "android_37_google_apis_ps16k_x64", - "path": ".android_emulator/android_37_google_apis_ps16k_x64" - } - ], - "optional_dimensions": { - "60": { - "caches": "android_37_google_apis_ps16k_x64" - } - }, - "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" - }, - "test": "content_shell_crash_test", - "test_id_prefix": "ninja://content/shell:content_shell_crash_test/" - }, - { - "args": [ "--avd-config=../../tools/android/avd/proto/android_37_google_apis_ps16k_x64.textpb" ], "description": "Run with android_37_google_apis_ps16k_x64",
diff --git a/infra/config/generated/builders/try/android-canary-x64-fyi-rel/targets/chromium.android.fyi.json b/infra/config/generated/builders/try/android-canary-x64-fyi-rel/targets/chromium.android.fyi.json index 47795cc0..6235079 100644 --- a/infra/config/generated/builders/try/android-canary-x64-fyi-rel/targets/chromium.android.fyi.json +++ b/infra/config/generated/builders/try/android-canary-x64-fyi-rel/targets/chromium.android.fyi.json
@@ -3788,48 +3788,6 @@ }, { "args": [ - "--platform=android", - "--avd-config=../../tools/android/avd/proto/android_canary_google_apis_x64.textpb" - ], - "description": "Run with android_canary_google_apis_x64", - "merge": { - "script": "//testing/merge_scripts/standard_isolated_script_merge.py" - }, - "module_name": "//content/shell:content_shell_crash_test", - "module_scheme": "single", - "name": "content_shell_crash_test", - "resultdb": { - "enable": true, - "has_native_resultdb_integration": true, - "result_format": "single" - }, - "swarming": { - "dimensions": { - "cores": "8", - "cpu": "x86-64", - "device_os": null, - "device_type": null, - "os": "Ubuntu-22.04", - "pool": "chromium.tests.avd" - }, - "named_caches": [ - { - "name": "android_canary_google_apis_x64", - "path": ".android_emulator/android_canary_google_apis_x64" - } - ], - "optional_dimensions": { - "60": { - "caches": "android_canary_google_apis_x64" - } - }, - "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" - }, - "test": "content_shell_crash_test", - "test_id_prefix": "ninja://content/shell:content_shell_crash_test/" - }, - { - "args": [ "--avd-config=../../tools/android/avd/proto/android_canary_google_apis_x64.textpb" ], "description": "Run with android_canary_google_apis_x64",
diff --git a/infra/config/generated/builders/try/gpu-fyi-try-linux-amd-rel/targets/chromium.gpu.fyi.json b/infra/config/generated/builders/try/gpu-fyi-try-linux-amd-rel/targets/chromium.gpu.fyi.json index 4237115f..4470ca84 100644 --- a/infra/config/generated/builders/try/gpu-fyi-try-linux-amd-rel/targets/chromium.gpu.fyi.json +++ b/infra/config/generated/builders/try/gpu-fyi-try-linux-amd-rel/targets/chromium.gpu.fyi.json
@@ -17,8 +17,8 @@ "dimensions": { "display_attached": "1", "display_server": "x11", - "gpu": "1002:7340-23.2.1|1002:7340-25.2.8", - "os": "Ubuntu-22.04|Ubuntu-24.04", + "gpu": "1002:7340-25.2.8", + "os": "Ubuntu-24.04", "pool": "chromium.tests.gpu" }, "hard_timeout": 1800, @@ -47,8 +47,8 @@ "dimensions": { "display_attached": "1", "display_server": "x11", - "gpu": "1002:7340-23.2.1|1002:7340-25.2.8", - "os": "Ubuntu-22.04|Ubuntu-24.04", + "gpu": "1002:7340-25.2.8", + "os": "Ubuntu-24.04", "pool": "chromium.tests.gpu" }, "hard_timeout": 1800, @@ -81,8 +81,8 @@ "dimensions": { "display_attached": "1", "display_server": "x11", - "gpu": "1002:7340-23.2.1|1002:7340-25.2.8", - "os": "Ubuntu-22.04|Ubuntu-24.04", + "gpu": "1002:7340-25.2.8", + "os": "Ubuntu-24.04", "pool": "chromium.tests.gpu" }, "hard_timeout": 1800, @@ -110,8 +110,8 @@ "dimensions": { "display_attached": "1", "display_server": "x11", - "gpu": "1002:7340-23.2.1|1002:7340-25.2.8", - "os": "Ubuntu-22.04|Ubuntu-24.04", + "gpu": "1002:7340-25.2.8", + "os": "Ubuntu-24.04", "pool": "chromium.tests.gpu" }, "hard_timeout": 1800, @@ -140,8 +140,8 @@ "dimensions": { "display_attached": "1", "display_server": "x11", - "gpu": "1002:7340-23.2.1|1002:7340-25.2.8", - "os": "Ubuntu-22.04|Ubuntu-24.04", + "gpu": "1002:7340-25.2.8", + "os": "Ubuntu-24.04", "pool": "chromium.tests.gpu" }, "hard_timeout": 1800, @@ -167,8 +167,8 @@ "dimensions": { "display_attached": "1", "display_server": "x11", - "gpu": "1002:7340-23.2.1|1002:7340-25.2.8", - "os": "Ubuntu-22.04|Ubuntu-24.04", + "gpu": "1002:7340-25.2.8", + "os": "Ubuntu-24.04", "pool": "chromium.tests.gpu" }, "hard_timeout": 1800, @@ -207,8 +207,8 @@ "dimensions": { "display_attached": "1", "display_server": "x11", - "gpu": "1002:7340-23.2.1|1002:7340-25.2.8", - "os": "Ubuntu-22.04|Ubuntu-24.04", + "gpu": "1002:7340-25.2.8", + "os": "Ubuntu-24.04", "pool": "chromium.tests.gpu" }, "hard_timeout": 1800, @@ -255,8 +255,8 @@ "dimensions": { "display_attached": "1", "display_server": "x11", - "gpu": "1002:7340-23.2.1|1002:7340-25.2.8", - "os": "Ubuntu-22.04|Ubuntu-24.04", + "gpu": "1002:7340-25.2.8", + "os": "Ubuntu-24.04", "pool": "chromium.tests.gpu" }, "hard_timeout": 1800, @@ -294,8 +294,8 @@ "dimensions": { "display_attached": "1", "display_server": "x11", - "gpu": "1002:7340-23.2.1|1002:7340-25.2.8", - "os": "Ubuntu-22.04|Ubuntu-24.04", + "gpu": "1002:7340-25.2.8", + "os": "Ubuntu-24.04", "pool": "chromium.tests.gpu" }, "hard_timeout": 1800, @@ -333,8 +333,8 @@ "dimensions": { "display_attached": "1", "display_server": "x11", - "gpu": "1002:7340-23.2.1|1002:7340-25.2.8", - "os": "Ubuntu-22.04|Ubuntu-24.04", + "gpu": "1002:7340-25.2.8", + "os": "Ubuntu-24.04", "pool": "chromium.tests.gpu" }, "hard_timeout": 1800, @@ -376,8 +376,8 @@ "dimensions": { "display_attached": "1", "display_server": "x11", - "gpu": "1002:7340-23.2.1|1002:7340-25.2.8", - "os": "Ubuntu-22.04|Ubuntu-24.04", + "gpu": "1002:7340-25.2.8", + "os": "Ubuntu-24.04", "pool": "chromium.tests.gpu" }, "hard_timeout": 1800, @@ -424,8 +424,8 @@ "dimensions": { "display_attached": "1", "display_server": "x11", - "gpu": "1002:7340-23.2.1|1002:7340-25.2.8", - "os": "Ubuntu-22.04|Ubuntu-24.04", + "gpu": "1002:7340-25.2.8", + "os": "Ubuntu-24.04", "pool": "chromium.tests.gpu" }, "hard_timeout": 1800, @@ -464,8 +464,8 @@ "dimensions": { "display_attached": "1", "display_server": "x11", - "gpu": "1002:7340-23.2.1|1002:7340-25.2.8", - "os": "Ubuntu-22.04|Ubuntu-24.04", + "gpu": "1002:7340-25.2.8", + "os": "Ubuntu-24.04", "pool": "chromium.tests.gpu" }, "hard_timeout": 1800, @@ -503,8 +503,8 @@ "dimensions": { "display_attached": "1", "display_server": "x11", - "gpu": "1002:7340-23.2.1|1002:7340-25.2.8", - "os": "Ubuntu-22.04|Ubuntu-24.04", + "gpu": "1002:7340-25.2.8", + "os": "Ubuntu-24.04", "pool": "chromium.tests.gpu" }, "hard_timeout": 1800, @@ -542,8 +542,8 @@ "dimensions": { "display_attached": "1", "display_server": "x11", - "gpu": "1002:7340-23.2.1|1002:7340-25.2.8", - "os": "Ubuntu-22.04|Ubuntu-24.04", + "gpu": "1002:7340-25.2.8", + "os": "Ubuntu-24.04", "pool": "chromium.tests.gpu" }, "hard_timeout": 1800, @@ -583,8 +583,8 @@ "dimensions": { "display_attached": "1", "display_server": "x11", - "gpu": "1002:7340-23.2.1|1002:7340-25.2.8", - "os": "Ubuntu-22.04|Ubuntu-24.04", + "gpu": "1002:7340-25.2.8", + "os": "Ubuntu-24.04", "pool": "chromium.tests.gpu" }, "hard_timeout": 1800, @@ -624,8 +624,8 @@ "dimensions": { "display_attached": "1", "display_server": "x11", - "gpu": "1002:7340-23.2.1|1002:7340-25.2.8", - "os": "Ubuntu-22.04|Ubuntu-24.04", + "gpu": "1002:7340-25.2.8", + "os": "Ubuntu-24.04", "pool": "chromium.tests.gpu" }, "hard_timeout": 1800,
diff --git a/infra/config/generated/builders/try/gpu-fyi-try-linux-amd-rx-5500xt-exp/targets/chromium.gpu.fyi.json b/infra/config/generated/builders/try/gpu-fyi-try-linux-amd-rx-5500xt-exp/targets/chromium.gpu.fyi.json index c8b16ed..b9a86c7 100644 --- a/infra/config/generated/builders/try/gpu-fyi-try-linux-amd-rx-5500xt-exp/targets/chromium.gpu.fyi.json +++ b/infra/config/generated/builders/try/gpu-fyi-try-linux-amd-rx-5500xt-exp/targets/chromium.gpu.fyi.json
@@ -1,283 +1,10 @@ { "GPU FYI Linux Builder": {}, "Linux FYI Experimental Release (AMD RX 5500XT)": { - "gtest_tests": [ - { - "args": [ - "--no-xvfb" - ], - "merge": { - "script": "//testing/merge_scripts/standard_isolated_script_merge.py" - }, - "module_name": "//third_party/angle/src/tests:angle_unittests", - "module_scheme": "gtest", - "name": "angle_unittests", - "swarming": { - "containment_type": "AUTO", - "dimensions": { - "display_attached": "1", - "display_server": "x11", - "gpu": "1002:7340-25.2.8", - "os": "Ubuntu-24.04", - "pool": "chromium.tests.gpu" - }, - "expiration": 21600, - "hard_timeout": 1800, - "io_timeout": 1800, - "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" - }, - "test": "angle_unittests", - "test_id_prefix": "ninja://third_party/angle/src/tests:angle_unittests/", - "use_isolated_scripts_api": true - }, - { - "args": [ - "--use-cmd-decoder=passthrough", - "--use-gl=angle", - "--use-gpu-in-tests", - "--no-xvfb" - ], - "merge": { - "script": "//testing/merge_scripts/standard_gtest_merge.py" - }, - "module_name": "//gpu:gl_tests", - "module_scheme": "gtest", - "name": "gl_tests_passthrough", - "swarming": { - "containment_type": "AUTO", - "dimensions": { - "display_attached": "1", - "display_server": "x11", - "gpu": "1002:7340-25.2.8", - "os": "Ubuntu-24.04", - "pool": "chromium.tests.gpu" - }, - "expiration": 21600, - "hard_timeout": 1800, - "io_timeout": 1800, - "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com", - "shards": 2 - }, - "test": "gl_tests", - "test_id_prefix": "ninja://gpu:gl_tests/" - }, - { - "args": [ - "--use-gpu-in-tests", - "--no-xvfb", - "--git-revision=${got_revision}" - ], - "merge": { - "script": "//testing/merge_scripts/standard_gtest_merge.py" - }, - "module_name": "//ui/gl:gl_unittests", - "module_scheme": "gtest", - "name": "gl_unittests", - "precommit_args": [ - "--gerrit-issue=${patch_issue}", - "--gerrit-patchset=${patch_set}", - "--buildbucket-id=${buildbucket_build_id}" - ], - "swarming": { - "containment_type": "AUTO", - "dimensions": { - "display_attached": "1", - "display_server": "x11", - "gpu": "1002:7340-25.2.8", - "os": "Ubuntu-24.04", - "pool": "chromium.tests.gpu" - }, - "expiration": 21600, - "hard_timeout": 1800, - "io_timeout": 1800, - "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" - }, - "test": "gl_unittests", - "test_id_prefix": "ninja://ui/gl:gl_unittests/" - }, - { - "args": [ - "--enable-gpu", - "--use-gpu-in-tests", - "--gtest_filter=*MappableBufferTest*", - "--no-xvfb" - ], - "merge": { - "script": "//testing/merge_scripts/standard_gtest_merge.py" - }, - "module_name": "//gpu:gpu_unittests", - "module_scheme": "gtest", - "name": "mappable_buffer_tests", - "swarming": { - "containment_type": "AUTO", - "dimensions": { - "display_attached": "1", - "display_server": "x11", - "gpu": "1002:7340-25.2.8", - "os": "Ubuntu-24.04", - "pool": "chromium.tests.gpu" - }, - "expiration": 21600, - "hard_timeout": 1800, - "io_timeout": 1800, - "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" - }, - "test": "gpu_unittests", - "test_id_prefix": "ninja://gpu:gpu_unittests/" - }, - { - "args": [ - "--enable-gpu", - "--test-launcher-bot-mode", - "--test-launcher-jobs=1", - "--gtest_filter=TabCaptureApiPixelTest.EndToEnd*", - "--no-xvfb" - ], - "merge": { - "script": "//testing/merge_scripts/standard_gtest_merge.py" - }, - "module_name": "//chrome/test:browser_tests", - "module_scheme": "gtest", - "name": "tab_capture_end2end_tests", - "swarming": { - "containment_type": "AUTO", - "dimensions": { - "display_attached": "1", - "display_server": "x11", - "gpu": "1002:7340-25.2.8", - "os": "Ubuntu-24.04", - "pool": "chromium.tests.gpu" - }, - "expiration": 21600, - "hard_timeout": 1800, - "io_timeout": 1800, - "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" - }, - "test": "browser_tests", - "test_id_prefix": "ninja://chrome/test:browser_tests/" - }, - { - "args": [ - "--use-gpu-in-tests", - "--no-xvfb" - ], - "merge": { - "script": "//testing/merge_scripts/standard_gtest_merge.py" - }, - "module_name": "//gpu/vulkan:vulkan_tests", - "module_scheme": "gtest", - "name": "vulkan_tests", - "swarming": { - "containment_type": "AUTO", - "dimensions": { - "display_attached": "1", - "display_server": "x11", - "gpu": "1002:7340-25.2.8", - "os": "Ubuntu-24.04", - "pool": "chromium.tests.gpu" - }, - "expiration": 21600, - "hard_timeout": 1800, - "io_timeout": 1800, - "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" - }, - "test": "vulkan_tests", - "test_id_prefix": "ninja://gpu/vulkan:vulkan_tests/" - } - ], "isolated_scripts": [ { "args": [ - "context_lost", - "--show-stdout", - "--browser=release", - "--passthrough", - "-v", - "--stable-jobs", - "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc --use-cmd-decoder=passthrough --use-gl=angle", - "--enforce-browser-version", - "--jobs=4" - ], - "merge": { - "script": "//testing/merge_scripts/standard_isolated_script_merge.py" - }, - "module_name": "//chrome/test:telemetry_gpu_integration_test", - "module_scheme": "flat", - "name": "context_lost_passthrough_tests", - "resultdb": { - "enable": true, - "has_native_resultdb_integration": true - }, - "swarming": { - "containment_type": "AUTO", - "dimensions": { - "display_attached": "1", - "display_server": "x11", - "gpu": "1002:7340-25.2.8", - "os": "Ubuntu-24.04", - "pool": "chromium.tests.gpu" - }, - "expiration": 21600, - "hard_timeout": 1800, - "idempotent": false, - "io_timeout": 1800, - "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" - }, - "test": "telemetry_gpu_integration_test", - "test_id_prefix": "ninja://chrome/test:telemetry_gpu_integration_test/" - }, - { - "args": [ - "expected_color", - "--show-stdout", - "--browser=release", - "--passthrough", - "-v", - "--stable-jobs", - "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc --use-cmd-decoder=passthrough --use-gl=angle", - "--enforce-browser-version", - "--git-revision=${got_revision}", - "--dont-restore-color-profile-after-test", - "--test-machine-name", - "${buildername}", - "--jobs=4" - ], - "merge": { - "script": "//testing/merge_scripts/standard_isolated_script_merge.py" - }, - "module_name": "//chrome/test:telemetry_gpu_integration_test", - "module_scheme": "flat", - "name": "expected_color_pixel_passthrough_test", - "precommit_args": [ - "--gerrit-issue=${patch_issue}", - "--gerrit-patchset=${patch_set}", - "--buildbucket-id=${buildbucket_build_id}" - ], - "resultdb": { - "enable": true, - "has_native_resultdb_integration": true - }, - "swarming": { - "containment_type": "AUTO", - "dimensions": { - "display_attached": "1", - "display_server": "x11", - "gpu": "1002:7340-25.2.8", - "os": "Ubuntu-24.04", - "pool": "chromium.tests.gpu" - }, - "expiration": 21600, - "hard_timeout": 1800, - "idempotent": false, - "io_timeout": 1800, - "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" - }, - "test": "telemetry_gpu_integration_test", - "test_id_prefix": "ninja://chrome/test:telemetry_gpu_integration_test/" - }, - { - "args": [ - "gpu_process", + "noop_sleep", "--show-stdout", "--browser=release", "--passthrough", @@ -292,11 +19,7 @@ }, "module_name": "//chrome/test:telemetry_gpu_integration_test", "module_scheme": "flat", - "name": "gpu_process_launch_tests", - "resultdb": { - "enable": true, - "has_native_resultdb_integration": true - }, + "name": "noop_sleep_tests", "swarming": { "containment_type": "AUTO", "dimensions": { @@ -314,345 +37,6 @@ }, "test": "telemetry_gpu_integration_test", "test_id_prefix": "ninja://chrome/test:telemetry_gpu_integration_test/" - }, - { - "args": [ - "hardware_accelerated_feature", - "--show-stdout", - "--browser=release", - "--passthrough", - "-v", - "--stable-jobs", - "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc", - "--enforce-browser-version", - "--jobs=4" - ], - "merge": { - "script": "//testing/merge_scripts/standard_isolated_script_merge.py" - }, - "module_name": "//chrome/test:telemetry_gpu_integration_test", - "module_scheme": "flat", - "name": "hardware_accelerated_feature_tests", - "resultdb": { - "enable": true, - "has_native_resultdb_integration": true - }, - "swarming": { - "containment_type": "AUTO", - "dimensions": { - "display_attached": "1", - "display_server": "x11", - "gpu": "1002:7340-25.2.8", - "os": "Ubuntu-24.04", - "pool": "chromium.tests.gpu" - }, - "expiration": 21600, - "hard_timeout": 1800, - "idempotent": false, - "io_timeout": 1800, - "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" - }, - "test": "telemetry_gpu_integration_test", - "test_id_prefix": "ninja://chrome/test:telemetry_gpu_integration_test/" - }, - { - "args": [ - "info_collection", - "--show-stdout", - "--browser=release", - "--passthrough", - "-v", - "--stable-jobs", - "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc --force_high_performance_gpu", - "--enforce-browser-version", - "--expected-vendor-id", - "1002", - "--expected-device-id", - "7340", - "--jobs=4" - ], - "merge": { - "script": "//testing/merge_scripts/standard_isolated_script_merge.py" - }, - "module_name": "//chrome/test:telemetry_gpu_integration_test", - "module_scheme": "flat", - "name": "info_collection_tests", - "resultdb": { - "enable": true, - "has_native_resultdb_integration": true - }, - "swarming": { - "containment_type": "AUTO", - "dimensions": { - "display_attached": "1", - "display_server": "x11", - "gpu": "1002:7340-25.2.8", - "os": "Ubuntu-24.04", - "pool": "chromium.tests.gpu" - }, - "expiration": 21600, - "hard_timeout": 1800, - "idempotent": false, - "io_timeout": 1800, - "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" - }, - "test": "telemetry_gpu_integration_test", - "test_id_prefix": "ninja://chrome/test:telemetry_gpu_integration_test/" - }, - { - "args": [ - "pixel", - "--show-stdout", - "--browser=release", - "--passthrough", - "-v", - "--stable-jobs", - "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc --use-cmd-decoder=passthrough --use-gl=angle", - "--enforce-browser-version", - "--git-revision=${got_revision}", - "--dont-restore-color-profile-after-test", - "--test-machine-name", - "${buildername}", - "--jobs=4" - ], - "merge": { - "script": "//testing/merge_scripts/standard_isolated_script_merge.py" - }, - "module_name": "//chrome/test:telemetry_gpu_integration_test", - "module_scheme": "flat", - "name": "pixel_skia_gold_passthrough_test", - "precommit_args": [ - "--gerrit-issue=${patch_issue}", - "--gerrit-patchset=${patch_set}", - "--buildbucket-id=${buildbucket_build_id}" - ], - "resultdb": { - "enable": true, - "has_native_resultdb_integration": true - }, - "swarming": { - "containment_type": "AUTO", - "dimensions": { - "display_attached": "1", - "display_server": "x11", - "gpu": "1002:7340-25.2.8", - "os": "Ubuntu-24.04", - "pool": "chromium.tests.gpu" - }, - "expiration": 21600, - "hard_timeout": 1800, - "idempotent": false, - "io_timeout": 1800, - "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" - }, - "test": "telemetry_gpu_integration_test", - "test_id_prefix": "ninja://chrome/test:telemetry_gpu_integration_test/" - }, - { - "args": [ - "screenshot_sync", - "--show-stdout", - "--browser=release", - "--passthrough", - "-v", - "--stable-jobs", - "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc --use-cmd-decoder=passthrough --use-gl=angle", - "--enforce-browser-version", - "--dont-restore-color-profile-after-test", - "--jobs=4" - ], - "merge": { - "script": "//testing/merge_scripts/standard_isolated_script_merge.py" - }, - "module_name": "//chrome/test:telemetry_gpu_integration_test", - "module_scheme": "flat", - "name": "screenshot_sync_passthrough_tests", - "resultdb": { - "enable": true, - "has_native_resultdb_integration": true - }, - "swarming": { - "containment_type": "AUTO", - "dimensions": { - "display_attached": "1", - "display_server": "x11", - "gpu": "1002:7340-25.2.8", - "os": "Ubuntu-24.04", - "pool": "chromium.tests.gpu" - }, - "expiration": 21600, - "hard_timeout": 1800, - "idempotent": false, - "io_timeout": 1800, - "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" - }, - "test": "telemetry_gpu_integration_test", - "test_id_prefix": "ninja://chrome/test:telemetry_gpu_integration_test/" - }, - { - "args": [ - "trace_test", - "--show-stdout", - "--browser=release", - "--passthrough", - "-v", - "--stable-jobs", - "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc", - "--enforce-browser-version", - "--jobs=4" - ], - "merge": { - "script": "//testing/merge_scripts/standard_isolated_script_merge.py" - }, - "module_name": "//chrome/test:telemetry_gpu_integration_test", - "module_scheme": "flat", - "name": "trace_test", - "resultdb": { - "enable": true, - "has_native_resultdb_integration": true - }, - "swarming": { - "containment_type": "AUTO", - "dimensions": { - "display_attached": "1", - "display_server": "x11", - "gpu": "1002:7340-25.2.8", - "os": "Ubuntu-24.04", - "pool": "chromium.tests.gpu" - }, - "expiration": 21600, - "hard_timeout": 1800, - "idempotent": false, - "io_timeout": 1800, - "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" - }, - "test": "telemetry_gpu_integration_test", - "test_id_prefix": "ninja://chrome/test:telemetry_gpu_integration_test/" - }, - { - "args": [ - "webcodecs", - "--show-stdout", - "--browser=release", - "--passthrough", - "-v", - "--stable-jobs", - "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc", - "--enforce-browser-version", - "--jobs=4" - ], - "merge": { - "script": "//testing/merge_scripts/standard_isolated_script_merge.py" - }, - "module_name": "//chrome/test:telemetry_gpu_integration_test", - "module_scheme": "flat", - "name": "webcodecs_tests", - "resultdb": { - "enable": true, - "has_native_resultdb_integration": true - }, - "swarming": { - "containment_type": "AUTO", - "dimensions": { - "display_attached": "1", - "display_server": "x11", - "gpu": "1002:7340-25.2.8", - "os": "Ubuntu-24.04", - "pool": "chromium.tests.gpu" - }, - "expiration": 21600, - "hard_timeout": 1800, - "idempotent": false, - "io_timeout": 1800, - "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" - }, - "test": "telemetry_gpu_integration_test", - "test_id_prefix": "ninja://chrome/test:telemetry_gpu_integration_test/" - }, - { - "args": [ - "webgl2_conformance", - "--show-stdout", - "--browser=release", - "--passthrough", - "-v", - "--stable-jobs", - "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc --use-cmd-decoder=passthrough --use-gl=angle --use-angle=gl --force_high_performance_gpu", - "--enforce-browser-version", - "--webgl-conformance-version=2.0.1", - "--read-abbreviated-json-results-from=../../content/test/data/gpu/webgl2_conformance_linux_runtimes.json", - "--jobs=4" - ], - "merge": { - "script": "//testing/merge_scripts/standard_isolated_script_merge.py" - }, - "module_name": "//chrome/test:telemetry_gpu_integration_test", - "module_scheme": "flat", - "name": "webgl2_conformance_gl_passthrough_tests", - "resultdb": { - "enable": true, - "has_native_resultdb_integration": true - }, - "swarming": { - "containment_type": "AUTO", - "dimensions": { - "display_attached": "1", - "display_server": "x11", - "gpu": "1002:7340-25.2.8", - "os": "Ubuntu-24.04", - "pool": "chromium.tests.gpu" - }, - "expiration": 21600, - "hard_timeout": 1800, - "idempotent": false, - "io_timeout": 1800, - "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com", - "shards": 5 - }, - "test": "telemetry_gpu_integration_test", - "test_id_prefix": "ninja://chrome/test:telemetry_gpu_integration_test/" - }, - { - "args": [ - "webgl1_conformance", - "--show-stdout", - "--browser=release", - "--passthrough", - "-v", - "--stable-jobs", - "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc --use-cmd-decoder=passthrough --use-gl=angle --use-angle=gl --force_high_performance_gpu", - "--enforce-browser-version", - "--read-abbreviated-json-results-from=../../content/test/data/gpu/webgl1_conformance_linux_runtimes.json", - "--jobs=4" - ], - "merge": { - "script": "//testing/merge_scripts/standard_isolated_script_merge.py" - }, - "module_name": "//chrome/test:telemetry_gpu_integration_test", - "module_scheme": "flat", - "name": "webgl_conformance_gl_passthrough_tests", - "resultdb": { - "enable": true, - "has_native_resultdb_integration": true - }, - "swarming": { - "containment_type": "AUTO", - "dimensions": { - "display_attached": "1", - "display_server": "x11", - "gpu": "1002:7340-25.2.8", - "os": "Ubuntu-24.04", - "pool": "chromium.tests.gpu" - }, - "expiration": 21600, - "hard_timeout": 1800, - "idempotent": false, - "io_timeout": 1800, - "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com", - "shards": 2 - }, - "test": "telemetry_gpu_integration_test", - "test_id_prefix": "ninja://chrome/test:telemetry_gpu_integration_test/" } ] }
diff --git a/infra/config/generated/builders/try/gpu-fyi-try-linux-wayland-amd-rel/targets/chromium.gpu.fyi.json b/infra/config/generated/builders/try/gpu-fyi-try-linux-wayland-amd-rel/targets/chromium.gpu.fyi.json index 63793c42..40e70ce0 100644 --- a/infra/config/generated/builders/try/gpu-fyi-try-linux-wayland-amd-rel/targets/chromium.gpu.fyi.json +++ b/infra/config/generated/builders/try/gpu-fyi-try-linux-wayland-amd-rel/targets/chromium.gpu.fyi.json
@@ -23,8 +23,8 @@ "dimensions": { "display_attached": "1", "display_server": "x11", - "gpu": "1002:7340-23.2.1|1002:7340-25.2.8", - "os": "Ubuntu-22.04|Ubuntu-24.04", + "gpu": "1002:7340-25.2.8", + "os": "Ubuntu-24.04", "pool": "chromium.tests.gpu" }, "hard_timeout": 1800, @@ -63,8 +63,8 @@ "dimensions": { "display_attached": "1", "display_server": "x11", - "gpu": "1002:7340-23.2.1|1002:7340-25.2.8", - "os": "Ubuntu-22.04|Ubuntu-24.04", + "gpu": "1002:7340-25.2.8", + "os": "Ubuntu-24.04", "pool": "chromium.tests.gpu" }, "hard_timeout": 1800,
diff --git a/infra/config/generated/builders/try/linux-arm64-rel-fyi/targets/chromium.fyi.json b/infra/config/generated/builders/try/linux-arm64-rel-fyi/targets/chromium.fyi.json index 852867ae..e9394ee1 100644 --- a/infra/config/generated/builders/try/linux-arm64-rel-fyi/targets/chromium.fyi.json +++ b/infra/config/generated/builders/try/linux-arm64-rel-fyi/targets/chromium.fyi.json
@@ -239,6 +239,9 @@ "test_id_prefix": "ninja://third_party/boringssl:boringssl_ssl_tests/" }, { + "args": [ + "--test-launcher-filter-file=../../testing/buildbot/filters/linux-arm64-rel-fyi.browser_tests.filter" + ], "merge": { "script": "//testing/merge_scripts/standard_gtest_merge.py" }, @@ -415,6 +418,9 @@ "test_id_prefix": "ninja://ui/compositor:compositor_unittests/" }, { + "args": [ + "--test-launcher-filter-file=../../testing/buildbot/filters/linux-arm64-rel-fyi.content_browsertests.filter" + ], "merge": { "script": "//testing/merge_scripts/standard_gtest_merge.py" }, @@ -797,6 +803,9 @@ "test_id_prefix": "ninja://headless:headless_unittests/" }, { + "args": [ + "--test-launcher-filter-file=../../testing/buildbot/filters/linux-arm64-rel-fyi.interactive_ui_tests.filter" + ], "merge": { "script": "//testing/merge_scripts/standard_gtest_merge.py" },
diff --git a/infra/config/generated/builders/try/linux-arm64-wayland-rel-fyi/targets/chromium.fyi.json b/infra/config/generated/builders/try/linux-arm64-wayland-rel-fyi/targets/chromium.fyi.json index e10b7197..47014a2 100644 --- a/infra/config/generated/builders/try/linux-arm64-wayland-rel-fyi/targets/chromium.fyi.json +++ b/infra/config/generated/builders/try/linux-arm64-wayland-rel-fyi/targets/chromium.fyi.json
@@ -7,7 +7,7 @@ "--use-mutter", "--ozone-platform=wayland", "--mutter-display=1280x800", - "--test-launcher-filter-file=../../testing/buildbot/filters/ozone-linux.browser_tests_mutter.filter" + "--test-launcher-filter-file=../../testing/buildbot/filters/ozone-linux.browser_tests_mutter.filter;../../testing/buildbot/filters/linux-arm64-wayland-rel-fyi.browser_tests.filter" ], "merge": { "script": "//testing/merge_scripts/standard_gtest_merge.py" @@ -60,7 +60,7 @@ "--no-xvfb", "--use-mutter", "--ozone-platform=wayland", - "--test-launcher-filter-file=../../testing/buildbot/filters/ozone-linux.interactive_ui_tests_mutter.filter" + "--test-launcher-filter-file=../../testing/buildbot/filters/ozone-linux.interactive_ui_tests_mutter.filter;../../testing/buildbot/filters/linux-arm64-wayland-rel-fyi.interactive_ui_tests.filter" ], "merge": { "script": "//testing/merge_scripts/standard_gtest_merge.py"
diff --git a/infra/config/generated/testing/mixins.pyl b/infra/config/generated/testing/mixins.pyl index 3533e46..7f96fcf1 100644 --- a/infra/config/generated/testing/mixins.pyl +++ b/infra/config/generated/testing/mixins.pyl
@@ -569,8 +569,8 @@ 'fail_if_unused': False, 'swarming': { 'dimensions': { - 'gpu': '1002:7340-23.2.1|1002:7340-25.2.8', - 'os': 'Ubuntu-22.04|Ubuntu-24.04', + 'gpu': '1002:7340-25.2.8', + 'os': 'Ubuntu-24.04', 'display_attached': '1', 'display_server': 'x11', 'pool': 'chromium.tests.gpu',
diff --git a/infra/config/subprojects/chromium/ci/chromium.android.fyi.star b/infra/config/subprojects/chromium/ci/chromium.android.fyi.star index 5275481..61e56d4 100644 --- a/infra/config/subprojects/chromium/ci/chromium.android.fyi.star +++ b/infra/config/subprojects/chromium/ci/chromium.android.fyi.star
@@ -454,6 +454,9 @@ shards = 30, ), ), + "content_shell_crash_test": targets.remove( + reason = "crbug.com/40131701", + ), "telemetry_perf_unittests_android_chrome": targets.mixin( # For whatever reason, automatic browser selection on this bot chooses # webview instead of the full browser, so explicitly specify it here. @@ -575,6 +578,9 @@ "--browser=android-chromium", ], ), + "content_shell_crash_test": targets.remove( + reason = "crbug.com/40131701", + ), }, ), targets_settings = targets.settings(
diff --git a/infra/config/subprojects/chromium/ci/chromium.android.star b/infra/config/subprojects/chromium/ci/chromium.android.star index 84d1f2c..8181aef 100644 --- a/infra/config/subprojects/chromium/ci/chromium.android.star +++ b/infra/config/subprojects/chromium/ci/chromium.android.star
@@ -2403,7 +2403,7 @@ ), # If you change this, make similar changes in android-x86-code-coverage "content_shell_crash_test": targets.remove( - reason = "crbug.com/1084353", + reason = "crbug.com/40131701", ), # If you change this, make similar changes in android-x86-code-coverage "content_shell_test_apk": targets.mixin( @@ -2611,7 +2611,7 @@ ), ), "content_shell_crash_test": targets.remove( - reason = "crbug.com/1084353", + reason = "crbug.com/40131701", ), "content_shell_test_apk": targets.mixin( args = [
diff --git a/infra/config/subprojects/chromium/ci/chromium.fyi.star b/infra/config/subprojects/chromium/ci/chromium.fyi.star index c9ee96d9..02521506 100644 --- a/infra/config/subprojects/chromium/ci/chromium.fyi.star +++ b/infra/config/subprojects/chromium/ci/chromium.fyi.star
@@ -1365,7 +1365,20 @@ "very_limited_capacity_bot", ], per_test_modifications = { + "browser_tests": targets.mixin( + args = [ + "--test-launcher-filter-file=../../testing/buildbot/filters/linux-arm64-rel-fyi.browser_tests.filter", + ], + ), + "content_browsertests": targets.mixin( + args = [ + "--test-launcher-filter-file=../../testing/buildbot/filters/linux-arm64-rel-fyi.content_browsertests.filter", + ], + ), "interactive_ui_tests": targets.mixin( + args = [ + "--test-launcher-filter-file=../../testing/buildbot/filters/linux-arm64-rel-fyi.interactive_ui_tests.filter", + ], swarming = targets.swarming( shards = 5, ), @@ -1435,7 +1448,7 @@ "browser_tests": targets.mixin( args = [ "--mutter-display=1280x800", - "--test-launcher-filter-file=../../testing/buildbot/filters/ozone-linux.browser_tests_mutter.filter", + "--test-launcher-filter-file=../../testing/buildbot/filters/ozone-linux.browser_tests_mutter.filter;../../testing/buildbot/filters/linux-arm64-wayland-rel-fyi.browser_tests.filter", ], retry_only_failed_tests = True, swarming = targets.swarming( @@ -1447,7 +1460,7 @@ ), "interactive_ui_tests": targets.mixin( args = [ - "--test-launcher-filter-file=../../testing/buildbot/filters/ozone-linux.interactive_ui_tests_mutter.filter", + "--test-launcher-filter-file=../../testing/buildbot/filters/ozone-linux.interactive_ui_tests_mutter.filter;../../testing/buildbot/filters/linux-arm64-wayland-rel-fyi.interactive_ui_tests.filter", ], swarming = targets.swarming( shards = 5,
diff --git a/infra/config/subprojects/chromium/ci/chromium.gpu.fyi.star b/infra/config/subprojects/chromium/ci/chromium.gpu.fyi.star index 7e15bf68..5fe1ae14 100644 --- a/infra/config/subprojects/chromium/ci/chromium.gpu.fyi.star +++ b/infra/config/subprojects/chromium/ci/chromium.gpu.fyi.star
@@ -1387,8 +1387,7 @@ # should have the same test suites as "Linux FYI Release (AMD RX # 5500XT)". targets = [ - "gpu_fyi_linux_release_gtests", - "gpu_fyi_linux_release_telemetry_tests", + "gpu_noop_sleep_telemetry_test", ], mixins = [ "limited_capacity_bot",
diff --git a/infra/config/targets/mixins.star b/infra/config/targets/mixins.star index 72489cf..529beec2 100644 --- a/infra/config/targets/mixins.star +++ b/infra/config/targets/mixins.star
@@ -1199,8 +1199,8 @@ name = "linux_amd_rx_5500_xt", swarming = targets.swarming( dimensions = { - "gpu": "1002:7340-23.2.1|1002:7340-25.2.8", - "os": "Ubuntu-22.04|Ubuntu-24.04", + "gpu": "1002:7340-25.2.8", + "os": "Ubuntu-24.04", "display_attached": "1", "display_server": "x11", "pool": "chromium.tests.gpu",
diff --git a/internal b/internal index dc7e5ad..4744296d 160000 --- a/internal +++ b/internal
@@ -1 +1 @@ -Subproject commit dc7e5ad5e5fdd614ce0e6883a9d04ec6e68026a2 +Subproject commit 4744296d88ca4b44359a69b4c5b77b5a82ab38b1
diff --git a/ios/chrome/browser/app_bar/ui/app_bar_background_view.mm b/ios/chrome/browser/app_bar/ui/app_bar_background_view.mm index d9bc893..e415d51 100644 --- a/ios/chrome/browser/app_bar/ui/app_bar_background_view.mm +++ b/ios/chrome/browser/app_bar/ui/app_bar_background_view.mm
@@ -7,6 +7,7 @@ #import <QuartzCore/QuartzCore.h> #import "ios/chrome/browser/app_bar/ui/app_bar_constants.h" +#import "ios/chrome/browser/shared/public/features/features.h" #import "ios/chrome/common/ui/colors/semantic_color_names.h" namespace { @@ -45,6 +46,7 @@ UIBezierPath* _maskPath; CGRect _lastBounds; CAShapeLayer* _shadowLayer; + UIVisualEffectView* _blurView; } - (instancetype)initWithFrame:(CGRect)frame { @@ -54,6 +56,13 @@ _maskLayer.fillRule = kCAFillRuleEvenOdd; self.layer.mask = _maskLayer; + if (IsFullscreenRefactoringEnabled()) { + UIBlurEffect* blurEffect = + [UIBlurEffect effectWithStyle:UIBlurEffectStyleSystemMaterialDark]; + _blurView = [[UIVisualEffectView alloc] initWithEffect:blurEffect]; + [self addSubview:_blurView]; + } + _shadowLayer = [CAShapeLayer layer]; [self.layer addSublayer:_shadowLayer]; @@ -65,6 +74,9 @@ - (void)layoutSubviews { [super layoutSubviews]; + if (_blurView) { + _blurView.frame = self.bounds; + } [self updateMask]; } @@ -92,12 +104,26 @@ return; } CAShapeLayer* shadowLayer = _shadowLayer; + UIVisualEffectView* blurView = _blurView; _hideColorBackground = hideColorBackground; + + if (!hideColorBackground && blurView) { + blurView.hidden = NO; + } + [UIView animateWithDuration:kColorTransitionDuration - animations:^{ - [self updateBackgroundColor]; - shadowLayer.opacity = hideColorBackground ? 0 : 1; - }]; + animations:^{ + [self updateBackgroundColor]; + shadowLayer.opacity = hideColorBackground ? 0 : 1; + if (blurView) { + blurView.alpha = hideColorBackground ? 0 : 1; + } + } + completion:^(BOOL finished) { + if (hideColorBackground && blurView) { + blurView.hidden = YES; + } + }]; } #pragma mark - Private @@ -112,6 +138,10 @@ self.backgroundColor = [UIColor colorNamed:kAppBarIncognitoColor]; return; } + if (IsFullscreenRefactoringEnabled()) { + self.backgroundColor = [UIColor clearColor]; + return; + } self.backgroundColor = [UIColor colorNamed:kAppBarColor]; }
diff --git a/ios/chrome/browser/autofill/form_input_accessory/ui/form_input_accessory_view_controller.mm b/ios/chrome/browser/autofill/form_input_accessory/ui/form_input_accessory_view_controller.mm index 0d7b49ec..f7a43fc0 100644 --- a/ios/chrome/browser/autofill/form_input_accessory/ui/form_input_accessory_view_controller.mm +++ b/ios/chrome/browser/autofill/form_input_accessory/ui/form_input_accessory_view_controller.mm
@@ -221,9 +221,7 @@ ? FormInputAccessoryViewSubitemGroup::kExpandButton : FormInputAccessoryViewSubitemGroup::kManualFillButtons]; - if (suggestions.count > kKeyboardAccessorySuggestionsLimit && - base::FeatureList::IsEnabled( - kIOSKeyboardAccessorySuggestionsCutOffLimit)) { + if (suggestions.count > kKeyboardAccessorySuggestionsLimit) { suggestions = [suggestions subarrayWithRange:NSMakeRange(0, kKeyboardAccessorySuggestionsLimit)]; }
diff --git a/ios/chrome/browser/autofill/form_input_accessory/ui/form_input_accessory_view_controller_unittest.mm b/ios/chrome/browser/autofill/form_input_accessory/ui/form_input_accessory_view_controller_unittest.mm index 1f179ba..77b7813 100644 --- a/ios/chrome/browser/autofill/form_input_accessory/ui/form_input_accessory_view_controller_unittest.mm +++ b/ios/chrome/browser/autofill/form_input_accessory/ui/form_input_accessory_view_controller_unittest.mm
@@ -115,13 +115,9 @@ } // Tests that the number of suggestions to show is capped at -// kKeyboardAccessorySuggestionsLimit when -// kIOSKeyboardAccessorySuggestionsCutOffLimit is enabled. +// kKeyboardAccessorySuggestionsLimit. TEST_F(FormInputAccessoryViewControllerTest, - ShowAccessorySuggestions_CutOffLimitEnabled) { - base::test::ScopedFeatureList scoped_feature_list( - kIOSKeyboardAccessorySuggestionsCutOffLimit); - + ShowAccessorySuggestions_CappedAtLimit) { id mock_view_controller = OCMPartialMock(view_controller_); NSArray<FormSuggestion*>* manySuggestions = @@ -138,30 +134,6 @@ EXPECT_OCMOCK_VERIFY(mock_view_controller); } -// Tests that the number of suggestions shown is NOT capped when -// kIOSKeyboardAccessorySuggestionsCutOffLimit is disabled. -TEST_F(FormInputAccessoryViewControllerTest, - ShowAccessorySuggestions_CutOffLimitDisabled) { - base::test::ScopedFeatureList scoped_feature_list; - scoped_feature_list.InitAndDisableFeature( - kIOSKeyboardAccessorySuggestionsCutOffLimit); - - id mock_view_controller = OCMPartialMock(view_controller_); - - NSArray<FormSuggestion*>* manySuggestions = - SimpleFormSuggestions(kKeyboardAccessorySuggestionsLimit + 1); - - OCMExpect([mock_view_controller - updateFormSuggestionView:[OCMArg checkWithBlock:^BOOL( - NSArray* suggestions) { - return suggestions.count == manySuggestions.count; - }]]); - - [mock_view_controller showAccessorySuggestions:manySuggestions]; - - EXPECT_OCMOCK_VERIFY(mock_view_controller); -} - // Tests that updateFormSuggestionView takes less than a threshold with the // amount of suggestions we intend to support. Updating suggestions should be // done within this threshold to maintain smooth UI animations.
diff --git a/ios/chrome/browser/autofill/model/features.h b/ios/chrome/browser/autofill/model/features.h index d313ad6..978ce9ff 100644 --- a/ios/chrome/browser/autofill/model/features.h +++ b/ios/chrome/browser/autofill/model/features.h
@@ -33,10 +33,6 @@ // when the app is in the background. BASE_DECLARE_FEATURE(kFormInputAccessorySkipInputViewReloadInBackground); -// Feature flag to apply a limit on the number of suggestions to show in the -// Keyboard Accessory view. -BASE_DECLARE_FEATURE(kIOSKeyboardAccessorySuggestionsCutOffLimit); - // Enables the keyboard accessory view to let touches pass through blank areas. BASE_DECLARE_FEATURE(kFormInputAccessoryPassThroughTouches);
diff --git a/ios/chrome/browser/autofill/model/features.mm b/ios/chrome/browser/autofill/model/features.mm index 2eacf0e..02d6630 100644 --- a/ios/chrome/browser/autofill/model/features.mm +++ b/ios/chrome/browser/autofill/model/features.mm
@@ -26,8 +26,5 @@ BASE_FEATURE(kFormInputAccessorySkipInputViewReloadInBackground, base::FEATURE_ENABLED_BY_DEFAULT); -BASE_FEATURE(kIOSKeyboardAccessorySuggestionsCutOffLimit, - base::FEATURE_DISABLED_BY_DEFAULT); - BASE_FEATURE(kFormInputAccessoryPassThroughTouches, base::FEATURE_ENABLED_BY_DEFAULT);
diff --git a/ios/chrome/browser/backend_promo_service/OWNERS b/ios/chrome/browser/backend_promo/OWNERS similarity index 100% rename from ios/chrome/browser/backend_promo_service/OWNERS rename to ios/chrome/browser/backend_promo/OWNERS
diff --git a/ios/chrome/browser/backend_promo_service/model/BUILD.gn b/ios/chrome/browser/backend_promo/model/BUILD.gn similarity index 100% rename from ios/chrome/browser/backend_promo_service/model/BUILD.gn rename to ios/chrome/browser/backend_promo/model/BUILD.gn
diff --git a/ios/chrome/browser/backend_promo_service/model/backend_promo_service.h b/ios/chrome/browser/backend_promo/model/backend_promo_service.h similarity index 75% rename from ios/chrome/browser/backend_promo_service/model/backend_promo_service.h rename to ios/chrome/browser/backend_promo/model/backend_promo_service.h index 429ca3f..e996f9d 100644 --- a/ios/chrome/browser/backend_promo_service/model/backend_promo_service.h +++ b/ios/chrome/browser/backend_promo/model/backend_promo_service.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 IOS_CHROME_BROWSER_BACKEND_PROMO_SERVICE_MODEL_BACKEND_PROMO_SERVICE_H_ -#define IOS_CHROME_BROWSER_BACKEND_PROMO_SERVICE_MODEL_BACKEND_PROMO_SERVICE_H_ +#ifndef IOS_CHROME_BROWSER_BACKEND_PROMO_MODEL_BACKEND_PROMO_SERVICE_H_ +#define IOS_CHROME_BROWSER_BACKEND_PROMO_MODEL_BACKEND_PROMO_SERVICE_H_ #import <memory> @@ -27,4 +27,4 @@ } // namespace ios::provider -#endif // IOS_CHROME_BROWSER_BACKEND_PROMO_SERVICE_MODEL_BACKEND_PROMO_SERVICE_H_ +#endif // IOS_CHROME_BROWSER_BACKEND_PROMO_MODEL_BACKEND_PROMO_SERVICE_H_
diff --git a/ios/chrome/browser/bookmarks/test/BUILD.gn b/ios/chrome/browser/bookmarks/test/BUILD.gn index 79d33e9f..c2f9bf3 100644 --- a/ios/chrome/browser/bookmarks/test/BUILD.gn +++ b/ios/chrome/browser/bookmarks/test/BUILD.gn
@@ -11,6 +11,7 @@ "bookmarks_entries_egtest.mm", "bookmarks_interaction_egtest.mm", "bookmarks_search_egtest.mm", + "bookmarks_security_egtest.mm", "managed_bookmarks_egtest.mm", ] deps = [
diff --git a/ios/chrome/browser/bookmarks/test/bookmarks_security_egtest.mm b/ios/chrome/browser/bookmarks/test/bookmarks_security_egtest.mm new file mode 100644 index 0000000..50746e4 --- /dev/null +++ b/ios/chrome/browser/bookmarks/test/bookmarks_security_egtest.mm
@@ -0,0 +1,144 @@ +// Copyright 2026 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#import <UIKit/UIKit.h> +#import <XCTest/XCTest.h> + +#import "ios/chrome/browser/bookmarks/model/bookmark_storage_type.h" +#import "ios/chrome/browser/bookmarks/public/bookmarks_ui_constants.h" +#import "ios/chrome/browser/bookmarks/test/bookmark_earl_grey.h" +#import "ios/chrome/browser/bookmarks/test/bookmark_earl_grey_ui.h" +#import "ios/chrome/test/earl_grey/chrome_coordinator_app_interface.h" +#import "ios/chrome/test/earl_grey/chrome_earl_grey.h" +#import "ios/chrome/test/earl_grey/chrome_earl_grey_ui.h" +#import "ios/chrome/test/earl_grey/chrome_matchers.h" +#import "ios/chrome/test/earl_grey/chrome_test_case.h" +#import "ios/testing/earl_grey/earl_grey_test.h" +#import "net/test/embedded_test_server/embedded_test_server.h" + +using chrome_test_util::TappableBookmarkNodeWithLabel; + +@interface BookmarksSecurityTestCase : ChromeTestCase +@end + +@implementation BookmarksSecurityTestCase + +- (void)setUp { + [super setUp]; + [BookmarkEarlGrey waitForBookmarkModelLoaded]; + [BookmarkEarlGrey clearBookmarks]; +} + +- (void)tearDownHelper { + [ChromeCoordinatorAppInterface reset]; + [super tearDownHelper]; + [BookmarkEarlGrey clearBookmarks]; + [BookmarkEarlGrey clearBookmarksPositionCache]; +} + +// Tests that a bookmarklet executed during Bookmarks UI dismissal is blocked +// if the active tab navigated to a different origin in the background during +// the animation. +- (void)testBookmarkletTOCTOUMitigation { + // Add the bookmarklet programmatically. + NSString* bookmarkletURL = + @"javascript:document.getElementById('result').innerText='EXECUTED';"; + [BookmarkEarlGrey addBookmarkWithTitle:@"TOCTOU_Bookmarklet" + URL:bookmarkletURL + inStorage:BookmarkStorageType::kLocalOrSyncable]; + + // Start the test server. + GREYAssertTrue(self.testServer->Start(), @"Test server failed to start."); + + // Load the attacker page. + GURL attackerURL = self.testServer->GetURL("/toctou_attacker.html"); + [ChromeEarlGrey loadURL:attackerURL]; + + // Open Bookmarks UI using the real UI helper. + [BookmarkEarlGreyUI openBookmarks]; + [BookmarkEarlGreyUI openMobileBookmarks]; + + // Tap the bookmarklet in the UI. + [[EarlGrey selectElementWithMatcher:TappableBookmarkNodeWithLabel( + @"TOCTOU_Bookmarklet")] + performAction:grey_tap()]; + + // Trigger background navigation programmatically immediately after tapping + // the bookmarklet. + GURL sensitiveURL = self.testServer->GetURL("/toctou_sensitive.html"); + [ChromeEarlGrey loadURL:sensitiveURL]; + + // Tapping the bookmarklet should close the bookmarks UI. + [[EarlGrey selectElementWithMatcher:grey_accessibilityID( + kBookmarksHomeTableViewIdentifier)] + assertWithMatcher:grey_nil()]; + + // Verify that the bookmarklet was NOT executed on the sensitive page. + [ChromeEarlGrey waitForWebStateContainingText:"Not executed"]; +} + +// Tests that a bookmarklet executed during Bookmarks UI dismissal works +// successfully in the common case (where no background origin navigation +// occurs). +- (void)testBookmarkletExecutionInCommonCase { + // Add the bookmarklet programmatically. + NSString* bookmarkletURL = + @"javascript:document.getElementById('result').innerText='EXECUTED';"; + [BookmarkEarlGrey addBookmarkWithTitle:@"TOCTOU_Bookmarklet" + URL:bookmarkletURL + inStorage:BookmarkStorageType::kLocalOrSyncable]; + + // Start the test server. + GREYAssertTrue(self.testServer->Start(), @"Test server failed to start."); + + // Load the sensitive page directly (which has no background navigation). + [ChromeEarlGrey loadURL:self.testServer->GetURL("/toctou_sensitive.html")]; + + // Open Bookmarks UI using the real UI helper. + [BookmarkEarlGreyUI openBookmarks]; + [BookmarkEarlGreyUI openMobileBookmarks]; + + // Tap the bookmarklet in the UI. + [[EarlGrey selectElementWithMatcher:TappableBookmarkNodeWithLabel( + @"TOCTOU_Bookmarklet")] + performAction:grey_tap()]; + + // Tapping the bookmarklet should close the bookmarks UI. + [[EarlGrey selectElementWithMatcher:grey_accessibilityID( + kBookmarksHomeTableViewIdentifier)] + assertWithMatcher:grey_nil()]; + + // Verify that the bookmarklet WAS executed successfully on the page. + [ChromeEarlGrey waitForWebStateContainingText:"EXECUTED"]; +} + +// Tests that a bookmarklet executed during Bookmarks UI dismissal works +// successfully on a fresh blank tab (where the last committed URL is empty). +- (void)testBookmarkletExecutionOnBlankTab { + // Add the bookmarklet programmatically. + NSString* bookmarkletURL = + @"javascript:document.getElementById('result').innerText='EXECUTED';"; + [BookmarkEarlGrey addBookmarkWithTitle:@"TOCTOU_Bookmarklet" + URL:bookmarkletURL + inStorage:BookmarkStorageType::kLocalOrSyncable]; + + // Open a fresh new blank tab. + [ChromeEarlGrey openNewTab]; + + // Open Bookmarks UI using the real UI helper. + [BookmarkEarlGreyUI openBookmarks]; + [BookmarkEarlGreyUI openMobileBookmarks]; + + // Tap the bookmarklet in the UI. + [[EarlGrey selectElementWithMatcher:TappableBookmarkNodeWithLabel( + @"TOCTOU_Bookmarklet")] + performAction:grey_tap()]; + + // Tapping the bookmarklet should close the bookmarks UI successfully. + [[EarlGrey selectElementWithMatcher:grey_accessibilityID( + kBookmarksHomeTableViewIdentifier)] + assertWithMatcher:grey_nil()]; +} + +@end
diff --git a/ios/chrome/browser/bookmarks/ui_bundled/home/bookmarks_coordinator.mm b/ios/chrome/browser/bookmarks/ui_bundled/home/bookmarks_coordinator.mm index 1255de4d..4c8d24e5 100644 --- a/ios/chrome/browser/bookmarks/ui_bundled/home/bookmarks_coordinator.mm +++ b/ios/chrome/browser/bookmarks/ui_bundled/home/bookmarks_coordinator.mm
@@ -333,18 +333,28 @@ _currentBrowserState.get())); } + GURL urlBeforeDismissal; + if (self.browser && self.browser->GetWebStateList()) { + web::WebState* activeWebState = + self.browser->GetWebStateList()->GetActiveWebState(); + if (activeWebState) { + urlBeforeDismissal = activeWebState->GetLastCommittedURL(); + } + } + // First the bookmark view should be dismissed to have the animation, and // the URLs should be opened. // Otherwise, opening directly the URLs would automatically dismiss the // bookmark view without animation. ProceduralBlock dismissCompletion = base::CallbackToBlock(base::BindOnce( [](__weak __typeof(self) weakSelf, std::vector<GURL> urls_to_open, - BOOL in_incognito, BOOL new_tab) { + BOOL in_incognito, BOOL new_tab, GURL url_before_dismissal) { [weakSelf openUrls:urls_to_open - inIncognito:in_incognito - newTab:new_tab]; + inIncognito:in_incognito + newTab:new_tab + urlBeforeDismissal:url_before_dismissal]; }, - self, urlsToOpen, inIncognito, newTab)); + self, urlsToOpen, inIncognito, newTab, urlBeforeDismissal)); if (self.baseViewController.presentedViewController) { [self.baseViewController dismissViewControllerAnimated:animated @@ -482,9 +492,18 @@ newTab:newTab]; } +// Opens `urls` using the specified tab settings. +// `urls`: The list of URLs to open. Only the first URL is opened in the +// foreground, others are opened in background tabs. +// `inIncognito`: Whether the URLs should be opened in an incognito tab. +// `newTab`: Whether the URLs should be forced to open in a new tab. +// `urlBeforeDismissal`: The GURL of the active web state before the bookmarks +// UI dismissal animation started. Used to prevent Universal Cross-Site +// Scripting (UXSS). - (void)openUrls:(const std::vector<GURL>&)urls - inIncognito:(BOOL)inIncognito - newTab:(BOOL)newTab { + inIncognito:(BOOL)inIncognito + newTab:(BOOL)newTab + urlBeforeDismissal:(const GURL&)urlBeforeDismissal { BOOL openInForegroundTab = YES; WebStateList* webStateList = self.browser->GetWebStateList(); for (const GURL& url : urls) { @@ -516,7 +535,7 @@ [self openURLInNewTab:url inIncognito:inIncognito inBackground:NO]; } else { // Open in current tab otherwise. - [self openURLInCurrentTab:url]; + [self openURLInCurrentTab:url urlBeforeDismissal:urlBeforeDismissal]; } } else { // Open other URLs (if any) in background tabs. @@ -681,11 +700,21 @@ completion:nil]; } -- (void)openURLInCurrentTab:(const GURL&)url { +- (void)openURLInCurrentTab:(const GURL&)url + urlBeforeDismissal:(const GURL&)urlBeforeDismissal { Browser* browser = self.browser; WebStateList* webStateList = browser->GetWebStateList(); if (url.SchemeIs(url::kJavaScriptScheme) && webStateList) { // bookmarklet - LoadJavaScriptURL(url, browser, webStateList->GetActiveWebState()); + web::WebState* activeWebState = webStateList->GetActiveWebState(); + // Both the last committed URL and visible URL of the active WebState must + // be equal to the URL before dismissal in order to avoid UXSS (Universal + // Cross-Site Scripting) caused by background/pending navigations during + // Bookmarks UI dismissal animation. + if (activeWebState && + activeWebState->GetLastCommittedURL() == urlBeforeDismissal && + activeWebState->GetVisibleURL() == urlBeforeDismissal) { + LoadJavaScriptURL(url, browser, activeWebState); + } return; } UrlLoadParams params = UrlLoadParams::InCurrentTab(url);
diff --git a/ios/chrome/browser/bookmarks/ui_bundled/home/bookmarks_home_mediator.h b/ios/chrome/browser/bookmarks/ui_bundled/home/bookmarks_home_mediator.h index 408e7b0..b8ec9a8 100644 --- a/ios/chrome/browser/bookmarks/ui_bundled/home/bookmarks_home_mediator.h +++ b/ios/chrome/browser/bookmarks/ui_bundled/home/bookmarks_home_mediator.h
@@ -28,6 +28,9 @@ class PrefRegistrySyncable; } // namespace user_prefs +using queryLocalBookmarksCompletion = void (^)(int local_bookmarks_count, + std::string user_email); + typedef NS_ENUM(NSInteger, BookmarksHomeSectionIdentifier) { // Section to invite the user to sign in and sync. BookmarksHomeSectionIdentifierPromo = kSectionIdentifierEnumZero, @@ -129,8 +132,7 @@ - (void)triggerBatchUpload; // Queries the sync service for the count of local bookmarks. -- (void)queryLocalBookmarks:(void (^)(int local_bookmarks_count, - std::string user_email))completion; +- (void)queryLocalBookmarks:(queryLocalBookmarksCompletion)completion; // Returns weather the slashed cloud icon should be displayed for // `bookmarkNode`.
diff --git a/ios/chrome/browser/bookmarks/ui_bundled/home/bookmarks_home_mediator.mm b/ios/chrome/browser/bookmarks/ui_bundled/home/bookmarks_home_mediator.mm index 1e9b9ba0..dc82970d 100644 --- a/ios/chrome/browser/bookmarks/ui_bundled/home/bookmarks_home_mediator.mm +++ b/ios/chrome/browser/bookmarks/ui_bundled/home/bookmarks_home_mediator.mm
@@ -85,6 +85,21 @@ return false; } +void QueryLocalBookmarksCompletionWithDescription( + std::string user_email, + queryLocalBookmarksCompletion completion, + std::map<syncer::DataType, syncer::LocalDataDescription> description) { + CHECK(completion); + auto it = description.find(syncer::BOOKMARKS); + // GetLocalDataDescriptions() can return an empty result if data type is + // still in configuration, or has an error. + if (it != description.end()) { + completion(it->second.item_count, std::move(user_email)); + return; + } + completion(0, std::move(user_email)); +} + } // namespace bool IsABookmarkNodeSectionForIdentifier( @@ -448,22 +463,13 @@ true); } -- (void)queryLocalBookmarks:(void (^)(int local_bookmarks_count, - std::string user_email))completion { - std::string user_email = self.syncService->GetAccountInfo().email; +- (void)queryLocalBookmarks:(queryLocalBookmarksCompletion)completion { + std::string userEmail = self.syncService->GetAccountInfo().email; + CHECK(completion); self.syncService->GetLocalDataDescriptions( syncer::DataTypeSet({syncer::BOOKMARKS}), - base::BindOnce(^(std::map<syncer::DataType, syncer::LocalDataDescription> - description) { - auto it = description.find(syncer::BOOKMARKS); - // GetLocalDataDescriptions() can return an empty result if data type is - // still in configuration, or has an error. - if (it != description.end()) { - completion(it->second.item_count, std::move(user_email)); - return; - } - completion(0, std::move(user_email)); - })); + base::BindOnce(&QueryLocalBookmarksCompletionWithDescription, userEmail, + completion)); } - (bookmark_utils_ios::NodeSet&)selectedNodesForEditMode {
diff --git a/ios/chrome/browser/browser_view/ui_bundled/browser_view_controller.mm b/ios/chrome/browser/browser_view/ui_bundled/browser_view_controller.mm index b564d82..6d2b1db 100644 --- a/ios/chrome/browser/browser_view/ui_bundled/browser_view_controller.mm +++ b/ios/chrome/browser/browser_view/ui_bundled/browser_view_controller.mm
@@ -1393,8 +1393,6 @@ // (Landscape). self.secondaryToolbarRegularBottomConstraint = [toolbarView.bottomAnchor constraintEqualToAnchor:self.view.bottomAnchor]; - self.secondaryToolbarRegularBottomConstraint.active = YES; - } else { [toolbarView.bottomAnchor constraintEqualToAnchor:self.view.bottomAnchor] .active = YES;
diff --git a/ios/chrome/browser/flags/about_flags.mm b/ios/chrome/browser/flags/about_flags.mm index 26130af..42331ccc 100644 --- a/ios/chrome/browser/flags/about_flags.mm +++ b/ios/chrome/browser/flags/about_flags.mm
@@ -1361,24 +1361,12 @@ {kExplainGeminiEditMenuParams, "1"}}; const FeatureEntry::FeatureParam kAfterSearchForExplainGeminiEditMenu[] = { {kExplainGeminiEditMenuParams, "2"}}; -const FeatureEntry::FeatureParam - kAfterEditMinLength10ForExplainGeminiEditMenu[] = { - {kExplainGeminiEditMenuParams, "1"}, - {kExplainGeminiEditMenuMinTextLengthParam, "10"}}; -const FeatureEntry::FeatureParam - kAfterEditMinLength20ForExplainGeminiEditMenu[] = { - {kExplainGeminiEditMenuParams, "1"}, - {kExplainGeminiEditMenuMinTextLengthParam, "20"}}; const FeatureEntry::FeatureVariation kPositionForExplainGeminiEditMenu[] = { {"Explain Gemini shows up after Edit", kAfterEditForExplainGeminiEditMenu, nullptr}, {"Explain Gemini shows up after Search with Google", - kAfterSearchForExplainGeminiEditMenu, nullptr}, - {"Explain Gemini after Edit, Min Length 10", - kAfterEditMinLength10ForExplainGeminiEditMenu, nullptr}, - {"Explain Gemini after Edit, Min Length 20", - kAfterEditMinLength20ForExplainGeminiEditMenu, nullptr}}; + kAfterSearchForExplainGeminiEditMenu, nullptr}}; const FeatureEntry::FeatureParam kPageActionMenuIconSparkles1[] = { {kPageActionMenuIconParams, "1"}};
diff --git a/ios/chrome/browser/intelligence/actor/model/actor_service_unittest.mm b/ios/chrome/browser/intelligence/actor/model/actor_service_unittest.mm index 8ea9f2a..49102c2 100644 --- a/ios/chrome/browser/intelligence/actor/model/actor_service_unittest.mm +++ b/ios/chrome/browser/intelligence/actor/model/actor_service_unittest.mm
@@ -85,7 +85,8 @@ protected: web::ScopedTestingWebClient web_client_; - web::WebTaskEnvironment task_environment_; + web::WebTaskEnvironment task_environment_{ + base::test::TaskEnvironment::TimeSource::MOCK_TIME}; std::unique_ptr<TestProfileIOS> profile_; }; @@ -345,4 +346,151 @@ EXPECT_EQ(nullptr, resolved_web_state); } +// Tests that PerformActions completes immediately when the WebState is not +// loading. +TEST_F(ActorServiceTest, PerformActions_NoLoading_InstantCompletion) { + base::test::ScopedFeatureList scoped_feature_list; + scoped_feature_list.InitAndEnableFeature(kActorTools); + + ActorService* service = ActorServiceFactory::GetForProfile(profile_.get()); + ASSERT_NE(nullptr, service); + + ActorTaskId task_id = + service->CreateTask("Test Task", /*allow_incognito_web_states=*/false); + + BrowserList* browser_list = BrowserListFactory::GetForProfile(profile_.get()); + auto test_browser = std::make_unique<TestBrowser>(profile_.get()); + browser_list->AddBrowser(test_browser.get()); + + auto fake_web_state = std::make_unique<web::FakeWebState>(); + auto* fake_web_state_ptr = fake_web_state.get(); + test_browser->GetWebStateList()->InsertWebState(std::move(fake_web_state)); + + std::vector<std::unique_ptr<ActorTool>> actions; + actions.push_back( + std::make_unique<TestTool>(fake_web_state_ptr->GetWeakPtr())); + + bool callback_called = false; + service->PerformActions( + task_id, std::move(actions), "Update", + base::BindOnce( + [](bool* called, PerformActionsResult result) { *called = true; }, + base::Unretained(&callback_called))); + + // Run the queued tasks (tool execution and completion) deterministically. + base::RunLoop run_loop; + base::SingleThreadTaskRunner::GetCurrentDefault()->PostTask( + FROM_HERE, run_loop.QuitClosure()); + run_loop.Run(); + + EXPECT_TRUE(callback_called); + + browser_list->RemoveBrowser(test_browser.get()); +} + +// Tests that PerformActions is deferred when the WebState is loading, and only +// resolves when loading completes. +TEST_F(ActorServiceTest, PerformActions_Loading_DeferredUntilStopLoading) { + base::test::ScopedFeatureList scoped_feature_list; + scoped_feature_list.InitAndEnableFeature(kActorTools); + + ActorService* service = ActorServiceFactory::GetForProfile(profile_.get()); + ASSERT_NE(nullptr, service); + + ActorTaskId task_id = + service->CreateTask("Test Task", /*allow_incognito_web_states=*/false); + + BrowserList* browser_list = BrowserListFactory::GetForProfile(profile_.get()); + auto test_browser = std::make_unique<TestBrowser>(profile_.get()); + browser_list->AddBrowser(test_browser.get()); + + auto fake_web_state = std::make_unique<web::FakeWebState>(); + auto* fake_web_state_ptr = fake_web_state.get(); + test_browser->GetWebStateList()->InsertWebState(std::move(fake_web_state)); + + // Set the WebState to a loading state. + fake_web_state_ptr->SetLoading(true); + + std::vector<std::unique_ptr<ActorTool>> actions; + actions.push_back( + std::make_unique<TestTool>(fake_web_state_ptr->GetWeakPtr())); + + bool callback_called = false; + service->PerformActions( + task_id, std::move(actions), "Update", + base::BindOnce( + [](bool* called, PerformActionsResult result) { *called = true; }, + base::Unretained(&callback_called))); + + // Run the queued execution tasks. + base::RunLoop run_loop; + base::SingleThreadTaskRunner::GetCurrentDefault()->PostTask( + FROM_HERE, run_loop.QuitClosure()); + run_loop.Run(); + + // Gating: The callback should not be executed yet because the page is + // loading. + EXPECT_FALSE(callback_called); + + // Stop the load. + fake_web_state_ptr->SetLoading(false); + + // Now the callback should execute successfully. + EXPECT_TRUE(callback_called); + + browser_list->RemoveBrowser(test_browser.get()); +} + +// Tests that a loading WebState times out after 5 seconds, forcing the +// deferred PerformActions callback to run to prevent hanging. +TEST_F(ActorServiceTest, PerformActions_Loading_TimeoutResolvesCallback) { + base::test::ScopedFeatureList scoped_feature_list; + scoped_feature_list.InitAndEnableFeature(kActorTools); + + ActorService* service = ActorServiceFactory::GetForProfile(profile_.get()); + ASSERT_NE(nullptr, service); + + ActorTaskId task_id = + service->CreateTask("Test Task", /*allow_incognito_web_states=*/false); + + BrowserList* browser_list = BrowserListFactory::GetForProfile(profile_.get()); + auto test_browser = std::make_unique<TestBrowser>(profile_.get()); + browser_list->AddBrowser(test_browser.get()); + + auto fake_web_state = std::make_unique<web::FakeWebState>(); + auto* fake_web_state_ptr = fake_web_state.get(); + test_browser->GetWebStateList()->InsertWebState(std::move(fake_web_state)); + + // Set the WebState to a loading state. + fake_web_state_ptr->SetLoading(true); + + std::vector<std::unique_ptr<ActorTool>> actions; + actions.push_back( + std::make_unique<TestTool>(fake_web_state_ptr->GetWeakPtr())); + + bool callback_called = false; + service->PerformActions( + task_id, std::move(actions), "Update", + base::BindOnce( + [](bool* called, PerformActionsResult result) { *called = true; }, + base::Unretained(&callback_called))); + + // Run the queued execution tasks. + base::RunLoop run_loop; + base::SingleThreadTaskRunner::GetCurrentDefault()->PostTask( + FROM_HERE, run_loop.QuitClosure()); + run_loop.Run(); + + // Callback should be deferred. + EXPECT_FALSE(callback_called); + + // Fast forward the environment by 7 seconds to trigger the load timeout. + task_environment_.FastForwardBy(base::Seconds(7)); + + // The callback must be resolved now due to the timeout. + EXPECT_TRUE(callback_called); + + browser_list->RemoveBrowser(test_browser.get()); +} + } // namespace actor
diff --git a/ios/chrome/browser/intelligence/actor/model/actor_task.h b/ios/chrome/browser/intelligence/actor/model/actor_task.h index 24e0226..5b9bff7 100644 --- a/ios/chrome/browser/intelligence/actor/model/actor_task.h +++ b/ios/chrome/browser/intelligence/actor/model/actor_task.h
@@ -12,7 +12,10 @@ #import "base/functional/callback.h" #import "base/memory/raw_ptr.h" #import "base/memory/weak_ptr.h" +#import "base/scoped_multi_source_observation.h" +#import "base/timer/timer.h" #import "ios/chrome/browser/intelligence/actor/public/actor_types.h" +#import "ios/web/public/web_state_observer.h" namespace web { class WebState; @@ -27,13 +30,13 @@ // A class representing a task managed by `ActorService`. A task should live for // a whole Actor journey and be passed multiple sets of actions to execute // sequentially. -class ActorTask { +class ActorTask : public web::WebStateObserver { public: ActorTask(ActorTaskId task_id, const std::string& title, bool allow_incognito_web_states, AggregatedJournal* journal); - ~ActorTask(); + ~ActorTask() override; ActorTask(const ActorTask&) = delete; ActorTask& operator=(const ActorTask&) = delete; @@ -73,6 +76,10 @@ // Returns whether this task allows actuating on incognito WebStates. bool allow_incognito_web_states() const; + // web::WebStateObserver overrides. + void DidStopLoading(web::WebState* web_state) override; + void WebStateDestroyed(web::WebState* web_state) override; + private: friend class ActorTaskTest; @@ -87,6 +94,23 @@ void AddControlledWebStates( const std::vector<std::unique_ptr<ActorTool>>& actions); + // Starts observing controlled WebStates that are loading. Returns true if any + // observations are active, and false otherwise. + bool ObserveLoadingWebStates(); + + // Defers the `Act()` completion callback and registers the safety timeout + // timer. + void DeferActCompletion(ActCallback callback, + std::vector<ActionResult> results); + + // Handles observation removal when a WebState finishes loading or is + // destroyed. Also resolves the deferred callback if no more WebStates are + // loading. + void OnWebStateFinishedLoading(web::WebState* web_state); + + // Handles the page load timeout. + void OnPageLoadedTimeout(); + // The task state. ActorTaskState state_ = ActorTaskState::kInit; @@ -108,6 +132,19 @@ // by this task. std::vector<base::WeakPtr<web::WebState>> controlled_web_states_; + // Scoped observation to safely observe events of controlled WebStates. + base::ScopedMultiSourceObservation<web::WebState, web::WebStateObserver> + scoped_web_state_observations_{this}; + + // Deferred Act callback. The `Act()` callback can be deferred if any of the + // controlled WebStates are loading when Act is done executing actions. + base::OnceClosure deferred_act_callback_; + + // Timer to enforce the page load timeout. The timeout exists to limit the + // amount of time ActorTask can wait for a page to finish loading before + // executing the Act callback. + base::OneShotTimer load_timeout_timer_; + // Weak pointer factory. base::WeakPtrFactory<ActorTask> weak_ptr_factory_{this}; };
diff --git a/ios/chrome/browser/intelligence/actor/model/actor_task.mm b/ios/chrome/browser/intelligence/actor/model/actor_task.mm index de7bd83..e6a53bd 100644 --- a/ios/chrome/browser/intelligence/actor/model/actor_task.mm +++ b/ios/chrome/browser/intelligence/actor/model/actor_task.mm
@@ -9,6 +9,8 @@ #import "base/functional/bind.h" #import "base/stl_util.h" #import "base/strings/string_number_conversions.h" +#import "base/time/time.h" +#import "base/timer/timer.h" #import "ios/chrome/browser/intelligence/actor/model/actor_engine.h" #import "ios/chrome/browser/intelligence/actor/model/aggregated_journal.h" #import "ios/chrome/browser/intelligence/actor/tools/model/actor_tool.h" @@ -18,6 +20,9 @@ namespace { +// Safety timeout duration to wait for pages to finish loading. +constexpr base::TimeDelta kPageLoadTimeout = base::Seconds(7); + // Returns the string representation of the ActorTaskState. std::string ActorTaskStateToString(ActorTaskState state) { switch (state) { @@ -72,7 +77,9 @@ engine_ = std::make_unique<ActorEngine>(task_id_, journal_); } -ActorTask::~ActorTask() = default; +ActorTask::~ActorTask() { + load_timeout_timer_.Stop(); +} ActorTaskState ActorTask::GetState() const { return state_; @@ -112,9 +119,67 @@ void ActorTask::OnActCompleted(ActCallback callback, std::vector<ActionResult> results) { // TODO(crbug.com/503054406): Check for tool errors. + + if (ObserveLoadingWebStates()) { + DeferActCompletion(std::move(callback), std::move(results)); + return; + } + std::move(callback).Run(std::move(results)); } +bool ActorTask::ObserveLoadingWebStates() { + for (const auto& weak_web_state : controlled_web_states_) { + web::WebState* web_state = weak_web_state.get(); + if (web_state && web_state->IsLoading()) { + scoped_web_state_observations_.AddObservation(web_state); + } + } + + return scoped_web_state_observations_.IsObservingAnySource(); +} + +void ActorTask::DeferActCompletion(ActCallback callback, + std::vector<ActionResult> results) { + deferred_act_callback_ = + base::BindOnce(std::move(callback), std::move(results)); + + load_timeout_timer_.Start(FROM_HERE, kPageLoadTimeout, + base::BindOnce(&ActorTask::OnPageLoadedTimeout, + weak_ptr_factory_.GetWeakPtr())); +} + +void ActorTask::DidStopLoading(web::WebState* web_state) { + OnWebStateFinishedLoading(web_state); +} + +void ActorTask::WebStateDestroyed(web::WebState* web_state) { + OnWebStateFinishedLoading(web_state); +} + +void ActorTask::OnWebStateFinishedLoading(web::WebState* web_state) { + scoped_web_state_observations_.RemoveObservation(web_state); + + if (scoped_web_state_observations_.IsObservingAnySource()) { + return; + } + + // Stop the timeout and execute the deferred callback since no more observed + // WebStates are still loading. + load_timeout_timer_.Stop(); + if (deferred_act_callback_) { + std::move(deferred_act_callback_).Run(); + } +} + +void ActorTask::OnPageLoadedTimeout() { + scoped_web_state_observations_.RemoveAllObservations(); + + if (deferred_act_callback_) { + std::move(deferred_act_callback_).Run(); + } +} + void ActorTask::Stop(ActorTaskStoppedReason stop_reason) { // TODO(crbug.com/496164697): Implement and test. }
diff --git a/ios/chrome/browser/intelligence/bwg/model/gemini_browser_agent_unittest.mm b/ios/chrome/browser/intelligence/bwg/model/gemini_browser_agent_unittest.mm index 0ad02cc7..a4f8c0bd 100644 --- a/ios/chrome/browser/intelligence/bwg/model/gemini_browser_agent_unittest.mm +++ b/ios/chrome/browser/intelligence/bwg/model/gemini_browser_agent_unittest.mm
@@ -714,8 +714,9 @@ EXPECT_EQ(ios::provider::GetCurrentMode(), ios::provider::GeminiViewMode::kLive); - // Simulate app backgrounding via SceneState activation level. - browser_->GetSceneState().activationLevel = SceneActivationLevelBackground; + // Simulate app backgrounding via SceneState activation level callback. + gemini_browser_agent_->OnSceneActivationLevelChanged( + SceneActivationLevelBackground); // Verify it switched to Floaty (text mode). EXPECT_EQ(ios::provider::GetCurrentMode(), @@ -736,8 +737,9 @@ EXPECT_EQ(ios::provider::GetCurrentMode(), ios::provider::GeminiViewMode::kFloaty); - // Simulate app backgrounding via SceneState activation level. - browser_->GetSceneState().activationLevel = SceneActivationLevelBackground; + // Simulate app backgrounding via SceneState activation level callback. + gemini_browser_agent_->OnSceneActivationLevelChanged( + SceneActivationLevelBackground); // Verify it remained Floaty (text mode). EXPECT_EQ(ios::provider::GetCurrentMode(),
diff --git a/ios/chrome/browser/intelligence/bwg/model/gemini_tab_helper.mm b/ios/chrome/browser/intelligence/bwg/model/gemini_tab_helper.mm index c1f3272..835dd77f 100644 --- a/ios/chrome/browser/intelligence/bwg/model/gemini_tab_helper.mm +++ b/ios/chrome/browser/intelligence/bwg/model/gemini_tab_helper.mm
@@ -669,8 +669,17 @@ return; } - UIImage* badge_image = - [GeminiUIUtils brandedGeminiSymbolWithPointSize:kBadgeSymbolPointSize]; + UIImage* badge_image; + BOOL should_hide_badge_after_chip_collapse = NO; + if (IsChromeNextIaEnabled()) { + badge_image = CustomSymbolTemplateWithPointSize(kTextSparkSymbol, + kBadgeSymbolPointSize); + should_hide_badge_after_chip_collapse = NO; + } else { + badge_image = + [GeminiUIUtils brandedGeminiSymbolWithPointSize:kBadgeSymbolPointSize]; + should_hide_badge_after_chip_collapse = YES; + } NSString* cue_label = l10n_util::GetNSString(IDS_IOS_ASK_GEMINI_CHIP_CUE_LABEL); LocationBarBadgeConfiguration* badge_config = @@ -680,7 +689,8 @@ badgeImage:badge_image]; badge_config.badgeText = cue_label; - badge_config.shouldHideBadgeAfterChipCollapse = true; + badge_config.shouldHideBadgeAfterChipCollapse = + should_hide_badge_after_chip_collapse; bool success = false; if ([(id)location_bar_badge_commands_handler_ respondsToSelector:@selector(updateBadgeConfig:)]) {
diff --git a/ios/chrome/browser/intelligence/explain_with_gemini/coordinator/explain_with_gemini_mediator.mm b/ios/chrome/browser/intelligence/explain_with_gemini/coordinator/explain_with_gemini_mediator.mm index b5616e25..2a9a688 100644 --- a/ios/chrome/browser/intelligence/explain_with_gemini/coordinator/explain_with_gemini_mediator.mm +++ b/ios/chrome/browser/intelligence/explain_with_gemini/coordinator/explain_with_gemini_mediator.mm
@@ -107,11 +107,6 @@ web::WebState* capturedWebState = weakWebState.get(); if (weakSelf && capturedWebState && response.valid && response.selectedText.length) { - NSUInteger minLength = - static_cast<NSUInteger>(ExplainGeminiEditMenuMinTextLength()); - if (minLength > 0 && response.selectedText.length < minLength) { - return; - } [weakSelf triggerExplainWithGeminiForText:response.selectedText webState:capturedWebState]; } @@ -156,12 +151,6 @@ return; } NSString* text = response.selectedText; - NSUInteger minLength = - static_cast<NSUInteger>(ExplainGeminiEditMenuMinTextLength()); - if (minLength > 0 && text.length < minLength) { - completion(@[]); - return; - } if ([[text stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]]
diff --git a/ios/chrome/browser/intelligence/explain_with_gemini/coordinator/explain_with_gemini_mediator_unittest.mm b/ios/chrome/browser/intelligence/explain_with_gemini/coordinator/explain_with_gemini_mediator_unittest.mm index 9e882cf..8b19f2f 100644 --- a/ios/chrome/browser/intelligence/explain_with_gemini/coordinator/explain_with_gemini_mediator_unittest.mm +++ b/ios/chrome/browser/intelligence/explain_with_gemini/coordinator/explain_with_gemini_mediator_unittest.mm
@@ -315,79 +315,3 @@ EXPECT_EQ(items.count, 0u); } } - -// Tests that no action is added when the selection length is less than -// MinTextLength. -TEST_F(ExplainWithGeminiMediatorTest, AddItem_BelowMinLength) { - @autoreleasepool { - base::test::ScopedFeatureList scoped_feature_list; - scoped_feature_list.InitWithFeaturesAndParameters( - {{kPageActionMenu, {}}, - {kExplainGeminiEditMenu, - {{"PositionForExplainGeminiEditMenu", "1"}, - {"ExplainGeminiEditMenuMinTextLength", "20"}}}}, - {}); - - id partialMock = OCMPartialMock(mediator_); - OCMStub( - [partialMock canPerformExplainWithGeminiInWebState:web_state_.get()]) - .andReturn(YES); - - WebSelectionResponse* response = - [[WebSelectionResponse alloc] initWithSelectedText:@"Short" - sourceView:nil - sourceRect:CGRectZero - valid:YES]; - - __block BOOL completionCalled = NO; - __block NSArray* items = nil; - - [partialMock addItemWithResponse:response - completion:^(NSArray* result) { - completionCalled = YES; - items = result; - } - webState:web_state_.get()]; - - EXPECT_TRUE(completionCalled); - EXPECT_EQ(items.count, 0u); - } -} - -// Tests that action is added when the selection length is greater than or equal -// to MinTextLength. -TEST_F(ExplainWithGeminiMediatorTest, AddItem_AboveMinLength) { - @autoreleasepool { - base::test::ScopedFeatureList scoped_feature_list; - scoped_feature_list.InitWithFeaturesAndParameters( - {{kPageActionMenu, {}}, - {kExplainGeminiEditMenu, - {{"PositionForExplainGeminiEditMenu", "1"}, - {"ExplainGeminiEditMenuMinTextLength", "10"}}}}, - {}); - - id partialMock = OCMPartialMock(mediator_); - OCMStub( - [partialMock canPerformExplainWithGeminiInWebState:web_state_.get()]) - .andReturn(YES); - - WebSelectionResponse* response = - [[WebSelectionResponse alloc] initWithSelectedText:@"Long enough text" - sourceView:nil - sourceRect:CGRectZero - valid:YES]; - - __block BOOL completionCalled = NO; - __block NSArray* items = nil; - - [partialMock addItemWithResponse:response - completion:^(NSArray* result) { - completionCalled = YES; - items = result; - } - webState:web_state_.get()]; - - EXPECT_TRUE(completionCalled); - EXPECT_EQ(items.count, 1u); - } -}
diff --git a/ios/chrome/browser/intelligence/features/features.h b/ios/chrome/browser/intelligence/features/features.h index 5c2726d..d506d137 100644 --- a/ios/chrome/browser/intelligence/features/features.h +++ b/ios/chrome/browser/intelligence/features/features.h
@@ -112,7 +112,6 @@ BASE_DECLARE_FEATURE(kExplainGeminiEditMenu); extern const char kExplainGeminiEditMenuParams[]; -extern const char kExplainGeminiEditMenuMinTextLengthParam[]; // Holds the position of Explain Gemini button in the EditMenu. enum class PositionForExplainGeminiEditMenu { @@ -124,10 +123,6 @@ // Returns the position of Explain Gemini in the EditMenu. PositionForExplainGeminiEditMenu ExplainGeminiEditMenuPosition(); -// Returns the minimum text length requirement for Explain Gemini in Edit Menu -// to be shown. If value is 0, there is no minimum length requirement. -int ExplainGeminiEditMenuMinTextLength(); - // Feature flag to enable Precise Location in Gemini Settings Menu. BASE_DECLARE_FEATURE(kGeminiPreciseLocation);
diff --git a/ios/chrome/browser/intelligence/features/features.mm b/ios/chrome/browser/intelligence/features/features.mm index dd377ed7..b942b4a 100644 --- a/ios/chrome/browser/intelligence/features/features.mm +++ b/ios/chrome/browser/intelligence/features/features.mm
@@ -257,19 +257,6 @@ return PositionForExplainGeminiEditMenu::kDisabled; } -const char kExplainGeminiEditMenuMinTextLengthParam[] = - "ExplainGeminiEditMenuMinTextLength"; - -BASE_FEATURE_PARAM(int, - kExplainGeminiEditMenuMinTextLengthFeatureParam, - &kExplainGeminiEditMenu, - kExplainGeminiEditMenuMinTextLengthParam, - 0); - -int ExplainGeminiEditMenuMinTextLength() { - return kExplainGeminiEditMenuMinTextLengthFeatureParam.Get(); -} - BASE_FEATURE(kExplainGeminiEditMenu, base::FEATURE_DISABLED_BY_DEFAULT); BASE_FEATURE(kGeminiPreciseLocation, base::FEATURE_DISABLED_BY_DEFAULT); @@ -441,7 +428,7 @@ return base::FeatureList::IsEnabled(kGeminiUpdatedEligibility); } -BASE_FEATURE(kGeminiImageRemixTool, base::FEATURE_DISABLED_BY_DEFAULT); +BASE_FEATURE(kGeminiImageRemixTool, base::FEATURE_ENABLED_BY_DEFAULT); bool IsGeminiImageRemixToolEnabled() { if (!IsPageActionMenuEnabled()) { @@ -457,7 +444,7 @@ return false; } return base::GetFieldTrialParamByFeatureAsBool( - kGeminiImageRemixTool, kGeminiImageRemixToolShowFRERow, false); + kGeminiImageRemixTool, kGeminiImageRemixToolShowFRERow, true); } const char kGeminiImageRemixToolShowAboveSearchImage[] = "ShowAboveSearchImage"; @@ -487,7 +474,7 @@ return false; } return base::GetFieldTrialParamByFeatureAsBool( - kGeminiImageRemixTool, kGeminiImageRemixToolRemovePageContext, false); + kGeminiImageRemixTool, kGeminiImageRemixToolRemovePageContext, true); } BASE_FEATURE(kGeminiEligibilityAblation, base::FEATURE_DISABLED_BY_DEFAULT);
diff --git a/ios/chrome/browser/ntp/ui_bundled/new_tab_page_header_view.mm b/ios/chrome/browser/ntp/ui_bundled/new_tab_page_header_view.mm index bfe37b21..e85f6a8 100644 --- a/ios/chrome/browser/ntp/ui_bundled/new_tab_page_header_view.mm +++ b/ios/chrome/browser/ntp/ui_bundled/new_tab_page_header_view.mm
@@ -599,6 +599,8 @@ constraintEqualToAnchor:searchField.trailingAnchor]; self.fakeLocationBarHeightConstraint = [self.fakeLocationBar.heightAnchor constraintEqualToConstant:content_suggestions::FakeOmniboxHeight()]; + self.fakeLocationBar.layer.cornerRadius = + content_suggestions::FakeOmniboxHeight() / 2; [NSLayoutConstraint activateConstraints:@[ self.fakeLocationBarTopConstraint, self.fakeLocationBarLeadingConstraint,
diff --git a/ios/chrome/browser/ntp/ui_bundled/new_tab_page_header_view_controller.mm b/ios/chrome/browser/ntp/ui_bundled/new_tab_page_header_view_controller.mm index 4d51e0f9..63ab348 100644 --- a/ios/chrome/browser/ntp/ui_bundled/new_tab_page_header_view_controller.mm +++ b/ios/chrome/browser/ntp/ui_bundled/new_tab_page_header_view_controller.mm
@@ -524,11 +524,14 @@ self.traitCollection); self.fakeOmniboxTopMarginConstraint.constant = -content_suggestions::SearchFieldTopMargin(_searchEngineLogoState); + // Trigger relayout so that it immediately returns the updated content height // for the NTP to update content inset. [self.view setNeedsLayout]; - [self.view layoutIfNeeded]; - [self.commandHandler updateForHeaderSizeChange]; + [UIView performWithoutAnimation:^{ + [self.view layoutIfNeeded]; + [self.commandHandler updateForHeaderSizeChange]; + }]; [self updateFakeboxDisplay]; }
diff --git a/ios/chrome/browser/ntp/ui_bundled/new_tab_page_view_controller.mm b/ios/chrome/browser/ntp/ui_bundled/new_tab_page_view_controller.mm index 659b8ba..5b99d68 100644 --- a/ios/chrome/browser/ntp/ui_bundled/new_tab_page_view_controller.mm +++ b/ios/chrome/browser/ntp/ui_bundled/new_tab_page_view_controller.mm
@@ -1368,6 +1368,20 @@ [NSLayoutConstraint activateConstraints:self.fakeOmniboxConstraints]; } +// Returns YES if scroll and constraint animations should be run for the header. +- (BOOL)shouldAnimateScrollAnimation { + if (self.disableScrollAnimation) { + return NO; + } + if (IsChromeNextIaEnabled() && !IsSplitToolbarMode(self) && + !CanShowTabStrip(self)) { + /// TODO(crbug.com/508170459): Implement NTP toolbars for split toolbar + /// mode. + return NO; + } + return YES; +} + // Update the header for a new width size depending on if the change needs to be // animated. - (void)updateFakeOmniboxOnNewWidth:(CGFloat)width { @@ -1383,7 +1397,7 @@ updateFakeOmniboxForOffset:[self adjustedOffset].y screenWidth:width safeAreaInsets:insets - animateScrollAnimation:!self.disableScrollAnimation]; + animateScrollAnimation:[self shouldAnimateScrollAnimation]]; } else { [self.headerViewController updateFakeOmniboxForWidth:width]; } @@ -1403,19 +1417,11 @@ if (self.shouldAnimateHeader) { UIEdgeInsets insets = self.collectionView.safeAreaInsets; - BOOL animateScrollAnimation = !self.disableScrollAnimation; - if (IsChromeNextIaEnabled() && !IsSplitToolbarMode(self) && - !CanShowTabStrip(self)) { - /// TODO(crbug.com/508170459): Implement NTP toolbars for split toolbar - /// mode. - animateScrollAnimation = NO; - } - [self.headerViewController updateFakeOmniboxForOffset:[self adjustedOffset].y screenWidth:self.collectionView.frame.size.width safeAreaInsets:insets - animateScrollAnimation:animateScrollAnimation]; + animateScrollAnimation:[self shouldAnimateScrollAnimation]]; } }
diff --git a/ios/chrome/browser/policy/model/BUILD.gn b/ios/chrome/browser/policy/model/BUILD.gn index 62c8a23..0b2b51d 100644 --- a/ios/chrome/browser/policy/model/BUILD.gn +++ b/ios/chrome/browser/policy/model/BUILD.gn
@@ -88,6 +88,7 @@ "//components/enterprise/connectors/core", "//components/enterprise/data_controls/core/browser", "//components/enterprise/idle", + "//components/enterprise/isolated_mode", "//components/history/core/common", "//components/lens:enterprise_policy", "//components/metrics",
diff --git a/ios/chrome/browser/policy/model/configuration_policy_handler_list_factory.mm b/ios/chrome/browser/policy/model/configuration_policy_handler_list_factory.mm index 9342904..608f5a5 100644 --- a/ios/chrome/browser/policy/model/configuration_policy_handler_list_factory.mm +++ b/ios/chrome/browser/policy/model/configuration_policy_handler_list_factory.mm
@@ -28,6 +28,7 @@ #import "components/enterprise/data_controls/core/browser/data_controls_policy_handler.h" #import "components/enterprise/data_controls/core/browser/prefs.h" #import "components/enterprise/idle/idle_timeout_policy_handler.h" +#import "components/enterprise/isolated_mode/prefs.h" #import "components/history/core/common/pref_names.h" #import "components/lens/lens_overlay_permission_utils.h" #import "components/metrics/metrics_pref_names.h" @@ -192,6 +193,9 @@ { policy::key::kProvisionManagedClientCertificateForBrowser, client_certificates::prefs::kProvisionManagedClientCertificateForBrowserPrefs, base::Value::Type::INTEGER }, + { policy::key::kIsolatedModeSettings, + enterprise_isolated_mode::kEnterpriseIsolatedModeSettings, + base::Value::Type::INTEGER }, }); // clang-format on
diff --git a/ios/chrome/browser/providers/BUILD.gn b/ios/chrome/browser/providers/BUILD.gn index 2982fb7..01db50b 100644 --- a/ios/chrome/browser/providers/BUILD.gn +++ b/ios/chrome/browser/providers/BUILD.gn
@@ -11,7 +11,7 @@ "//ios/chrome/browser/providers/app_store_bundle:chromium_app_store_bundle", "//ios/chrome/browser/providers/app_utils:chromium_app_utils", "//ios/chrome/browser/providers/application_mode_fetcher:chromium_application_mode_fetcher", - "//ios/chrome/browser/providers/backend_promo_service:chromium_backend_promo_service", + "//ios/chrome/browser/providers/backend_promo:chromium_backend_promo", "//ios/chrome/browser/providers/branded_images:chromium_branded_images", "//ios/chrome/browser/providers/bwg:chromium_bwg", "//ios/chrome/browser/providers/cobalt:chromium_cobalt",
diff --git a/ios/chrome/browser/providers/backend_promo/BUILD.gn b/ios/chrome/browser/providers/backend_promo/BUILD.gn new file mode 100644 index 0000000..ee3e6d4 --- /dev/null +++ b/ios/chrome/browser/providers/backend_promo/BUILD.gn
@@ -0,0 +1,8 @@ +# Copyright 2026 The Chromium Authors +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +source_set("chromium_backend_promo") { + sources = [ "chromium_backend_promo.mm" ] + deps = [ "//ios/chrome/browser/backend_promo/model" ] +}
diff --git a/ios/chrome/browser/providers/backend_promo/DEPS b/ios/chrome/browser/providers/backend_promo/DEPS new file mode 100644 index 0000000..974e5df9 --- /dev/null +++ b/ios/chrome/browser/providers/backend_promo/DEPS
@@ -0,0 +1,3 @@ +include_rules = [ + "+ios/chrome/browser/backend_promo/model/backend_promo_service.h", +]
diff --git a/ios/chrome/browser/providers/backend_promo/OWNERS b/ios/chrome/browser/providers/backend_promo/OWNERS new file mode 100644 index 0000000..c87e9d61 --- /dev/null +++ b/ios/chrome/browser/providers/backend_promo/OWNERS
@@ -0,0 +1 @@ +file://ios/chrome/browser/backend_promo/OWNERS
diff --git a/ios/chrome/browser/providers/backend_promo_service/chromium_backend_promo_service.mm b/ios/chrome/browser/providers/backend_promo/chromium_backend_promo.mm similarity index 85% rename from ios/chrome/browser/providers/backend_promo_service/chromium_backend_promo_service.mm rename to ios/chrome/browser/providers/backend_promo/chromium_backend_promo.mm index df5d3da..ae75808 100644 --- a/ios/chrome/browser/providers/backend_promo_service/chromium_backend_promo_service.mm +++ b/ios/chrome/browser/providers/backend_promo/chromium_backend_promo.mm
@@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#import "ios/chrome/browser/backend_promo_service/model/backend_promo_service.h" +#import "ios/chrome/browser/backend_promo/model/backend_promo_service.h" class ChromiumBackendPromoService final : public BackendPromoService { public:
diff --git a/ios/chrome/browser/providers/backend_promo_service/BUILD.gn b/ios/chrome/browser/providers/backend_promo_service/BUILD.gn deleted file mode 100644 index 6574b6e7..0000000 --- a/ios/chrome/browser/providers/backend_promo_service/BUILD.gn +++ /dev/null
@@ -1,8 +0,0 @@ -# Copyright 2026 The Chromium Authors -# Use of this source code is governed by a BSD-style license that can be -# found in the LICENSE file. - -source_set("chromium_backend_promo_service") { - sources = [ "chromium_backend_promo_service.mm" ] - deps = [ "//ios/chrome/browser/backend_promo_service/model" ] -}
diff --git a/ios/chrome/browser/providers/backend_promo_service/DEPS b/ios/chrome/browser/providers/backend_promo_service/DEPS deleted file mode 100644 index bd119b9..0000000 --- a/ios/chrome/browser/providers/backend_promo_service/DEPS +++ /dev/null
@@ -1,3 +0,0 @@ -include_rules = [ - "+ios/chrome/browser/backend_promo_service/model/backend_promo_service.h", -]
diff --git a/ios/chrome/browser/providers/backend_promo_service/OWNERS b/ios/chrome/browser/providers/backend_promo_service/OWNERS deleted file mode 100644 index ccb46dc7..0000000 --- a/ios/chrome/browser/providers/backend_promo_service/OWNERS +++ /dev/null
@@ -1 +0,0 @@ -file://ios/chrome/browser/backend_promo_service/OWNERS
diff --git a/ios/chrome/browser/shared/model/prefs/BUILD.gn b/ios/chrome/browser/shared/model/prefs/BUILD.gn index 326ab68..bc831444 100644 --- a/ios/chrome/browser/shared/model/prefs/BUILD.gn +++ b/ios/chrome/browser/shared/model/prefs/BUILD.gn
@@ -49,6 +49,7 @@ "//components/enterprise/connectors/core", "//components/enterprise/data_controls/core/browser", "//components/enterprise/idle", + "//components/enterprise/isolated_mode", "//components/feature_engagement/public:prefs", "//components/feed/core/v2/public/ios:feed_ios_public", "//components/gcm_driver",
diff --git a/ios/chrome/browser/shared/model/prefs/browser_prefs.mm b/ios/chrome/browser/shared/model/prefs/browser_prefs.mm index f7cffff..4169de8 100644 --- a/ios/chrome/browser/shared/model/prefs/browser_prefs.mm +++ b/ios/chrome/browser/shared/model/prefs/browser_prefs.mm
@@ -29,6 +29,7 @@ #import "components/enterprise/connectors/core/connectors_prefs.h" #import "components/enterprise/data_controls/core/browser/prefs.h" #import "components/enterprise/idle/idle_pref_names.h" +#import "components/enterprise/isolated_mode/prefs.h" #import "components/feature_engagement/public/pref_names.h" #import "components/feed/core/v2/public/ios/pref_names.h" #import "components/handoff/handoff_manager.h" @@ -779,6 +780,9 @@ registry->RegisterBooleanPref( enterprise_reporting::kPoliciesEverFetchedWithProfileId, false); + // Register prefs related to Enterprise Isolated Mode. + enterprise_isolated_mode::RegisterProfilePrefs(registry); + // Register prefs used to skip too frequent History Sync Opt-In prompt. history_sync::RegisterProfilePrefs(registry);
diff --git a/ios/chrome/browser/shared/model/url/chrome_url_constants.h b/ios/chrome/browser/shared/model/url/chrome_url_constants.h index 41167ae..a4331f6c 100644 --- a/ios/chrome/browser/shared/model/url/chrome_url_constants.h +++ b/ios/chrome/browser/shared/model/url/chrome_url_constants.h
@@ -224,7 +224,7 @@ // URL navigated to after the Gemini App Store External Actions event. inline constexpr char kGeminiAppStorePromoURL[] = - "https://www.google.com/chrome/ai-innovations/"; + "https://www.google.com/chrome/mobile/#scrollable-gemini-in-chrome-slide-1"; // Gets the hosts/domains that are shown in chrome://chrome-urls. inline constexpr std::array<std::string_view, 22> kChromeHostURLs = {
diff --git a/ios/chrome/test/data/policy/policy_test_bundle_data.filelist b/ios/chrome/test/data/policy/policy_test_bundle_data.filelist index e74129b..77907582 100644 --- a/ios/chrome/test/data/policy/policy_test_bundle_data.filelist +++ b/ios/chrome/test/data/policy/policy_test_bundle_data.filelist
@@ -56,6 +56,7 @@ //ios/chrome/test/data/policy/pref_mapping/IncognitoModeUrlAllowlist.json //ios/chrome/test/data/policy/pref_mapping/IncognitoModeUrlBlocklist.json //ios/chrome/test/data/policy/pref_mapping/InsecureFormsWarningsEnabled.json +//ios/chrome/test/data/policy/pref_mapping/IsolatedModeSettings.json //ios/chrome/test/data/policy/pref_mapping/LensCameraAssistedSearchEnabled.json //ios/chrome/test/data/policy/pref_mapping/LensOverlaySettings.json //ios/chrome/test/data/policy/pref_mapping/ManagedBookmarks.json
diff --git a/ios/chrome/test/data/policy/pref_mapping/IsolatedModeSettings.json b/ios/chrome/test/data/policy/pref_mapping/IsolatedModeSettings.json new file mode 100644 index 0000000..1328a3f0 --- /dev/null +++ b/ios/chrome/test/data/policy/pref_mapping/IsolatedModeSettings.json
@@ -0,0 +1,29 @@ +[ + { + "os": [ + "ios" + ], + "policy_pref_mapping_tests": [ + { + "policies": { + "IsolatedModeSettings": 1 + }, + "prefs": { + "enterprise.isolated_mode": { + "value": 1 + } + } + }, + { + "policies": { + "IsolatedModeSettings": 2 + }, + "prefs": { + "enterprise.isolated_mode": { + "value": 2 + } + } + } + ] + } +] \ No newline at end of file
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 121b10c..e892db3 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 @@ -3b456243ab5b8009e37e6087fe2af0d0f963348c \ No newline at end of file +4173cb1869a43c86b6b0728c5ce51c2e7c43b233 \ 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 a7de7227..d6b44860 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 @@ -96b60f0af446abb3245f9d7a316958098c9069eb \ No newline at end of file +0e28a780205b1fb4fec001a914621d67a9b0cbae \ 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 950edf0e..9ffba230 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 @@ -1faf8b248d1157aa750a3acbf8648fb21865e1e5 \ No newline at end of file +5d6c22fe2a3fa64e4cdefd329477df508c73abba \ 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 fa05b69..490c94ab 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 @@ -4c23cd10e999c3196241c9f5c9f328f38cc9d5ca \ No newline at end of file +2b58af70f2337350478fcb1755ec931d7b560881 \ 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 5e8e8d7..c1953cb9 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 @@ -85c176282d7fdf806e8d6a381d1b353ef86dda3e \ No newline at end of file +eaf53500ad54cb04f2970d714b35b487316878d6 \ 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 ef59554..267cfdf 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 @@ -e5f531ef622504ef6037d384eb49269ac9736b23 \ No newline at end of file +f3862eef9804a6785d0cef374b7c0f70716bd2f1 \ 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 fe631486..6449642 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 @@ -afba61dc3025c7fde4c3497b83454a4f4ecd83ca \ No newline at end of file +226217afdb962ba6282d06ac138fefe80cf0fcea \ 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 f009d27..2fd15b8c 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 @@ -91206541051fe3be8affb80dc598b12e44ac9c48 \ No newline at end of file +c0cc90ce07f1c701a32c5307963643ce1813200c \ 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 b635dbc..fa33c1f4 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 @@ -1d072da19e19fd358ef06238ba7f1ed792799f45 \ No newline at end of file +bbcaa919d1fc83b163f6151a03c62b8d0b4a9a2d \ 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 0cd141e..5cf05589 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 @@ -8716c5ef21c88f14aff349dcad196b3726df4308 \ No newline at end of file +08b3cfb6e0a078582f7d30100825081f2359e799 \ 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 51b9d149..9139adb 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 @@ -59c9c64618dc2d636430f6f6fcb0c9bcd525207a \ No newline at end of file +4d76d8a3d03f23248a4b2c4775220a5810050cb9 \ 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 d6e75b03..bf5784b 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 @@ -9a9317adf38f0773b96f38e7eb815c2890672afa \ No newline at end of file +4f64c99a891361378989462be126db21475e42aa \ 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 a0c12be2..9d005835 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 @@ -46a5e029f372ff57645fc36b043dd9dddc3a3f4a \ No newline at end of file +66eddd529162da79fc36d8700f6331a72b0fcc8e \ 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 f97f6dd..ab5de0a 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 @@ -f80807fd9acb22fc94c0c72b1284ed48253a48a0 \ No newline at end of file +c6a6d86124451d74185e43d597006b074839c326 \ 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 0c941d89..2c9d41c 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 @@ -946a782b177454eab2e3c74c713df28f21926616 \ No newline at end of file +f2d2954cab2f7a95ab40fe34dc0265d399672704 \ 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 6b18648..f0e7cf6 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 @@ -eba7d3d777c3567b20c93fd0d3ac2f47e136892b \ No newline at end of file +77824181f7ed915c4b21c68568c296b31efc269d \ 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 6f56a59..42f20ec 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 @@ -5ed28b4ac8d1f725cd7f5b69f4a278ba776eb7ee \ No newline at end of file +cd0bd48032c7de4ed53c74b3fba1811abe6ea01a \ 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 3212bcc..0970ff0 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 @@ -465dce5451210aa2aa162455c11b150a23e9db02 \ No newline at end of file +d4155c1686007bd06b867b86d16d00f8341efbcb \ 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 0e0fefd..a9b4050 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 @@ -579061c67f85fc30d6436f47787a88e6940251f8 \ No newline at end of file +8ba7e1abca22093062298afb20125f05121d83a4 \ 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 f11e1121..a04f24a 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 @@ -102052442921b48e2a24c27fdd39950e7a730186 \ No newline at end of file +2a03cfbaef62beaf4f01b4447988f5beb26fc469 \ 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 4426acc..24fe840 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 @@ -1ed19a78bf37cb5458ff1fea8cc0f62bc351d87c \ No newline at end of file +fd35367adea328a8c9a1dedbd8af081d30bd296d \ 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 edab893..79929ae47 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 @@ -1e1e4ea5d57919e630d0db5921f122655247bfd6 \ No newline at end of file +a9f0818277bafd9c157092d402645fa9647aa95c \ 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 01cbe9cb1..105e1f4 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 @@ -06f5337ae30261ee92ebf1b3600c5aae9f0ac471 \ No newline at end of file +269593fb0e89b05253f3f5814aff75187e4d0a5a \ 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 cef0b83b..8718df3 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 @@ -60f6490a3bfe462fe5e6b758445291b6d8aacdfb \ No newline at end of file +8fa94a14f28b5272cfb19151db2228966f229217 \ No newline at end of file
diff --git a/ios/testing/data/http_server_files/toctou_attacker.html b/ios/testing/data/http_server_files/toctou_attacker.html new file mode 100644 index 0000000..96def491 --- /dev/null +++ b/ios/testing/data/http_server_files/toctou_attacker.html
@@ -0,0 +1,13 @@ +<!DOCTYPE html> +<html> +<head> + <title>Attacker Page</title> + <script> + // Attacker page with no listeners. + </script> +</head> +<body> + <h1>Attacker Page</h1> + <p>Open bookmarks and tap the bookmarklet.</p> +</body> +</html>
diff --git a/ios/testing/data/http_server_files/toctou_sensitive.html b/ios/testing/data/http_server_files/toctou_sensitive.html new file mode 100644 index 0000000..c6058aa --- /dev/null +++ b/ios/testing/data/http_server_files/toctou_sensitive.html
@@ -0,0 +1,11 @@ +<!DOCTYPE html> +<html> +<head> + <title>Sensitive Page</title> +</head> +<body> + <h1>Sensitive Page</h1> + <p>This is a sensitive origin.</p> + <div id="result">Not executed</div> +</body> +</html>
diff --git a/ios/testing/http_server_bundle_data.filelist b/ios/testing/http_server_bundle_data.filelist index 2db8783..e665ca4 100644 --- a/ios/testing/http_server_bundle_data.filelist +++ b/ios/testing/http_server_bundle_data.filelist
@@ -73,6 +73,8 @@ data/http_server_files/state_operations.js data/http_server_files/tall_page.html data/http_server_files/testpage.pdf +data/http_server_files/toctou_attacker.html +data/http_server_files/toctou_sensitive.html data/http_server_files/two_pages.pdf data/http_server_files/uff_login_forms.html data/http_server_files/user_agent_test_page.html
diff --git a/ios/web/navigation/crw_web_view_navigation_observer.mm b/ios/web/navigation/crw_web_view_navigation_observer.mm index 7a8e400..b9130e5 100644 --- a/ios/web/navigation/crw_web_view_navigation_observer.mm +++ b/ios/web/navigation/crw_web_view_navigation_observer.mm
@@ -19,7 +19,9 @@ #import "ios/web/navigation/crw_wk_navigation_handler.h" #import "ios/web/navigation/crw_wk_navigation_states.h" #import "ios/web/navigation/navigation_context_impl.h" +#import "ios/web/navigation/navigation_manager_impl.h" #import "ios/web/navigation/wk_navigation_util.h" +#import "ios/web/public/navigation/navigation_item.h" #import "ios/web/public/web_client.h" #import "ios/web/util/wk_web_view_util.h" #import "ios/web/web_state/web_state_impl.h"
diff --git a/ios/web/navigation/crw_wk_navigation_handler.h b/ios/web/navigation/crw_wk_navigation_handler.h index d19348b..1a46061 100644 --- a/ios/web/navigation/crw_wk_navigation_handler.h +++ b/ios/web/navigation/crw_wk_navigation_handler.h
@@ -10,7 +10,6 @@ #import <memory> -#import "ios/web/security/cert_verification_error.h" #import "ios/web/web_state/ui/crw_web_view_handler.h" #import "ios/web/web_state/ui/crw_web_view_handler_delegate.h" #include "ui/base/page_transition_types.h"
diff --git a/ios/web/navigation/crw_wk_navigation_handler.mm b/ios/web/navigation/crw_wk_navigation_handler.mm index 3048add..7320446 100644 --- a/ios/web/navigation/crw_wk_navigation_handler.mm +++ b/ios/web/navigation/crw_wk_navigation_handler.mm
@@ -40,6 +40,7 @@ #import "ios/web/public/download/download_controller.h" #import "ios/web/public/navigation/form_warning_type.h" #import "ios/web/public/web_client.h" +#import "ios/web/security/cert_verification_error.h" #import "ios/web/security/crw_cert_verification_controller.h" #import "ios/web/security/wk_web_view_security_util.h" #import "ios/web/session/session_certificate_policy_cache_impl.h"
diff --git a/ios_internal b/ios_internal index e67a7a6..fdd6578 160000 --- a/ios_internal +++ b/ios_internal
@@ -1 +1 @@ -Subproject commit e67a7a64f5d58d6fd22e4bc7ae4a607110a4de16 +Subproject commit fdd657893e5c5e824838933a1707f3ff5ce72c3b
diff --git a/media/audio/apple/audio_low_latency_input_unittest.cc b/media/audio/apple/audio_low_latency_input_unittest.cc index 464fc3c..4c933f0 100644 --- a/media/audio/apple/audio_low_latency_input_unittest.cc +++ b/media/audio/apple/audio_low_latency_input_unittest.cc
@@ -94,8 +94,7 @@ const AudioGlitchInfo& glitch_info) override { const int num_samples = src->frames() * src->channels(); auto interleaved = base::HeapArray<int16_t>::Uninit(num_samples); - src->ToInterleaved<SignedInt16SampleTypeTraits>(src->frames(), - interleaved.data()); + src->ToInterleaved<SignedInt16SampleTypeTraits>(interleaved.as_span()); // Store data in a temporary buffer to avoid making blocking fwrite() calls // in the audio callback. The complete buffer will be written to file in the
diff --git a/media/audio/audio_debug_file_writer_unittest.cc b/media/audio/audio_debug_file_writer_unittest.cc index 930a086..b78704b 100644 --- a/media/audio/audio_debug_file_writer_unittest.cc +++ b/media/audio/audio_debug_file_writer_unittest.cc
@@ -226,15 +226,16 @@ EXPECT_CALL(*mock_audio_bus_pool_, OnInsertAudioBus(_)) .Times(expect_buses_returned_to_pool ? writes_ : 0); + auto remaining_data = source_interleaved_.as_span(); + const size_t samples_per_buffer = + params_.frames_per_buffer() * params_.channels(); + for (int i = 0; i < writes_; ++i) { std::unique_ptr<AudioBus> bus = AudioBus::Create(params_.channels(), params_.frames_per_buffer()); bus->FromInterleaved<media::SignedInt16SampleTypeTraits>( - source_interleaved_ - .subspan(i * params_.channels() * params_.frames_per_buffer()) - .data(), - params_.frames_per_buffer()); + remaining_data.take_first(samples_per_buffer)); debug_writer_->Write(*bus); }
diff --git a/media/audio/win/audio_low_latency_input_win_unittest.cc b/media/audio/win/audio_low_latency_input_win_unittest.cc index 4863b25..b48b82b 100644 --- a/media/audio/win/audio_low_latency_input_win_unittest.cc +++ b/media/audio/win/audio_low_latency_input_win_unittest.cc
@@ -233,8 +233,7 @@ const AudioGlitchInfo& glitch_info) override { const int num_samples = src->frames() * src->channels(); auto interleaved = base::HeapArray<int16_t>::Uninit(num_samples); - src->ToInterleaved<SignedInt16SampleTypeTraits>(src->frames(), - interleaved.data()); + src->ToInterleaved<SignedInt16SampleTypeTraits>(interleaved.as_span()); // Store data data in a temporary buffer to avoid making blocking // fwrite() calls in the audio callback. The complete buffer will be
diff --git a/media/audio/win/audio_low_latency_output_win_unittest.cc b/media/audio/win/audio_low_latency_output_win_unittest.cc index 1c1ed03..56e85d9 100644 --- a/media/audio/win/audio_low_latency_output_win_unittest.cc +++ b/media/audio/win/audio_low_latency_output_win_unittest.cc
@@ -119,10 +119,8 @@ int frames = max_size / (dest->channels() * kBitsPerSample / 8); if (max_size) { static_assert(kBitsPerSample == 16, "FromInterleaved expects 2 bytes."); - dest->FromInterleaved<SignedInt16SampleTypeTraits>( - reinterpret_cast<const int16_t*>( - base::span(*file_).subspan(pos_).data()), - frames); + dest->FromInterleavedBytes<SignedInt16SampleTypeTraits>( + base::span(*file_).subspan(pos_, max_size)); pos_ += max_size; } return frames;
diff --git a/media/base/data_source.h b/media/base/data_source.h index 95cb8ac..f40f37bf 100644 --- a/media/base/data_source.h +++ b/media/base/data_source.h
@@ -51,6 +51,11 @@ kHitCache, }; + enum class EncodingMode { + kIdentity, + kAllowGzip, + }; + enum { kReadError = -1, kAborted = -2 }; // Used to specify video preload states. They are "hints" to the browser about @@ -71,6 +76,7 @@ virtual ~Factory(); virtual void Create(const GURL& uri, CacheMode cache_mode, + EncodingMode encoding_mode, DataSourceCb cb) = 0; };
diff --git a/media/filters/hls_data_source_provider.cc b/media/filters/hls_data_source_provider.cc index 03dc5133..cf783a73 100644 --- a/media/filters/hls_data_source_provider.cc +++ b/media/filters/hls_data_source_provider.cc
@@ -46,7 +46,10 @@ return std::get<0>(GetNextSegmentURIAndCacheStatus()); } -std::tuple<GURL, DataSource::CacheMode, DataSource::RangeMode> +std::tuple<GURL, + DataSource::CacheMode, + DataSource::RangeMode, + DataSource::EncodingMode> HlsDataSourceStream::GetNextSegmentURIAndCacheStatus() { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); CHECK(requires_next_data_source_); @@ -64,7 +67,8 @@ GURL new_url = std::move(first.uri); segments_.pop(); requires_next_data_source_ = false; - return std::make_tuple(new_url, first.cache_mode, range_mode); + return std::make_tuple(new_url, first.cache_mode, range_mode, + first.encoding_mode); } bool HlsDataSourceStream::CanReadMore() const {
diff --git a/media/filters/hls_data_source_provider.h b/media/filters/hls_data_source_provider.h index 52a82c6..89776304 100644 --- a/media/filters/hls_data_source_provider.h +++ b/media/filters/hls_data_source_provider.h
@@ -54,6 +54,8 @@ const GURL uri; const std::optional<hls::types::ByteRange> range; const DataSource::CacheMode cache_mode; + const DataSource::EncodingMode encoding_mode = + DataSource::EncodingMode::kIdentity; }; using SegmentQueue = base::queue<UrlDataSegment>; @@ -152,7 +154,10 @@ // segments. It is invalid to call this method if `RequiresNextDataSource` // does not return true. This method will also update the internal range if // the segment has one. - std::tuple<GURL, DataSource::CacheMode, DataSource::RangeMode> + std::tuple<GURL, + DataSource::CacheMode, + DataSource::RangeMode, + DataSource::EncodingMode> GetNextSegmentURIAndCacheStatus(); // Has the stream read all possible data?
diff --git a/media/filters/hls_data_source_provider_impl.cc b/media/filters/hls_data_source_provider_impl.cc index b098dc9..e32b130 100644 --- a/media/filters/hls_data_source_provider_impl.cc +++ b/media/filters/hls_data_source_provider_impl.cc
@@ -109,12 +109,12 @@ // try to make one. Creating a new data source will re-enter this function to // complete `callback`. if (stream->RequiresNextDataSource()) { - auto [new_uri, cache_mode, range_mode] = + auto [new_uri, cache_mode, range_mode, encoding_mode] = stream->GetNextSegmentURIAndCacheStatus(); TRACE_EVENT_BEGIN("media", "HLS::CreateDataSource", perfetto::Track::FromPointer(this), "uri", new_uri); data_source_factory_->Create( - std::move(new_uri), cache_mode, + std::move(new_uri), cache_mode, encoding_mode, base::BindOnce(&HlsDataSourceProviderImpl::OnDataSourceCreated, weak_factory_.GetWeakPtr(), range_mode, std::move(stream), std::move(callback)));
diff --git a/media/filters/hls_network_access_impl.cc b/media/filters/hls_network_access_impl.cc index 2790042..5739f70 100644 --- a/media/filters/hls_network_access_impl.cc +++ b/media/filters/hls_network_access_impl.cc
@@ -30,14 +30,16 @@ base::BindPostTaskToCurrentDefault(std::move(cb))); } -void HlsNetworkAccessImpl::ReadAllInternal(const GURL& uri, - HlsDataSourceProvider::ReadCb cb, - DataSource::CacheMode cache_mode) { +void HlsNetworkAccessImpl::ReadAllInternal( + const GURL& uri, + HlsDataSourceProvider::ReadCb cb, + DataSource::CacheMode cache_mode, + DataSource::EncodingMode encoding_mode) { DCHECK_CALLED_ON_VALID_SEQUENCE(media_sequence_checker_); // Callers of `ReadAllInternal` should enforce this. CHECK(data_source_provider_); HlsDataSourceProvider::SegmentQueue queue; - queue.emplace(uri, std::nullopt, cache_mode); + queue.emplace(uri, std::nullopt, cache_mode, encoding_mode); ReadSegmentQueueInternal( std::move(queue), base::BindOnce(&HlsNetworkAccessImpl::ReadUntilExhausted, @@ -79,7 +81,8 @@ std::move(cb).Run(HlsDataSourceProvider::ReadStatus::Codes::kStopped); return; } - ReadAllInternal(uri, std::move(cb), DataSource::CacheMode::kBypassCache); + ReadAllInternal(uri, std::move(cb), DataSource::CacheMode::kBypassCache, + DataSource::EncodingMode::kAllowGzip); } void HlsNetworkAccessImpl::ReadKey(
diff --git a/media/filters/hls_network_access_impl.h b/media/filters/hls_network_access_impl.h index cea14e07..648320c2 100644 --- a/media/filters/hls_network_access_impl.h +++ b/media/filters/hls_network_access_impl.h
@@ -39,7 +39,9 @@ void ReadAllInternal( const GURL& uri, HlsDataSourceProvider::ReadCb cb, - DataSource::CacheMode cache_mode = DataSource::CacheMode::kHitCache); + DataSource::CacheMode cache_mode = DataSource::CacheMode::kHitCache, + DataSource::EncodingMode encoding_mode = + DataSource::EncodingMode::kIdentity); void OnKeyFetch( scoped_refptr<hls::MediaSegment::EncryptionData> enc_data, base::OnceCallback<void(HlsDataSourceProvider::ReadCb)> next_op,
diff --git a/media/filters/hls_network_access_impl_unittest.cc b/media/filters/hls_network_access_impl_unittest.cc index 975873b..ba4d41f0 100644 --- a/media/filters/hls_network_access_impl_unittest.cc +++ b/media/filters/hls_network_access_impl_unittest.cc
@@ -396,4 +396,60 @@ task_environment_.RunUntilIdle(); } +TEST_F(HlsNetworkAccessImplUnittest, TestReadManifestAllowsGzip) { + factory_->AddReadExpectation(0, 16384, 800); + factory_->AddReadExpectation(800, 16384, 0); + + EXPECT_CALL(*factory_, MockCreate(GURL("https://example.com/manifest.m3u8"), + DataSource::CacheMode::kBypassCache, + DataSource::EncodingMode::kAllowGzip)) + .Times(1); + + network_access_->ReadManifest( + GURL("https://example.com/manifest.m3u8"), + base::BindOnce([](HlsDataSourceProvider::ReadResult result) { + ASSERT_TRUE(result.has_value()); + })); + task_environment_.RunUntilIdle(); +} + +TEST_F(HlsNetworkAccessImplUnittest, TestReadKeyDisallowsGzip) { + auto segment = MakeSegment(std::nullopt, std::nullopt, InitMode::kAbsent, + "https://example.com/enc.key"); + + auto* ds_for_keyfetch = factory_->PregenerateNextMock(); + EXPECT_CALL(*ds_for_keyfetch, Initialize) + .WillOnce(base::test::RunOnceCallback<0>(true)); + EXPECT_CALL(*ds_for_keyfetch, Read(0, SpanSizeEq(16384), _)) + .WillOnce([](int64_t, base::span<uint8_t> data, DataSource::ReadCB cb) { + std::ranges::fill(data.first<16>(), 'x'); + std::move(cb).Run(16); + }); + EXPECT_CALL(*ds_for_keyfetch, Read(16, SpanSizeEq(16384), _)) + .WillOnce(base::test::RunOnceCallback<2>(0)); + EXPECT_CALL(*ds_for_keyfetch, WouldTaintOrigin()) + .WillRepeatedly(testing::Return(true)); + + EXPECT_CALL(*factory_, MockCreate(GURL("https://example.com/enc.key"), + DataSource::CacheMode::kHitCache, + DataSource::EncodingMode::kIdentity)) + .Times(1); + EXPECT_CALL(*factory_, MockCreate(GURL("https://example.com/"), + DataSource::CacheMode::kHitCache, + DataSource::EncodingMode::kIdentity)) + .Times(1); + + factory_->AddReadExpectation(0, 16384, 1000); + factory_->AddReadExpectation(1000, 16384, 0); + + ASSERT_NE(segment->GetEncryptionData(), nullptr); + ASSERT_TRUE(segment->GetEncryptionData()->NeedsKeyFetch()); + network_access_->ReadMediaSegment( + *segment, /*read_chunked=*/false, /*include_init_segment=*/true, + base::BindOnce([](HlsDataSourceProvider::ReadResult result) { + ASSERT_TRUE(result.has_value()); + })); + task_environment_.RunUntilIdle(); +} + } // namespace media
diff --git a/media/filters/hls_test_helpers.cc b/media/filters/hls_test_helpers.cc index 585e51a..dc7ca1a 100644 --- a/media/filters/hls_test_helpers.cc +++ b/media/filters/hls_test_helpers.cc
@@ -86,9 +86,11 @@ MockDataSourceFactory::~MockDataSourceFactory() = default; MockDataSourceFactory::MockDataSourceFactory() = default; -void MockDataSourceFactory::Create(const GURL&, - DataSource::CacheMode, +void MockDataSourceFactory::Create(const GURL& uri, + DataSource::CacheMode cache_mode, + DataSource::EncodingMode encoding_mode, DataSource::DataSourceCb cb) { + MockCreate(uri, cache_mode, encoding_mode); if (!next_mock_) { PregenerateNextMock(); EXPECT_CALL(*next_mock_, Initialize)
diff --git a/media/filters/hls_test_helpers.h b/media/filters/hls_test_helpers.h index c0f1f549..f29246c 100644 --- a/media/filters/hls_test_helpers.h +++ b/media/filters/hls_test_helpers.h
@@ -214,8 +214,12 @@ public: ~MockDataSourceFactory() override; MockDataSourceFactory(); + MOCK_METHOD(void, + MockCreate, + (const GURL&, DataSource::CacheMode, DataSource::EncodingMode)); void Create(const GURL& uri, DataSource::CacheMode cache_mode, + DataSource::EncodingMode encoding_mode, DataSource::DataSourceCb cb) override; void AddReadExpectation(size_t from, size_t to, int response); testing::NiceMock<MockDataSource>* PregenerateNextMock();
diff --git a/media/gpu/h264_ratectrl_rtc.cc b/media/gpu/h264_ratectrl_rtc.cc index bebfdb9..51410c0 100644 --- a/media/gpu/h264_ratectrl_rtc.cc +++ b/media/gpu/h264_ratectrl_rtc.cc
@@ -118,7 +118,7 @@ return rate_ctrl; } -void H264RateCtrlRTC::UpdateRateControl( +bool H264RateCtrlRTC::UpdateRateControl( const H264RateControlConfigRTC& config) { CheckRateControlConfig(config); @@ -128,6 +128,7 @@ // New settings are applied on ComputeQP() method call. new_config_ = config; config_changed_ = true; + return true; } H264RateCtrlRTC::FrameDropDecision H264RateCtrlRTC::ComputeQP(
diff --git a/media/gpu/h264_ratectrl_rtc.h b/media/gpu/h264_ratectrl_rtc.h index 40b2c76..7e1dbd2 100644 --- a/media/gpu/h264_ratectrl_rtc.h +++ b/media/gpu/h264_ratectrl_rtc.h
@@ -40,7 +40,7 @@ const H264RateControlConfigRTC& config); // Updates Rate Control using the given `config`. - void UpdateRateControl(const H264RateControlConfigRTC& config); + [[nodiscard]] bool UpdateRateControl(const H264RateControlConfigRTC& config); // GetQP() needs to be called after ComputeQP() to get the latest QP. int GetQP();
diff --git a/media/gpu/h264_ratectrl_rtc_unittest.cc b/media/gpu/h264_ratectrl_rtc_unittest.cc index 7b2bffcf..7343536 100644 --- a/media/gpu/h264_ratectrl_rtc_unittest.cc +++ b/media/gpu/h264_ratectrl_rtc_unittest.cc
@@ -175,7 +175,7 @@ rate_control_config_rtc_.layer_settings[1].peak_bitrate = kCommonPeakBitrate * 4 / 3; - rate_ctrl_rtc_->UpdateRateControl(rate_control_config_rtc_); + EXPECT_TRUE(rate_ctrl_rtc_->UpdateRateControl(rate_control_config_rtc_)); start_frame_index = last_frame_index; last_frame_index =
diff --git a/media/gpu/vaapi/av1_vaapi_video_encoder_delegate.cc b/media/gpu/vaapi/av1_vaapi_video_encoder_delegate.cc index ada7917..cd0183e 100644 --- a/media/gpu/vaapi/av1_vaapi_video_encoder_delegate.cc +++ b/media/gpu/vaapi/av1_vaapi_video_encoder_delegate.cc
@@ -554,7 +554,10 @@ return !!rate_ctrl_; } - rate_ctrl_->UpdateRateControl(rc_config); + if (!rate_ctrl_->UpdateRateControl(rc_config)) { + LOG(ERROR) << "Failed to update rate control parameters"; + return false; + } return true; }
diff --git a/media/gpu/vaapi/h264_vaapi_video_encoder_delegate.cc b/media/gpu/vaapi/h264_vaapi_video_encoder_delegate.cc index 4b7cea2..6868f962 100644 --- a/media/gpu/vaapi/h264_vaapi_video_encoder_delegate.cc +++ b/media/gpu/vaapi/h264_vaapi_video_encoder_delegate.cc
@@ -316,10 +316,10 @@ H264RateControlWrapper::~H264RateControlWrapper() = default; -void H264RateControlWrapper::UpdateRateControl( +bool H264RateControlWrapper::UpdateRateControl( const H264RateControlConfigRTC& config) { DCHECK(impl_); - impl_->UpdateRateControl(config); + return impl_->UpdateRateControl(config); } H264RateCtrlRTC::FrameDropDecision H264RateControlWrapper::ComputeQP( @@ -753,7 +753,7 @@ DVLOGF(1) << "Failed creating rate control config"; return false; } - rate_ctrl_->UpdateRateControl(*rc_config); + return rate_ctrl_->UpdateRateControl(*rc_config); } return true; }
diff --git a/media/gpu/vaapi/h264_vaapi_video_encoder_delegate.h b/media/gpu/vaapi/h264_vaapi_video_encoder_delegate.h index 7e8cb8d2..2bdb944 100644 --- a/media/gpu/vaapi/h264_vaapi_video_encoder_delegate.h +++ b/media/gpu/vaapi/h264_vaapi_video_encoder_delegate.h
@@ -28,7 +28,8 @@ virtual ~H264RateControlWrapper(); - virtual void UpdateRateControl(const H264RateControlConfigRTC& config); + [[nodiscard]] virtual bool UpdateRateControl( + const H264RateControlConfigRTC& config); virtual H264RateCtrlRTC::FrameDropDecision ComputeQP( const H264FrameParamsRTC& frame_params); // GetQP() needs to be called after ComputeQP() to get the current frame's
diff --git a/media/gpu/vaapi/h264_vaapi_video_encoder_delegate_unittest.cc b/media/gpu/vaapi/h264_vaapi_video_encoder_delegate_unittest.cc index cc073ca..7e84047 100644 --- a/media/gpu/vaapi/h264_vaapi_video_encoder_delegate_unittest.cc +++ b/media/gpu/vaapi/h264_vaapi_video_encoder_delegate_unittest.cc
@@ -227,7 +227,7 @@ MockH264RateControl() = default; ~MockH264RateControl() override = default; - MOCK_METHOD1(UpdateRateControl, void(const H264RateControlConfigRTC&)); + MOCK_METHOD1(UpdateRateControl, bool(const H264RateControlConfigRTC&)); MOCK_METHOD1(ComputeQP, H264RateCtrlRTC::FrameDropDecision(const H264FrameParamsRTC&)); MOCK_CONST_METHOD0(GetQP, int()); @@ -337,7 +337,8 @@ EXPECT_CALL(*mock_rate_ctrl_, UpdateRateControl(MatchRtcConfigWithRates( initial_bitrate_allocation, vea_config.framerate, kDefaultVisibleSize, - num_temporal_layers, kDefaultContentType))); + num_temporal_layers, kDefaultContentType))) + .WillOnce(Return(true)); EXPECT_TRUE(InitializeEncoder(num_temporal_layers)); } @@ -474,7 +475,8 @@ EXPECT_CALL(*mock_rate_ctrl_, UpdateRateControl(MatchRtcConfigWithRates( bitrate_allocation, framerate, kDefaultVisibleSize, - num_temporal_layers, kDefaultContentType))); + num_temporal_layers, kDefaultContentType))) + .WillOnce(Return(true)); EXPECT_TRUE(encoder_->UpdateRates(bitrate_allocation, framerate)); EXPECT_EQ(encoder_->curr_params_.bitrate_allocation, bitrate_allocation); EXPECT_EQ(encoder_->curr_params_.framerate, framerate);
diff --git a/media/gpu/windows/d3d12_video_encode_av1_delegate.cc b/media/gpu/windows/d3d12_video_encode_av1_delegate.cc index 9c2d0e3..ea9a97cc 100644 --- a/media/gpu/windows/d3d12_video_encode_av1_delegate.cc +++ b/media/gpu/windows/d3d12_video_encode_av1_delegate.cc
@@ -835,9 +835,12 @@ } if (bitrate_allocation != bitrate_allocation_ || framerate != framerate_) { - software_brc_->UpdateRateControl( - ConvertToRateControlConfig(is_screen_, bitrate_allocation, input_size_, - framerate, GetNumTemporalLayers())); + if (!software_brc_->UpdateRateControl(ConvertToRateControlConfig( + is_screen_, bitrate_allocation, input_size_, framerate, + GetNumTemporalLayers()))) { + LOG(ERROR) << "Failed to update rate control parameters"; + return false; + } bitrate_allocation_ = bitrate_allocation; framerate_ = framerate;
diff --git a/media/gpu/windows/media_foundation_video_encode_accelerator_win.cc b/media/gpu/windows/media_foundation_video_encode_accelerator_win.cc index 768a96d..959010d 100644 --- a/media/gpu/windows/media_foundation_video_encode_accelerator_win.cc +++ b/media/gpu/windows/media_foundation_video_encode_accelerator_win.cc
@@ -1008,9 +1008,13 @@ frame_rate_ = framerate; // For SW BRC we don't reconfigure the encoder. if (rate_ctrl_) { - rate_ctrl_->UpdateRateControl(CreateRateControllerConfig( - bitrate_allocation_, size.value_or(input_visible_size_), frame_rate_, - num_temporal_layers_, codec_, content_type_)); + if (!rate_ctrl_->UpdateRateControl(CreateRateControllerConfig( + bitrate_allocation_, size.value_or(input_visible_size_), + frame_rate_, num_temporal_layers_, codec_, content_type_))) { + NotifyErrorStatus({EncoderStatus::Codes::kEncoderUnsupportedConfig, + "Failed to update rate control parameters"}); + return; + } } else { VARIANT var; var.vt = VT_UI4;
diff --git a/media/gpu/windows/video_rate_control_wrapper.h b/media/gpu/windows/video_rate_control_wrapper.h index 6470c96f..4f128e0 100644 --- a/media/gpu/windows/video_rate_control_wrapper.h +++ b/media/gpu/windows/video_rate_control_wrapper.h
@@ -75,7 +75,8 @@ }; virtual ~VideoRateControlWrapper() = default; - virtual void UpdateRateControl(const RateControlConfig& config) = 0; + [[nodiscard]] virtual bool UpdateRateControl( + const RateControlConfig& config) = 0; // ComputeQP() returns qp table index and the range is up to the codec. virtual int ComputeQP(const FrameParams& frame_params) = 0; // GetLoopfilterLevel() is only available for VP9, others return -1. @@ -106,9 +107,10 @@ explicit VideoRateControlWrapperInternal(std::unique_ptr<RateCtrlType> impl) : impl_(std::move(impl)) {} ~VideoRateControlWrapperInternal() override = default; - void UpdateRateControl(const RateControlConfig& config) override { + [[nodiscard]] bool UpdateRateControl( + const RateControlConfig& config) override { DCHECK(impl_); - impl_->UpdateRateControl(ConvertControlConfig(config)); + return impl_->UpdateRateControl(ConvertControlConfig(config)); } int ComputeQP(const FrameParams& frame_params) override { DCHECK(impl_);
diff --git a/media/test/pipeline_integration_test_base.cc b/media/test/pipeline_integration_test_base.cc index 70c9da3..8d5b5a4 100644 --- a/media/test/pipeline_integration_test_base.cc +++ b/media/test/pipeline_integration_test_base.cc
@@ -83,6 +83,7 @@ ~TestDataSourceFactory() override = default; void Create(const GURL& uri, DataSource::CacheMode, + DataSource::EncodingMode, DataSource::DataSourceCb callback) override { auto file_data_source = std::make_unique<FileDataSource>(); base::FilePath file_path(
diff --git a/pdf/pdfium/pdfium_engine.cc b/pdf/pdfium/pdfium_engine.cc index c6e7392..ebb261b 100644 --- a/pdf/pdfium/pdfium_engine.cc +++ b/pdf/pdfium/pdfium_engine.cc
@@ -109,6 +109,7 @@ #endif #if BUILDFLAG(ENABLE_PDF_INK2) +#include "base/rand_util.h" #include "pdf/pdf_ink_metrics_handler.h" #include "pdf/pdf_ink_transform.h" #include "pdf/pdfium/pdfium_ink_reader.h" @@ -158,6 +159,10 @@ constexpr base::TimeDelta kTouchLongPressTimeout = base::Milliseconds(300); +#if BUILDFLAG(ENABLE_PDF_INK2) +constexpr int kMinTextboxId = 0; +constexpr int kMaxTextboxId = std::numeric_limits<int>::max(); + // Saves the provided `attributes` as parameters on the `mark` of the // `text_object`. Used to reload text objects in future PDF sessions. void AddMetadataToTextObject(FPDF_DOCUMENT doc, @@ -191,6 +196,7 @@ CHECK(FPDFPageObjMark_SetStringParam(doc, text_object, mark, "Text", attributes.text.c_str())); } +#endif // BUILDFLAG(ENABLE_PDF_INK2) // Windows has native panning capabilities. No need to use our own. #if BUILDFLAG(IS_WIN) @@ -665,6 +671,18 @@ } return SkData::MakeFromStream(stream, stream->getLength()); } + +void RemovePageObjectsFromPage(FPDF_PAGE page, + base::span<FPDF_PAGEOBJECT> page_objects) { + for (FPDF_PAGEOBJECT page_object : page_objects) { + CHECK(FPDFPage_RemoveObject(page, page_object)); + + // FPDFPage_RemoveObject() transferred ownership of `page_object` to the + // caller. Free it since `page_object` is being discarded. + FPDFPageObj_Destroy(page_object); + } +} + #endif // BUILDFLAG(ENABLE_PDF_INK2) void CheckBitmapProperties(const SkBitmap& sk_bitmap, FPDF_BITMAP fpdf_bitmap) { @@ -751,6 +769,10 @@ IFSDK_PAUSE::version = 1; IFSDK_PAUSE::user = nullptr; IFSDK_PAUSE::NeedToPauseNow = Pause_NeedToPauseNow; + +#if BUILDFLAG(ENABLE_PDF_INK2) + next_textbox_id_ = base::RandIntInclusive(kMinTextboxId, kMaxTextboxId); +#endif } PDFiumEngine::~PDFiumEngine() { @@ -5128,7 +5150,7 @@ const SkColor color = attributes.color; const float pdf_font_size = CSSFontSizeToPdfFontSize(attributes.css_font_size); - const int textbox_id = next_textbox_id_++; + const int textbox_id = GetNextTextboxId(); for (const InkTextInfo& item : text_info) { FPDF_FONT font = GetAddedFont(item.font_id); @@ -5243,23 +5265,11 @@ CHECK(PageIndexInBounds(page_index)); auto it = ink_stroke_data_.find(id); CHECK(it != ink_stroke_data_.end()); - for (FPDF_PAGEOBJECT page_object : it->second.page_objects) { - bool result = - FPDFPage_RemoveObject(pages_[page_index]->GetPage(), page_object); - CHECK(result); - - // FPDFPage_RemoveObject() transferred ownership of `page_object` to the - // caller. Free it since `page_object` is being discarded. - FPDFPageObj_Destroy(page_object); - } + RemovePageObjectsFromPage(pages_[page_index]->GetPage(), + it->second.page_objects); ink_stroke_data_.erase(it); - bool page_still_has_shapes_or_strokes = - pages_with_loaded_v2_ink_shapes_.contains(page_index) || - std::ranges::any_of(ink_stroke_data_, [page_index](const auto& it) { - return it.second.page_index == page_index; - }); - if (!page_still_has_shapes_or_strokes) { + if (!PageStillHasEdits(page_index)) { edited_pages_unload_preventers_.erase(page_index); } } @@ -5328,12 +5338,18 @@ if (page_textboxes.empty()) { continue; } + + // Note that the textbox IDs in the PDF are ONLY used for grouping multiple + // text objects belonging to the same textbox in the PDF on a per-page + // basis (and not for global tracking). Generating globally unique IDs + // prevents collisions across all pages. + for (const auto& textbox : page_textboxes) { + existing_textbox_ids_.insert(textbox.id); + } document_textboxes[i] = std::move(page_textboxes); } // TODO(crbug.com/504697272): Track the textboxes. - - // TODO(crbug.com/408926609): Implement ID collision prevention. return document_textboxes; } @@ -5425,6 +5441,31 @@ return active_page_objects; } +int PDFiumEngine::GetNextTextboxId() { + while (true) { + int candidate = next_textbox_id_; + + if (next_textbox_id_ == kMaxTextboxId) { + next_textbox_id_ = kMinTextboxId; + } else { + ++next_textbox_id_; + } + + bool inserted = existing_textbox_ids_.insert(candidate).second; + if (inserted) { + return candidate; + } + } +} + +bool PDFiumEngine::PageStillHasEdits(int page_index) const { + CHECK(PageIndexInBounds(page_index)); + return pages_with_loaded_v2_ink_shapes_.contains(page_index) || + std::ranges::any_of(ink_stroke_data_, [page_index](const auto& it) { + return it.second.page_index == page_index; + }); +} + PDFiumEngine::InkStrokeData::InkStrokeData( int page_index, std::vector<FPDF_PAGEOBJECT> page_objects)
diff --git a/pdf/pdfium/pdfium_engine.h b/pdf/pdfium/pdfium_engine.h index 9f5a0be..9c0925f 100644 --- a/pdf/pdfium/pdfium_engine.h +++ b/pdf/pdfium/pdfium_engine.h
@@ -519,6 +519,12 @@ edited_pages_unload_preventers_for_testing() const { return edited_pages_unload_preventers_; } + + void set_next_textbox_id_for_testing(int id) { next_textbox_id_ = id; } + + void set_existing_textbox_ids_for_testing(std::set<int> ids) { + existing_textbox_ids_ = std::move(ids); + } #endif // BUILDFLAG(ENABLE_PDF_INK2) // DocumentLoader::Client: @@ -1134,6 +1140,13 @@ #if BUILDFLAG(ENABLE_PDF_INK2) std::vector<FPDF_PAGEOBJECT> GetActiveInkPageObjectsForPage( int page_index) const; + + // Returns the next available textbox ID, avoiding collisions with + // `existing_textbox_ids_` and handling integer overflow. Adds the returned + // ID to `existing_textbox_ids_`. + int GetNextTextboxId(); + + bool PageStillHasEdits(int page_index) const; #endif const raw_ptr<PDFiumEngineClient> client_; @@ -1433,9 +1446,17 @@ // Value: The associated PDFium font objects. std::map<FontId, ScopedFPDFFont> font_map_; - // The next available ID for a textbox for writing into the PDF. - // TODO(crbug.com/408926609): Implement ID collision avoidance. - int next_textbox_id_ = 0; + // The next available ID for a textbox for writing into the PDF. Initialized + // to a random value to make collisions rare. + int next_textbox_id_; + + // The set of textbox IDs currently in use in the document. Used to prevent + // collisions when generating new textbox IDs. Note that the textbox IDs in + // the PDF are ONLY used for grouping multiple text objects belonging to the + // same textbox in the PDF on a per-page basis (and not for global tracking). + // Generating globally unique IDs is a simple and safe way to prevent + // collisions on all pages. + std::set<int> existing_textbox_ids_; #endif // BUILDFLAG(ENABLE_PDF_INK2) base::WeakPtrFactory<PDFiumEngine> weak_factory_{this};
diff --git a/pdf/pdfium/pdfium_engine_unittest.cc b/pdf/pdfium/pdfium_engine_unittest.cc index dc453eec..7131280 100644 --- a/pdf/pdfium/pdfium_engine_unittest.cc +++ b/pdf/pdfium/pdfium_engine_unittest.cc
@@ -3084,6 +3084,35 @@ /*is_italic=*/false, /*text=*/"Hello!"); } + + void DrawAndVerifyTextboxId(PDFiumEngine* engine, + PDFiumPage& page, + FontId font_id, + const GlyphsAndPositions& text_data, + InkTextId ink_text_id, + int expected_textbox_id) { + int initial_obj_count = FPDFPage_CountObjects(page.GetPage()); + + engine->DrawText( + page.index(), ink_text_id, + {InkTextInfo(font_id, text_data.glyphs, text_data.glyph_positions, + /*location=*/gfx::RectF(0.0f, 0.0f, 100.0f, 20.0f), + /*is_horizontal=*/true)}, + /*pdf_zoom=*/1.0, SampleInkTextBoxAttributes()); + + int new_obj_count = FPDFPage_CountObjects(page.GetPage()); + ASSERT_EQ(new_obj_count, initial_obj_count + 1); + + // The new object should be at the end. + FPDF_PAGEOBJECT new_obj = + FPDFPage_GetObject(page.GetPage(), new_obj_count - 1); + ASSERT_EQ(1, FPDFPageObj_CountMarks(new_obj)); + FPDF_PAGEOBJECTMARK mark = FPDFPageObj_GetMark(new_obj, 0); + ASSERT_EQ(kInkTextAnnotationIdentifierKey, + base::UTF16ToUTF8(GetPageObjectMarkName(mark))); + EXPECT_THAT(GetPageObjectMarkIntParam(mark, "TextboxId"), + testing::Optional(expected_textbox_id)); + } }; TEST_P(PDFiumEngineInkDrawTextTest, DrawText) { @@ -3184,6 +3213,7 @@ InkTextBoxAttributes attribute = SampleInkTextBoxAttributes(); attribute.is_bold = true; attribute.text = kExpectedText; + engine->set_next_textbox_id_for_testing(1); engine->DrawText( kPageIndex, InkTextId(1), {InkTextInfo(font_id, text_data1.glyphs, text_data1.glyph_positions, @@ -3206,7 +3236,7 @@ EXPECT_EQ(kInkTextAnnotationIdentifierKey, base::UTF16ToUTF8(GetPageObjectMarkName(mark))); EXPECT_THAT(GetPageObjectMarkIntParam(mark, "TextboxId"), - testing::Optional(0)); + testing::Optional(1)); if (i == 0) { // Verify the first text object contains the full textbox metadata. @@ -3291,6 +3321,68 @@ EXPECT_EQ("Hello Page 2", page2_boxes[0].attributes.text); } +TEST_P(PDFiumEngineInkDrawTextTest, DrawTextAvoidsTextboxIdCollisions) { + NiceMock<TestClient> client(/*use_skia_renderer=*/GetParam()); + std::unique_ptr<PDFiumEngine> engine = InitializeEngine( + &client, FILE_PATH_LITERAL("ink_text_multi_textboxes.pdf")); + ASSERT_TRUE(engine); + + // Load existing annotations to populate `existing_textbox_ids_`. + // ink_text_multi_textboxes.pdf has textbox IDs 0 and 42. + engine->LoadTextAnnotationsFromPdf(); + + constexpr int kPageIndex = 0; + PDFiumPage& page = GetPDFiumPage(*engine, kPageIndex); + + FontId font_id = AddDefaultFont(engine.get()); + constexpr std::string_view kTextToDraw = "New!"; + GlyphsAndPositions text_data = + GetGlyphsForText(kTextToDraw, /*font_size=*/10.0f); + ASSERT_FALSE(text_data.glyphs.empty()); + + engine->set_next_textbox_id_for_testing(0); + + // Draw text. The next ID should be 1, because 0 is already taken. + DrawAndVerifyTextboxId(engine.get(), page, font_id, text_data, InkTextId(100), + /*expected_textbox_id=*/1); + + // Draw text again. The next ID should be 2. + DrawAndVerifyTextboxId(engine.get(), page, font_id, text_data, InkTextId(101), + /*expected_textbox_id=*/2); +} + +TEST_P(PDFiumEngineInkDrawTextTest, DrawTextWrapsTextboxId) { + NiceMock<TestClient> client(/*use_skia_renderer=*/GetParam()); + std::unique_ptr<PDFiumEngine> engine = + InitializeEngine(&client, FILE_PATH_LITERAL("blank.pdf")); + ASSERT_TRUE(engine); + + constexpr int kPageIndex = 0; + PDFiumPage& page = GetPDFiumPage(*engine, kPageIndex); + + FontId font_id = AddDefaultFont(engine.get()); + constexpr std::string_view kTextToDraw = "Test"; + GlyphsAndPositions text_data = + GetGlyphsForText(kTextToDraw, /*font_size=*/10.0f); + ASSERT_FALSE(text_data.glyphs.empty()); + + constexpr int kMaxId = std::numeric_limits<int>::max(); + engine->set_next_textbox_id_for_testing(kMaxId - 1); + engine->set_existing_textbox_ids_for_testing({kMaxId - 1}); + + // First draw: Should skip `kMaxId` - 1, wrap to 0, and use `kMaxId`. + DrawAndVerifyTextboxId(engine.get(), page, font_id, text_data, InkTextId(100), + /*expected_textbox_id=*/kMaxId); + + // Second draw: Should use 0. + DrawAndVerifyTextboxId(engine.get(), page, font_id, text_data, InkTextId(101), + /*expected_textbox_id=*/0); + + // Third draw: Should use 1. + DrawAndVerifyTextboxId(engine.get(), page, font_id, text_data, InkTextId(102), + /*expected_textbox_id=*/1); +} + // Don't be concerned about any slight rendering differences in AGG vs. Skia, // covering one of these is sufficient for checking how data is written out. INSTANTIATE_TEST_SUITE_P(All,
diff --git a/pdf/test/pdf_ink_test_helpers.cc b/pdf/test/pdf_ink_test_helpers.cc index d54c60b..45ffe71 100644 --- a/pdf/test/pdf_ink_test_helpers.cc +++ b/pdf/test/pdf_ink_test_helpers.cc
@@ -5,12 +5,15 @@ #include "pdf/test/pdf_ink_test_helpers.h" #include <array> +#include <ostream> #include <string_view> #include <utility> #include "base/notreached.h" +#include "base/strings/to_string.h" #include "base/values.h" #include "pdf/pdf_ink_conversions.h" +#include "testing/gtest/include/gtest/gtest.h" namespace chrome_pdf { @@ -101,4 +104,13 @@ return kInkTestVariations; } +void PrintTo(const InkTextInfo& info, std::ostream* os) { + *os << "{\n font_id=" << info.font_id + << ", is_horizontal=" << base::ToString(info.is_horizontal) + << ",\n location=" << info.location.ToString() + << ",\n glyphs=" << testing::PrintToString(info.glyphs) + << ",\n glyph_positions=" << testing::PrintToString(info.glyph_positions) + << "\n}"; +} + } // namespace chrome_pdf
diff --git a/pdf/test/pdf_ink_test_helpers.h b/pdf/test/pdf_ink_test_helpers.h index 78ead48..2452fa2e 100644 --- a/pdf/test/pdf_ink_test_helpers.h +++ b/pdf/test/pdf_ink_test_helpers.h
@@ -7,6 +7,7 @@ #include <stdint.h> +#include <iosfwd> #include <optional> #include <string> #include <string_view> @@ -109,12 +110,18 @@ glyph_positions, location, is_horizontal, - "matches InkTextInfo") { + testing::PrintToString(InkTextInfo(font_id, + glyphs, + glyph_positions, + location, + is_horizontal))) { return arg.font_id == font_id && arg.glyphs == glyphs && arg.glyph_positions == glyph_positions && arg.location == location && arg.is_horizontal == is_horizontal; } +void PrintTo(const InkTextInfo& info, std::ostream* os); + // Generate the path for test files specific to Ink. base::FilePath GetInkTestDataFilePath(base::FilePath::StringViewType filename);
diff --git a/printing/android/java/src/org/chromium/printing/PrintingContext.java b/printing/android/java/src/org/chromium/printing/PrintingContext.java index 288821b0..4c9a75e0 100644 --- a/printing/android/java/src/org/chromium/printing/PrintingContext.java +++ b/printing/android/java/src/org/chromium/printing/PrintingContext.java
@@ -4,21 +4,27 @@ package org.chromium.printing; +import static org.chromium.printing.PrintingControllerImpl.INVALID_FD; + import android.app.Activity; +import android.os.ParcelFileDescriptor; import org.jni_zero.CalledByNative; import org.jni_zero.JNINamespace; import org.jni_zero.NativeMethods; +import org.chromium.base.Log; import org.chromium.base.ThreadUtils; import org.chromium.build.annotations.NullMarked; import org.chromium.build.annotations.Nullable; import org.chromium.ui.base.WindowAndroid; +import java.io.IOException; + /** - * This class is responsible for communicating with its native counterpart through JNI to handle - * the generation of PDF. On the Java side, it works with a {@link PrintingController} - * to talk to the framework. + * This class is responsible for communicating with its native counterpart through JNI to handle the + * generation of PDF. On the Java side, it works with a {@link PrintingController} to talk to the + * framework. */ @JNINamespace("printing") @NullMarked @@ -42,10 +48,28 @@ return new PrintingContext(nativeObjectPointer, window); } + /** + * Takes a duplicated file descriptor stored in the controller. The caller (typically native + * code) takes ownership of the returned file descriptor and is responsible for closing it. This + * is done to prevent Use-After-Close issues by ensuring both Java and C++ have their own + * independent references to the file. + * + * @return The duplicated file descriptor, or {@link PrintingControllerImpl#INVALID_FD} if + * failed. + */ @CalledByNative - public int getFileDescriptor() { + public int takeDuplicatedFileDescriptor() { ThreadUtils.assertOnUiThread(); - return mController.getFileDescriptor(); + ParcelFileDescriptor pfd = mController.getParcelFileDescriptor(); + if (pfd == null) return INVALID_FD; + try { + // Duplicate the file descriptor to pass ownership to C++. + // This prevents UAC as C++ holds its own reference. + return pfd.dup().detachFd(); + } catch (IOException e) { + Log.e(TAG, "Failed to duplicate file descriptor", e); + return INVALID_FD; + } } @CalledByNative
diff --git a/printing/android/java/src/org/chromium/printing/PrintingController.java b/printing/android/java/src/org/chromium/printing/PrintingController.java index 8910f16..c8e61cd 100644 --- a/printing/android/java/src/org/chromium/printing/PrintingController.java +++ b/printing/android/java/src/org/chromium/printing/PrintingController.java
@@ -4,7 +4,7 @@ package org.chromium.printing; -import android.print.PrintDocumentAdapter; +import android.os.ParcelFileDescriptor; import org.chromium.build.annotations.NullMarked; import org.chromium.build.annotations.Nullable; @@ -26,10 +26,9 @@ int getDpi(); /** - * @return The file descriptor number of the file into which Chromium will write the PDF. This - * is provided to us by {@link PrintDocumentAdapter#onWrite}. + * @return The ParcelFileDescriptor of the file into which Chromium will write the PDF. */ - int getFileDescriptor(); + @Nullable ParcelFileDescriptor getParcelFileDescriptor(); /** * @return The media height in mils (thousands of an inch).
diff --git a/printing/android/java/src/org/chromium/printing/PrintingControllerImpl.java b/printing/android/java/src/org/chromium/printing/PrintingControllerImpl.java index e0f4dc03..47b7e59 100644 --- a/printing/android/java/src/org/chromium/printing/PrintingControllerImpl.java +++ b/printing/android/java/src/org/chromium/printing/PrintingControllerImpl.java
@@ -82,6 +82,9 @@ private static final int BUFFER_SIZE = 8 * 1024; // 8 KB + /** Constant for invalid file descriptor- equivalent to base::kInvalidFd (-1) in C++. */ + public static final int INVALID_FD = -1; + private @Nullable String mErrorMessage; private int mRenderProcessId; @@ -205,8 +208,8 @@ } @Override - public int getFileDescriptor() { - return assumeNonNull(mFileDescriptor).getFd(); + public @Nullable ParcelFileDescriptor getParcelFileDescriptor() { + return mFileDescriptor; } @Override @@ -347,6 +350,11 @@ // TODO(cimamoglu): Make use of CancellationSignal. if (ranges == null || ranges.length == 0) { callback.onWriteFailed(null); + try { + destination.close(); + } catch (IOException e) { + /* ignore */ + } return; } @@ -360,6 +368,12 @@ mOnWriteCallback.onWriteFailed("ParcelFileDescriptor.dup() failed: " + e.toString()); resetCallbacks(); return; + } finally { + try { + destination.close(); + } catch (IOException e) { + /* ignore */ + } } mPages = convertPageRangesToIntegerArray(ranges); InputStream pdfInputStream = assumeNonNull(mPrintable).getPdfInputStream();
diff --git a/printing/printing_context_android.cc b/printing/printing_context_android.cc index eddbd41..a4abd19 100644 --- a/printing/printing_context_android.cc +++ b/printing/printing_context_android.cc
@@ -14,6 +14,7 @@ #include "base/android/jni_array.h" #include "base/android/jni_string.h" #include "base/check_op.h" +#include "base/file_descriptor_posix.h" #include "base/files/file.h" #include "base/logging.h" #include "base/strings/string_number_conversions.h" @@ -130,13 +131,20 @@ return; } + // Take a duplicated file descriptor from Java to pass ownership to C++. + // This prevents Use-After-Close as C++ holds its own reference. + int raw_fd = Java_PrintingContext_takeDuplicatedFileDescriptor( + env, j_printing_context_); + if (raw_fd < 0) { + std::move(callback_).Run(mojom::ResultCode::kFailed); + return; + } + scoped_fd_.reset(raw_fd); // We use device name variable to store the file descriptor. This is hacky // but necessary. Since device name is not necessary for the upstream // printing code for Android, this is harmless. // TODO(thestig): See if the call to set_device_name() can be removed. - fd_ = Java_PrintingContext_getFileDescriptor(env, j_printing_context_); - DCHECK(is_file_descriptor_valid()); - settings_->set_device_name(base::NumberToString16(fd_)); + settings_->set_device_name(base::NumberToString16(raw_fd)); ScopedJavaLocalRef<jintArray> intArr = Java_PrintingContext_getPages(env, j_printing_context_); @@ -223,10 +231,15 @@ if (abort_printing_) return mojom::ResultCode::kCanceled; DCHECK(in_print_job_); - DCHECK(is_file_descriptor_valid()); - return metafile.SaveToFileDescriptor(fd_) ? mojom::ResultCode::kSuccess - : mojom::ResultCode::kFailed; + if (!scoped_fd_.is_valid()) { + LOG(ERROR) << "Invalid file descriptor for printing."; + return mojom::ResultCode::kFailed; + } + + return metafile.SaveToFileDescriptor(scoped_fd_.get()) + ? mojom::ResultCode::kSuccess + : mojom::ResultCode::kFailed; } mojom::ResultCode PrintingContextAndroid::DocumentDone() { @@ -241,10 +254,11 @@ void PrintingContextAndroid::Cancel() { abort_printing_ = true; in_print_job_ = false; + ReleaseContext(); } void PrintingContextAndroid::ReleaseContext() { - // Intentional No-op. + scoped_fd_.reset(); } printing::NativeDrawingContext PrintingContextAndroid::context() const {
diff --git a/printing/printing_context_android.h b/printing/printing_context_android.h index 2523e42..e2a014e 100644 --- a/printing/printing_context_android.h +++ b/printing/printing_context_android.h
@@ -9,6 +9,7 @@ #include "base/android/scoped_java_ref.h" #include "base/file_descriptor_posix.h" +#include "base/files/scoped_file.h" #include "printing/mojom/print.mojom.h" #include "printing/printing_context.h" @@ -66,15 +67,14 @@ printing::NativeDrawingContext context() const override; private: - bool is_file_descriptor_valid() const { return fd_ > base::kInvalidFd; } - base::android::ScopedJavaGlobalRef<jobject> j_printing_context_; // The callback from AskUserForSettings to be called when the settings are // ready on the Java side PrintSettingsCallback callback_; - int fd_ = base::kInvalidFd; + // File descriptor for the PDF file and owned by this layer. + base::ScopedFD scoped_fd_; }; } // namespace printing
diff --git a/remoting/base/BUILD.gn b/remoting/base/BUILD.gn index 192a45f5..2f3549ee 100644 --- a/remoting/base/BUILD.gn +++ b/remoting/base/BUILD.gn
@@ -112,6 +112,8 @@ "fifo_buffer.h", "fqdn.cc", "fqdn.h", + "in_memory_fifo_buffer.cc", + "in_memory_fifo_buffer.h", "instance_identity_token.cc", "instance_identity_token.h", "instance_identity_token_getter.h", @@ -458,6 +460,7 @@ "compound_buffer_unittest.cc", "ecdh_key_exchange_unittest.cc", "fifo_buffer_test_base.h", + "in_memory_fifo_buffer_unittest.cc", "instance_identity_token_getter_impl_unittest.cc", "ipc_fifo_buffer_unittest.cc", "jitter_buffer_unittest.cc",
diff --git a/remoting/base/in_memory_fifo_buffer.cc b/remoting/base/in_memory_fifo_buffer.cc new file mode 100644 index 0000000..9071641 --- /dev/null +++ b/remoting/base/in_memory_fifo_buffer.cc
@@ -0,0 +1,222 @@ +// Copyright 2026 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "remoting/base/in_memory_fifo_buffer.h" + +#include <algorithm> +#include <bit> + +#include "base/check.h" +#include "base/check_op.h" +#include "base/logging.h" + +namespace remoting { + +// Shared SPSC lock-free in-memory FIFO buffer storage. +// Ref-counted to ensure memory safety between producer and consumer threads. +class InMemoryFifoBuffer + : public base::RefCountedThreadSafe<InMemoryFifoBuffer> { + public: + // `capacity` must be a power of two. + explicit InMemoryFifoBuffer(size_t capacity); + + InMemoryFifoBuffer(const InMemoryFifoBuffer&) = delete; + InMemoryFifoBuffer& operator=(const InMemoryFifoBuffer&) = delete; + + WriteResult Write(base::span<const uint8_t> data); + std::optional<size_t> Read(base::span<uint8_t> destination); + std::optional<size_t> Skip(size_t bytes); + void Clear(); + std::optional<size_t> GetBufferedBytes() const; + + private: + friend class base::RefCountedThreadSafe<InMemoryFifoBuffer>; + ~InMemoryFifoBuffer(); + + const size_t capacity_; + // Bitmask used for fast modulo operations on circular indices (`capacity_ - + // 1`). + const size_t mask_; + + // Circular memory buffer storage. Size is `capacity_`. + std::vector<uint8_t> buffer_; + + // Atomically shared indices. + std::atomic<size_t> read_index_{0}; + std::atomic<size_t> write_index_{0}; + + SEQUENCE_CHECKER(producer_sequence_checker_); + SEQUENCE_CHECKER(consumer_sequence_checker_); +}; + +// ============================================================================= +// InMemoryFifoBuffer (Shared Storage Implementation) +// ============================================================================= + +InMemoryFifoBuffer::InMemoryFifoBuffer(size_t capacity) + : capacity_(capacity), mask_(capacity - 1), buffer_(capacity) { + CHECK_GT(capacity, 0u); + CHECK(std::has_single_bit(capacity)) << "Capacity must be a power of two."; + + DETACH_FROM_SEQUENCE(producer_sequence_checker_); + DETACH_FROM_SEQUENCE(consumer_sequence_checker_); +} + +InMemoryFifoBuffer::~InMemoryFifoBuffer() = default; + +WriteResult InMemoryFifoBuffer::Write(base::span<const uint8_t> data) { + DCHECK_CALLED_ON_VALID_SEQUENCE(producer_sequence_checker_); + + // Producer thread: load `read_index_` with acquire to see the latest consumer + // state. + size_t read_idx = read_index_.load(std::memory_order_acquire); + size_t write_idx = write_index_.load(std::memory_order_relaxed); + + size_t buffered = write_idx - read_idx; + size_t space = capacity_ - buffered; + + if (space < data.size()) { + LOG(WARNING) << "InMemoryFifoBuffer overflow, dropping " << data.size() + << " bytes. Buffered: " << buffered; + return WriteResult::kFull; + } + + size_t first_part = std::min(data.size(), capacity_ - (write_idx & mask_)); + std::copy(data.begin(), data.begin() + first_part, + buffer_.begin() + (write_idx & mask_)); + + if (first_part < data.size()) { + std::copy(data.begin() + first_part, data.begin() + data.size(), + buffer_.begin()); + } + + // Producer thread: store `write_index_` with release to make data visible to + // consumer. + write_index_.store(write_idx + data.size(), std::memory_order_release); + return WriteResult::kSuccess; +} + +std::optional<size_t> InMemoryFifoBuffer::Read( + base::span<uint8_t> destination) { + DCHECK_CALLED_ON_VALID_SEQUENCE(consumer_sequence_checker_); + + // Consumer thread: load `write_index_` with acquire to see the latest + // producer state. + size_t write_idx = write_index_.load(std::memory_order_acquire); + size_t read_idx = read_index_.load(std::memory_order_relaxed); + + size_t buffered = write_idx - read_idx; + size_t to_read = std::min(destination.size(), buffered); + + if (to_read == 0) { + return 0; + } + + size_t first_part = std::min(to_read, capacity_ - (read_idx & mask_)); + std::copy(buffer_.begin() + (read_idx & mask_), + buffer_.begin() + (read_idx & mask_) + first_part, + destination.begin()); + + if (first_part < to_read) { + std::copy(buffer_.begin(), buffer_.begin() + to_read - first_part, + destination.begin() + first_part); + } + + // Consumer thread: store `read_index_` with release to signal to producer. + read_index_.store(read_idx + to_read, std::memory_order_release); + return to_read; +} + +std::optional<size_t> InMemoryFifoBuffer::Skip(size_t bytes) { + DCHECK_CALLED_ON_VALID_SEQUENCE(consumer_sequence_checker_); + + size_t write_idx = write_index_.load(std::memory_order_acquire); + size_t read_idx = read_index_.load(std::memory_order_relaxed); + + size_t buffered = write_idx - read_idx; + size_t to_skip = std::min(bytes, buffered); + + if (to_skip == 0) { + return 0; + } + + read_index_.store(read_idx + to_skip, std::memory_order_release); + return to_skip; +} + +void InMemoryFifoBuffer::Clear() { + DCHECK_CALLED_ON_VALID_SEQUENCE(consumer_sequence_checker_); + read_index_.store(write_index_.load(std::memory_order_acquire), + std::memory_order_release); +} + +std::optional<size_t> InMemoryFifoBuffer::GetBufferedBytes() const { + size_t read_idx = read_index_.load(std::memory_order_acquire); + size_t write_idx = write_index_.load(std::memory_order_acquire); + return std::min(write_idx - read_idx, capacity_); +} + +// ============================================================================= +// InMemoryFifoBufferWriter +// ============================================================================= + +InMemoryFifoBufferWriter::InMemoryFifoBufferWriter( + scoped_refptr<InMemoryFifoBuffer> buffer) + : buffer_(std::move(buffer)) { + CHECK(buffer_); + DETACH_FROM_SEQUENCE(sequence_checker_); +} + +InMemoryFifoBufferWriter::~InMemoryFifoBufferWriter() = default; + +WriteResult InMemoryFifoBufferWriter::Write(base::span<const uint8_t> data) { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + return buffer_->Write(data); +} + +// ============================================================================= +// InMemoryFifoBufferReader +// ============================================================================= + +InMemoryFifoBufferReader::InMemoryFifoBufferReader( + scoped_refptr<InMemoryFifoBuffer> buffer) + : buffer_(std::move(buffer)) { + CHECK(buffer_); + DETACH_FROM_SEQUENCE(sequence_checker_); +} + +InMemoryFifoBufferReader::~InMemoryFifoBufferReader() = default; + +std::optional<size_t> InMemoryFifoBufferReader::Read( + base::span<uint8_t> destination) { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + return buffer_->Read(destination); +} + +std::optional<size_t> InMemoryFifoBufferReader::Skip(size_t bytes) { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + return buffer_->Skip(bytes); +} + +void InMemoryFifoBufferReader::Clear() { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + buffer_->Clear(); +} + +std::optional<size_t> InMemoryFifoBufferReader::GetBufferedBytes() const { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + return buffer_->GetBufferedBytes(); +} + +bool CreateInMemoryFifoBuffer( + size_t capacity, + std::unique_ptr<InMemoryFifoBufferWriter>& writer, + std::unique_ptr<InMemoryFifoBufferReader>& reader) { + auto buffer = base::MakeRefCounted<InMemoryFifoBuffer>(capacity); + writer = std::make_unique<InMemoryFifoBufferWriter>(buffer); + reader = std::make_unique<InMemoryFifoBufferReader>(buffer); + return true; +} + +} // namespace remoting
diff --git a/remoting/base/in_memory_fifo_buffer.h b/remoting/base/in_memory_fifo_buffer.h new file mode 100644 index 0000000..97a7ff0 --- /dev/null +++ b/remoting/base/in_memory_fifo_buffer.h
@@ -0,0 +1,81 @@ +// Copyright 2026 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef REMOTING_BASE_IN_MEMORY_FIFO_BUFFER_H_ +#define REMOTING_BASE_IN_MEMORY_FIFO_BUFFER_H_ + +#include <atomic> +#include <vector> + +#include "base/containers/span.h" +#include "base/memory/ref_counted.h" +#include "base/memory/scoped_refptr.h" +#include "base/sequence_checker.h" +#include "base/thread_annotations.h" +#include "remoting/base/fifo_buffer.h" + +namespace remoting { + +class InMemoryFifoBuffer; +class InMemoryFifoBufferWriter; +class InMemoryFifoBufferReader; + +// Creates an in-memory SPSC lock-free FIFO buffer pair with the specified +// `capacity`. `capacity` must be a power of two. If successful, populates +// `writer` and `reader` and returns true. +bool CreateInMemoryFifoBuffer( + size_t capacity, + std::unique_ptr<InMemoryFifoBufferWriter>& writer, + std::unique_ptr<InMemoryFifoBufferReader>& reader); + +// Concrete implementation of FifoBufferWriter backed by InMemoryFifoBuffer. +// All SPSC methods must be called from a single thread (lazily bound on the +// first call), but the instance can be safely constructed and destructed on a +// different sequence (such as the owner main thread) as long as there is no +// concurrent access. +class InMemoryFifoBufferWriter : public FifoBufferWriter { + public: + explicit InMemoryFifoBufferWriter(scoped_refptr<InMemoryFifoBuffer> buffer); + + InMemoryFifoBufferWriter(const InMemoryFifoBufferWriter&) = delete; + InMemoryFifoBufferWriter& operator=(const InMemoryFifoBufferWriter&) = delete; + + ~InMemoryFifoBufferWriter() override; + + // FifoBufferWriter implementation. + WriteResult Write(base::span<const uint8_t> data) override; + + private: + const scoped_refptr<InMemoryFifoBuffer> buffer_; + SEQUENCE_CHECKER(sequence_checker_); +}; + +// Concrete implementation of FifoBufferReader backed by InMemoryFifoBuffer. +// All SPSC methods must be called from a single thread (lazily bound on the +// first call), but the instance can be safely constructed and destructed on a +// different sequence (such as the owner main thread) as long as there is no +// concurrent access. +class InMemoryFifoBufferReader : public FifoBufferReader { + public: + explicit InMemoryFifoBufferReader(scoped_refptr<InMemoryFifoBuffer> buffer); + + InMemoryFifoBufferReader(const InMemoryFifoBufferReader&) = delete; + InMemoryFifoBufferReader& operator=(const InMemoryFifoBufferReader&) = delete; + + ~InMemoryFifoBufferReader() override; + + // FifoBufferReader implementation. + std::optional<size_t> Read(base::span<uint8_t> destination) override; + std::optional<size_t> Skip(size_t bytes) override; + void Clear() override; + std::optional<size_t> GetBufferedBytes() const override; + + private: + const scoped_refptr<InMemoryFifoBuffer> buffer_; + SEQUENCE_CHECKER(sequence_checker_); +}; + +} // namespace remoting + +#endif // REMOTING_BASE_IN_MEMORY_FIFO_BUFFER_H_
diff --git a/remoting/base/in_memory_fifo_buffer_unittest.cc b/remoting/base/in_memory_fifo_buffer_unittest.cc new file mode 100644 index 0000000..0f7e17c --- /dev/null +++ b/remoting/base/in_memory_fifo_buffer_unittest.cc
@@ -0,0 +1,79 @@ +// Copyright 2026 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "remoting/base/in_memory_fifo_buffer.h" + +#include <memory> +#include <vector> + +#include "remoting/base/fifo_buffer_test_base.h" + +namespace remoting { + +namespace { +constexpr size_t kCapacity = 1024; +} // namespace + +class InMemoryFifoBufferTestDelegate { + public: + InMemoryFifoBufferTestDelegate() { + CHECK(CreateInMemoryFifoBuffer(kCapacity, writer_, reader_)); + } + + FifoBufferWriter& GetWriter() { return *writer_; } + FifoBufferReader& GetReader() { return *reader_; } + + private: + std::unique_ptr<InMemoryFifoBufferWriter> writer_; + std::unique_ptr<InMemoryFifoBufferReader> reader_; +}; + +using InMemoryFifoBufferTestTypes = + testing::Types<InMemoryFifoBufferTestDelegate>; +INSTANTIATE_TYPED_TEST_SUITE_P(InMemory, + FifoBufferTest, + InMemoryFifoBufferTestTypes); + +TEST(InMemoryFifoBufferTest, Overflow) { + // This test is specific to the overflow behavior of InMemoryFifoBuffer, + // which strictly drops extra bytes at capacity limit. + std::unique_ptr<InMemoryFifoBufferWriter> writer; + std::unique_ptr<InMemoryFifoBufferReader> reader; + ASSERT_TRUE(CreateInMemoryFifoBuffer(kCapacity, writer, reader)); + + std::vector<uint8_t> data(kCapacity, 0xAA); + EXPECT_EQ(writer->Write(data), WriteResult::kSuccess); + EXPECT_EQ(reader->GetBufferedBytes(), kCapacity); + + std::vector<uint8_t> extra_data = {1, 2, 3, 4}; + EXPECT_EQ(writer->Write(extra_data), WriteResult::kFull); + + std::vector<uint8_t> read_data(kCapacity); + EXPECT_EQ(reader->Read(read_data), kCapacity); + EXPECT_EQ(read_data, data); +} + +TEST(InMemoryFifoBufferTest, WrapAround) { + // This test is specific to the circular buffer implementation details of + // InMemoryFifoBuffer, so we keep it here as a standalone test. + std::unique_ptr<InMemoryFifoBufferWriter> writer; + std::unique_ptr<InMemoryFifoBufferReader> reader; + ASSERT_TRUE(CreateInMemoryFifoBuffer(kCapacity, writer, reader)); + + std::vector<uint8_t> data(kCapacity - 4, 0xAA); + EXPECT_EQ(writer->Write(data), WriteResult::kSuccess); + + std::vector<uint8_t> read_data(kCapacity - 4); + EXPECT_EQ(reader->Read(read_data), kCapacity - 4); + + // Write again, should wrap. + std::vector<uint8_t> wrap_data = {1, 2, 3, 4, 5, 6, 7, 8}; + EXPECT_EQ(writer->Write(wrap_data), WriteResult::kSuccess); + + std::vector<uint8_t> wrap_read(8); + EXPECT_EQ(reader->Read(wrap_read), 8u); + EXPECT_EQ(wrap_read, wrap_data); +} + +} // namespace remoting
diff --git a/remoting/base/ipc_fifo_buffer.cc b/remoting/base/ipc_fifo_buffer.cc index c48279a..12680e3 100644 --- a/remoting/base/ipc_fifo_buffer.cc +++ b/remoting/base/ipc_fifo_buffer.cc
@@ -20,11 +20,10 @@ mojo::ScopedDataPipeProducerHandle producer_handle) : producer_handle_(std::move(producer_handle)) { CHECK(producer_handle_.is_valid()); + DETACH_FROM_SEQUENCE(sequence_checker_); } -IpcFifoBufferWriter::~IpcFifoBufferWriter() { - DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); -} +IpcFifoBufferWriter::~IpcFifoBufferWriter() = default; WriteResult IpcFifoBufferWriter::Write(base::span<const uint8_t> data) { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); @@ -62,11 +61,10 @@ mojo::ScopedDataPipeConsumerHandle consumer_handle) : consumer_handle_(std::move(consumer_handle)) { CHECK(consumer_handle_.is_valid()); + DETACH_FROM_SEQUENCE(sequence_checker_); } -IpcFifoBufferReader::~IpcFifoBufferReader() { - DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); -} +IpcFifoBufferReader::~IpcFifoBufferReader() = default; std::optional<size_t> IpcFifoBufferReader::Read( base::span<uint8_t> destination) {
diff --git a/remoting/base/ipc_fifo_buffer.h b/remoting/base/ipc_fifo_buffer.h index 038a338f62..d303fcf4 100644 --- a/remoting/base/ipc_fifo_buffer.h +++ b/remoting/base/ipc_fifo_buffer.h
@@ -15,6 +15,10 @@ namespace remoting { // A FifoBufferWriter implementation backed by a Mojo Data Pipe Producer Handle. +// All SPSC methods must be called from a single thread (lazily bound on the +// first call), but the instance can be safely constructed and destructed on a +// different sequence (such as the owner main thread) as long as there is no +// concurrent access. class IpcFifoBufferWriter : public FifoBufferWriter { public: explicit IpcFifoBufferWriter( @@ -35,6 +39,10 @@ }; // A FifoBufferReader implementation backed by a Mojo Data Pipe Consumer Handle. +// All SPSC methods must be called from a single thread (lazily bound on the +// first call), but the instance can be safely constructed and destructed on a +// different sequence (such as the owner main thread) as long as there is no +// concurrent access. class IpcFifoBufferReader : public FifoBufferReader { public: explicit IpcFifoBufferReader(
diff --git a/remoting/base/jitter_buffer.cc b/remoting/base/jitter_buffer.cc index c4eed8f3..fcc0a652 100644 --- a/remoting/base/jitter_buffer.cc +++ b/remoting/base/jitter_buffer.cc
@@ -5,23 +5,18 @@ #include "remoting/base/jitter_buffer.h" #include <algorithm> -#include <bit> #include "base/check.h" #include "base/check_op.h" #include "base/logging.h" -#include "remoting/base/logging.h" namespace remoting { -JitterBuffer::JitterBuffer(const Config& config) - : config_(config), mask_(config.capacity - 1), buffer_(config.capacity) { - CHECK_GT(config.capacity, 0u); - CHECK(std::has_single_bit(config.capacity)) - << "Capacity must be a power of two."; +JitterBuffer::JitterBuffer(const Config& config, + std::unique_ptr<FifoBufferReader> fifo_buffer_reader) + : config_(config), fifo_buffer_reader_(std::move(fifo_buffer_reader)) { + CHECK(fifo_buffer_reader_); CHECK_GT(config.frame_size, 0u); - CHECK_EQ(config.capacity % config.frame_size, 0u) - << "Capacity must be a multiple of frame_size."; CHECK_EQ(config.minimum_threshold % config.frame_size, 0u) << "Minimum threshold must be aligned to frame_size."; if (config.max_latency_bytes > 0) { @@ -29,70 +24,23 @@ << "Max latency must be greater than minimum threshold."; } - DETACH_FROM_SEQUENCE(producer_sequence_checker_); DETACH_FROM_SEQUENCE(consumer_sequence_checker_); } JitterBuffer::~JitterBuffer() = default; -size_t JitterBuffer::Write(base::span<const uint8_t> data) { - DCHECK_CALLED_ON_VALID_SEQUENCE(producer_sequence_checker_); - DCHECK_EQ(data.size() % config_.frame_size, 0u); - - // Producer thread: load `read_index_` with acquire to see the latest consumer - // state. - size_t read_idx = read_index_.load(std::memory_order_acquire); - size_t write_idx = write_index_.load(std::memory_order_relaxed); - - size_t buffered = write_idx - read_idx; - size_t space = config_.capacity - buffered; - - size_t to_write = std::min(data.size(), space); - - if (to_write < data.size()) { - LOG(WARNING) << "JitterBuffer overflow, dropping " - << (data.size() - to_write) - << " bytes. Buffered: " << buffered; - } - - size_t first_part = - std::min(to_write, config_.capacity - (write_idx & mask_)); - std::copy(data.begin(), data.begin() + first_part, - buffer_.begin() + (write_idx & mask_)); - - if (first_part < to_write) { - std::copy(data.begin() + first_part, data.begin() + to_write, - buffer_.begin()); - } - - // Producer thread: store `write_index_` with release to make data visible to - // consumer. - write_index_.store(write_idx + to_write, std::memory_order_release); - - return to_write; -} - -size_t JitterBuffer::Read(base::span<uint8_t> destination) { +std::optional<size_t> JitterBuffer::Read(base::span<uint8_t> destination) { DCHECK_CALLED_ON_VALID_SEQUENCE(consumer_sequence_checker_); DCHECK_EQ(destination.size() % config_.frame_size, 0u); // Never use LOG in this method, since this is called on a real-time thread // and logging acquires locks. - // Consumer thread: load `write_index_` with acquire to see the latest - // producer state. - size_t write_idx = write_index_.load(std::memory_order_acquire); - size_t read_idx = read_index_.load(std::memory_order_relaxed); - - if (pending_clear_.load(std::memory_order_acquire)) { - read_index_.store(write_idx, std::memory_order_release); - state_ = State::kBuffering; - starvation_bytes_ = 0; - pending_clear_.store(false, std::memory_order_relaxed); - return 0; + std::optional<size_t> buffered_opt = fifo_buffer_reader_->GetBufferedBytes(); + if (!buffered_opt.has_value()) { + return std::nullopt; } - - size_t buffered = write_idx - read_idx; + size_t buffered = *buffered_opt; if (state_ == State::kBuffering) { if (buffered >= config_.minimum_threshold) { @@ -106,11 +54,13 @@ // threshold. if (config_.max_latency_bytes > 0 && buffered > config_.max_latency_bytes) { size_t skip_bytes = buffered - config_.minimum_threshold; - read_idx += skip_bytes; + skip_bytes -= skip_bytes % config_.frame_size; // Align to frame size + fifo_buffer_reader_->Skip(skip_bytes); buffered -= skip_bytes; } size_t to_read = std::min(destination.size(), buffered); + to_read -= to_read % config_.frame_size; // Align to frame size if (to_read == destination.size()) { starvation_bytes_ = 0; @@ -124,42 +74,27 @@ } if (to_read == 0) { - if (read_idx != read_index_.load(std::memory_order_relaxed)) { - read_index_.store(read_idx, std::memory_order_release); - } return 0; } - size_t first_part = std::min(to_read, config_.capacity - (read_idx & mask_)); - std::copy(buffer_.begin() + (read_idx & mask_), - buffer_.begin() + (read_idx & mask_) + first_part, - destination.begin()); + return fifo_buffer_reader_->Read(destination.first(to_read)); +} - if (first_part < to_read) { - std::copy(buffer_.begin(), buffer_.begin() + to_read - first_part, - destination.begin() + first_part); - } - - // Consumer thread: store `read_index_` with release to signal to producer - // that space is available. - read_index_.store(read_idx + to_read, std::memory_order_release); - - return to_read; +std::optional<size_t> JitterBuffer::Skip(size_t bytes) { + DCHECK_CALLED_ON_VALID_SEQUENCE(consumer_sequence_checker_); + DCHECK_EQ(bytes % config_.frame_size, 0u); + return fifo_buffer_reader_->Skip(bytes); } void JitterBuffer::Clear() { - pending_clear_.store(true, std::memory_order_release); + DCHECK_CALLED_ON_VALID_SEQUENCE(consumer_sequence_checker_); + state_ = State::kBuffering; + starvation_bytes_ = 0; + fifo_buffer_reader_->Clear(); } -size_t JitterBuffer::GetBufferedBytes() const { - if (pending_clear_.load(std::memory_order_relaxed)) { - return 0; - } - // Use acquire to get a consistent snapshot. To avoid underflow, read_index_ - // should be loaded before write_index_. - size_t read_idx = read_index_.load(std::memory_order_acquire); - size_t write_idx = write_index_.load(std::memory_order_acquire); - return std::min(write_idx - read_idx, config_.capacity); +std::optional<size_t> JitterBuffer::GetBufferedBytes() const { + return fifo_buffer_reader_->GetBufferedBytes(); } } // namespace remoting
diff --git a/remoting/base/jitter_buffer.h b/remoting/base/jitter_buffer.h index 742d808d..8ced96a0 100644 --- a/remoting/base/jitter_buffer.h +++ b/remoting/base/jitter_buffer.h
@@ -6,26 +6,26 @@ #define REMOTING_BASE_JITTER_BUFFER_H_ #include <atomic> -#include <vector> +#include <memory> #include "base/containers/span.h" #include "base/sequence_checker.h" #include "base/thread_annotations.h" +#include "remoting/base/fifo_buffer.h" namespace remoting { -// A Single-Producer Single-Consumer (SPSC) lock-free jitter buffer for raw -// bytes. This buffer is designed to be used by a non-time-sensitive producer -// thread and a real-time consumer thread. +// A decorator for FifoBufferReader that adds jitter control (buffering +// thresholds, latency recovery, and starvation tracking) for raw audio bytes. // -// All operations are thread-safe as long as there is only one producer and one -// consumer. -class JitterBuffer { +// Designed for Single-Producer Single-Consumer (SPSC) usage. +// All SPSC methods must be called from a single thread (lazily bound on the +// first call), but the instance can be safely constructed and destructed on a +// different sequence (such as the owner main thread) as long as there is no +// concurrent access. +class JitterBuffer : public FifoBufferReader { public: struct Config { - // Total buffer size in bytes. Must be a power of two. - size_t capacity; - // The size of a single frame (e.g., channels * bytes per sample). // All operations will be aligned to this size. size_t frame_size; @@ -42,30 +42,19 @@ size_t minimum_threshold; }; - explicit JitterBuffer(const Config& config); + JitterBuffer(const Config& config, + std::unique_ptr<FifoBufferReader> fifo_buffer_reader); JitterBuffer(const JitterBuffer&) = delete; JitterBuffer& operator=(const JitterBuffer&) = delete; - ~JitterBuffer(); + ~JitterBuffer() override; - // Appends data to the buffer. Returns the number of bytes written. - // If the buffer is full, it will write as much as possible and return. - // This is called from the producer thread. - size_t Write(base::span<const uint8_t> data); - - // Reads data from the buffer into `destination`. Returns the number of bytes - // read. If the buffer has not reached the minimum threshold since the last - // underrun, it returns 0. - // This is called from the consumer thread. - size_t Read(base::span<uint8_t> destination); - - // Resets the buffer to its initial state. - // Safe to call from any thread. - void Clear(); - - // Returns the number of bytes currently buffered. - size_t GetBufferedBytes() const; + // FifoBufferReader implementation. + std::optional<size_t> Read(base::span<uint8_t> destination) override; + std::optional<size_t> Skip(size_t bytes) override; + void Clear() override; + std::optional<size_t> GetBufferedBytes() const override; private: enum class State { @@ -76,17 +65,7 @@ }; const Config config_; - - // A bitmask used to wrap indices into `buffer_`. Equal to `config_.capacity - - // 1`. - const size_t mask_; - - std::vector<uint8_t> buffer_; - - std::atomic<size_t> read_index_{0}; - std::atomic<size_t> write_index_{0}; - - std::atomic<bool> pending_clear_{false}; + std::unique_ptr<FifoBufferReader> fifo_buffer_reader_; State state_ GUARDED_BY_CONTEXT(consumer_sequence_checker_) = State::kBuffering; @@ -94,7 +73,6 @@ // Track starvation (underruns) while in the Playing state. size_t starvation_bytes_ GUARDED_BY_CONTEXT(consumer_sequence_checker_) = 0; - SEQUENCE_CHECKER(producer_sequence_checker_); SEQUENCE_CHECKER(consumer_sequence_checker_); };
diff --git a/remoting/base/jitter_buffer_unittest.cc b/remoting/base/jitter_buffer_unittest.cc index 994d3ff4..2b504f1 100644 --- a/remoting/base/jitter_buffer_unittest.cc +++ b/remoting/base/jitter_buffer_unittest.cc
@@ -4,9 +4,12 @@ #include "remoting/base/jitter_buffer.h" +#include <memory> +#include <optional> #include <vector> -#include "testing/gtest/include/gtest/gtest.h" +#include "remoting/base/fifo_buffer_test_base.h" +#include "remoting/base/in_memory_fifo_buffer.h" namespace remoting { @@ -15,60 +18,65 @@ constexpr size_t kFrameSize = 4; } // namespace -class JitterBufferTest : public testing::Test { +class JitterBufferTestDelegate { public: - JitterBufferTest() - : buffer_({.capacity = kCapacity, - .frame_size = kFrameSize, - .max_starvation_bytes = 0, - .max_latency_bytes = 0, - .minimum_threshold = 0}) {} + JitterBufferTestDelegate() { + std::unique_ptr<InMemoryFifoBufferReader> reader; + CHECK(CreateInMemoryFifoBuffer(kCapacity, writer_, reader)); + jitter_buffer_ = std::make_unique<JitterBuffer>( + JitterBuffer::Config{.frame_size = kFrameSize, + .max_starvation_bytes = 0, + .max_latency_bytes = 0, + .minimum_threshold = 0}, + std::move(reader)); + } - protected: - JitterBuffer buffer_; + FifoBufferWriter& GetWriter() { return *writer_; } + FifoBufferReader& GetReader() { return *jitter_buffer_; } + + private: + std::unique_ptr<InMemoryFifoBufferWriter> writer_; + std::unique_ptr<JitterBuffer> jitter_buffer_; }; -TEST_F(JitterBufferTest, BasicWriteRead) { - std::vector<uint8_t> data = {1, 2, 3, 4, 5, 6, 7, 8}; - EXPECT_EQ(buffer_.Write(data), 8u); - EXPECT_EQ(buffer_.GetBufferedBytes(), 8u); +using JitterBufferTestTypes = testing::Types<JitterBufferTestDelegate>; +INSTANTIATE_TYPED_TEST_SUITE_P(Jitter, FifoBufferTest, JitterBufferTestTypes); - std::vector<uint8_t> read_data(8); - // Default threshold is 0, so it should play immediately. - EXPECT_EQ(buffer_.Read(read_data), 8u); - EXPECT_EQ(read_data, data); - EXPECT_EQ(buffer_.GetBufferedBytes(), 0u); -} +class JitterBufferTest : public testing::Test { + protected: + JitterBufferTestDelegate delegate_; +}; TEST_F(JitterBufferTest, Thresholding) { - JitterBuffer buffer({.capacity = kCapacity, - .frame_size = kFrameSize, + std::unique_ptr<InMemoryFifoBufferWriter> writer; + std::unique_ptr<InMemoryFifoBufferReader> reader; + ASSERT_TRUE(CreateInMemoryFifoBuffer(kCapacity, writer, reader)); + JitterBuffer buffer({.frame_size = kFrameSize, .max_starvation_bytes = 0, .max_latency_bytes = 0, - .minimum_threshold = 12}); + .minimum_threshold = 12}, + std::move(reader)); std::vector<uint8_t> data = {1, 2, 3, 4, 5, 6, 7, 8}; - EXPECT_EQ(buffer.Write(data), 8u); + EXPECT_EQ(writer->Write(data), WriteResult::kSuccess); std::vector<uint8_t> read_data(8); // Below threshold. EXPECT_EQ(buffer.Read(read_data), 0u); - EXPECT_EQ(buffer.Write(data), 8u); + EXPECT_EQ(writer->Write(data), WriteResult::kSuccess); // Now 16 bytes, above threshold. Reads full 8 bytes. EXPECT_EQ(buffer.Read(read_data), 8u); EXPECT_EQ(read_data, data); // Now 8 bytes left. Still in Playing state. - // Original test expected 0 because it was below threshold, but now we allow - // partial reads or remaining data in Playing state. EXPECT_EQ(buffer.Read(read_data), 8u); EXPECT_EQ(read_data, data); // Empty, should stay in Playing state for a bit (lazy re-buffering). EXPECT_EQ(buffer.Read(read_data), 0u); - EXPECT_EQ(buffer.Write(data), 8u); + EXPECT_EQ(writer->Write(data), WriteResult::kSuccess); // Still in Playing state. Should read immediately even though it's below // the threshold (12). EXPECT_EQ(buffer.Read(read_data), 8u); @@ -76,13 +84,16 @@ } TEST_F(JitterBufferTest, PartialReadInPlayingState) { - JitterBuffer buffer({.capacity = kCapacity, - .frame_size = kFrameSize, + std::unique_ptr<InMemoryFifoBufferWriter> writer; + std::unique_ptr<InMemoryFifoBufferReader> reader; + ASSERT_TRUE(CreateInMemoryFifoBuffer(kCapacity, writer, reader)); + JitterBuffer buffer({.frame_size = kFrameSize, .max_starvation_bytes = 0, .max_latency_bytes = 0, - .minimum_threshold = 8}); + .minimum_threshold = 8}, + std::move(reader)); std::vector<uint8_t> data = {1, 2, 3, 4, 5, 6, 7, 8}; - buffer.Write(data); + EXPECT_EQ(writer->Write(data), WriteResult::kSuccess); std::vector<uint8_t> read_data(12); // Reached threshold. Reads 8 bytes. @@ -92,7 +103,7 @@ // Buffer is empty, but we stay in Playing state for a while (lazy // re-buffering). - buffer.Write({9, 10, 11, 12}); + EXPECT_EQ(writer->Write({9, 10, 11, 12}), WriteResult::kSuccess); // Should read immediately even though it's below threshold (8). EXPECT_EQ(buffer.Read(read_data), 4u); EXPECT_EQ(std::vector<uint8_t>(read_data.begin(), read_data.begin() + 4), @@ -101,97 +112,54 @@ TEST_F(JitterBufferTest, LatencyRecovery) { constexpr size_t kLargeCapacity = 64 * 1024; - JitterBuffer large_buffer({.capacity = kLargeCapacity, - .frame_size = 4, + std::unique_ptr<InMemoryFifoBufferWriter> writer; + std::unique_ptr<InMemoryFifoBufferReader> reader; + ASSERT_TRUE(CreateInMemoryFifoBuffer(kLargeCapacity, writer, reader)); + JitterBuffer large_buffer({.frame_size = 4, .max_starvation_bytes = 0, .max_latency_bytes = 28800, - .minimum_threshold = 4}); + .minimum_threshold = 4}, + std::move(reader)); // Write a lot of data to trigger recovery (> 28800 bytes). std::vector<uint8_t> large_data(30000, 0xFF); - large_buffer.Write(large_data); + EXPECT_EQ(writer->Write(large_data), WriteResult::kSuccess); std::vector<uint8_t> read_data(4); // Read should trigger recovery and skip ahead to threshold (4). EXPECT_EQ(large_buffer.Read(read_data), 4u); - EXPECT_EQ(large_buffer.GetBufferedBytes(), - 0u); // read 4, so 0 left (it skipped to threshold). -} - -TEST_F(JitterBufferTest, WrapAround) { - std::vector<uint8_t> data(kCapacity - 4, 0xAA); - EXPECT_EQ(buffer_.Write(data), kCapacity - 4); - - std::vector<uint8_t> read_data(kCapacity - 4); - EXPECT_EQ(buffer_.Read(read_data), kCapacity - 4); - - // Write again, should wrap. - std::vector<uint8_t> wrap_data = {1, 2, 3, 4, 5, 6, 7, 8}; - EXPECT_EQ(buffer_.Write(wrap_data), 8u); - - std::vector<uint8_t> wrap_read(8); - EXPECT_EQ(buffer_.Read(wrap_read), 8u); - EXPECT_EQ(wrap_read, wrap_data); -} - -TEST_F(JitterBufferTest, FullBuffer) { - std::vector<uint8_t> data(kCapacity, 0xBB); - EXPECT_EQ(buffer_.Write(data), kCapacity); - EXPECT_EQ(buffer_.GetBufferedBytes(), kCapacity); - - // Try to write more. - std::vector<uint8_t> extra_data = {1, 2, 3, 4}; - EXPECT_EQ(buffer_.Write(extra_data), 0u); - - std::vector<uint8_t> read_data(kCapacity); - EXPECT_EQ(buffer_.Read(read_data), kCapacity); - EXPECT_EQ(read_data, data); -} - -TEST_F(JitterBufferTest, Clear) { - std::vector<uint8_t> data = {1, 2, 3, 4}; - buffer_.Write(data); - EXPECT_EQ(buffer_.GetBufferedBytes(), 4u); - - buffer_.Clear(); - EXPECT_EQ(buffer_.GetBufferedBytes(), 0u); - - // Call Read() with an empty span to process the clear. - buffer_.Read({}); - - std::vector<uint8_t> read_data(4); - EXPECT_EQ(buffer_.Read(read_data), 0u); + EXPECT_EQ(large_buffer.GetBufferedBytes(), 0u); // read 4, so 0 left. } TEST_F(JitterBufferTest, ClearAdvancesReadIndex) { std::vector<uint8_t> data1 = {1, 2, 3, 4}; - buffer_.Write(data1); + EXPECT_EQ(delegate_.GetWriter().Write(data1), WriteResult::kSuccess); - // Clear should set the pending flag. - buffer_.Clear(); - EXPECT_EQ(buffer_.GetBufferedBytes(), 0u); - - // Call Read() with an empty span to process the clear. - buffer_.Read({}); + // Clear the buffer immediately. + delegate_.GetReader().Clear(); + EXPECT_EQ(delegate_.GetReader().GetBufferedBytes(), 0u); // Next write should be fine. std::vector<uint8_t> data2 = {5, 6, 7, 8}; - buffer_.Write(data2); - EXPECT_EQ(buffer_.GetBufferedBytes(), 4u); + EXPECT_EQ(delegate_.GetWriter().Write(data2), WriteResult::kSuccess); + EXPECT_EQ(delegate_.GetReader().GetBufferedBytes(), 4u); std::vector<uint8_t> read_data(4); - EXPECT_EQ(buffer_.Read(read_data), 4u); + EXPECT_EQ(delegate_.GetReader().Read(read_data), 4u); EXPECT_EQ(read_data, data2); } TEST_F(JitterBufferTest, LazyRebuffering) { - JitterBuffer buffer({.capacity = kCapacity, - .frame_size = 4, + std::unique_ptr<InMemoryFifoBufferWriter> writer; + std::unique_ptr<InMemoryFifoBufferReader> reader; + ASSERT_TRUE(CreateInMemoryFifoBuffer(kCapacity, writer, reader)); + JitterBuffer buffer({.frame_size = 4, .max_starvation_bytes = 100, .max_latency_bytes = 0, - .minimum_threshold = 100}); + .minimum_threshold = 100}, + std::move(reader)); std::vector<uint8_t> data(100, 0xAA); - buffer.Write(data); + EXPECT_EQ(writer->Write(data), WriteResult::kSuccess); std::vector<uint8_t> read_data(100); // Reached threshold. Reads full 100 bytes. @@ -206,7 +174,7 @@ // If we write a small amount of data now, it should be readable immediately // even though it's below the threshold (100). std::vector<uint8_t> small_data = {1, 2, 3, 4}; - buffer.Write(small_data); + EXPECT_EQ(writer->Write(small_data), WriteResult::kSuccess); std::vector<uint8_t> small_read(4); EXPECT_EQ(buffer.Read(small_read), 4u); EXPECT_EQ(small_read, small_data); @@ -217,7 +185,7 @@ EXPECT_EQ(buffer.Read(silence_req), 0u); // Total 120 bytes silence. // Should now be in Buffering state. - buffer.Write(small_data); + EXPECT_EQ(writer->Write(small_data), WriteResult::kSuccess); EXPECT_EQ(buffer.Read(small_read), 0u); // Waiting for threshold (100) }
diff --git a/remoting/host/client_session.cc b/remoting/host/client_session.cc index 2044f13..3141fd2 100644 --- a/remoting/host/client_session.cc +++ b/remoting/host/client_session.cc
@@ -101,6 +101,30 @@ constexpr base::TimeDelta kDefaultBoostCaptureInterval = base::Milliseconds(5); constexpr base::TimeDelta kDefaultBoostDuration = base::Milliseconds(50); +std::string_view PixelTypeToString( + remoting::protocol::VideoLayout::PixelType pixel_type) { + switch (pixel_type) { + case remoting::protocol::VideoLayout_PixelType_LOGICAL: + return "DIPs"; + case remoting::protocol::VideoLayout_PixelType_PHYSICAL: + return "Physical pixels"; + default: + return "Unknown pixel type"; + } +} + +void LogVideoTrack(int index, + const remoting::protocol::VideoTrackLayout& track) { + HOST_LOG << " track " << index << ": " + << "id=" + << (track.has_screen_id() ? base::NumberToString(track.screen_id()) + : "[none]") + << ", name='" << track.display_name() + << "', pos=" << track.position_x() << "," << track.position_y() + << ", " << track.width() << "x" << track.height() << ", dpi=[" + << track.x_dpi() << "," << track.y_dpi() << "]"; +} + } // namespace namespace remoting { @@ -164,10 +188,14 @@ const protocol::ClientResolution& resolution) { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); DCHECK(resolution.width_pixels() >= 0 && resolution.height_pixels() >= 0); - VLOG(1) << "Received ClientResolution (width=" << resolution.width_pixels() - << ", height=" << resolution.height_pixels() - << ", x_dpi=" << resolution.x_dpi() - << ", y_dpi=" << resolution.y_dpi() << ")"; + HOST_LOG << "Received ClientResolution (width=" << resolution.width_pixels() + << ", height=" << resolution.height_pixels() + << ", x_dpi=" << resolution.x_dpi() + << ", y_dpi=" << resolution.y_dpi() << ", screen_id=" + << (resolution.has_screen_id() + ? base::NumberToString(resolution.screen_id()) + : "[none]") + << ")"; if (!screen_controls_) { return; @@ -476,6 +504,15 @@ } void ClientSession::SetVideoLayout(const protocol::VideoLayout& video_layout) { + HOST_LOG << "Received VideoLayout (" + << PixelTypeToString(video_layout.pixel_type()) << ", primary_id=" + << (video_layout.has_primary_screen_id() + ? base::NumberToString(video_layout.primary_screen_id()) + : "[none]") + << ")"; + for (int i = 0; i < video_layout.video_track_size(); i++) { + LogVideoTrack(i, video_layout.video_track(i)); + } screen_controls_->SetVideoLayout(video_layout); } @@ -1126,7 +1163,7 @@ return; } - LOG(INFO) << "ClientSession::OnDesktopDisplayChanged"; + HOST_LOG << "ClientSession::OnDesktopDisplayChanged"; // Scan display list to calculate the full desktop size. int min_x = 0; @@ -1135,27 +1172,18 @@ int max_y = 0; int dpi_x = 0; int dpi_y = 0; - std::string_view dips_or_physical_pixels; - switch (displays->pixel_type()) { - case protocol::VideoLayout::PixelType::VideoLayout_PixelType_LOGICAL: - dips_or_physical_pixels = "DIPs"; - break; - case protocol::VideoLayout::PixelType::VideoLayout_PixelType_PHYSICAL: - dips_or_physical_pixels = "Physical pixels"; - break; - default: - dips_or_physical_pixels = "Unknown pixel type"; - } - LOG(INFO) << " Scanning display info... (" << dips_or_physical_pixels << ")"; + std::string_view dips_or_physical_pixels = + PixelTypeToString(displays->pixel_type()); + HOST_LOG << "Scanning display info... (" << dips_or_physical_pixels + << ", primary_id=" + << (displays->has_primary_screen_id() + ? base::NumberToString(displays->primary_screen_id()) + : "[none]") + << ")"; for (int display_id = 0; display_id < displays->video_track_size(); display_id++) { - protocol::VideoTrackLayout track = displays->video_track(display_id); - LOG(INFO) << " #" << display_id << " : " << track.position_x() << "," - << track.position_y() << " " << track.width() << "x" - << track.height() << " [" << track.x_dpi() << "," << track.y_dpi() - << "], screen_id=" << track.screen_id() << ", primary=" - << (displays->has_primary_screen_id() && - track.screen_id() == displays->primary_screen_id()); + const protocol::VideoTrackLayout& track = displays->video_track(display_id); + LogVideoTrack(display_id, track); if (dpi_x == 0) { dpi_x = track.x_dpi(); } @@ -1240,9 +1268,9 @@ video_track->set_height(size.height()); video_track->set_x_dpi(dpi_x); video_track->set_y_dpi(dpi_y); - LOG(INFO) << " Full Desktop (" << dips_or_physical_pixels << ") = 0,0 " - << size.width() << "x" << size.height() << " [" << dpi_x << "," - << dpi_y << "]"; + HOST_LOG << "Full Desktop (" << dips_or_physical_pixels << ") = 0,0 " + << size.width() << "x" << size.height() << ", dpi=[" << dpi_x << "," + << dpi_y << "]"; desktop_display_info_.CopyFromVideoLayoutProto(*displays); @@ -1255,13 +1283,7 @@ video_track->set_media_stream_id( protocol::WebrtcVideoStream::StreamNameForId(display.screen_id())); - LOG(INFO) << " Display " << display_id << " = " << display.position_x() - << "," << display.position_y() << " " << display.width() << "x" - << display.height() << " [" << display.x_dpi() << "," - << display.y_dpi() << "], screen_id=" << display.screen_id() - << ", primary=" - << (displays->has_primary_screen_id() && - display.screen_id() == displays->primary_screen_id()); + LogVideoTrack(display_id, display); } // Set the display index, if this is the first message being processed or if
diff --git a/remoting/host/linux/pipewire_audio_injector.cc b/remoting/host/linux/pipewire_audio_injector.cc index c0bfa88..4079bee0 100644 --- a/remoting/host/linux/pipewire_audio_injector.cc +++ b/remoting/host/linux/pipewire_audio_injector.cc
@@ -24,6 +24,7 @@ #include "base/strings/string_number_conversions.h" #include "base/task/bind_post_task.h" #include "base/task/sequenced_task_runner.h" +#include "remoting/base/in_memory_fifo_buffer.h" #include "remoting/base/jitter_buffer.h" #include "remoting/host/linux/pipewire_utils.h" #include "remoting/proto/audio.pb.h" @@ -38,9 +39,10 @@ constexpr uint32_t kAudioChannels = 2; constexpr size_t kBytesPerFrame = kAudioChannels * sizeof(int16_t); +// 256KB is about 1.3s of audio. This must be a power of two. +static constexpr size_t kJitterBufferCapacity = 256 * 1024; + static constexpr JitterBuffer::Config kJitterBufferConfig = { - // 256KB is about 1.3s of audio. This must be a power of two. - .capacity = 256 * 1024, .frame_size = kBytesPerFrame, // 30ms of audio: 48000 * 4 * 0.03 = 5760 bytes. .max_starvation_bytes = 5760, @@ -114,13 +116,22 @@ // `stream_node_id_` is known. Cleared once `stream_node_id_` is known. std::map<uint32_t, uint32_t> pending_links_; - JitterBuffer jitter_buffer_{kJitterBufferConfig}; + std::unique_ptr<InMemoryFifoBufferWriter> audio_writer_; + std::unique_ptr<JitterBuffer> audio_reader_; + // Flag to defer `JitterBuffer::Clear()` from PipeWire's main thread to the + // real-time processing thread (`HandleStreamProcess`) to ensure + // thread-safety. + std::atomic<bool> pending_clear_{false}; std::atomic<bool> has_consumers_{false}; }; PipewireAudioInjector::Core::Core() { CHECK(EnsurePipewireInitialized()) << "PipeWire library is not initialized."; + std::unique_ptr<InMemoryFifoBufferReader> reader; + CHECK(CreateInMemoryFifoBuffer(kJitterBufferCapacity, audio_writer_, reader)); + audio_reader_ = + std::make_unique<JitterBuffer>(kJitterBufferConfig, std::move(reader)); } DISABLE_CFI_DLSYM @@ -241,7 +252,7 @@ LOG(ERROR) << "Dropped misaligned audio data packet."; continue; } - jitter_buffer_.Write(base::as_byte_span(data)); + audio_writer_->Write(base::as_byte_span(data)); } } @@ -280,7 +291,7 @@ pending_links_.clear(); if (was_empty && !active_links_.empty()) { - jitter_buffer_.Clear(); + pending_clear_.store(true, std::memory_order_release); has_consumers_.store(true, std::memory_order_relaxed); on_audio_injector_consumers_changed_cb_.Run(true); } @@ -294,6 +305,10 @@ DISABLE_CFI_DLSYM void PipewireAudioInjector::Core::HandleStreamProcess() { + if (pending_clear_.exchange(false, std::memory_order_acquire)) { + audio_reader_->Clear(); + } + while (struct pw_buffer* b = pw_->pw_stream_dequeue_buffer(pw_stream_.get())) { struct spa_buffer* buf = b->buffer; @@ -319,7 +334,8 @@ auto dst_span = UNSAFE_BUFFERS(base::span<uint8_t>( static_cast<uint8_t*>(buf->datas[0].data), buf->datas[0].maxsize)); - size_t bytes_read = jitter_buffer_.Read(dst_span.first(target_bytes)); + size_t bytes_read = + audio_reader_->Read(dst_span.first(target_bytes)).value_or(0); if (bytes_read < target_bytes) { // Fill the rest of the buffer with silence. @@ -378,7 +394,7 @@ return; } if (active_links_.empty()) { - jitter_buffer_.Clear(); + pending_clear_.store(true, std::memory_order_release); has_consumers_.store(true, std::memory_order_relaxed); on_audio_injector_consumers_changed_cb_.Run(true); } @@ -395,7 +411,7 @@ pending_links_.erase(id); if (active_links_.erase(id) > 0 && active_links_.empty()) { has_consumers_.store(false, std::memory_order_relaxed); - jitter_buffer_.Clear(); + pending_clear_.store(true, std::memory_order_release); on_audio_injector_consumers_changed_cb_.Run(false); } }
diff --git a/remoting/tools/magi-mode/PRESUBMIT.py b/remoting/tools/magi-mode/PRESUBMIT.py index d201fcd..eb01da13 100644 --- a/remoting/tools/magi-mode/PRESUBMIT.py +++ b/remoting/tools/magi-mode/PRESUBMIT.py
@@ -418,6 +418,35 @@ ) ) + # 3. Checklist Validation (Correctness & Schema) + checklist = content.get('checklist') + if checklist is not None: + if not isinstance(checklist, dict): + results.append( + output_api.PresubmitError( + f'File {f.LocalPath()} key "checklist" must be an object.' + ) + ) + else: + is_persona = active_schema is persona_def_schema + for k, v in checklist.items(): + if is_persona: + if not isinstance(v, str): + results.append( + output_api.PresubmitError( + f'File {f.LocalPath()} persona checklist key "{k}" ' + f'must have a string description, got {type(v).__name__}' + ) + ) + else: + if not isinstance(v, bool): + results.append( + output_api.PresubmitError( + f'File {f.LocalPath()} checklist key "{k}" ' + f'must be a boolean, got {type(v).__name__}' + ) + ) + # 4. Decision Graph Validation review_mode = content.get('review_mode') next_p = content.get('next_phase') @@ -483,6 +512,85 @@ ) except ValueError: pass + + # Cross-file validation for checklist union merge + personas = content.get('personas') + state_checklist = content.get('checklist') + if isinstance(personas, list) and isinstance(state_checklist, dict): + repo_root = input_api.change.RepositoryRoot() + union_checklist_keys = set() + for persona_path in personas: + if not isinstance(persona_path, str): + results.append( + output_api.PresubmitError( + f'File {f.LocalPath()} personas list contains a non-string ' + f'element: {persona_path}' + ) + ) + continue + if persona_path.startswith('src/'): + persona_path = persona_path[4:] + elif persona_path.startswith('src\\'): + persona_path = persona_path[4:] + + abs_persona_path = input_api.os_path.normpath( + input_api.os_path.join(repo_root, persona_path) + ) + + persona_content_str = None + is_present = False + for af in input_api.AffectedFiles(include_deletes=False): + if af.AbsoluteLocalPath() == abs_persona_path: + persona_content_str = input_api.ReadFile(af) + is_present = True + break + + if not is_present: + if input_api.os_path.exists(abs_persona_path): + try: + with open(abs_persona_path, 'r', encoding='utf-8') as pf: + persona_content_str = pf.read() + is_present = True + except IOError: + pass + + if not is_present: + results.append( + output_api.PresubmitError( + f'File {f.LocalPath()} references a non-existent persona ' + f'file: {persona_path}' + ) + ) + + if persona_content_str: + try: + persona_json = json.loads(persona_content_str) + if isinstance(persona_json, dict): + persona_checklist = persona_json.get('checklist', {}) + if isinstance(persona_checklist, dict): + union_checklist_keys.update(persona_checklist.keys()) + except ValueError: + pass + + state_checklist_keys = set(state_checklist.keys()) + missing_in_state = union_checklist_keys - state_checklist_keys + extra_in_state = state_checklist_keys - union_checklist_keys + + if missing_in_state: + results.append( + output_api.PresubmitError( + f'File {f.LocalPath()} checklist is missing keys defined in ' + f"selected personas: {', '.join(sorted(missing_in_state))}" + ) + ) + if extra_in_state: + results.append( + output_api.PresubmitError( + f'File {f.LocalPath()} checklist contains arbitrary keys not ' + f'defined in selected personas: ' + f"{', '.join(sorted(extra_in_state))}" + ) + ) elif filename.startswith('project'): if next_p and next_p != 'SCAFFOLDING': results.append( @@ -490,6 +598,31 @@ f'File {f.LocalPath()} must signal next_phase: SCAFFOLDING' ) ) + if 'environment' in content: + environment = content['environment'] + if not isinstance(environment, dict): + results.append( + output_api.PresubmitError( + f'File {f.LocalPath()} key "environment" must be an object.' + ) + ) + else: + vcs = environment.get('vcs') + harness = environment.get('harness') + if vcs not in ('GIT', 'JJ'): + results.append( + output_api.PresubmitError( + f'File {f.LocalPath()} environment.vcs must be GIT or JJ, ' + f'got {vcs}' + ) + ) + if harness not in ('JETSKI', 'GENERIC_CLI'): + results.append( + output_api.PresubmitError( + f'File {f.LocalPath()} environment.harness must be ' + f'JETSKI or GENERIC_CLI, got {harness}' + ) + ) elif filename.startswith('review'): if next_p and next_p != 'ANALYSIS': results.append(
diff --git a/remoting/tools/magi-mode/PRESUBMIT_test.py b/remoting/tools/magi-mode/PRESUBMIT_test.py index b7469e7083..ba71615 100755 --- a/remoting/tools/magi-mode/PRESUBMIT_test.py +++ b/remoting/tools/magi-mode/PRESUBMIT_test.py
@@ -184,14 +184,20 @@ valid_json = ( '{"checklist": {"checked_xyz": true}, "unlisted_issues_found": [], ' '"iteration": 1, "stall_count": 0, "active_constraints": [], ' - '"resolved_constraints": [], "personas": ["Security"], ' + '"resolved_constraints": [],' + '"personas": [' + '"src/remoting/tools/magi-mode/personas/core/security.json"], ' '"review_mode": "SUPERVISOR", "state_transport": "EPHEMERAL", ' '"next_phase": "CRITIQUE"}') self.mock_input.affected_files = [ - MockAffectedFile('remoting/tools/magi-mode/state_block.magi.json') + MockAffectedFile('remoting/tools/magi-mode/state_block.magi.json'), + MockAffectedFile( + 'remoting/tools/magi-mode/personas/core/security.json') ] self.mock_input.files_content = { - 'remoting/tools/magi-mode/state_block.magi.json': valid_json + 'remoting/tools/magi-mode/state_block.magi.json': valid_json, + 'remoting/tools/magi-mode/personas/core/security.json': ( + '{"checklist": {"checked_xyz": "Desc"}}') } # We need to mock the schema file @@ -249,6 +255,100 @@ self.assertTrue(any( "key 'iteration' should be integer" in r for r in results)) + def testJsonStateBlockInvalidChecklistValue(self): + # Non-boolean value in checklist ("checked_xyz": "not_a_boolean") + invalid_checklist_json = ( + '{"checklist": {"checked_xyz": "not_a_boolean"}, ' + '"unlisted_issues_found": [], "iteration": 1, ' + '"stall_count": 0, "active_constraints": [], ' + '"resolved_constraints": [], "personas": ' + '["src/remoting/tools/magi-mode/personas/core/security.json"], ' + '"review_mode": "SUPERVISOR", "state_transport": "EPHEMERAL", ' + '"next_phase": "CRITIQUE"}') + self.mock_input.affected_files = [ + MockAffectedFile('remoting/tools/magi-mode/state_block.magi.json'), + MockAffectedFile( + 'remoting/tools/magi-mode/personas/core/security.json') + ] + self.mock_input.files_content = { + 'remoting/tools/magi-mode/state_block.magi.json': ( + invalid_checklist_json), + 'remoting/tools/magi-mode/personas/core/security.json': ( + '{"checklist": {"checked_xyz": "Desc"}}') + } + schema_json = ( + '{"definitions": {"ChecklistObject": {"type": "object", ' + '"patternProperties": {"^.*$": {"type": "boolean"}}}, ' + '"StateBlock": {"required": ["checklist", "iteration", ' + '"stall_count", "active_constraints", "resolved_constraints", ' + '"personas", "review_mode", "state_transport", "next_phase"], ' + '"properties": {"checklist": ' + '{"$ref": "#/definitions/ChecklistObject"}, ' + '"unlisted_issues_found": {"type": "array"}, "iteration": ' + '{"type": "integer"}, "stall_count": {"type": "integer"}, ' + '"active_constraints": {"type": "array"}, "resolved_constraints": ' + '{"type": "array"}, "personas": {"type": "array"}, ' + '"next_phase": {"type": "string"}, ' + '"review_mode": {"type": "string", "enum": ' + '["SUPERVISOR", "CONSENSUS"]}, ' + '"state_transport": {"type": "string", "enum": ' + '["FILE_IO", "EPHEMERAL", "EPHEMERAL_WITH_LOGS"]}}}}}') + + with patch('builtins.open', + unittest.mock.mock_open(read_data=schema_json)): + results = PRESUBMIT.CheckJsonFiles( + self.mock_input, self.mock_output) + self.assertTrue(any( + 'checklist key "checked_xyz" must be a boolean' in r + for r in results)) + + def testJsonStateBlockArbitraryChecklistKey(self): + # Arbitrary key in checklist ("check_arbitrary": true) not in security.json + arbitrary_key_json = ( + '{"checklist": {"checked_xyz": true, "check_arbitrary": true}, ' + '"unlisted_issues_found": [], "iteration": 1, ' + '"stall_count": 0, "active_constraints": [], ' + '"resolved_constraints": [], "personas": ' + '["src/remoting/tools/magi-mode/personas/core/security.json"], ' + '"review_mode": "SUPERVISOR", "state_transport": "EPHEMERAL", ' + '"next_phase": "CRITIQUE"}') + self.mock_input.affected_files = [ + MockAffectedFile('remoting/tools/magi-mode/state_block.magi.json'), + MockAffectedFile( + 'remoting/tools/magi-mode/personas/core/security.json') + ] + self.mock_input.files_content = { + 'remoting/tools/magi-mode/state_block.magi.json': ( + arbitrary_key_json), + 'remoting/tools/magi-mode/personas/core/security.json': ( + '{"checklist": {"checked_xyz": "Desc"}}') + } + schema_json = ( + '{"definitions": {"ChecklistObject": {"type": "object", ' + '"patternProperties": {"^.*$": {"type": "boolean"}}}, ' + '"StateBlock": {"required": ["checklist", "iteration", ' + '"stall_count", "active_constraints", "resolved_constraints", ' + '"personas", "review_mode", "state_transport", "next_phase"], ' + '"properties": {"checklist": ' + '{"$ref": "#/definitions/ChecklistObject"}, ' + '"unlisted_issues_found": {"type": "array"}, "iteration": ' + '{"type": "integer"}, "stall_count": {"type": "integer"}, ' + '"active_constraints": {"type": "array"}, "resolved_constraints": ' + '{"type": "array"}, "personas": {"type": "array"}, ' + '"next_phase": {"type": "string"}, ' + '"review_mode": {"type": "string", "enum": ' + '["SUPERVISOR", "CONSENSUS"]}, ' + '"state_transport": {"type": "string", "enum": ' + '["FILE_IO", "EPHEMERAL", "EPHEMERAL_WITH_LOGS"]}}}}}') + + with patch('builtins.open', + unittest.mock.mock_open(read_data=schema_json)): + results = PRESUBMIT.CheckJsonFiles( + self.mock_input, self.mock_output) + self.assertTrue(any( + 'checklist contains arbitrary keys not defined in selected ' + 'personas: check_arbitrary' in r for r in results)) + def testJsonProjectSpecValidation(self): # Valid project spec valid_json = ( @@ -477,7 +577,8 @@ state_json = ( '{"checklist": {}, "unlisted_issues_found": [], ' '"iteration": 1, "stall_count": 0, "active_constraints": [], ' - '"resolved_constraints": [], "personas": ["Security"], ' + '"resolved_constraints": [], "personas": ' + '["src/remoting/tools/magi-mode/personas/core/security.json"], ' '"review_mode": "SUPERVISOR", "state_transport": "EPHEMERAL", ' '"next_phase": "CRITIQUE"}') @@ -485,7 +586,9 @@ proj_verbose = '{"auditability_level": "VERBOSE"}' self.mock_input.affected_files = [ - MockAffectedFile('remoting/tools/magi-mode/state_block.magi.json') + MockAffectedFile('remoting/tools/magi-mode/state_block.magi.json'), + MockAffectedFile( + 'remoting/tools/magi-mode/personas/core/security.json') ] def mock_exists(path): @@ -493,7 +596,9 @@ # Test EPHEMERAL with paranoia_mode: true self.mock_input.files_content = { - 'remoting/tools/magi-mode/state_block.magi.json': state_json + 'remoting/tools/magi-mode/state_block.magi.json': state_json, + 'remoting/tools/magi-mode/personas/core/security.json': ( + '{"checklist": {}}') } with patch('os.path.exists', side_effect=mock_exists):
diff --git a/remoting/tools/magi-mode/SKILL.md b/remoting/tools/magi-mode/SKILL.md index 498d6ba..da7988a 100644 --- a/remoting/tools/magi-mode/SKILL.md +++ b/remoting/tools/magi-mode/SKILL.md
@@ -64,6 +64,34 @@ `write_file`). Use generic terms like "read from disk," "save to disk," or "report status." +**ENVIRONMENT GROUNDING MANDATE:** All sub-agents MUST read +`project.magi.json#environment` immediately upon invocation to ground +themselves in the active VCS (`JJ` or `GIT`) and Harness (`JETSKI` or +`GENERIC_CLI`). They MUST adjust their tool usage and command construction +natively to match this environment. + +**THE CHECKLIST LIFECYCLE STATE MACHINE:** The session's verification +integrity is governed by a deterministic boolean checklist state machine. +While the MAGI protocol has 11 overall workflow phases (Phases 0–10), the +automated checklist state itself only transitions or is modified during four +key phases: +* **Phase 0 (Initialization):** Scoping Lead initializes + `project.magi.json#checklist` as an empty object `{}`. +* **Phase 2 (Activation):** The Orchestrator reads all selected + `personas/**/*.json` checklists, takes the **Union Set** of all keys, + and initializes the active `checklist` in `state_block.magi.json` with all + values set to `false`. +* **Phase 5 (Assertion):** Reviewers toggle their domain-specific keys in + their `ReviewFeedback` checklist. The Supervisor/Review Analyst performs a + **Logical AND** consolidation across all reviews. A key in the consolidated + `state_block.magi.json#checklist` only becomes `true` if **ALL** reviewers + evaluating that key asserted `true`. Any `false` keys or + `unlisted_issues_found` are translated into strict constraints in + `constraints.magi.[iteration].json`. +* **Phase 8 (Upgrades):** Once consensus is reached (all checklist items are + `true`), the Trainer uses `unlisted_issues_found` history to append new + keys to the appropriate `personas/**/*.json` checklists. + **PHASE SIGNALING:** The Orchestrator MUST use an appropriate status-reporting mechanism prior to invoking any sub-agents to clearly identify the current phase of the MAGI protocol to the user (e.g., "MAGI Phase 2: Engineering Manager"). @@ -93,18 +121,34 @@ context window. Instead, invoke a "Scoping Lead" sub-agent. - **The Specification:** The Scoping Lead investigates the codebase (`grep_search`, `read_file`) to locate the relevant code and writes a strict - specification to `project.magi.json` conforming to `magi_schema.json`. This - file MUST contain a `next_phase` of `SCAFFOLDING` and the following structure: + specification to `project.magi.json` conforming to `magi_schema.json`. + The `"checklist"` field MUST be initialized as an empty object `{}`. + + **Environment Discovery:** Before writing the file, the Scoping Lead MUST + discover the environment: + * **VCS:** Check for a `.jj/` directory or run `jj status`. If successful, + set `vcs` to `"JJ"`. Otherwise, default to `"GIT"`. + * **Harness:** Check if Jetski tools (e.g., `code_search`, `view_file`) are + available. If yes, set `harness` to `"JETSKI"`. Otherwise, set to + `"GENERIC_CLI"`. + + The `project.magi.json` file MUST contain a `next_phase` of `SCAFFOLDING` and + the following structure: ```json { "$schema": "./magi_schema.json#definitions/ProjectSpec", + "checklist": {}, "goal": "A one-sentence summary of the fix/feature.", "target_files": ["Absolute paths to the files that must be modified."], "anti_goals": ["What should explicitly NOT be changed."], "edge_cases": ["Specific warnings from logs or code context."], "next_phase": "SCAFFOLDING", "paranoia_mode": false, - "auditability_level": "NORMAL" + "auditability_level": "NORMAL", + "environment": { + "vcs": "JJ", + "harness": "JETSKI" + } } ``` @@ -135,6 +179,10 @@ catalog) to assess and select the most appropriate Domain Experts required to implement the stubs. It returns the absolute file paths of their definition files to the Orchestrator. +- **Checklist Initialization:** The Orchestrator MUST read the `checklist` from + every selected persona JSON file, compute the **Union Set** of all checklist + keys, and initialize `state_block.magi.json#checklist` with all these keys + set to `false`. - **Review Mode Selection:** The Engineering Manager MUST select the `review_mode` (`SUPERVISOR` or `CONSENSUS`) and include it in the initial `state_block.magi.json`. @@ -154,6 +202,10 @@ *In-Memory Validation:* If an `EPHEMERAL` mode is active, the Orchestrator MUST strictly validate incoming JSON payloads against `magi_schema.json` in memory before proceeding, as disk-based presubmit checks will be bypassed. + If JSON parsing fails (e.g., malformed JSON), the Orchestrator SHOULD NOT + request a retry from the same sub-agent. Instead, it SHOULD discard that + sub-agent instance, spawn a new one with a fresh context window, and replay + the prompt. - **The Recruiter (Talent Acquisition):** If the Engineering Manager determines that a required expertise is lacking in the current catalog, they MUST invoke a "Recruiter" sub-agent. The Recruiter is responsible for dynamically @@ -189,6 +241,9 @@ ```json { "$schema": "./magi_schema.json#definitions/StateBlock", + "checklist": { + "[Merged keys from selected personas]": false + }, "iteration": 1, "stall_count": 0, "active_constraints": [], @@ -220,18 +275,27 @@ > prevention. Accept unless catastrophic."] > Project Spec: Read the requirements from `project.magi.json`. > Priority: [Priority]. - > Task: Review Draft [filename]. Save a JSON object with `verdict` - > ("ACCEPT" or "REJECT"), `reasoning` (array of bullet points), `comments` - > (array of objects with `file`, optional `line`, and `comment`), and - > `next_phase` ("ANALYSIS") to `review.[persona].magi.[iteration].json`. + > Task: Review Draft [filename]. Auditing against your persona checklist. + > Save a JSON object conforming to + > `magi_schema.json#definitions/ReviewFeedback` with `checklist` + > (updating your evaluated keys to true/false), `verdict` ("ACCEPT" or + > "REJECT"), `reasoning` (array of bullet points), `comments` (array of + > objects with `file`, optional `line`, and `comment`), optional + > `unlisted_issues_found` (array of strings), and `next_phase` + > ("ANALYSIS") to `review.[persona].magi.[iteration].json`. #### Path A: Supervisor Synthesis (Default) If `review_mode == SUPERVISOR`, the Orchestrator (or a specialized Supervisor agent) performs the following in a single turn: 1. **Decision:** Read all `review.*.magi.[iteration].json` files. -2. **State Update:** Update `state_block.magi.json` with the new iteration - and stall count. -3. **Constraint Generation:** Save a strict list of Actionable Constraints and +2. **State Update:** Consolidate the checklists using **Logical AND** (a key + only becomes `true` in `state_block.magi.json#checklist` if all reviewers + evaluating it set it to `true`). Append any `unlisted_issues_found` to the + historical logs. Update `state_block.magi.json` with the new iteration and + stall count. (The stall count MUST only be incremented if the iteration + fails to resolve any checklist items or Actionable Constraints). +3. **Constraint Generation:** Save a strict list of Actionable Constraints + (generated from all `false` checklist keys and `unlisted_issues_found`) and the current `review_mode` to `constraints.magi.[iteration].json`. 4. **Handoff:** Signal `next_phase: SYNTHESIS` (if more work is needed) or `TRAINING`. @@ -239,14 +303,17 @@ #### Path B: Consensus Loop (Verbose/Paranoia) If `review_mode == CONSENSUS`, use the granular relay: 1. **The Review Analyst:** If any agent rejects, this agent reads all - `review.*.magi.[iteration].json` files and saves a strict list of 3-5 - Actionable Constraints, `review_mode: "CONSENSUS"`, and + `review.*.magi.[iteration].json` files, performs the **Logical AND** + consolidation on the checklists, and saves a strict list of 3-5 Actionable + Constraints (derived from `false` checklist keys and + `unlisted_issues_found`), `review_mode: "CONSENSUS"`, and `next_phase: TPM_UPDATE` to `constraints.magi.[iteration].json` on disk. 2. **The Technical Program Manager:** Reads `constraints.magi.[iteration].json` - and updates `state_block.magi.json` conforming to `magi_schema.json`. Checks - for "flip-flopping" (e.g., Constraint 1 violates a constraint from Round 1). - Set `next_phase` to `SYNTHESIS` if more work is needed, otherwise - `TRAINING`. + and updates `state_block.magi.json` conforming to `magi_schema.json`. The + stall count MUST only be incremented if the iteration fails to resolve + any checklist items or Actionable Constraints. Checks for "flip-flopping" + (e.g., Constraint 1 violates a constraint from Round 1). Set `next_phase` + to `SYNTHESIS` if more work is needed, otherwise `TRAINING`. **Deadlock API:** If `Stall Count` exceeds 3, the Technical Program Manager MUST output a valid `state_block.magi.json` with `next_phase: DEADLOCK` and append a structured deadlock report (Core @@ -291,14 +358,22 @@ limit, the Trainer MUST "fork" the persona using a nested directory structure representing `[category]/[domain]/[specialty].json` (e.g., split `core/security.json` into `core/security/memory.json` and -`core/security/network.json`). Do not use flat files with underscores. Migrate +`core/security/network.json`). Do not use flat files with underscores. The +directory depth MUST NOT exceed 5 levels (counting from `/personas`). Migrate the relevant checks and update `PERSONAS.md`. The Trainer SHOULD signal `next_phase: VALIDATION`. **VCS Isolation Rule:** Any modifications to MAGI files (e.g., adding/updating personas by the Recruiter or Trainer) MUST be excluded from the feature/bugfix -CL. Ensure MAGI paths are not staged during upload. Once the main solution is -uploaded, create a completely separate, independent CL for the MAGI upgrades. +CL. The staging and submission workflow branches dynamically based on +`project.magi.json#environment/vcs`: +* **For JJ (Jujutsu):** Work in parallel sibling changes (both rooted at + `main@origin`) from the start: one for the feature/bugfix and one for the + MAGI upgrades. If they accidentally get mixed, the Release Engineer MUST + use `jj split` or `jj squash -i` to cleanly separate the changes before + pushing. +* **For GIT:** Use standard git branching. Stage *only* product source files + for the feature CL. Stage *only* MAGI updates for the secondary CL. ### 9. Validation Run the standard suite (`git cl presubmit`, `gn check`, and unit tests). Upon @@ -311,15 +386,29 @@ updated by the Trainer/Recruiter. The Release Engineer's **exclusive mandate** is: -1. **Workspace Hygiene:** Run `git status` / `jj st`. Detect and revert - accidental submodule bumps. Remove any lingering temporary files generated by - the protocol (e.g., `*.magi`, `*.magi.*`). +1. **Workspace Hygiene:** Read the discovered VCS from + `project.magi.json#environment/vcs`. Run `jj st` (for JJ) or `git status` + (for Git). Detect and revert accidental submodule bumps. Remove any + lingering temporary files generated by the protocol (e.g., `*.magi`, + `*.magi.*`). 2. **Formatting:** Enforce `git cl format` or project-specific formatters. -3. **The Feature CL:** Stage *only* the product source files. Verify the commit - message. Upload the main feature CL. -4. **The MAGI CL:** Create a new branch/bookmark. Stage the `PERSONAS.md` and - `personas/*.json` files updated by the Recruiter or Trainer. Upload the - secondary CL. +3. **The Feature CL:** Upload the main feature CL containing only the product + source changes (using the VCS-specific track defined in the VCS Isolation + Rule). +4. **The MAGI CL:** Create a separate change/bookmark (for JJ) or branch (for + Git). Stage and upload the `PERSONAS.md` and `personas/**/*.json` files + updated by the Recruiter or Trainer as a secondary CL. + +## Harness Optimizations (Jetski Mode) +If `project.magi.json#environment/harness == "JETSKI"`, the Orchestrator: +1. **Direct Prompt Injection:** SHOULD read the `personas/**/*.json` files and + inject their `mandate` and `checklist` directly into the `Prompt` or `Role` + arguments of `invoke_subagent` tool calls. This eliminates a redundant + file-reading turn (`view_file` call) for every sub-agent. +2. **Orchestrator Routing:** MUST act as the active routing environment by + parsing the `next_phase` token from sub-agent output JSONs and manually + calling the next tool (standard Jetski does not have an automatic + background router). ## When to Invoke - When an automated review finds a flaw that is hard to resolve without
diff --git a/remoting/tools/magi-mode/magi_schema.json b/remoting/tools/magi-mode/magi_schema.json index 7c8bfb08..6dd4d3d4 100644 --- a/remoting/tools/magi-mode/magi_schema.json +++ b/remoting/tools/magi-mode/magi_schema.json
@@ -21,6 +21,14 @@ "^.*$": { "type": "boolean" } } }, + "EnvironmentDef": { + "type": "object", + "properties": { + "vcs": { "type": "string", "enum": ["GIT", "JJ"] }, + "harness": { "type": "string", "enum": ["JETSKI", "GENERIC_CLI"] } + }, + "required": ["vcs", "harness"] + }, "StateBlock": { "type": "object", "properties": { @@ -79,7 +87,8 @@ "auditability_level": { "type": "string", "enum": ["NORMAL", "VERBOSE"] - } + }, + "environment": { "$ref": "#/definitions/EnvironmentDef" } }, "required": [ "checklist", @@ -89,7 +98,8 @@ "edge_cases", "next_phase", "paranoia_mode", - "auditability_level" + "auditability_level", + "environment" ] }, "ReviewFeedback": {
diff --git a/services/data_decoder/public/cpp/xml_dom.cc b/services/data_decoder/public/cpp/xml_dom.cc index 155939a..2639e6e 100644 --- a/services/data_decoder/public/cpp/xml_dom.cc +++ b/services/data_decoder/public/cpp/xml_dom.cc
@@ -13,6 +13,7 @@ #include "base/memory/ptr_util.h" #include "base/notreached.h" #include "base/types/pass_key.h" +#include "services/data_decoder/public/mojom/xml_parser.mojom.h" #include "third_party/abseil-cpp/absl/functional/overload.h" namespace data_decoder::xml { @@ -60,7 +61,7 @@ } Node::Element::Element() = default; -Node::Element::Element(std::string local_name) : name{std::move(local_name)} {} +Node::Element::Element(OwnedName name) : name(std::move(name)) {} Node::Element::Element(Element&&) = default; Node::Element& Node::Element::operator=(Element&&) = default; Node::Element::~Element() = default; @@ -82,6 +83,10 @@ return GetName().local_name; } +const std::string& Node::GetNamespacePrefix() const { + return GetName().prefix; +} + const absl::flat_hash_map<OwnedName, std::string>& Node::GetAttributes() const { return std::get<Element>(data_).attributes; } @@ -90,6 +95,11 @@ return base::FindOrNull(GetAttributes(), name); } +const absl::flat_hash_map<std::string, std::string>& Node::GetNamespaces() + const { + return std::get<Element>(data_).namespaces; +} + const std::vector<std::unique_ptr<Node>>& Node::GetChildren() const { return std::get<Element>(data_).children; } @@ -128,8 +138,10 @@ // static std::unique_ptr<Node> Node::CreateElement( base::PassKey<ffi::DomBuilder> pass_key, - std::string local_name) { - return base::WrapUnique(new Node(Element(std::move(local_name)))); + std::string local_name, + std::string prefix) { + return base::WrapUnique( + new Node(Element(OwnedName{std::move(local_name), std::move(prefix)}))); } // static @@ -148,11 +160,19 @@ void Node::SetAttribute(base::PassKey<ffi::DomBuilder>, std::string local_name, + std::string prefix, std::string value) { - std::get<Element>(data_).attributes[OwnedName{std::move(local_name)}] = + std::get<Element>(data_) + .attributes[OwnedName{std::move(local_name), std::move(prefix)}] = std::move(value); } +void Node::SetNamespace(base::PassKey<ffi::DomBuilder>, + std::string prefix, + std::string uri) { + std::get<Element>(data_).namespaces[std::move(prefix)] = std::move(uri); +} + void Node::AddChild(base::PassKey<ffi::DomBuilder>, std::unique_ptr<Node> child) { DCHECK(child);
diff --git a/services/data_decoder/public/cpp/xml_dom.h b/services/data_decoder/public/cpp/xml_dom.h index e78a09aa..85c2c77 100644 --- a/services/data_decoder/public/cpp/xml_dom.h +++ b/services/data_decoder/public/cpp/xml_dom.h
@@ -8,7 +8,6 @@ #include <stdint.h> #include <memory> -#include <optional> #include <string> #include <string_view> #include <utility> @@ -17,6 +16,7 @@ #include "base/containers/span.h" #include "base/memory/raw_ptr.h" +#include "base/types/expected.h" #include "base/types/pass_key.h" #include "third_party/abseil-cpp/absl/container/flat_hash_map.h" @@ -30,30 +30,30 @@ // A qualified name in XML, as defined in https://www.w3.org/TR/xml-names/. // Used for both element and attribute names. -// -// TODO(dcheng): Implement prefix support. struct Name { std::string_view local_name; + std::string_view prefix; }; // Equivalent to `Name` but owns its fields. struct OwnedName { std::string local_name; + std::string prefix; bool operator==(const OwnedName& other) const = default; bool operator==(const Name& other) const { - return local_name == other.local_name; + return local_name == other.local_name && prefix == other.prefix; } struct absl_container_hash { using is_transparent = void; size_t operator()(const OwnedName& name) const { - return absl::HashOf(name.local_name); + return absl::HashOf(name.local_name, name.prefix); } size_t operator()(const Name& name) const { - return absl::HashOf(name.local_name); + return absl::HashOf(name.local_name, name.prefix); } }; }; @@ -68,8 +68,9 @@ Document(Document&&); Document& operator=(Document&&); - static std::optional<Document> FromBytes(base::span<const uint8_t> bytes); - static std::optional<Document> FromUtf8(std::string_view str); + static base::expected<Document, std::string> FromBytes( + base::span<const uint8_t> bytes); + static base::expected<Document, std::string> FromUtf8(std::string_view str); const Node* GetRoot() const; @@ -102,12 +103,19 @@ // non-element nodes. const OwnedName& GetName() const; const std::string& GetLocalName() const; + // The namespace prefix of the element name, or the empty string if the + // element name is unprefixed. Shorthand for `GetName().prefix`. + // <html:br /> -> returns "html" + // <element /> -> returns "" + const std::string& GetNamespacePrefix() const; const absl::flat_hash_map<OwnedName, std::string>& GetAttributes() const; // The value of the attribute with the given `name`, or `nullptr` if the // element does not specify an attribute with `name`. const std::string* GetAttribute(Name name) const; + const absl::flat_hash_map<std::string, std::string>& GetNamespaces() const; + const std::vector<std::unique_ptr<Node>>& GetChildren() const; std::vector<const Node*> GetChildrenByTagName(Name name) const; const Node* FindFirstChildByTagName(Name name) const; @@ -118,14 +126,19 @@ // Rust FFI helpers: static std::unique_ptr<Node> CreateElement(base::PassKey<ffi::DomBuilder>, - std::string local_name); + std::string local_name, + std::string prefix); static std::unique_ptr<Node> CreateTextNode(base::PassKey<ffi::DomBuilder>, std::string text); static std::unique_ptr<Node> CreateCdataNode(base::PassKey<ffi::DomBuilder>, std::string text); void SetAttribute(base::PassKey<ffi::DomBuilder>, std::string local_name, + std::string prefix, std::string value); + void SetNamespace(base::PassKey<ffi::DomBuilder>, + std::string prefix, + std::string uri); void AddChild(base::PassKey<ffi::DomBuilder>, std::unique_ptr<Node> child); private: @@ -134,13 +147,14 @@ struct Element { Element(); - explicit Element(std::string local_name); + explicit Element(OwnedName name); Element(Element&&); Element& operator=(Element&&); ~Element(); OwnedName name; absl::flat_hash_map<OwnedName, std::string> attributes; + absl::flat_hash_map<std::string, std::string> namespaces; std::vector<std::unique_ptr<Node>> children; }; struct Text {
diff --git a/services/data_decoder/public/cpp/xml_dom_parser.cc b/services/data_decoder/public/cpp/xml_dom_parser.cc index 8dd1117a..92a917e 100644 --- a/services/data_decoder/public/cpp/xml_dom_parser.cc +++ b/services/data_decoder/public/cpp/xml_dom_parser.cc
@@ -2,7 +2,6 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include <optional> #include <utility> #include "base/containers/span_rust.h" @@ -12,14 +11,23 @@ namespace data_decoder::xml { -std::optional<Document> Document::FromBytes(base::span<const uint8_t> bytes) { - auto root = ffi::decode_xml_bytes(base::SpanToRustSlice(bytes)); - return root ? std::optional(Document(std::move(root))) : std::nullopt; +base::expected<Document, std::string> Document::FromBytes( + base::span<const uint8_t> bytes) { + std::string err; + auto root = ffi::decode_xml_bytes(base::SpanToRustSlice(bytes), err); + if (root) { + return Document(std::move(root)); + } + return base::unexpected(err); } -std::optional<Document> Document::FromUtf8(std::string_view str) { - auto root = ffi::decode_xml_str(base::StringViewToRustStrUTF8(str)); - return root ? std::optional(Document(std::move(root))) : std::nullopt; +base::expected<Document, std::string> Document::FromUtf8(std::string_view str) { + std::string err; + auto root = ffi::decode_xml_str(base::StringViewToRustStrUTF8(str), err); + if (root) { + return Document(std::move(root)); + } + return base::unexpected(err); } } // namespace data_decoder::xml
diff --git a/services/data_decoder/xml/cxx.rs b/services/data_decoder/xml/cxx.rs index 7f938e24..7c2ec65c 100644 --- a/services/data_decoder/xml/cxx.rs +++ b/services/data_decoder/xml/cxx.rs
@@ -11,18 +11,19 @@ #[namespace = "data_decoder::xml"] type Node; - fn create_element(local_name: &str) -> UniquePtr<Node>; + fn create_element(local_name: &str, prefix: &str) -> UniquePtr<Node>; fn create_text_node(text_content: &str) -> UniquePtr<Node>; fn create_cdata_node(text_content: &str) -> UniquePtr<Node>; - fn set_attribute(element: Pin<&mut Node>, local_name: &str, value: &str); + fn set_attribute(element: Pin<&mut Node>, local_name: &str, prefix: &str, value: &str); + fn set_namespace(element: Pin<&mut Node>, prefix: &str, uri: &str); fn add_child(parent: Pin<&mut Node>, child: UniquePtr<Node>); } extern "Rust" { // TODO(dcheng): decide if we really need to handle both bytes and // strings and/or where the conversion should happen. - fn decode_xml_bytes(xml: &[u8]) -> UniquePtr<Node>; - fn decode_xml_str(xml: &str) -> UniquePtr<Node>; + fn decode_xml_bytes(xml: &[u8], err: Pin<&mut CxxString>) -> UniquePtr<Node>; + fn decode_xml_str(xml: &str, mut err: Pin<&mut CxxString>) -> UniquePtr<Node>; } }
diff --git a/services/data_decoder/xml/dom_builder.cc b/services/data_decoder/xml/dom_builder.cc index 64b4f416..ad05eab 100644 --- a/services/data_decoder/xml/dom_builder.cc +++ b/services/data_decoder/xml/dom_builder.cc
@@ -17,9 +17,9 @@ } }; -std::unique_ptr<Node> create_element(rust::Str local_name) { +std::unique_ptr<Node> create_element(rust::Str local_name, rust::Str prefix) { return Node::CreateElement(DomBuilder::CreatePassKey(), - std::string(local_name)); + std::string(local_name), std::string(prefix)); } std::unique_ptr<Node> create_text_node(rust::Str text_content) { @@ -32,9 +32,17 @@ std::string(text_content)); } -void set_attribute(Node& element, rust::Str local_name, rust::Str value) { +void set_attribute(Node& element, + rust::Str local_name, + rust::Str prefix, + rust::Str value) { element.SetAttribute(DomBuilder::CreatePassKey(), std::string(local_name), - std::string(value)); + std::string(prefix), std::string(value)); +} + +void set_namespace(Node& element, rust::Str prefix, rust::Str uri) { + element.SetNamespace(DomBuilder::CreatePassKey(), std::string(prefix), + std::string(uri)); } void add_child(Node& parent, std::unique_ptr<Node> child) {
diff --git a/services/data_decoder/xml/dom_builder.h b/services/data_decoder/xml/dom_builder.h index ca79dab..6271857 100644 --- a/services/data_decoder/xml/dom_builder.h +++ b/services/data_decoder/xml/dom_builder.h
@@ -13,10 +13,14 @@ namespace ffi { -std::unique_ptr<Node> create_element(rust::Str local_name); +std::unique_ptr<Node> create_element(rust::Str local_name, rust::Str prefix); std::unique_ptr<Node> create_text_node(rust::Str text_content); std::unique_ptr<Node> create_cdata_node(rust::Str text_content); -void set_attribute(Node& element, rust::Str local_name, rust::Str value); +void set_attribute(Node& element, + rust::Str local_name, + rust::Str prefix, + rust::Str value); +void set_namespace(Node& element, rust::Str prefix, rust::Str uri); void add_child(Node& parent, std::unique_ptr<Node> child); } // namespace ffi
diff --git a/services/data_decoder/xml/parser.rs b/services/data_decoder/xml/parser.rs index f0edb0a..d848048f 100644 --- a/services/data_decoder/xml/parser.rs +++ b/services/data_decoder/xml/parser.rs
@@ -5,54 +5,94 @@ #![forbid(unsafe_code)] use crate::cxx::ffi::Node; -use cxx::UniquePtr; +use cxx::{CxxString, UniquePtr}; +use std::fmt::Write; +use std::pin::Pin; use xml::attribute::OwnedAttribute; +use xml::namespace::Namespace; use xml::reader::{EventReader, XmlEvent}; const MAX_DEPTH: usize = 200; -fn populate_node(node: &mut UniquePtr<Node>, attributes: Vec<OwnedAttribute>) { +fn populate_node( + node: &mut UniquePtr<Node>, + attributes: Vec<OwnedAttribute>, + namespace: &Namespace, + parent_namespace: Option<&Namespace>, +) { for attr in attributes { - crate::cxx::ffi::set_attribute(node.as_mut().unwrap(), &attr.name.local_name, &attr.value); + let prefix = attr.name.prefix.as_deref().unwrap_or(""); + crate::cxx::ffi::set_attribute( + node.as_mut().unwrap(), + &attr.name.local_name, + prefix, + &attr.value, + ); + } + for (prefix, uri) in namespace { + // Per https://www.w3.org/TR/xml-names/#ns-decl, "xml" is reserved and may be declared, but + // must be bound to ttp://www.w3.org/XML/1998/namespace. xml-rs already checks + // that, so no need to validate that here. + // "xmlns" is also reserved but must not be declared; this is also enforced by + // xml-rs. + if prefix == "xml" || prefix == "xmlns" { + continue; + } + // For compatibility with the original C++ parser, which omits this on the root + // element if not otherwise specified. + if prefix.is_empty() && uri.is_empty() && parent_namespace.is_none() { + continue; + } + // `namespace` includes declarations from the parent (and transitively, ancestor + // nodes); avoid duplicating inherited declarations into all descendant + // nodes. + if parent_namespace.and_then(|ns| ns.get(prefix)) == Some(uri) { + continue; + } + crate::cxx::ffi::set_namespace(node.as_mut().unwrap(), prefix, uri); } } -pub fn decode_xml_bytes(xml: &[u8]) -> UniquePtr<Node> { +pub fn decode_xml_bytes(xml: &[u8], err: Pin<&mut CxxString>) -> UniquePtr<Node> { let Ok(xml) = str::from_utf8(xml) else { return UniquePtr::null(); }; - decode_xml_str(xml) + decode_xml_str(xml, err) } -pub fn decode_xml_str(xml: &str) -> UniquePtr<Node> { +pub fn decode_xml_str(xml: &str, mut err: Pin<&mut CxxString>) -> UniquePtr<Node> { let parser = EventReader::from_str(xml); - let mut stack: Vec<UniquePtr<Node>> = Vec::new(); + let mut stack: Vec<(UniquePtr<Node>, Namespace)> = Vec::new(); let mut root: UniquePtr<Node> = UniquePtr::null(); for e in parser { match e { - Ok(XmlEvent::StartElement { name, attributes, .. }) => { + Ok(XmlEvent::StartElement { name, attributes, namespace }) => { if !root.is_null() || stack.len() >= MAX_DEPTH { return UniquePtr::null(); } - let mut node = crate::cxx::ffi::create_element(&name.local_name); - populate_node(&mut node, attributes); - stack.push(node); + let prefix = name.prefix.as_deref().unwrap_or(""); + let mut node = crate::cxx::ffi::create_element(&name.local_name, prefix); + + let parent_namespace = stack.last().map(|(_, ns)| ns); + populate_node(&mut node, attributes, &namespace, parent_namespace); + + stack.push((node, namespace)); } Ok(XmlEvent::EndElement { .. }) => { - let child = match stack.pop() { + let (child, _) = match stack.pop() { Some(x) => x, None => return UniquePtr::null(), }; - if let Some(parent) = stack.last_mut() { + if let Some((parent, _)) = stack.last_mut() { crate::cxx::ffi::add_child(parent.as_mut().unwrap(), child); } else { root = child; } } Ok(XmlEvent::Characters(data)) => { - if let Some(parent) = stack.last_mut() { + if let Some((parent, _)) = stack.last_mut() { let text_node = crate::cxx::ffi::create_text_node(&data); crate::cxx::ffi::add_child(parent.as_mut().unwrap(), text_node); } else if !data.trim().is_empty() { @@ -60,7 +100,7 @@ } } Ok(XmlEvent::CData(data)) => { - if let Some(parent) = stack.last_mut() { + if let Some((parent, _)) = stack.last_mut() { let cdata_node = crate::cxx::ffi::create_cdata_node(&data); crate::cxx::ffi::add_child(parent.as_mut().unwrap(), cdata_node); } else { @@ -73,7 +113,10 @@ | Ok(XmlEvent::StartDocument { .. }) | Ok(XmlEvent::EndDocument) | Ok(XmlEvent::Doctype { .. }) => {} - Err(_) => return UniquePtr::null(), + Err(e) => { + write!(&mut err, "{}", e).unwrap(); + return UniquePtr::null(); + } } }
diff --git a/services/data_decoder/xml/xml_parser_rs_unittest.cc b/services/data_decoder/xml/xml_parser_rs_unittest.cc index 63f984a..51d5459 100644 --- a/services/data_decoder/xml/xml_parser_rs_unittest.cc +++ b/services/data_decoder/xml/xml_parser_rs_unittest.cc
@@ -3,15 +3,18 @@ // found in the LICENSE file. #include <memory> -#include <optional> #include <string> #include <string_view> #include <vector> +#include "base/test/gmock_expected_support.h" +#include "base/types/expected.h" #include "services/data_decoder/public/cpp/xml_dom.h" #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" +using ::testing::Eq; +using ::testing::IsEmpty; using ::testing::Pair; using ::testing::UnorderedElementsAre; @@ -21,24 +24,24 @@ class XmlParserRsTest : public ::testing::Test { protected: - std::optional<Document> ParseXml(std::string_view s) { + base::expected<Document, std::string> ParseXml(std::string_view s) { return Document::FromUtf8(s); } }; TEST_F(XmlParserRsTest, Basic) { auto doc = ParseXml("<root><child attr=\"value\">text</child></root>"); - ASSERT_TRUE(doc.has_value()); + ASSERT_OK(doc); const Node* root = doc->GetRoot(); ASSERT_EQ(root->GetType(), Node::Type::kElement); - EXPECT_EQ(root->GetLocalName(), "root"); + EXPECT_EQ(root->GetName(), Name{"root"}); const auto& children = root->GetChildren(); ASSERT_EQ(children.size(), 1u); const Node* child = children[0].get(); ASSERT_EQ(child->GetType(), Node::Type::kElement); - EXPECT_EQ(child->GetLocalName(), "child"); + EXPECT_EQ(child->GetName(), Name{"child"}); const auto* attr = child->GetAttribute(Name{"attr"}); ASSERT_NE(attr, nullptr); EXPECT_EQ(*attr, "value"); @@ -52,7 +55,7 @@ TEST_F(XmlParserRsTest, MultipleAttributes) { auto doc = ParseXml("<root a=\"1\" b=\"\" c=\"3\"></root>"); - ASSERT_TRUE(doc.has_value()); + ASSERT_OK(doc); const Node* root = doc->GetRoot(); EXPECT_THAT(root->GetAttributes(), @@ -68,26 +71,109 @@ TEST_F(XmlParserRsTest, MixedSiblingTypes) { auto doc = ParseXml("<root><![CDATA[cdata]]><child/>text</root>"); - ASSERT_TRUE(doc.has_value()); + ASSERT_OK(doc); const Node* root = doc->GetRoot(); ASSERT_EQ(root->GetType(), Node::Type::kElement); - EXPECT_EQ(root->GetLocalName(), "root"); + EXPECT_EQ(root->GetName(), Name{"root"}); ASSERT_EQ(root->GetChildren().size(), 3u); ASSERT_EQ(root->GetChildren()[0]->GetType(), Node::Type::kCdata); EXPECT_EQ(root->GetChildren()[0]->GetTextContent(), "cdata"); EXPECT_EQ(root->GetChildren()[1]->GetType(), Node::Type::kElement); - EXPECT_EQ(root->GetChildren()[1]->GetLocalName(), "child"); + EXPECT_EQ(root->GetChildren()[1]->GetName(), Name{"child"}); ASSERT_EQ(root->GetChildren()[2]->GetType(), Node::Type::kText); EXPECT_EQ(root->GetChildren()[2]->GetTextContent(), "text"); } +TEST_F(XmlParserRsTest, OmitUnspecifiedDefaultNamespaceOnRoot) { + // This is a test for backwards compatibility with the original + // libxml2-based parser. + auto doc = ParseXml("<root />"); + ASSERT_OK(doc); + + EXPECT_THAT(doc->GetRoot()->GetNamespaces(), IsEmpty()); +} + +TEST_F(XmlParserRsTest, BasicNamespaces) { + auto doc = ParseXml( + "<foo:root " + " xmlns='https://example.com/default'" + " xmlns:foo='https://example.com/foo'" + " xmlns:bar='https://example.com/bar'" + " bar:attr='fizz' >" + " <bar:child foo:attr='buzz' />" + "</foo:root>"); + ASSERT_OK(doc); + + const Node* root = doc->GetRoot(); + EXPECT_THAT(root->GetName(), Eq(Name{.local_name = "root", .prefix = "foo"})); + EXPECT_THAT(root->GetAttributes(), + UnorderedElementsAre( + Pair(Name{.local_name = "attr", .prefix = "bar"}, "fizz"))); + EXPECT_THAT(root->GetNamespaces(), + UnorderedElementsAre(Pair("", "https://example.com/default"), + Pair("foo", "https://example.com/foo"), + Pair("bar", "https://example.com/bar"))); + + ASSERT_EQ(root->GetChildren().size(), 1u); + EXPECT_THAT(root->GetChildren()[0]->GetName(), + Eq(Name{.local_name = "child", .prefix = "bar"})); + EXPECT_THAT(root->GetChildren()[0]->GetNamespaces(), IsEmpty()); + EXPECT_THAT(root->GetChildren()[0]->GetAttributes(), + UnorderedElementsAre( + Pair(Name{.local_name = "attr", .prefix = "foo"}, "buzz"))); +} + +TEST_F(XmlParserRsTest, OverrideNamespaceInChild) { + auto doc = ParseXml( + "<root xmlns:a='https://example.com/1'>" + " <child xmlns:a='https://example.com/2'>" + " <nested-child xmlns:a='https://example.com/2' />" + " </child>" + "</root>"); + ASSERT_OK(doc); + + const Node* root = doc->GetRoot(); + EXPECT_THAT(root->GetNamespaces(), + UnorderedElementsAre(Pair("a", "https://example.com/1"))); + + const auto& children = root->GetChildren(); + ASSERT_EQ(children.size(), 1u); + EXPECT_THAT(children[0]->GetNamespaces(), + UnorderedElementsAre(Pair("a", "https://example.com/2"))); + + // Even though `nested-child` explicitly declares the namespace, the + // declaration is identical to the one inherited from its parent, so it will + // be omitted. + const auto& grandchildren = children[0]->GetChildren(); + ASSERT_EQ(grandchildren.size(), 1u); + EXPECT_THAT(grandchildren[0]->GetNamespaces(), IsEmpty()); +} + +TEST_F(XmlParserRsTest, UndeclareDefaultNamespaceTest) { + auto doc = ParseXml( + "<root xmlns='https://example.com'><child xmlns=''></child></root>"); + ASSERT_OK(doc); + + const Node* root = doc->GetRoot(); + EXPECT_THAT(root->GetNamespaces(), + UnorderedElementsAre(Pair("", "https://example.com"))); + + // While an unspecified default namespace on the root is omitted (see + // `OmitUnspecifiedDefaultNamespaceOnRoot` above), it should be included on a + // child element if it the net effect is to clear the default namespace + // binding. + ASSERT_EQ(root->GetChildren().size(), 1u); + EXPECT_THAT(root->GetChildren()[0]->GetNamespaces(), + UnorderedElementsAre(Pair("", ""))); +} + TEST_F(XmlParserRsTest, CdataNode) { auto doc = ParseXml("<root><![CDATA[some unescaped & chars < >]]></root>"); - ASSERT_TRUE(doc.has_value()); + ASSERT_OK(doc); const Node* root = doc->GetRoot(); ASSERT_EQ(root->GetChildren().size(), 1u); @@ -99,21 +185,21 @@ TEST_F(XmlParserRsTest, NestedSiblings) { auto doc = ParseXml("<root><a><b></b></a><c><d><e></e></d></c></root>"); - ASSERT_TRUE(doc.has_value()); + ASSERT_OK(doc); const Node* root = doc->GetRoot(); ASSERT_EQ(root->GetType(), Node::Type::kElement); - EXPECT_EQ(root->GetLocalName(), "root"); + EXPECT_EQ(root->GetName(), Name{"root"}); ASSERT_EQ(root->GetChildren().size(), 2u); } TEST_F(XmlParserRsTest, CharacterEntities) { auto doc = ParseXml("<root><tag> & "quoted"</root>"); - ASSERT_TRUE(doc.has_value()); + ASSERT_OK(doc); const Node* root = doc->GetRoot(); ASSERT_EQ(root->GetType(), Node::Type::kElement); - EXPECT_EQ(root->GetLocalName(), "root"); + EXPECT_EQ(root->GetName(), Name{"root"}); ASSERT_EQ(root->GetChildren().size(), 1u); EXPECT_EQ(root->GetChildren()[0]->GetType(), Node::Type::kText); @@ -186,7 +272,7 @@ const Node* root = doc->GetRoot(); ASSERT_EQ(root->GetType(), Node::Type::kElement); - EXPECT_EQ(root->GetLocalName(), "root"); + EXPECT_EQ(root->GetName(), Name{"root"}); } TEST_F(XmlParserRsTest, MultipleRootElements) { @@ -233,7 +319,7 @@ TEST_F(XmlParserRsTest, DoctypeIgnored) { auto doc = ParseXml("<!DOCTYPE html><html><body /></html>"); - ASSERT_TRUE(doc.has_value()); + ASSERT_OK(doc); const Node* root = doc->GetRoot(); ASSERT_EQ(root->GetType(), Node::Type::kElement); @@ -249,28 +335,28 @@ // A top-level processing instruction should be ignored and the actual root // element should still be correctly set. auto doc = ParseXml("<?pi content?><root><child/></root>"); - ASSERT_TRUE(doc.has_value()); + ASSERT_OK(doc); const Node* root = doc->GetRoot(); ASSERT_EQ(root->GetType(), Node::Type::kElement); - EXPECT_EQ(root->GetLocalName(), "root"); + EXPECT_EQ(root->GetName(), Name{"root"}); ASSERT_EQ(root->GetChildren().size(), 1u); ASSERT_EQ(root->GetChildren()[0]->GetType(), Node::Type::kElement); - EXPECT_EQ(root->GetChildren()[0]->GetLocalName(), "child"); + EXPECT_EQ(root->GetChildren()[0]->GetName(), Name{"child"}); } { auto doc = ParseXml("<root><?pi content?><child/></root>"); - ASSERT_TRUE(doc.has_value()); + ASSERT_OK(doc); const Node* root = doc->GetRoot(); ASSERT_EQ(root->GetType(), Node::Type::kElement); - EXPECT_EQ(root->GetLocalName(), "root"); + EXPECT_EQ(root->GetName(), Name{"root"}); ASSERT_EQ(root->GetChildren().size(), 1u); ASSERT_EQ(root->GetChildren()[0]->GetType(), Node::Type::kElement); - EXPECT_EQ(root->GetChildren()[0]->GetLocalName(), "child"); + EXPECT_EQ(root->GetChildren()[0]->GetName(), Name{"child"}); } } @@ -279,28 +365,28 @@ // A top-level comment should be ignored and the actual root element should // still be correctly set. auto doc = ParseXml("<!-- comment --><root><child/></root>"); - ASSERT_TRUE(doc.has_value()); + ASSERT_OK(doc); const Node* root = doc->GetRoot(); ASSERT_EQ(root->GetType(), Node::Type::kElement); - EXPECT_EQ(root->GetLocalName(), "root"); + EXPECT_EQ(root->GetName(), Name{"root"}); ASSERT_EQ(root->GetChildren().size(), 1u); ASSERT_EQ(root->GetChildren()[0]->GetType(), Node::Type::kElement); - EXPECT_EQ(root->GetChildren()[0]->GetLocalName(), "child"); + EXPECT_EQ(root->GetChildren()[0]->GetName(), Name{"child"}); } { auto doc = ParseXml("<root><!-- comment --><child/></root>"); - ASSERT_TRUE(doc.has_value()); + ASSERT_OK(doc); const Node* root = doc->GetRoot(); ASSERT_EQ(root->GetType(), Node::Type::kElement); - EXPECT_EQ(root->GetLocalName(), "root"); + EXPECT_EQ(root->GetName(), Name{"root"}); ASSERT_EQ(root->GetChildren().size(), 1u); ASSERT_EQ(root->GetChildren()[0]->GetType(), Node::Type::kElement); - EXPECT_EQ(root->GetChildren()[0]->GetLocalName(), "child"); + EXPECT_EQ(root->GetChildren()[0]->GetName(), Name{"child"}); } }
diff --git a/services/viz/public/cpp/compositing/mojom_traits_unittest.cc b/services/viz/public/cpp/compositing/mojom_traits_unittest.cc index 6131559..b02c16d 100644 --- a/services/viz/public/cpp/compositing/mojom_traits_unittest.cc +++ b/services/viz/public/cpp/compositing/mojom_traits_unittest.cc
@@ -1181,7 +1181,6 @@ const gfx::Size mask_texture_size(1234, 5678); gfx::Vector2dF filters_scale(1234.1f, 4321.2f); gfx::PointF filters_origin(8765.4f, 4567.8f); - gfx::RectF tex_coord_rect(1.f, 1.f, 1234.f, 5678.f); const float backdrop_filter_quality = 1.0f; const bool intersects_damage_under = false; @@ -1189,7 +1188,7 @@ render_pass->CreateAndAppendDrawQuad<CompositorRenderPassDrawQuad>(); render_pass_quad->SetAll(sqs, rect4, rect4, needs_blending, render_pass_id, resource_id4, mask_uv_rect, mask_texture_size, - filters_scale, filters_origin, tex_coord_rect, + filters_scale, filters_origin, force_anti_aliasing_off, backdrop_filter_quality, intersects_damage_under); @@ -1281,7 +1280,6 @@ EXPECT_EQ(mask_texture_size, out_render_pass_draw_quad->mask_texture_size); EXPECT_EQ(filters_scale, out_render_pass_draw_quad->filters_scale); EXPECT_EQ(filters_origin, out_render_pass_draw_quad->filters_origin); - EXPECT_EQ(tex_coord_rect, out_render_pass_draw_quad->tex_coord_rect); EXPECT_EQ(force_anti_aliasing_off, out_render_pass_draw_quad->force_anti_aliasing_off); EXPECT_EQ(backdrop_filter_quality,
diff --git a/services/viz/public/cpp/compositing/quads_mojom_traits.cc b/services/viz/public/cpp/compositing/quads_mojom_traits.cc index 702753b9..5b38e2d 100644 --- a/services/viz/public/cpp/compositing/quads_mojom_traits.cc +++ b/services/viz/public/cpp/compositing/quads_mojom_traits.cc
@@ -98,7 +98,6 @@ !data.ReadMaskTextureSize(&quad->mask_texture_size) || !data.ReadFiltersScale(&quad->filters_scale) || !data.ReadFiltersOrigin(&quad->filters_origin) || - !data.ReadTexCoordRect(&quad->tex_coord_rect) || !data.ReadRenderPassId(&quad->render_pass_id) || !data.ReadMaskResourceId(&quad->resource_id)) { return false;
diff --git a/services/viz/public/cpp/compositing/quads_mojom_traits.h b/services/viz/public/cpp/compositing/quads_mojom_traits.h index f96538a..dfeb6e6 100644 --- a/services/viz/public/cpp/compositing/quads_mojom_traits.h +++ b/services/viz/public/cpp/compositing/quads_mojom_traits.h
@@ -298,12 +298,6 @@ return quad->filters_origin; } - static const gfx::RectF& tex_coord_rect(const viz::DrawQuad& input) { - const viz::CompositorRenderPassDrawQuad* quad = - viz::CompositorRenderPassDrawQuad::MaterialCast(&input); - return quad->tex_coord_rect; - } - static bool force_anti_aliasing_off(const viz::DrawQuad& input) { const viz::CompositorRenderPassDrawQuad* quad = viz::CompositorRenderPassDrawQuad::MaterialCast(&input);
diff --git a/services/viz/public/mojom/compositing/quads.mojom b/services/viz/public/mojom/compositing/quads.mojom index d3380ae..4169c4c 100644 --- a/services/viz/public/mojom/compositing/quads.mojom +++ b/services/viz/public/mojom/compositing/quads.mojom
@@ -61,8 +61,6 @@ // original primitive. This used to correctly position crop rects, lights, // etc. gfx.mojom.PointF filters_origin; - // Render surface's non-normalized texture rect. - gfx.mojom.RectF tex_coord_rect; bool force_anti_aliasing_off; float backdrop_filter_quality;
diff --git a/testing/buildbot/filters/BUILD.gn b/testing/buildbot/filters/BUILD.gn index a06f83a..92a552f 100644 --- a/testing/buildbot/filters/BUILD.gn +++ b/testing/buildbot/filters/BUILD.gn
@@ -91,6 +91,8 @@ "//testing/buildbot/filters/code_coverage.browser_tests.filter", "//testing/buildbot/filters/linux.asan.browser_tests.filter", "//testing/buildbot/filters/linux.linux-rel-cft.browser_tests.filter", + "//testing/buildbot/filters/linux-arm64-rel-fyi.browser_tests.filter", + "//testing/buildbot/filters/linux-arm64-wayland-rel-fyi.browser_tests.filter", "//testing/buildbot/filters/mac.mac-rel-cft.browser_tests.filter", "//testing/buildbot/filters/mac.mac-rel.browser_tests.filter", "//testing/buildbot/filters/mac.mac11-arm64-rel.browser_tests.filter", @@ -231,6 +233,7 @@ "//testing/buildbot/filters/android.mte.content_browsertests.filter", "//testing/buildbot/filters/chromium.webrtc.fyi.android.tests.dbg.content_browsertests.filter", "//testing/buildbot/filters/fuchsia.coverage.content_browsertests.filter", + "//testing/buildbot/filters/linux-arm64-rel-fyi.content_browsertests.filter", "//testing/buildbot/filters/site_isolation_android.content_browsertests.filter", "//testing/buildbot/filters/trees_in_viz.content_browsertests.filter", "//testing/buildbot/filters/vulkan.content_browsertests.filter", @@ -410,6 +413,8 @@ data = [ "//testing/buildbot/filters/accessibility-linux.interactive_ui_tests.filter", "//testing/buildbot/filters/linux.linux-rel-cft.interactive_ui_tests.filter", + "//testing/buildbot/filters/linux-arm64-rel-fyi.interactive_ui_tests.filter", + "//testing/buildbot/filters/linux-arm64-wayland-rel-fyi.interactive_ui_tests.filter", "//testing/buildbot/filters/mac.mac11-arm64-rel.interactive_ui_tests.filter", "//testing/buildbot/filters/mac.mac-rel-cft.interactive_ui_tests.filter", "//testing/buildbot/filters/ozone-linux.interactive_ui_tests.filter",
diff --git a/testing/buildbot/filters/linux-arm64-rel-fyi.browser_tests.filter b/testing/buildbot/filters/linux-arm64-rel-fyi.browser_tests.filter new file mode 100644 index 0000000..4a8fcbc --- /dev/null +++ b/testing/buildbot/filters/linux-arm64-rel-fyi.browser_tests.filter
@@ -0,0 +1,4 @@ +# TODO(https://crbug.com/493612000): Tests are flaky on linux-arm64-rel-fyi. +-ReadAnythingMochaTest.LineFocusMoveMode* +-PasswordChangeUIControllerBrowserTest.PageIsInteractableWhileToastIsShowing +-ReadAnythingMochaTest.RectCalculations*
diff --git a/testing/buildbot/filters/linux-arm64-rel-fyi.content_browsertests.filter b/testing/buildbot/filters/linux-arm64-rel-fyi.content_browsertests.filter new file mode 100644 index 0000000..5c151e2 --- /dev/null +++ b/testing/buildbot/filters/linux-arm64-rel-fyi.content_browsertests.filter
@@ -0,0 +1,2 @@ +# TODO(https://crbug.com/493612000): Tests are flaky on linux-arm64-rel-fyi. +-WebContentsVideoCaptureDeviceBrowserTest.DeliversRefreshFramesUponRequest
diff --git a/testing/buildbot/filters/linux-arm64-rel-fyi.interactive_ui_tests.filter b/testing/buildbot/filters/linux-arm64-rel-fyi.interactive_ui_tests.filter new file mode 100644 index 0000000..0f6f71f --- /dev/null +++ b/testing/buildbot/filters/linux-arm64-rel-fyi.interactive_ui_tests.filter
@@ -0,0 +1,5 @@ +# TODO(https://crbug.com/493612000): Tests are flaky on linux-arm64-rel-fyi. +-TabDragging/DetachToBrowserTabDragControllerTest.DragToSeparateWindowDuringDragEnd/* +-All/ForwardButtonAccessibilityTest.ContextMenu/* +-All/MultiContentsViewTabDragEntrypointsUiParamTest.DragAndDrop/* +-SidePanelBookmarksTest.ShoppingList
diff --git a/testing/buildbot/filters/linux-arm64-wayland-rel-fyi.browser_tests.filter b/testing/buildbot/filters/linux-arm64-wayland-rel-fyi.browser_tests.filter new file mode 100644 index 0000000..23377ad --- /dev/null +++ b/testing/buildbot/filters/linux-arm64-wayland-rel-fyi.browser_tests.filter
@@ -0,0 +1,18 @@ +# TODO(https://crbug.com/493612000): Tests are flaky on linux-arm64-wayland-rel-fyi. +-IsolatedWebAppLinkCapturingFromAppWindowBrowserTest.ShiftClickOpensNewAppWindow/NavigateExisting +-ReadAnythingMochaTest.LineFocusMoveMode* +-BrowserSwitcherServiceTest.ExternalFirstFetchFailsButSecondWorks +-ContextualTasksBrowserTest.App_Composebox_BasicMode* +-IsolatedWebAppLinkCapturingFromAppWindowBrowserTest.TargetBlankClickOpensAppWindow/NavigateNew +-BoundSessionCookieRefreshServiceImplBrowserTest.CookieRotationOnStartup +-BrowserInstantControllerTest.DefaultSearchProviderChanged +-ChromeRenderProcessHostTest.ProcessPerTab +-ReadAnythingMochaTest.RectCalculations* +-ExtensionWebRequestApiTest.DISABLE_SecurityInfo_Secure +-ServiceWorker/ExtensionWebRequestApiTestWithContextType.WebRequestCORSWithExtraHeaders/BackgroundResourceFetchDisabled +-PlatformAppBrowserTest.TouchpadPinchSyntheticWheelEvents +-ExtensionWebRequestApiTest.DISABLE_SecurityInfo_WebSocket_Secure +-ExtensionApiTabPrerenderingTest.PrerenderingIntoANewTab +-DataSharingSDKDelegateDesktopBrowserTest.ReadGroupLoadsWebContents +-SitePermissionsHelperBrowserTest.UpdateSiteAccess_DismissReloadBubble_ReloadPageManually +-IsolatedWebAppLinkCapturingFromAppWindowBrowserTest.NormalClickRespondsToClientMode/FocusExisting
diff --git a/testing/buildbot/filters/linux-arm64-wayland-rel-fyi.interactive_ui_tests.filter b/testing/buildbot/filters/linux-arm64-wayland-rel-fyi.interactive_ui_tests.filter new file mode 100644 index 0000000..74aefe94 --- /dev/null +++ b/testing/buildbot/filters/linux-arm64-wayland-rel-fyi.interactive_ui_tests.filter
@@ -0,0 +1,10 @@ +# TODO(https://crbug.com/493612000): Tests are flaky on linux-arm64-wayland-rel-fyi. +-SidePanelBookmarksTest.ShoppingList +-WebAppInteractiveUiTest.FindBarFocusEvents +-NotificationsApiTest.TestShouldDisplayPopupNotification +-NotificationsApiTest.TestShouldDisplayFullscreen +-MenuItemViewTestInsertWithSubmenu1.InsertItemWithSubmenu1 +-AutomaticFullscreenTest.EventuallyAfterPopupExit/* +-AutomaticFullscreenTest.Popup/* +-AutomaticFullscreenTest.ImmediatelyAfterPopupExit/* +-PerformanceInterventionInteractiveTest.SuggestTabsOnlyForLastActiveProfile
diff --git a/testing/buildbot/mixins.pyl b/testing/buildbot/mixins.pyl index 3533e46..7f96fcf1 100644 --- a/testing/buildbot/mixins.pyl +++ b/testing/buildbot/mixins.pyl
@@ -569,8 +569,8 @@ 'fail_if_unused': False, 'swarming': { 'dimensions': { - 'gpu': '1002:7340-23.2.1|1002:7340-25.2.8', - 'os': 'Ubuntu-22.04|Ubuntu-24.04', + 'gpu': '1002:7340-25.2.8', + 'os': 'Ubuntu-24.04', 'display_attached': '1', 'display_server': 'x11', 'pool': 'chromium.tests.gpu',
diff --git a/testing/libfuzzer/fuzzer_test.gni b/testing/libfuzzer/fuzzer_test.gni index 770d5ec..0e1dff5 100644 --- a/testing/libfuzzer/fuzzer_test.gni +++ b/testing/libfuzzer/fuzzer_test.gni
@@ -48,6 +48,9 @@ _additional_configs = invoker.additional_configs } + # TODO(crbug.com/505034799): Remove this when adding the wrapper. + not_needed(invoker, [ "is_fuzztest_compatible" ]) + # This ensures that the target name is suffixed by `_fuzzer` if # "//testing/libfuzzer:no_clusterfuzz" cannot be found in # `additional_configs`.
diff --git a/testing/libfuzzer/fuzzers/BUILD.gn b/testing/libfuzzer/fuzzers/BUILD.gn index 86c90f48..6804f2d2 100644 --- a/testing/libfuzzer/fuzzers/BUILD.gn +++ b/testing/libfuzzer/fuzzers/BUILD.gn
@@ -20,6 +20,7 @@ fuzzer_test("string_compare_fuzzer") { sources = [ "string_compare_fuzzer.cc" ] additional_configs = [ "//testing/libfuzzer:no_clusterfuzz" ] + is_fuzztest_compatible = false } fuzzer_test("string_compare_proto_fuzzer") {
diff --git a/testing/perf/cbb_ref_info/chrome/dev/android.json b/testing/perf/cbb_ref_info/chrome/dev/android.json index 374fd314..e827048 100644 --- a/testing/perf/cbb_ref_info/chrome/dev/android.json +++ b/testing/perf/cbb_ref_info/chrome/dev/android.json
@@ -2,5 +2,5 @@ "browser": "chrome", "channel": "dev", "platform": "android", - "version": "149.0.7815.0" + "version": "150.0.7828.3" } \ No newline at end of file
diff --git a/testing/perf/cbb_ref_info/chrome/dev/mac.json b/testing/perf/cbb_ref_info/chrome/dev/mac.json index bb60587..3fd7a4f6 100644 --- a/testing/perf/cbb_ref_info/chrome/dev/mac.json +++ b/testing/perf/cbb_ref_info/chrome/dev/mac.json
@@ -2,5 +2,5 @@ "browser": "chrome", "channel": "dev", "platform": "mac", - "version": "149.0.7815.2" + "version": "150.0.7828.2" } \ No newline at end of file
diff --git a/testing/perf/cbb_ref_info/chrome/dev/windows.json b/testing/perf/cbb_ref_info/chrome/dev/windows.json index edfa12ed..891ce72 100644 --- a/testing/perf/cbb_ref_info/chrome/dev/windows.json +++ b/testing/perf/cbb_ref_info/chrome/dev/windows.json
@@ -2,5 +2,5 @@ "browser": "chrome", "channel": "dev", "platform": "windows", - "version": "149.0.7815.2" + "version": "150.0.7828.2" } \ No newline at end of file
diff --git a/testing/variations/fieldtrial_testing_config.json b/testing/variations/fieldtrial_testing_config.json index e016168..7d0c556 100644 --- a/testing/variations/fieldtrial_testing_config.json +++ b/testing/variations/fieldtrial_testing_config.json
@@ -891,6 +891,21 @@ ] } ], + "AndroidDesktopAimGate": [ + { + "platforms": [ + "android" + ], + "experiments": [ + { + "name": "Enabled", + "enable_features": [ + "AndroidDesktopAimGate" + ] + } + ] + } + ], "AndroidDesktopStyleScrollbars": [ { "platforms": [ @@ -2610,7 +2625,6 @@ "AutofillSupportDateInput", "FormsClassificationsMqlsLogging", "IPH_AutofillAiValuables", - "SuggestionManageButtonSplitForEnhancedAutofill", "SyncAccountSettings", "SyncAutofillValuable", "SyncAutofillValuableMetadata", @@ -3087,26 +3101,6 @@ ] } ], - "AutofillImproveAddressFieldSwapping": [ - { - "platforms": [ - "android", - "chromeos", - "ios", - "linux", - "mac", - "windows" - ], - "experiments": [ - { - "name": "Enabled", - "enable_features": [ - "AutofillImproveAddressFieldSwapping" - ] - } - ] - } - ], "AutofillImprovePhoneNumberDetection": [ { "platforms": [ @@ -8259,26 +8253,6 @@ ] } ], - "DefaultPassthroughCommandDecoder": [ - { - "platforms": [ - "android", - "android_webview" - ], - "experiments": [ - { - "name": "Enabled", - "params": { - "BlockListByDevice": "", - "BlockListByModel": "" - }, - "enable_features": [ - "DefaultPassthroughCommandDecoder" - ] - } - ] - } - ], "DeferSpeculativeRFHCreation": [ { "platforms": [ @@ -12878,27 +12852,6 @@ ] } ], - "HeliosBanana": [ - { - "platforms": [ - "ios" - ], - "experiments": [ - { - "name": "Enabled", - "params": { - "RemovePageContext": "true", - "ShowAboveSearchImage": "true", - "ShowBelowSearchImage": "false", - "ShowFRERow": "true" - }, - "enable_features": [ - "GeminiImageRemixTool" - ] - } - ] - } - ], "HeliosClientMigration": [ { "platforms": [ @@ -13675,21 +13628,6 @@ ] } ], - "IOSKeyboardAccessorySuggestionsCutOffLimit": [ - { - "platforms": [ - "ios" - ], - "experiments": [ - { - "name": "Enabled_20260130", - "enable_features": [ - "IOSKeyboardAccessorySuggestionsCutOffLimit" - ] - } - ] - } - ], "IOSKeyboardAccessoryTwoBubble": [ { "platforms": [ @@ -17221,7 +17159,6 @@ "compact_fusebox": "true" }, "enable_features": [ - "AndroidDesktopAimGate", "ComposeboxUsesChromeComposeClient", "OmniboxMultilineEditField", "OmniboxMultimodalInput" @@ -21164,7 +21101,7 @@ ] } ], - "ScrollEndRepaintFollowsScrollUpdate": [ + "ScrollJankV4SlowPathScrollHandling": [ { "platforms": [ "android", @@ -21175,10 +21112,23 @@ ], "experiments": [ { - "name": "Enabled", + "name": "Enabled_Both", + "enable_features": [ + "ScrollEndRepaintFollowsScrollUpdate", + "UseScrollIdToCalculateScrollJankV4FrameStages" + ] + }, + { + "name": "Enabled_ScrollEndRepaintFollowsScrollUpdateOnly", "enable_features": [ "ScrollEndRepaintFollowsScrollUpdate" ] + }, + { + "name": "Enabled_UseScrollIdToCalculateScrollJankV4FrameStagesOnly", + "enable_features": [ + "UseScrollIdToCalculateScrollJankV4FrameStages" + ] } ] } @@ -25242,6 +25192,21 @@ ] } ], + "WebGPUUseHLSL2021": [ + { + "platforms": [ + "windows" + ], + "experiments": [ + { + "name": "Enabled", + "enable_features": [ + "WebGPUUseHLSL2021" + ] + } + ] + } + ], "WebGPUUseSpirv14": [ { "platforms": [ @@ -26712,6 +26677,36 @@ ] } ], + "WinSboxHighGPUJobMemoryLimits": [ + { + "platforms": [ + "windows" + ], + "experiments": [ + { + "name": "Enabled", + "enable_features": [ + "WinSboxHighGPUJobMemoryLimits" + ] + } + ] + } + ], + "WinSboxModuleTamperingProtection": [ + { + "platforms": [ + "windows" + ], + "experiments": [ + { + "name": "Enabled", + "enable_features": [ + "WinSboxModuleTamperingProtection" + ] + } + ] + } + ], "WinSboxNoFakeGdiInit": [ { "platforms": [
diff --git a/third_party/angle b/third_party/angle index 272c2ae..ab41985 160000 --- a/third_party/angle +++ b/third_party/angle
@@ -1 +1 @@ -Subproject commit 272c2ae3264a7ae019546c163c230a7cd29a2252 +Subproject commit ab41985470ba0a74e80ecfd7ec21e4d9ada428da
diff --git a/third_party/blink/public/mojom/content_extraction/script_tools.mojom b/third_party/blink/public/mojom/content_extraction/script_tools.mojom index 1654b4f..b77979b 100644 --- a/third_party/blink/public/mojom/content_extraction/script_tools.mojom +++ b/third_party/blink/public/mojom/content_extraction/script_tools.mojom
@@ -4,6 +4,7 @@ module blink.mojom; +import "third_party/blink/public/mojom/tokens/tokens.mojom"; import "url/mojom/origin.mojom"; // See ToolAnnotations in model_context_tool.idl for details of this @@ -23,6 +24,14 @@ ScriptToolAnnotations? annotations; // See the documentation in `model_context.idl` for more about this member. array<url.mojom.Origin> exposed_origins; + + // The token of the frame that registered this tool. + blink.mojom.FrameToken tool_owner_frame_token; + // The origin of the frame that registered this tool. This can be an opaque + // origin, however it would only have access to tools on its own origin, as no + // other document would be able to express its origin in `exposed_origins` + // above. + url.mojom.Origin origin; }; // Interface for the renderer to request actions from the browser related to @@ -52,6 +61,28 @@ // Returns all script tools visible to the caller document. GetScriptTools() => (array<ScriptTool> tools); + + // Executes a ScriptTool hosted in another Document. The tool can be located + // based on the unique combination of (`tool_owner_frame_token`, `name`), but + // we pass `expected_target_origin` too, since it is possible that + // `tool_owner_frame_token` points to a remote frame whose frame tree node's + // host has changed since tool registration. + // + // Note that `success` generally + // controls whether the caller's Promise resolves or rejects: + // - When the Promise returned by the remote tool resolves, then `success` + // will be true, and the caller's Promise resolves. + // - The same is true in the inverse, for rejection. + // ... but note that `success` can be false even before the remote tool's + // response is obtained, e.g., if the browser process failed to locate the + // tool, or if it was unregistered mid-execution. + // + // TODO(https://crbug.com/509555636): Consider making `success` more granular + // in the error case, perhaps by using `ScriptToolErrorCode`. + ExecuteRemoteScriptTool(blink.mojom.FrameToken tool_owner_frame_token, + url.mojom.Origin expected_target_origin, + string name, + string input_arguments) => (string? result, bool success); }; // Interface implemented by `blink::ModelContext` for the browser to notify the @@ -59,4 +90,14 @@ interface ModelContext { // Fires the `toolchange` event against the `blink::ModelContext` object. NotifyToolChange(); + + // Executes a ScriptTool by the name of `name`, hosted in this Document. This + // invokes JavaScript and obtains a Promise representing the tool result. The + // tool result is always a string for now; but see https://crbug.com/508306795 + // which tracks the task of changing this. + // + // Resolution of the JavaScript Promise results in the callback being called + // with `success=true`, while rejection results in `success=false`. + ExecuteScriptTool(string name, string input_arguments) + => (string? result, bool success); };
diff --git a/third_party/blink/public/mojom/frame/frame.mojom b/third_party/blink/public/mojom/frame/frame.mojom index 3836008..913d342 100644 --- a/third_party/blink/public/mojom/frame/frame.mojom +++ b/third_party/blink/public/mojom/frame/frame.mojom
@@ -1323,6 +1323,10 @@ FinalizeNavigationConfidence( double randomized_trigger_rate, ConfidenceLevel confidence); + + // Called when the related pages set is finalized and cannot change due to + // previous page's mutations. Called when this (new page) is being committed. + NotifyRelatedPagesFinalized(bool has_other_related_pages); }; // Implemented in Browser, this interface defines local-main-frame-specific
diff --git a/third_party/blink/public/mojom/performance_manager/v8_detailed_memory_reporter.mojom b/third_party/blink/public/mojom/performance_manager/v8_detailed_memory_reporter.mojom index 43a4852..9bffaa7 100644 --- a/third_party/blink/public/mojom/performance_manager/v8_detailed_memory_reporter.mojom +++ b/third_party/blink/public/mojom/performance_manager/v8_detailed_memory_reporter.mojom
@@ -18,6 +18,9 @@ // filled only for dedicated workers and it may be empty if the URL is too // long. string? url; + // For non-main-world contexts (e.g. isolated worlds), this is the stable ID + // of the world. Null for main world and worker contexts. + string? world_stable_id; }; struct PerContextCanvasMemoryUsage {
diff --git a/third_party/blink/renderer/bindings/generated_in_core.gni b/third_party/blink/renderer/bindings/generated_in_core.gni index 5ff9a0c..d0b3a981 100644 --- a/third_party/blink/renderer/bindings/generated_in_core.gni +++ b/third_party/blink/renderer/bindings/generated_in_core.gni
@@ -126,6 +126,8 @@ generated_dictionary_sources_in_core = [ "$root_gen_dir/third_party/blink/renderer/bindings/core/v8/v8_registered_tool.cc", "$root_gen_dir/third_party/blink/renderer/bindings/core/v8/v8_registered_tool.h", + "$root_gen_dir/third_party/blink/renderer/bindings/core/v8/v8_registered_tool_deprecated.cc", + "$root_gen_dir/third_party/blink/renderer/bindings/core/v8/v8_registered_tool_deprecated.h", "$root_gen_dir/third_party/blink/renderer/bindings/core/v8/v8_accelerator.cc", "$root_gen_dir/third_party/blink/renderer/bindings/core/v8/v8_accelerator.h", "$root_gen_dir/third_party/blink/renderer/bindings/core/v8/v8_autofill_field_data.cc",
diff --git a/third_party/blink/renderer/controller/performance_manager/v8_detailed_memory_reporter_impl.cc b/third_party/blink/renderer/controller/performance_manager/v8_detailed_memory_reporter_impl.cc index a47f6cea..6b7ec71 100644 --- a/third_party/blink/renderer/controller/performance_manager/v8_detailed_memory_reporter_impl.cc +++ b/third_party/blink/renderer/controller/performance_manager/v8_detailed_memory_reporter_impl.cc
@@ -72,10 +72,17 @@ continue; } v8::Isolate* isolate = v8::Isolate::GetCurrent(); - if (DOMWrapperWorld::World(isolate, context).GetWorldId() != - DOMWrapperWorld::kMainWorldId) { - // TODO(crbug.com/1085129): Handle extension contexts once they get - // their own V8ContextToken. + DOMWrapperWorld& world = DOMWrapperWorld::World(isolate, context); + if (world.GetWorldId() != DOMWrapperWorld::kMainWorldId) { + // Non-main-world: report with stable ID if available. + String stable_id = world.NonMainWorldStableId(); + if (!stable_id.IsNull() && !stable_id.empty()) { + auto nmw_usage = mojom::blink::PerContextV8MemoryUsage::New(); + nmw_usage->token = frame->DomWindow()->GetExecutionContextToken(); + nmw_usage->memory_used = size; + nmw_usage->world_stable_id = stable_id; + isolate_memory_usage->contexts.push_back(std::move(nmw_usage)); + } continue; } auto context_memory_usage = mojom::blink::PerContextV8MemoryUsage::New();
diff --git a/third_party/blink/renderer/controller/tests/run_all_tests.cc b/third_party/blink/renderer/controller/tests/run_all_tests.cc index 54e362f6..230282e 100644 --- a/third_party/blink/renderer/controller/tests/run_all_tests.cc +++ b/third_party/blink/renderer/controller/tests/run_all_tests.cc
@@ -5,6 +5,7 @@ #include "base/functional/bind.h" #include "base/test/launcher/unit_test_launcher.h" #include "base/test/test_suite.h" +#include "build/build_config.h" #include "content/public/test/blink_test_environment.h" #include "third_party/blink/renderer/controller/tests/thread_state_test_environment.h" #include "third_party/blink/renderer/platform/testing/task_environment.h" @@ -18,3 +19,12 @@ argc, argv, base::BindOnce(&base::TestSuite::Run, base::Unretained(&test_suite))); } + +#if BUILDFLAG(IS_IOS) +// Required by content/app/ios/appex/child_process_bridge.mm when linking +// against content for iOS device builds. blink_unittests doesn't spawn +// child processes, so this is just a stub. +extern "C" int ChildProcessMain(int argc, const char** argv) { + return 0; +} +#endif
diff --git a/third_party/blink/renderer/core/clipboard/data_object.cc b/third_party/blink/renderer/core/clipboard/data_object.cc index 8d26c69..f07f86d0 100644 --- a/third_party/blink/renderer/core/clipboard/data_object.cc +++ b/third_party/blink/renderer/core/clipboard/data_object.cc
@@ -34,6 +34,7 @@ #include <variant> #include "base/notreached.h" +#include "base/task/single_thread_task_runner.h" #include "third_party/abseil-cpp/absl/functional/overload.h" #include "third_party/abseil-cpp/absl/numeric/int128.h" #include "third_party/blink/public/platform/file_path_conversion.h" @@ -44,9 +45,15 @@ #include "third_party/blink/renderer/core/clipboard/paste_mode.h" #include "third_party/blink/renderer/core/clipboard/system_clipboard.h" #include "third_party/blink/renderer/core/execution_context/execution_context.h" +#include "third_party/blink/renderer/core/fileapi/file_reader_client.h" +#include "third_party/blink/renderer/core/fileapi/file_reader_data.h" +#include "third_party/blink/renderer/core/typed_arrays/array_buffer/array_buffer_contents.h" #include "third_party/blink/renderer/platform/blob/blob_data.h" #include "third_party/blink/renderer/platform/file_metadata.h" +#include "third_party/blink/renderer/platform/image-decoders/image_decoder.h" +#include "third_party/blink/renderer/platform/runtime_enabled_features.h" #include "third_party/blink/renderer/platform/wtf/hash_set.h" +#include "third_party/blink/renderer/platform/wtf/shared_buffer.h" #include "ui/base/clipboard/clipboard_constants.h" namespace blink { @@ -438,7 +445,59 @@ return Create(/*context=*/nullptr, data); } -WebDragData DataObject::ToWebDragData() { +namespace { + +// Synchronously reads all bytes from a BlobDataHandle into a SharedBuffer. +// Used to populate file contents for JS-constructed File objects +// (e.g. new File([bytes], 'photo.jpg')) during drag start. +// 256MB matches the upper limit used for synchronous reads in the Clipboard +// API. +constexpr size_t kMaxSyncReadSize = 256 * 1024 * 1024; +scoped_refptr<SharedBuffer> SyncReadBlobDataHandle( + scoped_refptr<BlobDataHandle> handle, + scoped_refptr<base::SingleThreadTaskRunner> task_runner) { + if (!handle || !task_runner) { + return nullptr; + } + + uint64_t size = handle->size(); + if (size == 0 || size > kMaxSyncReadSize) { + VLOG(1) << "Blob empty or too large for synchronous DND read: " << size; + return nullptr; + } + + auto [error_code, data] = SyncedFileReaderAccumulator::Load( + std::move(handle), std::move(task_runner)); + + if (error_code != FileErrorCode::kOK) { + return nullptr; + } + + ArrayBufferContents contents = std::move(data).AsArrayBufferContents(); + if (!contents.IsValid() || contents.DataLength() == 0) { + return nullptr; + } + + return SharedBuffer::Create(contents.ByteSpan()); +} + +// Returns true if |buf| begins with magic bytes recognized by ImageDecoder +// (JPEG, PNG, GIF, WebP, BMP, ICO, etc.). This guards against disguised +// executables such as new File([exeBytes], 'photo.jpg') — ImageDecoder::Create +// returns nullptr when the magic bytes do not match any supported image format. +// No full decode is performed; only the file signature is checked. +bool IsImageDataValid(scoped_refptr<SharedBuffer> buf) { + std::unique_ptr<ImageDecoder> decoder = ImageDecoder::Create( + SegmentReader::CreateFromSharedBuffer(buf), + /*data_complete=*/true, ImageDecoder::kAlphaPremultiplied, + ImageDecoder::kDefaultBitDepth, ColorBehavior::kTag, + cc::AuxImage::kDefault, Platform::GetMaxDecodedImageBytes()); + return decoder != nullptr; +} + +} // namespace + +WebDragData DataObject::ToWebDragData(ExecutionContext* context) { WebDragData data; std::vector<WebDragData::Item> item_list(length()); @@ -479,11 +538,51 @@ file_system_file_item.file_system_id = original_item->FileSystemId(); } else { - // TODO(http://crbug.com/394955): support dragging constructed - // Files across renderers. - auto& string_item = item_list[i].emplace<WebDragData::StringItem>(); - string_item.type = "text/plain"; - string_item.data = file->name(); + scoped_refptr<SharedBuffer> buf; + if (context && + RuntimeEnabledFeatures::DragAndDropJSFileObjectsEnabled( + context)) { + scoped_refptr<base::SingleThreadTaskRunner> task_runner = + context->GetTaskRunner(TaskType::kFileReading); + buf = SyncReadBlobDataHandle(file->GetBlobDataHandle(), + std::move(task_runner)); + } + // TODO(crbug.com/510410319): Gate this path on an image MIME type + // (e.g. image/*) in addition to magic-byte validation. + if (buf && buf->size() > 0 && IsImageDataValid(buf)) { + auto& binary_item = + item_list[i].emplace<WebDragData::BinaryDataItem>(); + binary_item.data = buf; + // The image data has been validated, so it is safe to allow the + // browser process to access it as file contents. Mark it + // accessible so that drops onto frames within the same + // WebContents (e.g. a parent frame dropping onto an iframe) + // pass the browser-side IsImageAccessibleFromFrame() check. + binary_item.image_accessible = true; + // Encode the original filename into the source URL path so that + // GetAsFile() can recover it via base_url_.LastPathComponent() + // at the drop target. + if (!file->name().empty()) { + String source_url = + StrCat({"https://local/", + EncodeWithUrlEscapeSequences(file->name())}); + binary_item.source_url = KURL(source_url); + } + + const String& name = file->name(); + size_t dot_index = name.rfind('.'); + + if (dot_index != kNotFound && dot_index + 1 < name.length()) { + String ext = name.substr(dot_index + 1); + binary_item.filename_extension = ext; + } + } else { + // Fallback: only set the file name as text/plain. + auto& string_item = + item_list[i].emplace<WebDragData::StringItem>(); + string_item.type = "text/plain"; + string_item.data = file->name(); + } } } else { NOTREACHED();
diff --git a/third_party/blink/renderer/core/clipboard/data_object.h b/third_party/blink/renderer/core/clipboard/data_object.h index 1709bb3f..17c8af7 100644 --- a/third_party/blink/renderer/core/clipboard/data_object.h +++ b/third_party/blink/renderer/core/clipboard/data_object.h
@@ -141,7 +141,9 @@ void Trace(Visitor*) const override; - WebDragData ToWebDragData(); + // |context| is used to obtain a file-reading task runner for synchronously + // reading blob-backed File objects. + WebDragData ToWebDragData(ExecutionContext* context); private: DataObjectItem* FindStringItem(const String& type) const;
diff --git a/third_party/blink/renderer/core/clipboard/system_clipboard.cc b/third_party/blink/renderer/core/clipboard/system_clipboard.cc index 0246826..b414ae1 100644 --- a/third_party/blink/renderer/core/clipboard/system_clipboard.cc +++ b/third_party/blink/renderer/core/clipboard/system_clipboard.cc
@@ -377,7 +377,7 @@ // allow receiving side to extract the data required. // TODO(crbug.com/332571415): Properly support text/uri-list here. HashMap<String, String> custom_data; - WebDragData data = data_object->ToWebDragData(); + WebDragData data = data_object->ToWebDragData(nullptr); for (const WebDragData::Item& item : data.Items()) { if (const auto* string_item = std::get_if<WebDragData::StringItem>(&item)) { if (string_item->type == ui::kMimeTypePlainText) {
diff --git a/third_party/blink/renderer/core/css/counters_attachment_context.cc b/third_party/blink/renderer/core/css/counters_attachment_context.cc index 3c1e890..5bdb5d6 100644 --- a/third_party/blink/renderer/core/css/counters_attachment_context.cc +++ b/third_party/blink/renderer/core/css/counters_attachment_context.cc
@@ -502,8 +502,8 @@ const auto* current = To<Element>(counter_stack.back()->layout_object->GetNode()); DCHECK(current); - if (LayoutTreeBuilderTraversal::ParentElement(*current) == - LayoutTreeBuilderTraversal::ParentElement(*element)) { + if (LayoutTreeBuilderTraversal::LayoutParentElement(*current) == + LayoutTreeBuilderTraversal::LayoutParentElement(*element)) { counter_stack.pop_back(); } } @@ -543,7 +543,7 @@ const LayoutObject& last_object = *entry->layout_object; if (const auto* last_element = DynamicTo<Element>(last_object.GetNode())) { const Element* parent = - LayoutTreeBuilderTraversal::ParentElement(*last_element); + LayoutTreeBuilderTraversal::LayoutParentElement(*last_element); // We pop all elements whose parent is not ancestor of `element`. if (!parent || IsAncestorOf(*parent, *element)) { break; @@ -596,9 +596,9 @@ return; } const Element* parent = - LayoutTreeBuilderTraversal::ParentElement(*previous_element); + LayoutTreeBuilderTraversal::LayoutParentElement(*previous_element); if (parent && IsAncestorOf(*parent, *element) && - parent != LayoutTreeBuilderTraversal::ParentElement(*element)) { + parent != LayoutTreeBuilderTraversal::LayoutParentElement(*element)) { counter_stack.pop_back(); } }
diff --git a/third_party/blink/renderer/core/dom/element.cc b/third_party/blink/renderer/core/dom/element.cc index e0e5724a..13c50200 100644 --- a/third_party/blink/renderer/core/dom/element.cc +++ b/third_party/blink/renderer/core/dom/element.cc
@@ -11215,7 +11215,7 @@ return GetURLAttributeAsKURL(html_names::kHrefAttr); } if (auto* svg_a = DynamicTo<SVGAElement>(*this)) { - return svg_a->LegacyHrefURL(GetDocument()); + return svg_a->Url(); } return KURL(); }
diff --git a/third_party/blink/renderer/core/dom/events/listener_leak_test.cc b/third_party/blink/renderer/core/dom/events/listener_leak_test.cc index d581321..491ba67 100644 --- a/third_party/blink/renderer/core/dom/events/listener_leak_test.cc +++ b/third_party/blink/renderer/core/dom/events/listener_leak_test.cc
@@ -28,6 +28,8 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ +#include <string_view> + #include "base/compiler_specific.h" #include "testing/gtest/include/gtest/gtest.h" #include "third_party/blink/public/platform/platform.h" @@ -51,12 +53,12 @@ const v8::HeapGraphNode* GetProperty(v8::Isolate* isolate, const v8::HeapGraphNode* node, v8::HeapGraphEdge::Type type, - const char* name) { + std::string_view name) { for (int i = 0, count = node->GetChildrenCount(); i < count; ++i) { const v8::HeapGraphEdge* prop = node->GetChild(i); if (prop->GetType() == type) { v8::String::Utf8Value prop_name(isolate, prop->GetName()); - if (!UNSAFE_TODO(strcmp(name, *prop_name))) { + if (*prop_name && name == prop_name.as_view()) { return prop->GetToNode(); } } @@ -64,7 +66,7 @@ return nullptr; } -int GetNumObjects(v8::Isolate* isolate, const char* constructor) { +int GetNumObjects(v8::Isolate* isolate, std::string_view constructor) { v8::HandleScope scope(isolate); v8::HeapProfiler* profiler = isolate->GetHeapProfiler(); const v8::HeapSnapshot* snapshot = profiler->TakeHeapSnapshot(); @@ -76,14 +78,14 @@ if (node->GetType() != v8::HeapGraphNode::kObject) continue; v8::String::Utf8Value node_name(isolate, node->GetName()); - if (!UNSAFE_TODO(strcmp(constructor, *node_name))) { + if (*node_name && constructor == node_name.as_view()) { const v8::HeapGraphNode* constructor_prop = GetProperty( isolate, node, v8::HeapGraphEdge::kProperty, "constructor"); // Skip an Object instance named after the constructor. if (constructor_prop) { v8::String::Utf8Value constructor_name(isolate, constructor_prop->GetName()); - if (!UNSAFE_TODO(strcmp(constructor, *constructor_name))) { + if (*constructor_name && constructor == constructor_name.as_view()) { continue; } }
diff --git a/third_party/blink/renderer/core/dom/static_range.cc b/third_party/blink/renderer/core/dom/static_range.cc index d897de4d5..de7b482 100644 --- a/third_party/blink/renderer/core/dom/static_range.cc +++ b/third_party/blink/renderer/core/dom/static_range.cc
@@ -36,8 +36,7 @@ range.EndPosition().ComputeOffsetInContainerNode()); } -StaticRange* StaticRange::Create(Document& document, - const StaticRangeInit* static_range_init, +StaticRange* StaticRange::Create(const StaticRangeInit* static_range_init, ExceptionState& exception_state) { DCHECK(static_range_init); @@ -51,10 +50,17 @@ "Attribute node."); } + // Use the start container's document as the owner document so that the + // resulting StaticRange is associated with the document its endpoints + // actually live in, even when the range is constructed from a different + // realm (for example, when a parent document creates a StaticRange over + // nodes inside a same-origin iframe). The W3C StaticRange spec does not + // define an owner document; this is an internal field that must reflect + // the range's actual position in the DOM. return MakeGarbageCollected<StaticRange>( - document, static_range_init->startContainer(), - static_range_init->startOffset(), static_range_init->endContainer(), - static_range_init->endOffset()); + static_range_init->startContainer()->GetDocument(), + static_range_init->startContainer(), static_range_init->startOffset(), + static_range_init->endContainer(), static_range_init->endOffset()); } bool StaticRange::IsValid() const {
diff --git a/third_party/blink/renderer/core/dom/static_range.h b/third_party/blink/renderer/core/dom/static_range.h index 6aaa347..d5f957d 100644 --- a/third_party/blink/renderer/core/dom/static_range.h +++ b/third_party/blink/renderer/core/dom/static_range.h
@@ -28,9 +28,7 @@ range->endContainer(), range->endOffset()); } static StaticRange* Create(const EphemeralRange&); - static StaticRange* Create(Document&, - const StaticRangeInit*, - ExceptionState&); + static StaticRange* Create(const StaticRangeInit*, ExceptionState&); StaticRange(Document&, Node* start_container,
diff --git a/third_party/blink/renderer/core/dom/static_range.idl b/third_party/blink/renderer/core/dom/static_range.idl index 6d6bf44..662dc84 100644 --- a/third_party/blink/renderer/core/dom/static_range.idl +++ b/third_party/blink/renderer/core/dom/static_range.idl
@@ -6,5 +6,5 @@ [Exposed=Window] interface StaticRange : AbstractRange { - [CallWith=Document, RaisesException] constructor(StaticRangeInit init); + [RaisesException] constructor(StaticRangeInit init); }; \ No newline at end of file
diff --git a/third_party/blink/renderer/core/dom/static_range_test.cc b/third_party/blink/renderer/core/dom/static_range_test.cc index cd7c663d..7a1ebff 100644 --- a/third_party/blink/renderer/core/dom/static_range_test.cc +++ b/third_party/blink/renderer/core/dom/static_range_test.cc
@@ -7,6 +7,7 @@ #include "base/memory/scoped_refptr.h" #include "testing/gtest/include/gtest/gtest.h" #include "third_party/blink/renderer/bindings/core/v8/v8_binding_for_testing.h" +#include "third_party/blink/renderer/bindings/core/v8/v8_static_range_init.h" #include "third_party/blink/renderer/core/dom/element.h" #include "third_party/blink/renderer/core/dom/node_list.h" #include "third_party/blink/renderer/core/dom/range.h" @@ -257,4 +258,84 @@ EXPECT_TRUE(exception_state.HadException()); } +TEST_F(StaticRangeTest, OwnerDocumentMatchesStartContainerDocument) { + V8TestingScope scope; + // The StaticRange constructor surfaced via the IDL takes a + // |StaticRangeInit| dictionary. Its |owner_document_| must reflect the + // document that the start container actually lives in, regardless of which + // realm or document the constructor is being invoked from. This mirrors how + // |Range::setStart| updates a Range's owner document when given a node from + // a different document, and matches the W3C StaticRange specification which + // does not define an owner document for StaticRange (it is a Blink internal + // field). Without this, cross-realm consumers such as + // |HighlightRegistry::ValidateHighlightMarkers| reject the range because + // |range->OwnerDocument()| no longer matches the document being painted. + // See https://issues.chromium.org/issues/407812149. + ScopedNullExecutionContext other_execution_context; + Persistent<HTMLDocument> other_document = HTMLDocument::CreateForTest( + other_execution_context.GetExecutionContext()); + auto* other_html = MakeGarbageCollected<HTMLHtmlElement>(*other_document); + other_html->AppendChild( + MakeGarbageCollected<HTMLBodyElement>(*other_document)); + other_document->AppendChild(other_html); + other_document->body()->SetInnerHTMLWithoutTrustedTypes("hello"); + auto* other_text = To<Text>(other_document->body()->firstChild()); + + auto* init = MakeGarbageCollected<StaticRangeInit>(); + init->setStartContainer(other_text); + init->setStartOffset(0u); + init->setEndContainer(other_text); + init->setEndOffset(5u); + + auto* static_range = StaticRange::Create(init, ASSERT_NO_EXCEPTION); + ASSERT_TRUE(static_range); + EXPECT_EQ(&static_range->OwnerDocument(), other_document.Get()) + << "StaticRange::OwnerDocument() must follow the start container's " + "document, not the document associated with the calling realm."; + EXPECT_TRUE(static_range->IsValid()); + + // Sanity: the start/end containers and offsets round-trip unchanged. + EXPECT_EQ(other_text, static_range->startContainer()); + EXPECT_EQ(0u, static_range->startOffset()); + EXPECT_EQ(other_text, static_range->endContainer()); + EXPECT_EQ(5u, static_range->endOffset()); +} + +TEST_F(StaticRangeTest, + IsInvalidWhenStartAndEndContainersAreInDifferentDocuments) { + V8TestingScope scope; + // A StaticRange whose start and end containers live in different documents + // is constructible, but |IsValid()| must return false because the two + // containers have different roots. Downstream consumers such as + // |HighlightRegistry::ValidateHighlightMarkers| check |IsValid()| via + // |IsAbstractRangePaintable| and silently drop such ranges, so we lock the + // behavior in here. + GetDocument().body()->SetInnerHTMLWithoutTrustedTypes("hello"); + auto* main_text = To<Text>(GetDocument().body()->firstChild()); + + ScopedNullExecutionContext other_execution_context; + Persistent<HTMLDocument> other_document = HTMLDocument::CreateForTest( + other_execution_context.GetExecutionContext()); + auto* other_html = MakeGarbageCollected<HTMLHtmlElement>(*other_document); + other_html->AppendChild( + MakeGarbageCollected<HTMLBodyElement>(*other_document)); + other_document->AppendChild(other_html); + other_document->body()->SetInnerHTMLWithoutTrustedTypes("world"); + auto* other_text = To<Text>(other_document->body()->firstChild()); + + auto* init = MakeGarbageCollected<StaticRangeInit>(); + init->setStartContainer(main_text); + init->setStartOffset(0u); + init->setEndContainer(other_text); + init->setEndOffset(5u); + + auto* static_range = StaticRange::Create(init, ASSERT_NO_EXCEPTION); + ASSERT_TRUE(static_range); + // The owner document follows the start container per the IDL contract. + EXPECT_EQ(&static_range->OwnerDocument(), &GetDocument()); + EXPECT_FALSE(static_range->IsValid()) + << "A StaticRange whose endpoints are in different documents must " + "report IsValid() == false."; +} + } // namespace blink
diff --git a/third_party/blink/renderer/core/exported/web_drag_data_test.cc b/third_party/blink/renderer/core/exported/web_drag_data_test.cc index 273363f..7804d77 100644 --- a/third_party/blink/renderer/core/exported/web_drag_data_test.cc +++ b/third_party/blink/renderer/core/exported/web_drag_data_test.cc
@@ -68,7 +68,7 @@ url, metadata, File::kIsNotUserVisible, BlobDataHandle::Create())); } - WebDragData data = data_object->ToWebDragData(); + WebDragData data = data_object->ToWebDragData(&context.GetExecutionContext()); std::vector<WebDragData::Item> items = data.Items(); ASSERT_EQ(6u, items.size());
diff --git a/third_party/blink/renderer/core/exported/web_plugin_container_impl.cc b/third_party/blink/renderer/core/exported/web_plugin_container_impl.cc index 42c59f0..69c8af1a 100644 --- a/third_party/blink/renderer/core/exported/web_plugin_container_impl.cc +++ b/third_party/blink/renderer/core/exported/web_plugin_container_impl.cc
@@ -868,7 +868,8 @@ return; DataTransfer* data_transfer = event.dataTransfer(); - WebDragData drag_data = data_transfer->GetDataObject()->ToWebDragData(); + WebDragData drag_data = + data_transfer->GetDataObject()->ToWebDragData(nullptr); DragOperationsMask drag_operation_mask = data_transfer->SourceOperation(); gfx::PointF drag_screen_location(event.screenX(), event.screenY()); gfx::Point location(Location());
diff --git a/third_party/blink/renderer/core/fragment_directive/text_fragment_anchor.cc b/third_party/blink/renderer/core/fragment_directive/text_fragment_anchor.cc index 0f5ec51..0d8f696 100644 --- a/third_party/blink/renderer/core/fragment_directive/text_fragment_anchor.cc +++ b/third_party/blink/renderer/core/fragment_directive/text_fragment_anchor.cc
@@ -83,7 +83,8 @@ return false; } - if (frame.GetPage()->RelatedPages().size()) { + if (frame.GetPage()->RelatedPages().size() || + frame.GetPage()->HasOtherRelatedPagesDuringCommit()) { TRACE_EVENT_INSTANT("blink", "CheckSecurityRestrictions", "Result", "Non-Empty Browsing Context Group"); return false;
diff --git a/third_party/blink/renderer/core/fragment_directive/text_fragment_anchor_metrics_test.cc b/third_party/blink/renderer/core/fragment_directive/text_fragment_anchor_metrics_test.cc index cf2e513..68c35d94 100644 --- a/third_party/blink/renderer/core/fragment_directive/text_fragment_anchor_metrics_test.cc +++ b/third_party/blink/renderer/core/fragment_directive/text_fragment_anchor_metrics_test.cc
@@ -53,6 +53,12 @@ return scoped_fake_ukm_recorder_.recorder(); } + void ResetRelatedPagesFinalized() { + GetDocument() + .GetPage() + ->related_pages_mutation_from_previous_page_finalized_ = false; + } + base::HistogramTester histogram_tester_; ScopedFakeUkmRecorder scoped_fake_ukm_recorder_; }; @@ -250,6 +256,9 @@ <!DOCTYPE html> <p>This is a test page</p> )HTML"); + GetDocument().GetPage()->NotifyRelatedPagesFinalized(false); + GetDocument().GetFrame()->Loader().ProcessPendingCrossDocumentFragment(); + Compositor().BeginFrame(); Compositor().BeginFrame(); // The anchor should have been found and finalized. @@ -370,6 +379,8 @@ <!DOCTYPE html> <p id="element">This is a test page</p> )HTML"); + GetDocument().GetPage()->NotifyRelatedPagesFinalized(false); + GetDocument().GetFrame()->Loader().ProcessPendingCrossDocumentFragment(); FragmentAnchor* anchor = GetDocument().GetFrame()->View()->GetFragmentAnchor(); if (anchor && anchor->IsTextFragmentAnchor()) { @@ -668,6 +679,7 @@ } { + ResetRelatedPagesFinalized(); SimRequest request("https://example.com/shadowtest.html#:~:text=ShadowDOM", "text/html"); LoadURL("https://example.com/shadowtest.html#:~:text=ShadowDOM");
diff --git a/third_party/blink/renderer/core/fragment_directive/text_fragment_anchor_test.cc b/third_party/blink/renderer/core/fragment_directive/text_fragment_anchor_test.cc index adce4c0..098e431 100644 --- a/third_party/blink/renderer/core/fragment_directive/text_fragment_anchor_test.cc +++ b/third_party/blink/renderer/core/fragment_directive/text_fragment_anchor_test.cc
@@ -66,6 +66,11 @@ TextFragmentAnchorTestController() = default; void BeginEmptyFrame() { + if (auto_finalize_) { + GetDocument().GetPage()->NotifyRelatedPagesFinalized(false); + GetDocument().GetFrame()->Loader().ProcessPendingCrossDocumentFragment(); + } + // If a test case doesn't find a match and therefore doesn't schedule the // beforematch event, we should still render a second frame as if we did // schedule the event to retain test coverage. @@ -76,6 +81,9 @@ Compositor().BeginFrame(); } + void SetAutoFinalize(bool auto_finalize) { auto_finalize_ = auto_finalize; } + + public: ScrollableArea* LayoutViewport() { return GetDocument().View()->LayoutViewport(); } @@ -170,6 +178,8 @@ "Implement others if new modality is needed."; } } + + bool auto_finalize_ = true; }; class TextFragmentAnchorTest : public TextFragmentAnchorTestController { @@ -198,7 +208,7 @@ )HTML"); RunAsyncMatchingTasks(); - Compositor().BeginFrame(); + BeginEmptyFrame(); Element& p = *GetDocument().getElementById(AtomicString("text")); @@ -208,6 +218,109 @@ << LayoutViewport()->GetScrollOffset().ToString(); } +// Test that scroll to text fragment is deferred until related pages are +// finalized. +TEST_F(TextFragmentAnchorTest, DeferUntilRelatedPagesFinalized) { + SetAutoFinalize(false); + SimRequest::Params params; + params.requestor_origin = + WebSecurityOrigin::CreateFromString(WebString("https://example.org")); + SimRequest request("https://example.com/test.html#:~:text=test", "text/html", + params); + LoadURL("https://example.com/test.html#:~:text=test"); + + ASSERT_FALSE( + GetDocument().GetPage()->RelatedPagesMutationFromPreviousPageFinalized()); + + request.Complete(R"HTML( + <!DOCTYPE html> + <style> + body { + height: 1200px; + } + p { + position: absolute; + top: 1000px; + } + </style> + <p id="text">This is a test page</p> + )HTML"); + + ThreadScheduler::Current() + ->ToMainThreadScheduler() + ->StartIdlePeriodForTesting(); + task_environment().FastForwardUntilNoTasksRemain(); + BeginEmptyFrame(); + + ASSERT_FALSE( + GetDocument().GetPage()->RelatedPagesMutationFromPreviousPageFinalized()); + + Element& p = *GetDocument().getElementById(AtomicString("text")); + + EXPECT_EQ(nullptr, GetDocument().CssTarget()); + EXPECT_FALSE(ViewportRect().Contains(BoundingRectInFrame(p))); + + GetDocument().GetPage()->NotifyRelatedPagesFinalized( + /* has_other_related_pages= */ false); + GetDocument().GetFrame()->Loader().ProcessPendingCrossDocumentFragment(); + + RunAsyncMatchingTasks(); + BeginEmptyFrame(); + + EXPECT_EQ(p, *GetDocument().CssTarget()); + EXPECT_TRUE(ViewportRect().Contains(BoundingRectInFrame(p))); +} + +TEST_F(TextFragmentAnchorTest, AvoidScrollingIfHasOtherRelatedPages) { + SetAutoFinalize(false); + SimRequest::Params params; + params.requestor_origin = + WebSecurityOrigin::CreateFromString(WebString("https://example.org")); + SimRequest request("https://example.com/test.html#:~:text=test", "text/html", + params); + LoadURL("https://example.com/test.html#:~:text=test"); + + ASSERT_FALSE( + GetDocument().GetPage()->RelatedPagesMutationFromPreviousPageFinalized()); + + request.Complete(R"HTML( + <!DOCTYPE html> + <style> + body { + height: 1200px; + } + p { + position: absolute; + top: 1000px; + } + </style> + <p id="text">This is a test page</p> + )HTML"); + + ThreadScheduler::Current() + ->ToMainThreadScheduler() + ->StartIdlePeriodForTesting(); + task_environment().FastForwardUntilNoTasksRemain(); + BeginEmptyFrame(); + + ASSERT_FALSE( + GetDocument().GetPage()->RelatedPagesMutationFromPreviousPageFinalized()); + + Element& p = *GetDocument().getElementById(AtomicString("text")); + + EXPECT_EQ(nullptr, GetDocument().CssTarget()); + EXPECT_FALSE(ViewportRect().Contains(BoundingRectInFrame(p))); + + GetDocument().GetPage()->NotifyRelatedPagesFinalized( + /* has_other_related_pages= */ true); + GetDocument().GetFrame()->Loader().ProcessPendingCrossDocumentFragment(); + + test::RunPendingTasks(); + + EXPECT_EQ(nullptr, GetDocument().CssTarget()); + EXPECT_FALSE(ViewportRect().Contains(BoundingRectInFrame(p))); +} + // Basic test case for silent scroll directives, ensure we scroll the matching // text into view but do NOT apply :target or markers. TEST_F(TextFragmentAnchorTest, BasicSilentScrollTest) { @@ -388,7 +501,7 @@ )HTML"); RunAsyncMatchingTasks(); - Compositor().BeginFrame(); + BeginEmptyFrame(); EXPECT_EQ(nullptr, GetDocument().CssTarget()); EXPECT_FALSE(GetDocument().View()->GetFragmentAnchor()); @@ -423,7 +536,7 @@ // Force a layout GetDocument().body()->setAttribute(html_names::kStyleAttr, AtomicString("height: 1300px")); - Compositor().BeginFrame(); + BeginEmptyFrame(); EXPECT_EQ(nullptr, GetDocument().CssTarget()); EXPECT_TRUE(GetDocument().Markers().Markers().empty()); @@ -453,7 +566,7 @@ )HTML"); RunAsyncMatchingTasks(); - Compositor().BeginFrame(); + BeginEmptyFrame(); Element& first = *GetDocument().getElementById(AtomicString("first")); @@ -488,7 +601,7 @@ )HTML"); RunAsyncMatchingTasks(); - Compositor().BeginFrame(); + BeginEmptyFrame(); Element& match = *GetDocument().getElementById(AtomicString("match")); @@ -524,7 +637,7 @@ )HTML"); RunAsyncMatchingTasks(); - Compositor().BeginFrame(); + BeginEmptyFrame(); Element& first = *GetDocument().getElementById(AtomicString("first")); @@ -561,7 +674,7 @@ )HTML"); RunAsyncMatchingTasks(); - Compositor().BeginFrame(); + BeginEmptyFrame(); Element& second = *GetDocument().getElementById(AtomicString("second")); @@ -594,7 +707,7 @@ )HTML"); RunAsyncMatchingTasks(); - Compositor().BeginFrame(); + BeginEmptyFrame(); Element& p = *GetDocument().getElementById(AtomicString("text")); @@ -637,7 +750,7 @@ // Force a layout GetDocument().body()->setAttribute(html_names::kStyleAttr, AtomicString("height: 1300px")); - Compositor().BeginFrame(); + BeginEmptyFrame(); EXPECT_EQ(nullptr, GetDocument().CssTarget()); EXPECT_TRUE(GetDocument().Markers().Markers().empty()); @@ -1147,6 +1260,9 @@ )HTML"); GetDocument().View()->UpdateAllLifecyclePhasesForTest(); + GetDocument().GetPage()->NotifyRelatedPagesFinalized(false); + GetDocument().GetFrame()->Loader().ProcessPendingCrossDocumentFragment(); + mojom::blink::ScrollType scroll_type = GetParam(); cc::ScrollSourceType source_type = (scroll_type == mojom::blink::ScrollType::kAnchoring || @@ -1161,7 +1277,7 @@ img_request.Complete(""); RunAsyncMatchingTasks(); - Compositor().BeginFrame(); + BeginEmptyFrame(); Element& p = *GetDocument().getElementById(AtomicString("text")); @@ -1227,7 +1343,7 @@ LayoutViewport()->SetScrollOffset(ScrollOffset(0, -10), scroll_type, source_type); - Compositor().BeginFrame(); + BeginEmptyFrame(); EXPECT_EQ(2u, GetDocument().Markers().Markers().size()); } @@ -1256,7 +1372,7 @@ </p> )HTML"); RunPendingTasks(); - Compositor().BeginFrame(); + BeginEmptyFrame(); Element* iframe = GetDocument().getElementById(AtomicString("iframe")); auto* child_frame = @@ -1279,7 +1395,7 @@ main_request.Complete(R"HTML( <!DOCTYPE html> )HTML"); - Compositor().BeginFrame(); + BeginEmptyFrame(); LocalDOMWindow* main_window = GetDocument().GetFrame()->DomWindow(); @@ -1315,6 +1431,7 @@ // Ensure that the text fragment anchor is not activated by same-document script // navigations. TEST_F(TextFragmentAnchorTest, DisabledInSamePageNavigation) { + SetAutoFinalize(false); SimRequest main_request("https://example.com/test.html", "text/html"); LoadURL("https://example.com/test.html"); main_request.Complete(R"HTML( @@ -1329,7 +1446,7 @@ </p> )HTML"); RunPendingTasks(); - Compositor().BeginFrame(); + BeginEmptyFrame(); ASSERT_EQ(ScrollOffset(), GetDocument().View()->GetScrollableArea()->GetScrollOffset()); @@ -1339,7 +1456,10 @@ ScriptState::Scope entered_context_scope(script_state); GetDocument().GetFrame()->DomWindow()->location()->setHash( script_state->GetIsolate(), ":~:text=test", ASSERT_NO_EXCEPTION); - RunAsyncMatchingTasks(); + ThreadScheduler::Current() + ->ToMainThreadScheduler() + ->StartIdlePeriodForTesting(); + task_environment().FastForwardUntilNoTasksRemain(); EXPECT_EQ(nullptr, GetDocument().CssTarget()); EXPECT_EQ(ScrollOffset(), LayoutViewport()->GetScrollOffset()); @@ -1390,7 +1510,7 @@ <p id="text">test</p> )HTML"); RunAsyncMatchingTasks(); - Compositor().BeginFrame(); + BeginEmptyFrame(); EXPECT_FALSE(GetDocument().IsLoadCompleted()); EXPECT_TRUE(GetDocument().HasFinishedParsing()); @@ -1412,7 +1532,7 @@ EXPECT_TRUE(GetDocument().IsLoadCompleted()); EXPECT_TRUE(GetDocument().HasFinishedParsing()); - Compositor().BeginFrame(); + BeginEmptyFrame(); // Ensure the target text is still in view and stayed centered ASSERT_NE(first_scroll_offset, LayoutViewport()->GetScrollOffset()); @@ -1865,7 +1985,7 @@ css_request.Complete("p { visibility: visible; top: 1001px; }"); RunAsyncMatchingTasks(); - Compositor().BeginFrame(); + BeginEmptyFrame(); EXPECT_EQ(1u, GetDocument().Markers().Markers().size()); @@ -1992,7 +2112,7 @@ <div id="element:~:id">Some text</div> )HTML"); RunPendingTasks(); - Compositor().BeginFrame(); + BeginEmptyFrame(); Element& p = *GetDocument().getElementById(AtomicString("element")); @@ -2067,7 +2187,7 @@ MainFrame().StartReload(WebFrameLoadType::kReload); reload_request.Complete(html); - Compositor().BeginFrame(); + BeginEmptyFrame(); EXPECT_EQ(*GetDocument().getElementById(AtomicString("text")), *GetDocument().CssTarget()); @@ -2670,7 +2790,7 @@ )HTML"); // Parsing completed but load is still waiting on the <img>, this will run // matching and match "test". - Compositor().BeginFrame(); + BeginEmptyFrame(); // Ensure we've attached the annotation for the text fragment. auto* container = AnnotationAgentContainerImpl::CreateIfNeeded(GetDocument()); @@ -2687,7 +2807,7 @@ // Complete the <img> request (with an error). This will fire the load event // and perform another matching pass. Test passes if this doesn't crash. sub_request.Complete(""); - Compositor().BeginFrame(); + BeginEmptyFrame(); } // Test the behavior of removing matched text while waiting to expand a @@ -2715,7 +2835,7 @@ )HTML"); // Parsing completed but load is still waiting on the <img>, this will run // matching and match "test" but queue a rAF task to show the hidden <div>. - Compositor().BeginFrame(); + BeginEmptyFrame(); // Ensure we've queued the "DomMutation" rAF task. auto* container = AnnotationAgentContainerImpl::CreateIfNeeded(GetDocument()); @@ -2736,7 +2856,7 @@ GetDocument().UpdateStyleAndLayout(DocumentUpdateReason::kTest); // This will run the "DomMutation" rAF task from the first match. - Compositor().BeginFrame(); + BeginEmptyFrame(); // The directive should not have scrolled or created a marker. EXPECT_EQ(ScrollOffset(), LayoutViewport()->GetScrollOffset()); @@ -2839,15 +2959,15 @@ )HTML"); // Ensure the load event is run. test::RunPendingTasks(); - Compositor().BeginFrame(); + BeginEmptyFrame(); ASSERT_TRUE(GetDocument().View()->GetFragmentAnchor()); test::RunDelayedTasks(TextFragmentAnchor::PostLoadTaskDelay()); - Compositor().BeginFrame(); + BeginEmptyFrame(); // Final frame for finalization. - Compositor().BeginFrame(); + BeginEmptyFrame(); EXPECT_FALSE(GetDocument().View()->GetFragmentAnchor()); EXPECT_TRUE(GetDocument().Markers().Markers().empty()); @@ -2856,6 +2976,7 @@ // Ensure that the post-load text fragment search is pushed back each time DOM // is mutated. TEST_F(TextFragmentAnchorPostLoadTest, PostLoadSearchTimesOut) { + SetAutoFinalize(false); SimRequest request("https://example.com/test.html#:~:text=test", "text/html"); LoadURL("https://example.com/test.html#:~:text=test"); request.Complete(R"HTML( @@ -2882,6 +3003,8 @@ } </script> )HTML"); + GetDocument().GetPage()->NotifyRelatedPagesFinalized(false); + GetDocument().GetFrame()->Loader().ProcessPendingCrossDocumentFragment(); // Ensure the load event is run. test::RunPendingTasks();
diff --git a/third_party/blink/renderer/core/fragment_directive/text_fragment_generation_navigation_test.cc b/third_party/blink/renderer/core/fragment_directive/text_fragment_generation_navigation_test.cc index 86ab58a..25f88f6 100644 --- a/third_party/blink/renderer/core/fragment_directive/text_fragment_generation_navigation_test.cc +++ b/third_party/blink/renderer/core/fragment_directive/text_fragment_generation_navigation_test.cc
@@ -72,6 +72,10 @@ void TextFragmentGenerationNavigationTest::SetUp() { SimTest::SetUp(); WebView().MainFrameViewWidget()->Resize(gfx::Size(800, 600)); + if (WebView().GetPage()) { + WebView().GetPage()->related_pages_mutation_from_previous_page_finalized_ = + true; + } } void TextFragmentGenerationNavigationTest::RunAsyncMatchingTasks() {
diff --git a/third_party/blink/renderer/core/fragment_directive/text_fragment_handler_test.cc b/third_party/blink/renderer/core/fragment_directive/text_fragment_handler_test.cc index bef8d77..9e94d0c 100644 --- a/third_party/blink/renderer/core/fragment_directive/text_fragment_handler_test.cc +++ b/third_party/blink/renderer/core/fragment_directive/text_fragment_handler_test.cc
@@ -47,6 +47,11 @@ void SetUp() override { SimTest::SetUp(); WebView().MainFrameViewWidget()->Resize(gfx::Size(800, 600)); + if (WebView().GetPage()) { + WebView() + .GetPage() + ->related_pages_mutation_from_previous_page_finalized_ = true; + } } void RunAsyncMatchingTasks() {
diff --git a/third_party/blink/renderer/core/fragment_directive/text_fragment_test_util.cc b/third_party/blink/renderer/core/fragment_directive/text_fragment_test_util.cc index aa01b04..400e0d4 100644 --- a/third_party/blink/renderer/core/fragment_directive/text_fragment_test_util.cc +++ b/third_party/blink/renderer/core/fragment_directive/text_fragment_test_util.cc
@@ -27,6 +27,11 @@ void TextFragmentAnchorTestBase::SetUp() { SimTest::SetUp(); + if (GetDocument().GetPage()) { + GetDocument() + .GetPage() + ->related_pages_mutation_from_previous_page_finalized_ = false; + } if (enable_virtual_time_) { // Most tests aren't concerned with the post-load task timers so use virtual // time so tests don't spend time waiting for the real-clock timers to fire. @@ -42,6 +47,16 @@ } void TextFragmentAnchorTestBase::RunAsyncMatchingTasks() { + GetDocument() + .GetPage() + ->related_pages_mutation_from_previous_page_finalized_ = false; + test::RunPendingTasks(); + GetDocument().EnqueueAnimationFrameTask(BindOnce([]() {})); + Compositor().BeginFrame(); + + GetDocument().GetPage()->NotifyRelatedPagesFinalized(false); + GetDocument().GetFrame()->Loader().ProcessPendingCrossDocumentFragment(); + ThreadScheduler::Current() ->ToMainThreadScheduler() ->StartIdlePeriodForTesting(); @@ -53,6 +68,16 @@ } void TextFragmentAnchorTestBase::RunUntilTextFragmentFinalization() { + GetDocument() + .GetPage() + ->related_pages_mutation_from_previous_page_finalized_ = false; + test::RunPendingTasks(); + GetDocument().EnqueueAnimationFrameTask(BindOnce([]() {})); + Compositor().BeginFrame(); + + GetDocument().GetPage()->NotifyRelatedPagesFinalized(false); + GetDocument().GetFrame()->Loader().ProcessPendingCrossDocumentFragment(); + FragmentAnchor* base_anchor = GetDocument().GetFrame()->View()->GetFragmentAnchor(); CHECK(base_anchor);
diff --git a/third_party/blink/renderer/core/frame/local_frame_mojo_handler.cc b/third_party/blink/renderer/core/frame/local_frame_mojo_handler.cc index 1767cf5..f7c7fa3 100644 --- a/third_party/blink/renderer/core/frame/local_frame_mojo_handler.cc +++ b/third_party/blink/renderer/core/frame/local_frame_mojo_handler.cc
@@ -1372,6 +1372,14 @@ page->GetV8CrowdsourcedCompileHintsConsumer().SetData(memory); } +void LocalFrameMojoHandler::NotifyRelatedPagesFinalized( + bool has_other_related_pages) { + if (Page* page = GetPage()) { + page->NotifyRelatedPagesFinalized(has_other_related_pages); + frame_->Loader().ProcessPendingCrossDocumentFragment(); + } +} + void LocalFrameMojoHandler::SnapshotDocumentForViewTransition( const blink::ViewTransitionToken& transition_token, mojom::blink::PageSwapEventParamsPtr params,
diff --git a/third_party/blink/renderer/core/frame/local_frame_mojo_handler.h b/third_party/blink/renderer/core/frame/local_frame_mojo_handler.h index 31b6e33..34332fa 100644 --- a/third_party/blink/renderer/core/frame/local_frame_mojo_handler.h +++ b/third_party/blink/renderer/core/frame/local_frame_mojo_handler.h
@@ -272,6 +272,7 @@ double randomized_trigger_rate, mojom::blink::ConfidenceLevel confidence) final; void SetV8CompileHints(base::ReadOnlySharedMemoryRegion data) override; + void NotifyRelatedPagesFinalized(bool has_other_related_pages) final; // mojom::FullscreenVideoElementHandler implementation: void RequestFullscreenVideoElement() final;
diff --git a/third_party/blink/renderer/core/frame/webdx_feature_tracing.cc b/third_party/blink/renderer/core/frame/webdx_feature_tracing.cc index d239f564..1fa0646 100644 --- a/third_party/blink/renderer/core/frame/webdx_feature_tracing.cc +++ b/third_party/blink/renderer/core/frame/webdx_feature_tracing.cc
@@ -142,6 +142,13 @@ column = source_location->ColumnNumber(); } + // If the specific source location is unavailable we use the document's URL + // as a fallback. + if (url.empty() && source_frame && source_frame->DomWindow() && + source_frame->DomWindow()->document()) { + url = source_frame->DomWindow()->document()->Url().GetString(); + } + TRACE_EVENT_INSTANT("blink.webdx_feature_usage", "WebDXFeatureUsage", "feature", feature_name, "url", url.Utf8(), "scriptId", script_id, "lineNumber", line, "columnNumber", column);
diff --git a/third_party/blink/renderer/core/frame/webdx_feature_tracing_test.cc b/third_party/blink/renderer/core/frame/webdx_feature_tracing_test.cc index 7bb0bca..d319d86 100644 --- a/third_party/blink/renderer/core/frame/webdx_feature_tracing_test.cc +++ b/third_party/blink/renderer/core/frame/webdx_feature_tracing_test.cc
@@ -110,4 +110,61 @@ EXPECT_EQ(0, events[0]->GetKnownArgAsInt("columnNumber")); } +TEST(WebDXFeatureTracingTest, + MaybeEmitWebDXFeatureTraceEvent_DocumentUrlFallback) { + test::TaskEnvironment task_environment; + base::test::TracingEnvironment tracing_environment; + auto dummy_page_holder = std::make_unique<DummyPageHolder>(); + + // Set a document URL to verify fallback outside V8 context + KURL doc_url("https://example.com/plain-page.html"); + dummy_page_holder->GetFrame().GetDocument()->SetURL(doc_url); + + trace_analyzer::Start("blink.webdx_feature_usage"); + + UseCounterFeature feature( + mojom::blink::UseCounterFeatureType::kWebDXFeature, + static_cast<uint32_t>(mojom::blink::WebDXFeature::kViewTransitions)); + + // Calling outside of V8 context + MaybeEmitWebDXFeatureTraceEvent(feature, &dummy_page_holder->GetFrame()); + + auto analyzer = trace_analyzer::Stop(); + + TraceEventVector events; + analyzer->FindEvents(Query::EventName() == Query::String("WebDXFeatureUsage"), + &events); + + EXPECT_EQ(1u, events.size()); + EXPECT_EQ("view-transitions", events[0]->GetKnownArgAsString("feature")); + EXPECT_EQ("https://example.com/plain-page.html", + events[0]->GetKnownArgAsString("url")); + EXPECT_EQ(-1, events[0]->GetKnownArgAsInt("lineNumber")); + EXPECT_EQ(-1, events[0]->GetKnownArgAsInt("columnNumber")); +} + +TEST(WebDXFeatureTracingTest, MaybeEmitWebDXFeatureTraceEvent_NullFrame) { + test::TaskEnvironment task_environment; + base::test::TracingEnvironment tracing_environment; + + trace_analyzer::Start("blink.webdx_feature_usage"); + + UseCounterFeature feature( + mojom::blink::UseCounterFeatureType::kWebDXFeature, + static_cast<uint32_t>(mojom::blink::WebDXFeature::kViewTransitions)); + + // Should not crash when frame is null + MaybeEmitWebDXFeatureTraceEvent(feature, nullptr); + + auto analyzer = trace_analyzer::Stop(); + + TraceEventVector events; + analyzer->FindEvents(Query::EventName() == Query::String("WebDXFeatureUsage"), + &events); + + EXPECT_EQ(1u, events.size()); + EXPECT_EQ("view-transitions", events[0]->GetKnownArgAsString("feature")); + EXPECT_EQ("", events[0]->GetKnownArgAsString("url")); +} + } // namespace blink
diff --git a/third_party/blink/renderer/core/html/build.gni b/third_party/blink/renderer/core/html/build.gni index 5c8a87a..fe406ae 100644 --- a/third_party/blink/renderer/core/html/build.gni +++ b/third_party/blink/renderer/core/html/build.gni
@@ -802,6 +802,7 @@ "parser/text_resource_decoder_test.cc", "shadow/progress_shadow_element_test.cc", "time_ranges_test.cc", + "track/cue_timeline_test.cc", "track/text_track_list_test.cc", "track/vtt/buffered_line_reader_test.cc", "track/vtt/vtt_scanner_test.cc",
diff --git a/third_party/blink/renderer/core/html/forms/resources/calendar_picker.js b/third_party/blink/renderer/core/html/forms/resources/calendar_picker.js index 8cc7737..ef1d62b7 100644 --- a/third_party/blink/renderer/core/html/forms/resources/calendar_picker.js +++ b/third_party/blink/renderer/core/html/forms/resources/calendar_picker.js
@@ -3291,7 +3291,8 @@ */ constructor(maxWidth) { super(createElement('button', MonthPopupButton.ClassNameMonthPopupButton)); - this.element.setAttribute('aria-label', global.params.axShowMonthSelector); + this.element.setAttribute( + 'aria-description', global.params.axShowMonthSelector); /** * @type {!Element}
diff --git a/third_party/blink/renderer/core/html/track/cue_timeline.cc b/third_party/blink/renderer/core/html/track/cue_timeline.cc index 0acc52c..4c1a766 100644 --- a/third_party/blink/renderer/core/html/track/cue_timeline.cc +++ b/third_party/blink/renderer/core/html/track/cue_timeline.cc
@@ -500,7 +500,9 @@ } void CueTimeline::CueEventTimerFired(TimerBase*) { - InvokeTimeMarchesOn(); + if (!MediaElement().IsShowPosterFlagSet()) { + InvokeTimeMarchesOn(); + } } void CueTimeline::CueTimestampEventTimerFired(TimerBase*) {
diff --git a/third_party/blink/renderer/core/html/track/cue_timeline.h b/third_party/blink/renderer/core/html/track/cue_timeline.h index 1755659..b9e1c815 100644 --- a/third_party/blink/renderer/core/html/track/cue_timeline.h +++ b/third_party/blink/renderer/core/html/track/cue_timeline.h
@@ -7,7 +7,9 @@ #include <optional> +#include "base/gtest_prod_util.h" #include "base/types/pass_key.h" +#include "third_party/blink/renderer/core/core_export.h" #include "third_party/blink/renderer/core/html/track/text_track_cue.h" #include "third_party/blink/renderer/core/html/track/vtt/vtt_cue.h" #include "third_party/blink/renderer/platform/heap/garbage_collected.h" @@ -32,7 +34,7 @@ // This class manages the timeline and rendering updates of cues associated // with TextTracks. Owned by a HTMLMediaElement. -class CueTimeline final : public GarbageCollected<CueTimeline> { +class CORE_EXPORT CueTimeline final : public GarbageCollected<CueTimeline> { public: class IgnoreUpdateScope { STACK_ALLOCATED(); @@ -78,6 +80,9 @@ void Trace(Visitor*) const; private: + FRIEND_TEST_ALL_PREFIXES(CueTimelineTest, + CueEventTimerFiredWithPosterFlagSet); + HTMLMediaElement& MediaElement() const { return *media_element_; } void AddCueInternal(TextTrackCue*);
diff --git a/third_party/blink/renderer/core/html/track/cue_timeline_test.cc b/third_party/blink/renderer/core/html/track/cue_timeline_test.cc new file mode 100644 index 0000000..81a9cece --- /dev/null +++ b/third_party/blink/renderer/core/html/track/cue_timeline_test.cc
@@ -0,0 +1,38 @@ +// Copyright 2026 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "third_party/blink/renderer/core/html/track/cue_timeline.h" + +#include "testing/gtest/include/gtest/gtest.h" +#include "third_party/blink/renderer/core/html/media/html_video_element.h" +#include "third_party/blink/renderer/core/html/track/text_track.h" +#include "third_party/blink/renderer/core/html/track/text_track_list.h" +#include "third_party/blink/renderer/core/testing/dummy_page_holder.h" +#include "third_party/blink/renderer/platform/testing/task_environment.h" + +namespace blink { + +// Regression test: CueEventTimerFired should not crash when poster flag is set. +TEST(CueTimelineTest, CueEventTimerFiredWithPosterFlagSet) { + test::TaskEnvironment task_environment; + auto page_holder = std::make_unique<DummyPageHolder>(); + auto* video = + MakeGarbageCollected<HTMLVideoElement>(page_holder->GetDocument()); + + // Video starts with show_poster_flag_ = true + ASSERT_TRUE(video->IsShowPosterFlagSet()); + + // Create text track to initialize CueTimeline + TextTrack* track = MakeGarbageCollected<TextTrack>( + V8TextTrackKind(V8TextTrackKind::Enum::kCaptions), g_empty_atom, + g_empty_atom, *video); + video->textTracks()->Append(track); + + CueTimeline& cue_timeline = video->GetCueTimeline(); + + // Should not crash - guard in CueEventTimerFired + cue_timeline.CueEventTimerFired(nullptr); +} + +} // namespace blink
diff --git a/third_party/blink/renderer/core/layout/absolute_utils.cc b/third_party/blink/renderer/core/layout/absolute_utils.cc index 992160c..7519fd5 100644 --- a/third_party/blink/renderer/core/layout/absolute_utils.cc +++ b/third_party/blink/renderer/core/layout/absolute_utils.cc
@@ -426,43 +426,29 @@ bool CanComputeBlockSizeWithoutLayout( const BlockNode& node, - WritingDirectionMode container_writing_direction, - ItemPosition block_alignment_position, - bool has_auto_block_inset, - bool has_inline_size) { + AutoSizeBehavior block_auto_size_behavior) { // Tables (even with an explicit size) apply a min-content constraint. if (node.IsTable()) { return false; } - // Replaced elements always have their size computed ahead of time. - if (node.IsReplaced()) { - return true; - } const auto& style = node.Style(); if (style.LogicalHeight().HasContentOrIntrinsic() || style.LogicalMinHeight().HasContentOrIntrinsic() || style.LogicalMaxHeight().HasContentOrIntrinsic()) { return false; } - if (style.LogicalHeight().HasAuto()) { - // Any 'auto' inset will trigger fit-content. - if (has_auto_block_inset) { - return false; - } - // Check for an explicit stretch. - if (block_alignment_position == ItemPosition::kStretch) { - return true; - } - // Non-normal alignment will trigger fit-content. - if (block_alignment_position != ItemPosition::kNormal) { - return false; - } - // An aspect-ratio (with a definite inline-size) will trigger fit-content. - if (!style.AspectRatio().IsAuto() && has_inline_size) { - return false; - } + if (!style.LogicalHeight().HasAuto()) { + return true; } - return true; + switch (block_auto_size_behavior) { + case AutoSizeBehavior::kFitContent: + return false; + case AutoSizeBehavior::kStretchExplicit: + return true; + case AutoSizeBehavior::kStretchImplicit: + // An aspect-ratio will trigger fit-content. + return style.AspectRatio().IsAuto(); + } } } // namespace @@ -685,48 +671,26 @@ const BoxStrut& border_padding, const std::optional<LogicalSize>& replaced_size, const BoxStrut& container_insets, + AutoSizeBehavior inline_auto_size_behavior, + AutoSizeBehavior block_auto_size_behavior, WritingDirectionMode container_writing_direction, LogicalOofDimensions* dimensions) { DCHECK(dimensions); DCHECK_GE(imcb.InlineSize(), LayoutUnit()); - const auto alignment_position = alignment.inline_alignment.GetPosition(); - const auto block_alignment_position = alignment.block_alignment.GetPosition(); - bool depends_on_min_max_sizes = false; - const bool can_compute_block_size_without_layout = - CanComputeBlockSizeWithoutLayout(node, container_writing_direction, - block_alignment_position, - imcb.has_auto_block_inset, - /* has_inline_size */ false); - auto MinMaxSizesFunc = [&](SizeType type) -> MinMaxSizesResult { DCHECK(!node.IsReplaced()); // Mark the inline calculations as being dependent on min/max sizes. depends_on_min_max_sizes = true; - // If we can't compute our block-size without layout, we can use the - // provided space to determine our min/max sizes. - if (!can_compute_block_size_without_layout) - return node.ComputeMinMaxSizes(style.GetWritingMode(), type, space); - - // Compute our block-size if we haven't already. - if (dimensions->size.block_size == kIndefiniteSize) { - ComputeOofBlockDimensions( - node, break_token, style, space, imcb, anchor_center_position, - alignment, border_padding, /*replaced_size=*/std::nullopt, - container_insets, container_writing_direction, dimensions); - } - - // Create a new space, setting the fixed block-size. ConstraintSpaceBuilder builder(style.GetWritingMode(), style.GetWritingDirection(), /* is_new_fc */ true); - builder.SetAvailableSize( - {space.AvailableSize().inline_size, dimensions->size.block_size}); - builder.SetIsFixedBlockSize(true); + builder.SetAvailableSize(imcb.Size()); builder.SetPercentageResolutionSize(space.PercentageResolutionSize()); + builder.SetBlockAutoBehavior(block_auto_size_behavior); return node.ComputeMinMaxSizes(style.GetWritingMode(), type, builder.ToConstraintSpace()); }; @@ -736,50 +700,50 @@ DCHECK(node.IsReplaced()); inline_size = replaced_size->inline_size; } else { - const Length& main_inline_length = style.LogicalWidth(); - - const bool is_implicit_stretch = - !imcb.has_auto_inline_inset && - alignment_position == ItemPosition::kNormal; - const bool is_explicit_stretch = - !imcb.has_auto_inline_inset && - alignment_position == ItemPosition::kStretch; - const bool is_stretch = is_implicit_stretch || is_explicit_stretch; - - // If our block constraint is strong/explicit. - const bool is_block_explicit = - !style.LogicalHeight().HasAuto() || - (!imcb.has_auto_block_inset && - block_alignment_position == ItemPosition::kStretch); + auto may_apply_aspect_ratio = [&]() -> bool { + // Check if we have an aspect-ratio. + if (style.AspectRatio().IsAuto()) { + return false; + } + // Check if we can resolve our main block-size. + return ResolveMainBlockLength( + space, style, border_padding, style.LogicalHeight(), + block_auto_size_behavior == AutoSizeBehavior::kFitContent + ? &Length::FitContent() + : &Length::Stretch(), + kIndefiniteSize, imcb.BlockSize()) != kIndefiniteSize; + }; // Determine how "auto" should resolve. bool apply_automatic_min_size = false; const Length& auto_length = ([&]() { - // Tables always shrink-to-fit unless explicitly asked to stretch. - if (node.IsTable()) { - return is_explicit_stretch ? Length::Stretch() : Length::FitContent(); + switch (inline_auto_size_behavior) { + case AutoSizeBehavior::kFitContent: + // Check if we need to apply the auto min-size. + if (may_apply_aspect_ratio() && + style.OverflowInlineDirection() == EOverflow::kVisible) { + apply_automatic_min_size = true; + } + return Length::FitContent(); + case AutoSizeBehavior::kStretchExplicit: + return Length::Stretch(); + case AutoSizeBehavior::kStretchImplicit: + // Check if the aspect-ratio applies (we have an explicit block-size). + const bool is_block_explicit = + !style.LogicalHeight().HasAuto() || + block_auto_size_behavior == AutoSizeBehavior::kStretchExplicit; + if (is_block_explicit && may_apply_aspect_ratio()) { + if (style.OverflowInlineDirection() == EOverflow::kVisible) { + apply_automatic_min_size = true; + } + return Length::FitContent(); + } + return Length::Stretch(); } - // We'd like to apply the aspect-ratio. - // The aspect-ratio applies from the block-axis if we can compute our - // block-size without invoking layout, and either: - // - We aren't stretching our auto inline-size. - // - We are stretching our auto inline-size, but the block-size has a - // stronger (explicit) constraint, e.g: - // "height:10px" or "align-self:stretch". - if (!style.AspectRatio().IsAuto() && - can_compute_block_size_without_layout && - (!is_stretch || (is_implicit_stretch && is_block_explicit))) { - // See if we should apply the automatic minimum size. - if (style.OverflowInlineDirection() == EOverflow::kVisible) { - apply_automatic_min_size = true; - } - return Length::FitContent(); - } - return is_stretch ? Length::Stretch() : Length::FitContent(); })(); const LayoutUnit main_inline_size = ResolveMainInlineLength( - space, style, border_padding, MinMaxSizesFunc, main_inline_length, + space, style, border_padding, MinMaxSizesFunc, style.LogicalWidth(), &auto_length, imcb.InlineSize()); const MinMaxSizes min_max_inline_sizes = ComputeMinMaxInlineSizes( space, node, border_padding, @@ -843,23 +807,18 @@ const BoxStrut& border_padding, const std::optional<LogicalSize>& replaced_size, const BoxStrut& container_insets, + AutoSizeBehavior block_auto_size_behavior, WritingDirectionMode container_writing_direction, LogicalOofDimensions* dimensions) { DCHECK(dimensions); DCHECK_GE(imcb.BlockSize(), LayoutUnit()); - const auto alignment_position = alignment.block_alignment.GetPosition(); - const LayoutResult* result = nullptr; LayoutUnit block_size; if (replaced_size) { DCHECK(node.IsReplaced()); block_size = replaced_size->block_size; - } else if (CanComputeBlockSizeWithoutLayout( - node, container_writing_direction, alignment_position, - imcb.has_auto_block_inset, - /* has_inline_size */ dimensions->size.inline_size != - kIndefiniteSize)) { + } else if (CanComputeBlockSizeWithoutLayout(node, block_auto_size_behavior)) { DCHECK(!node.IsTable()); // Nothing depends on our intrinsic-size, so we can safely use the initial @@ -887,13 +846,7 @@ if (space.IsHiddenForPaint()) { builder.SetIsHiddenForPaint(true); } - - // Tables need to know about the explicit stretch constraint to produce - // the correct result. - if (!imcb.has_auto_block_inset && - alignment_position == ItemPosition::kStretch) { - builder.SetBlockAutoBehavior(AutoSizeBehavior::kStretchExplicit); - } + builder.SetBlockAutoBehavior(block_auto_size_behavior); if (space.IsInitialColumnBalancingPass()) { // The |fragmentainer_offset_delta| will not make a difference in the
diff --git a/third_party/blink/renderer/core/layout/absolute_utils.h b/third_party/blink/renderer/core/layout/absolute_utils.h index c041f42..d45a5ec5 100644 --- a/third_party/blink/renderer/core/layout/absolute_utils.h +++ b/third_party/blink/renderer/core/layout/absolute_utils.h
@@ -16,6 +16,7 @@ namespace blink { +enum class AutoSizeBehavior : uint8_t; class BlockNode; class ConstraintSpace; class LayoutResult; @@ -170,9 +171,6 @@ // It needs to be computed in 2 stages: // 1. The inline-dimensions with |ComputeOofInlineDimensions|. // 2. The block-dimensions with |ComputeOofBlockDimensions|. -// -// NOTE: |ComputeOofInlineDimensions| may call |ComputeOofBlockDimensions| if -// its required to correctly determine the min/max content sizes. // |replaced_size| should be set if and only if element is replaced element. // Will return true if |BlockNode::ComputeMinMaxSizes| was called. @@ -187,6 +185,8 @@ const BoxStrut& border_padding, const std::optional<LogicalSize>& replaced_size, const BoxStrut& container_insets, + AutoSizeBehavior inline_auto_size_behavior, + AutoSizeBehavior block_auto_size_behavior, WritingDirectionMode container_writing_direction, LogicalOofDimensions* dimensions); @@ -203,6 +203,7 @@ const BoxStrut& border_padding, const std::optional<LogicalSize>& replaced_size, const BoxStrut& container_insets, + AutoSizeBehavior block_auto_size_behavior, WritingDirectionMode container_writing_direction, LogicalOofDimensions* dimensions);
diff --git a/third_party/blink/renderer/core/layout/absolute_utils_test.cc b/third_party/blink/renderer/core/layout/absolute_utils_test.cc index 4688958..37972d4 100644 --- a/third_party/blink/renderer/core/layout/absolute_utils_test.cc +++ b/third_party/blink/renderer/core/layout/absolute_utils_test.cc
@@ -85,6 +85,11 @@ RunDocumentLifecycle(); } + static AutoSizeBehavior ToAutoSizeBehavior(bool has_auto_inset) { + return has_auto_inset ? AutoSizeBehavior::kFitContent + : AutoSizeBehavior::kStretchImplicit; + } + void ComputeOutOfFlowInlineDimensions( const BlockNode& node, const ConstraintSpace& space, @@ -105,10 +110,13 @@ node, space.AvailableSize(), LogicalAlignment(), insets, static_position, container_writing_direction, node.Style().GetWritingDirection()); - ComputeOofInlineDimensions( - node, /*break_token=*/nullptr, node.Style(), space, imcb, - LogicalAnchorCenterPosition(), LogicalAlignment(), border_padding, - std::nullopt, BoxStrut(), container_writing_direction, dimensions); + ComputeOofInlineDimensions(node, /*break_token=*/nullptr, node.Style(), + space, imcb, LogicalAnchorCenterPosition(), + LogicalAlignment(), border_padding, std::nullopt, + BoxStrut(), + ToAutoSizeBehavior(imcb.has_auto_inline_inset), + ToAutoSizeBehavior(imcb.has_auto_block_inset), + container_writing_direction, dimensions); GetDocument().Lifecycle().AdvanceTo(DocumentLifecycle::kAfterPerformLayout); GetDocument().Lifecycle().AdvanceTo(DocumentLifecycle::kLayoutClean); } @@ -136,7 +144,8 @@ ComputeOofBlockDimensions( node, /*break_token=*/nullptr, node.Style(), space, imcb, LogicalAnchorCenterPosition(), LogicalAlignment(), border_padding, - std::nullopt, BoxStrut(), container_writing_direction, dimensions); + std::nullopt, BoxStrut(), ToAutoSizeBehavior(imcb.has_auto_block_inset), + container_writing_direction, dimensions); GetDocument().Lifecycle().AdvanceTo(DocumentLifecycle::kAfterPerformLayout); GetDocument().Lifecycle().AdvanceTo(DocumentLifecycle::kLayoutClean); }
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 5aaa5b6c..d53a99a 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
@@ -2453,6 +2453,32 @@ const BoxStrut border_padding = ComputeBorders(space, node_info.node) + ComputePadding(space, candidate_style); + auto auto_size_behavior = [&](ItemPosition position, + bool has_auto_inset) -> AutoSizeBehavior { + // Any auto inset will trigger shrink-to-fit. + if (has_auto_inset) { + return AutoSizeBehavior::kFitContent; + } + + // An explicit stretch will always win. + if (position == ItemPosition::kStretch) { + return AutoSizeBehavior::kStretchExplicit; + } + + // Replaced/tables don't stretch in abspos. + if (node_info.node.IsTable() || node_info.node.IsReplaced()) { + return AutoSizeBehavior::kFitContent; + } + + return position == ItemPosition::kNormal + ? AutoSizeBehavior::kStretchImplicit + : AutoSizeBehavior::kFitContent; + }; + const AutoSizeBehavior inline_auto_size_behavior = auto_size_behavior( + alignment.inline_alignment.GetPosition(), imcb.has_auto_inline_inset); + const AutoSizeBehavior block_auto_size_behavior = auto_size_behavior( + alignment.block_alignment.GetPosition(), imcb.has_auto_block_inset); + std::optional<LogicalSize> replaced_size; if (node_info.node.IsReplaced()) { // Create a new space with the IMCB size, and stretch constraints. @@ -2461,29 +2487,8 @@ /* is_new_fc */ true); builder.SetAvailableSize(imcb.Size()); builder.SetPercentageResolutionSize(space.PercentageResolutionSize()); - - const bool is_parallel = - IsParallelWritingMode(container_writing_direction.GetWritingMode(), - candidate_writing_direction.GetWritingMode()); - const ItemPosition inline_position = - (is_parallel ? candidate_style.JustifySelf() - : candidate_style.AlignSelf()) - .GetPosition(); - const bool is_inline_stretch = !imcb.has_auto_inline_inset && - inline_position == ItemPosition::kStretch; - if (is_inline_stretch) { - builder.SetInlineAutoBehavior(AutoSizeBehavior::kStretchExplicit); - } - const ItemPosition block_position = - (is_parallel ? candidate_style.AlignSelf() - : candidate_style.JustifySelf()) - .GetPosition(); - const bool is_block_stretch = - !imcb.has_auto_block_inset && block_position == ItemPosition::kStretch; - if (is_block_stretch) { - builder.SetBlockAutoBehavior(AutoSizeBehavior::kStretchExplicit); - } - + builder.SetInlineAutoBehavior(inline_auto_size_behavior); + builder.SetBlockAutoBehavior(block_auto_size_behavior); replaced_size = ComputeReplacedSize(node_info.node, builder.ToConstraintSpace(), border_padding, ReplacedSizeMode::kNormal); @@ -2499,16 +2504,13 @@ offset_info.inline_size_depends_on_min_max_sizes = ComputeOofInlineDimensions( node_info.node, node_info.break_token, candidate_style, space, imcb, anchor_center_position, alignment, border_padding, replaced_size, - container_insets, container_writing_direction, &node_dimensions); - - // We may have already pre-computed our block-dimensions when determining - // our min/max sizes, only run if needed. - if (node_dimensions.size.block_size == kIndefiniteSize) { - offset_info.initial_layout_result = ComputeOofBlockDimensions( - node_info.node, node_info.break_token, candidate_style, space, imcb, - anchor_center_position, alignment, border_padding, replaced_size, - container_insets, container_writing_direction, &node_dimensions); - } + container_insets, inline_auto_size_behavior, block_auto_size_behavior, + container_writing_direction, &node_dimensions); + offset_info.initial_layout_result = ComputeOofBlockDimensions( + node_info.node, node_info.break_token, candidate_style, space, imcb, + anchor_center_position, alignment, border_padding, replaced_size, + container_insets, block_auto_size_behavior, container_writing_direction, + &node_dimensions); if (try_fit_available_space) { const PhysicalToLogicalGetter has_non_auto_inset( @@ -2563,6 +2565,8 @@ offset_info.container_content_size = ToLogicalSize(container_physical_content_size, candidate_writing_direction.GetWritingMode()); + offset_info.imcb_block_size = imcb.BlockSize(); + offset_info.block_auto_size_behavior = block_auto_size_behavior; if (const BlockBreakToken* break_token = node_info.break_token) { DCHECK(RuntimeEnabledFeatures::FragmentedOofInCbEnabled()); @@ -2762,6 +2766,7 @@ const BlockBreakToken* break_token = node_info.break_token; const BlockNode& node = node_info.node; const auto& style = node.Style(); + const bool is_replaced = node.IsReplaced(); const LayoutUnit block_offset = offset_info.offset.block_offset; const bool force_orthogonal_writing_mode_root = !IsParallelWritingMode( @@ -2773,26 +2778,26 @@ /* is_new_fc */ true, /* adjust_inline_size_if_needed */ true, force_orthogonal_writing_mode_root); - builder.SetAvailableSize(offset_info.node_dimensions.size); + builder.SetAvailableSize({offset_info.node_dimensions.size.inline_size, + is_replaced + ? offset_info.node_dimensions.size.block_size + : offset_info.imcb_block_size}); builder.SetPercentageResolutionSize(offset_info.container_content_size); builder.SetIsFixedInlineSize(true); builder.SetIsHiddenForPaint( node_info.base_container_info.is_hidden_for_paint); + if (is_replaced) { + builder.SetIsFixedBlockSize(true); + } else { + builder.SetBlockAutoBehavior(offset_info.block_auto_size_behavior); + } + const bool is_in_block_fragmentation = (RuntimeEnabledFeatures::FragmentedOofInCbEnabled() && GetConstraintSpace().HasBlockFragmentation()) || fragmentainer_constraint_space; - // In some cases we will need the fragment size in order to calculate the - // offset. We may have to lay out to get the fragment size. For block - // fragmentation, we *need* to know the block-offset before layout. In other - // words, in that case, we may have to lay out, calculate the offset, and then - // lay out again at the correct block-offset. - if (!is_in_block_fragmentation || !offset_info.initial_layout_result) { - builder.SetIsFixedBlockSize(true); - } - bool is_repeatable = false; if (is_in_block_fragmentation) { if (container_builder_->Node().IsPaginatedRoot() &&
diff --git a/third_party/blink/renderer/core/layout/out_of_flow_layout_part.h b/third_party/blink/renderer/core/layout/out_of_flow_layout_part.h index ff6cde8..892e8fe 100644 --- a/third_party/blink/renderer/core/layout/out_of_flow_layout_part.h +++ b/third_party/blink/renderer/core/layout/out_of_flow_layout_part.h
@@ -226,8 +226,11 @@ // re-used or replaced in the final layout pass. Member<const LayoutResult> initial_layout_result; - // `container_content_size` is wrt. the candidate's writing mode. + // The following fields are in the writing-direction of the candidate, and + // are used for creating the constraint space for layout. LogicalSize container_content_size; + LayoutUnit imcb_block_size; + AutoSizeBehavior block_auto_size_behavior; LogicalOofDimensions node_dimensions;
diff --git a/third_party/blink/renderer/core/loader/document_loader.h b/third_party/blink/renderer/core/loader/document_loader.h index ee90152..cfa5fda 100644 --- a/third_party/blink/renderer/core/loader/document_loader.h +++ b/third_party/blink/renderer/core/loader/document_loader.h
@@ -421,6 +421,10 @@ // Returns the text fragment to scroll to and clears it. std::optional<String> TakeInternalScrollToTextFragment(); + bool HasInternalScrollToTextFragment() const { + return internal_scroll_to_text_fragment_.has_value(); + } + // For testing purposes. void SetInternalScrollToTextFragment(const String& text_fragment) { internal_scroll_to_text_fragment_ = text_fragment;
diff --git a/third_party/blink/renderer/core/loader/frame_loader.cc b/third_party/blink/renderer/core/loader/frame_loader.cc index c590284..2271968 100644 --- a/third_party/blink/renderer/core/loader/frame_loader.cc +++ b/third_party/blink/renderer/core/loader/frame_loader.cc
@@ -74,6 +74,8 @@ #include "third_party/blink/renderer/core/events/page_transition_event.h" #include "third_party/blink/renderer/core/exported/web_plugin_container_impl.h" #include "third_party/blink/renderer/core/fetch/fetch_later_util.h" +#include "third_party/blink/renderer/core/fragment_directive/fragment_directive.h" +#include "third_party/blink/renderer/core/fragment_directive/text_directive.h" #include "third_party/blink/renderer/core/fragment_directive/text_fragment_anchor.h" #include "third_party/blink/renderer/core/frame/csp/content_security_policy.h" #include "third_party/blink/renderer/core/frame/csp/csp_source.h" @@ -475,9 +477,26 @@ !document_loader_ || document_loader_->IsCommittedButEmpty()); } - if (frame_->View()) { + bool has_text_fragment = false; + if (frame_->GetDocument()) { + has_text_fragment = !frame_->GetDocument() + ->fragmentDirective() + .GetDirectives<TextDirective>() + .empty(); + if (!has_text_fragment && document_loader_) { + has_text_fragment = document_loader_->HasInternalScrollToTextFragment(); + } + } + + if (!has_text_fragment || + (frame_->View() && frame_->GetPage() && + frame_->GetPage()->RelatedPagesMutationFromPreviousPageFinalized())) { ProcessFragment(frame_->GetDocument()->Url(), document_loader_->LoadType(), kNavigationToDifferentDocument); + } else { + // TODO(crbug.com/509527381): Consider adding a timeout to reset this back + // to false. + has_pending_cross_document_fragment_ = true; } frame_->GetDocument()->CheckCompleted(); @@ -554,6 +573,7 @@ // We need to scroll to the fragment whether or not a hash change occurred, // since the user might have scrolled since the previous navigation. ProcessFragment(url, frame_load_type, kNavigationWithinSameDocument); + has_pending_cross_document_fragment_ = false; TakeObjectSnapshot(); } @@ -1913,4 +1933,13 @@ return document_loader_->CreateCodeCacheHost(); } +void FrameLoader::ProcessPendingCrossDocumentFragment() { + if (!has_pending_cross_document_fragment_) { + return; + } + has_pending_cross_document_fragment_ = false; + ProcessFragment(frame_->GetDocument()->Url(), document_loader_->LoadType(), + kNavigationToDifferentDocument); +} + } // namespace blink
diff --git a/third_party/blink/renderer/core/loader/frame_loader.h b/third_party/blink/renderer/core/loader/frame_loader.h index b859bd1a7..fc6a7f10 100644 --- a/third_party/blink/renderer/core/loader/frame_loader.h +++ b/third_party/blink/renderer/core/loader/frame_loader.h
@@ -262,6 +262,8 @@ mojo::PendingRemote<mojom::blink::CodeCacheHost> CreateWorkerCodeCacheHost(); + void ProcessPendingCrossDocumentFragment(); + private: bool ShouldPerformFragmentNavigation(bool is_form_submission, const String& http_method, @@ -349,6 +351,10 @@ // The origins for which a legacy TLS version warning has been printed. The // size of this set is capped, after which no more warnings are printed. HashSet<String> tls_version_warning_origins_; + + // True if we skipped processing a fragment and may need to do it again when + // asked. + bool has_pending_cross_document_fragment_ = false; }; } // namespace blink
diff --git a/third_party/blink/renderer/core/page/context_menu_controller.cc b/third_party/blink/renderer/core/page/context_menu_controller.cc index 1d34bce..41e66cb4 100644 --- a/third_party/blink/renderer/core/page/context_menu_controller.cc +++ b/third_party/blink/renderer/core/page/context_menu_controller.cc
@@ -826,8 +826,7 @@ // Extract suggested filename for same-origin URLS for saving file. const SecurityOrigin* origin = selected_frame->GetSecurityContext()->GetSecurityOrigin(); - const KURL& complete_url = anchor->LegacyHrefURL(anchor->GetDocument()); - if (origin->CanReadContent(complete_url)) { + if (origin->CanReadContent(anchor->Url())) { data.suggested_filename = anchor->FastGetAttribute(svg_names::kDownloadAttr).Utf8(); }
diff --git a/third_party/blink/renderer/core/page/drag_controller.cc b/third_party/blink/renderer/core/page/drag_controller.cc index 0569273..5c21b6d3 100644 --- a/third_party/blink/renderer/core/page/drag_controller.cc +++ b/third_party/blink/renderer/core/page/drag_controller.cc
@@ -1430,7 +1430,8 @@ gfx::Point adjusted_event_pos = frame->View()->FrameToViewport(drag_initiation_location); gfx::Vector2d cursor_offset = adjusted_event_pos - adjusted_drag_obj_location; - WebDragData drag_data = data_transfer->GetDataObject()->ToWebDragData(); + WebDragData drag_data = + data_transfer->GetDataObject()->ToWebDragData(frame->DomWindow()); if (drag_data.SourceEffectAllowed().IsNull()) { drag_data.SetSourceEffectAllowed(data_transfer->effectAllowed()); }
diff --git a/third_party/blink/renderer/core/page/page.h b/third_party/blink/renderer/core/page/page.h index a1734a02..7a144a0 100644 --- a/third_party/blink/renderer/core/page/page.h +++ b/third_party/blink/renderer/core/page/page.h
@@ -102,6 +102,11 @@ class TopDocumentRootScrollerController; class ValidationMessageClient; class VisualViewport; +class TextFragmentAnchorTestBase; +class TextFragmentAnchorTest; +class TextFragmentAnchorMetricsTest; +class TextFragmentHandlerTest; +class TextFragmentGenerationNavigationTest; typedef uint64_t LinkHash; @@ -532,7 +537,24 @@ // related pages will include the new page instead of the old page, etc. void TakePropertiesForLocalMainFrameSwap(Page* old_page); + void NotifyRelatedPagesFinalized(bool has_other_related_pages) { + related_pages_mutation_from_previous_page_finalized_ = true; + has_other_related_pages_during_commit_ = has_other_related_pages; + } + + bool RelatedPagesMutationFromPreviousPageFinalized() const { + return related_pages_mutation_from_previous_page_finalized_; + } + bool HasOtherRelatedPagesDuringCommit() const { + return has_other_related_pages_during_commit_; + } + private: + friend class TextFragmentAnchorTestBase; + friend class TextFragmentAnchorTest; + friend class TextFragmentAnchorMetricsTest; + friend class TextFragmentHandlerTest; + friend class TextFragmentGenerationNavigationTest; friend class ScopedPagePauser; class CloseTaskHandler; @@ -665,6 +687,18 @@ Member<Page> next_related_page_; Member<Page> prev_related_page_; + // Indicates whether the related pages set can change due to previous page's + // mutations. Set when this (new page) is being committed. Once finalized, we + // would not expect the previous page to change the related pages set + // (although it can still change on this page). + bool related_pages_mutation_from_previous_page_finalized_ = false; + // Note that `has_other_related_pages_during_commit_` may not be in sync with + // RelatedPages() list and that's a bug. This is only used for text fragment + // checks to see if we're allowed to do a scroll or not. Ideally we don't need + // this and should just check the RelatedPages() set if the bug is fixed. See + // crbug.com/457771782 for details. + bool has_other_related_pages_during_commit_ = false; + // The Page that opened this Page. WeakMember<Page> opener_;
diff --git a/third_party/blink/renderer/core/script_tools/model_context.cc b/third_party/blink/renderer/core/script_tools/model_context.cc index 03c13869..91197f4 100644 --- a/third_party/blink/renderer/core/script_tools/model_context.cc +++ b/third_party/blink/renderer/core/script_tools/model_context.cc
@@ -330,6 +330,9 @@ script_tool->name = tool->name(); script_tool->description = tool->description(); script_tool->input_schema = input_schema; + // TODO(https://crbug.com/509568047): Stop setting these two members. + script_tool->tool_owner_frame_token = document_->GetFrame()->GetFrameToken(); + script_tool->origin = document_->GetExecutionContext()->GetSecurityOrigin(); Vector<scoped_refptr<const SecurityOrigin>> exposed_origins; if (options && options->hasExposedTo()) { @@ -661,6 +664,9 @@ script_tool->name = name; script_tool->description = description; script_tool->input_schema = "{}"; // For now + // TODO(https://crbug.com/509568047): Stop setting these two members. + script_tool->tool_owner_frame_token = document_->GetFrame()->GetFrameToken(); + script_tool->origin = document_->GetExecutionContext()->GetSecurityOrigin(); auto* tool_data = MakeGarbageCollected<ToolData>( base::PassKey<ModelContext>(), std::move(script_tool), declarative_tool); @@ -757,6 +763,27 @@ OnToolChange(/*force=*/true); } +void ModelContext::ExecuteScriptTool(const String& name, + const String& input_arguments, + ExecuteScriptToolCallback callback) { + // TODO(http://b/485810761): Pass `invocation_id` up from the browser, instead + // of generating it in the renderer. + ExecuteTool( + /*invocation_id=*/base::UnguessableToken::Create(), name, input_arguments, + /*signal=*/nullptr, + blink::BindOnce( + [](ExecuteScriptToolCallback callback, + base::expected<String, ScriptToolError> result) { + if (result.has_value()) { + std::move(callback).Run(result.value(), true); + } else { + std::move(callback).Run(GetToolErrorMessage(result.error()), + false); + } + }, + std::move(callback))); +} + HeapVector<Member<const ToolData>> ModelContext::ListTools() const { HeapVector<Member<const ToolData>> tools; tools.ReserveInitialCapacity(tool_map_.size()); @@ -829,12 +856,128 @@ if (!t->input_schema.IsNull()) { result->setInputSchema(t->input_schema); } + + Frame* frame = Frame::ResolveFrame(t->tool_owner_frame_token); + // If we can't resolve the token into a concrete frame, that means the + // document could have been discarded by the time the response IPC comes + // back to the renderer. In that case, the tool is unusable from our + // perspective, so exclude it from `registered_tools`. + if (!frame) { + continue; + } + + result->setWindow(frame->DomWindow()); + result->setOrigin(t->origin->ToString()); registered_tools.push_back(result); } resolver->Resolve(registered_tools); } +ScriptPromise<IDLNullable<IDLString>> ModelContext::executeTool( + ScriptState* script_state, + RegisteredTool* tool, + String input_arguments, + const ExecuteToolOptions* options) { + if (!document_->IsActive()) { + return ScriptPromise<IDLNullable<IDLString>>::RejectWithDOMException( + script_state, + MakeGarbageCollected<DOMException>(DOMExceptionCode::kInvalidStateError, + "The document is not active.")); + } + + auto* resolver = + MakeGarbageCollected<ScriptPromiseResolver<IDLNullable<IDLString>>>( + script_state); + ScriptPromise promise = resolver->Promise(); + + if (!ExecutionContext::From(script_state) + ->IsFeatureEnabled( + network::mojom::PermissionsPolicyFeature::kTools)) { + resolver->RejectWithSecurityError(kPermissionPolicyNotEnabledError, + kPermissionPolicyNotEnabledError); + return promise; + } + + DOMWindow* window = tool->window(); + // `window` is always non-null, but its frame might be missing if the document + // was detached or discarded in the gap between tool retrieval and tool + // execution. + CHECK(window); + + Frame* target_frame = window->GetFrame(); + if (!target_frame) { + resolver->RejectWithDOMException(DOMExceptionCode::kInvalidStateError, + "Target frame is detached."); + return promise; + } + blink::FrameToken frame_token = target_frame->GetFrameToken(); + + // Because the document is active, we know `local_frame` is non-null. + LocalFrame* local_frame = document_->GetFrame(); + CHECK(local_frame); + + std::unique_ptr<ScopedAbortState> scoped_abort_state; + if (options && options->hasSignal()) { + AbortSignal* signal = options->signal(); + if (signal->aborted()) { + resolver->RejectWithDOMException(DOMExceptionCode::kAbortError, + "Execution cancelled."); + return promise; + } + + auto* handle = signal->AddAlgorithm(BindOnce( + [](ScriptPromiseResolver<IDLNullable<IDLString>>* resolver, + ScriptState* script_state, AbortSignal* signal) { + if (resolver->GetScriptState() && + resolver->GetScriptState()->ContextIsValid()) { + resolver->Reject(signal->reason(script_state)); + } + }, + WrapPersistent(resolver), WrapPersistent(script_state), + WrapPersistent(signal))); + + scoped_abort_state = std::make_unique<ScopedAbortState>(signal, handle); + } + + model_context_host_remote_->ExecuteRemoteScriptTool( + frame_token, SecurityOrigin::CreateFromString(tool->origin()), + tool->name(), input_arguments, + blink::BindOnce( + [](ModelContext* self, + ScriptPromiseResolver<IDLNullable<IDLString>>* resolver, + std::unique_ptr<ScopedAbortState> abort_state, + const String& result, bool success) { + if (self) { + self->OnExecuteScriptToolCompleted(resolver, result, success); + } + }, + WrapWeakPersistent(this), WrapPersistent(resolver), + std::move(scoped_abort_state))); + return promise; +} + +void ModelContext::OnExecuteScriptToolCompleted( + ScriptPromiseResolver<IDLNullable<IDLString>>* resolver, + const String& result, + bool success) { + // For the execution result to have been received from the browser process + // over mojo, the frame/Document that sent the execution must not be detached, + // and the same goes for the `resolver` that is tied to those objects. + CHECK(resolver->GetScriptState() && + resolver->GetScriptState()->ContextIsValid()); + + // `result` is either the result or the error string, so we can use it + // unconditionally below. + if (success) { + resolver->Resolve(result); + } else { + // TODO(https://crbug.com/509555636): Support more granular execution error + // reasons. + resolver->RejectWithDOMException(DOMExceptionCode::kUnknownError, result); + } +} + void ModelContext::Trace(Visitor* visitor) const { EventTarget::Trace(visitor); visitor->Trace(tool_map_);
diff --git a/third_party/blink/renderer/core/script_tools/model_context.h b/third_party/blink/renderer/core/script_tools/model_context.h index ad51d81b..026e981 100644 --- a/third_party/blink/renderer/core/script_tools/model_context.h +++ b/third_party/blink/renderer/core/script_tools/model_context.h
@@ -14,6 +14,7 @@ #include "third_party/blink/public/mojom/content_extraction/script_tools.mojom-blink.h" #include "third_party/blink/renderer/bindings/core/v8/script_promise.h" #include "third_party/blink/renderer/bindings/core/v8/script_promise_resolver.h" +#include "third_party/blink/renderer/bindings/core/v8/v8_execute_tool_options.h" #include "third_party/blink/renderer/bindings/core/v8/v8_model_context.h" #include "third_party/blink/renderer/bindings/core/v8/v8_tool_execute_callback.h" #include "third_party/blink/renderer/core/core_export.h" @@ -29,6 +30,7 @@ namespace blink { class AbortSignal; class Element; +class ExecuteToolOptions; class SourceLocation; class ModelContextOptions; class ModelContextRegisterToolOptions; @@ -129,6 +131,11 @@ ExceptionState& exception_state); ScriptPromise<IDLSequence<RegisteredTool>> getTools( ScriptState* script_state); + ScriptPromise<IDLNullable<IDLString>> executeTool( + ScriptState* script_state, + RegisteredTool* tool, + String input_arguments, + const ExecuteToolOptions* options = nullptr); void UnregisterTool(const String& name); std::optional<ScriptToolDeclaration> GetScriptToolDeclaration( @@ -158,6 +165,9 @@ // mojom::blink::ScriptToolReceiver implementation: void NotifyToolChange() override; + void ExecuteScriptTool(const String& name, + const String& input_arguments, + ExecuteScriptToolCallback callback) override; void DidFinishParsing(); @@ -172,6 +182,11 @@ ScriptPromiseResolver<IDLSequence<RegisteredTool>>* resolver, Vector<mojom::blink::ScriptToolPtr> tools); + void OnExecuteScriptToolCompleted( + ScriptPromiseResolver<IDLNullable<IDLString>>* resolver, + const String& result, + bool success); + void Trace(Visitor*) const override; private:
diff --git a/third_party/blink/renderer/core/script_tools/model_context.idl b/third_party/blink/renderer/core/script_tools/model_context.idl index 05ec5556..d81491c 100644 --- a/third_party/blink/renderer/core/script_tools/model_context.idl +++ b/third_party/blink/renderer/core/script_tools/model_context.idl
@@ -21,6 +21,11 @@ sequence<USVString> exposedTo; }; +dictionary RegisteredTool : RegisteredToolDeprecated { + required Window window; + required USVString origin; +}; + [ Exposed=Window, SecureContext, @@ -28,5 +33,10 @@ ] interface ModelContext : EventTarget { [CallWith=ScriptState, RaisesException, MeasureAs=ModelContextRegisterTool] undefined registerTool(ModelContextTool tool, optional ModelContextRegisterToolOptions options = {}); [CallWith=ScriptState] Promise<sequence<RegisteredTool>> getTools(); + [CallWith=ScriptState] Promise<DOMString?> executeTool( + RegisteredTool tool, + DOMString input_arguments, + optional ExecuteToolOptions options = {} + ); attribute EventHandler ontoolchange; };
diff --git a/third_party/blink/renderer/core/script_tools/model_context_testing.cc b/third_party/blink/renderer/core/script_tools/model_context_testing.cc index 0a879d1..c6f5e23 100644 --- a/third_party/blink/renderer/core/script_tools/model_context_testing.cc +++ b/third_party/blink/renderer/core/script_tools/model_context_testing.cc
@@ -13,54 +13,17 @@ namespace blink { -namespace { - -String GetToolErrorMessage(const ScriptToolError& error) { - if (!error.message.empty()) { - return error.message; - } - String conversion; - switch (error.code) { - case ScriptToolErrorCode::kInvalidToolName: - conversion = "Tool was not executed due to invalid name"; - break; - case ScriptToolErrorCode::kInvalidInputArguments: - conversion = "Tool was not executed due to invalid input arguments"; - break; - case ScriptToolErrorCode::kMissingRequiredSubmitButton: - conversion = - "Tool was not executed due to missing required submit button"; - break; - case ScriptToolErrorCode::kToolInvocationFailed: - conversion = - "Tool was executed but the invocation failed. For example, the " - "script function threw an error"; - break; - case ScriptToolErrorCode::kToolCancelled: - conversion = "Tool was cancelled"; - break; - default: - NOTREACHED(); - } - if (error.message.empty()) { - return conversion; - } - return conversion + ": " + error.message; -} - -} // namespace - ModelContextTesting::ModelContextTesting(ModelContext& model_context) : model_context_(model_context) { model_context_->SetToolChangeCallback(blink::BindRepeating( &ModelContextTesting::OnToolChange, WrapWeakPersistent(this))); } -HeapVector<Member<RegisteredTool>> ModelContextTesting::listTools() { - HeapVector<Member<RegisteredTool>> tools; +HeapVector<Member<RegisteredToolDeprecated>> ModelContextTesting::listTools() { + HeapVector<Member<RegisteredToolDeprecated>> tools; model_context_->ForEachScriptTool( [&tools](const mojom::blink::ScriptTool& mojom_tool) { - auto* tool = MakeGarbageCollected<RegisteredTool>(); + auto* tool = MakeGarbageCollected<RegisteredToolDeprecated>(); tool->setName(mojom_tool.name); tool->setDescription(mojom_tool.description); tool->setInputSchema(mojom_tool.input_schema);
diff --git a/third_party/blink/renderer/core/script_tools/model_context_testing.h b/third_party/blink/renderer/core/script_tools/model_context_testing.h index 90e6f10..d05b0d7 100644 --- a/third_party/blink/renderer/core/script_tools/model_context_testing.h +++ b/third_party/blink/renderer/core/script_tools/model_context_testing.h
@@ -9,13 +9,13 @@ #include "third_party/blink/renderer/bindings/core/v8/script_promise.h" #include "third_party/blink/renderer/bindings/core/v8/v8_execute_tool_options.h" #include "third_party/blink/renderer/bindings/core/v8/v8_model_context_testing.h" -#include "third_party/blink/renderer/bindings/core/v8/v8_registered_tool.h" +#include "third_party/blink/renderer/bindings/core/v8/v8_registered_tool_deprecated.h" #include "third_party/blink/renderer/core/core_export.h" #include "third_party/blink/renderer/core/dom/events/event_target.h" namespace blink { class ModelContext; -class RegisteredTool; +class RegisteredToolDeprecated; class CORE_EXPORT ModelContextTesting : public EventTarget { DEFINE_WRAPPERTYPEINFO(); @@ -23,7 +23,7 @@ public: explicit ModelContextTesting(ModelContext& model_context); - HeapVector<Member<RegisteredTool>> listTools(); + HeapVector<Member<RegisteredToolDeprecated>> listTools(); ScriptPromise<IDLNullable<IDLString>> executeTool(ScriptState* state, String tool_name, String input_arguments,
diff --git a/third_party/blink/renderer/core/script_tools/model_context_testing.idl b/third_party/blink/renderer/core/script_tools/model_context_testing.idl index f2f381bf..1269e1d 100644 --- a/third_party/blink/renderer/core/script_tools/model_context_testing.idl +++ b/third_party/blink/renderer/core/script_tools/model_context_testing.idl
@@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -dictionary RegisteredTool { +dictionary RegisteredToolDeprecated { required DOMString name; required DOMString description; DOMString inputSchema; @@ -15,7 +15,7 @@ [ RuntimeEnabled=WebMCPTesting ] interface ModelContextTesting : EventTarget { - sequence<RegisteredTool> listTools(); + sequence<RegisteredToolDeprecated> listTools(); // Returns null when a navigation is triggered. [CallWith=ScriptState] Promise<DOMString?> executeTool(DOMString tool_name, DOMString input_arguments, optional ExecuteToolOptions options = {}); [CallWith=ScriptState] Promise<DOMString> getCrossDocumentScriptToolResult();
diff --git a/third_party/blink/renderer/core/script_tools/script_tool_types.h b/third_party/blink/renderer/core/script_tools/script_tool_types.h index 5213bb6..da02592 100644 --- a/third_party/blink/renderer/core/script_tools/script_tool_types.h +++ b/third_party/blink/renderer/core/script_tools/script_tool_types.h
@@ -36,6 +36,38 @@ } }; +// TODO(https://crbug.com/506393880): Move this to the `model_context.cc` +// anonymous namespace, when `model_context_testing.cc` stops relying on it. +inline String GetToolErrorMessage(const ScriptToolError& error) { + if (!error.message.empty()) { + return error.message; + } + String conversion; + switch (error.code) { + case ScriptToolErrorCode::kInvalidToolName: + conversion = "Tool was not executed due to invalid name"; + break; + case ScriptToolErrorCode::kInvalidInputArguments: + conversion = "Tool was not executed due to invalid input arguments"; + break; + case ScriptToolErrorCode::kMissingRequiredSubmitButton: + conversion = + "Tool was not executed due to missing required submit button"; + break; + case ScriptToolErrorCode::kToolInvocationFailed: + conversion = + "Tool was executed but the invocation failed. For example, the " + "script function threw an error"; + break; + case ScriptToolErrorCode::kToolCancelled: + conversion = "Tool was cancelled"; + break; + default: + conversion = "Unknown failure"; + } + return conversion; +} + struct ScriptToolDeclaration { String description; String input_schema;
diff --git a/third_party/blink/renderer/core/svg/svg_a_element.cc b/third_party/blink/renderer/core/svg/svg_a_element.cc index 4d9da1e13..2951f24 100644 --- a/third_party/blink/renderer/core/svg/svg_a_element.cc +++ b/third_party/blink/renderer/core/svg/svg_a_element.cc
@@ -234,6 +234,11 @@ return IsLink(); } +KURL SVGAElement::Url() const { + return GetDocument().CompleteURL( + StripLeadingAndTrailingHtmlSpaces(HrefString())); +} + bool SVGAElement::HasActivationBehavior() const { return true; }
diff --git a/third_party/blink/renderer/core/svg/svg_a_element.h b/third_party/blink/renderer/core/svg/svg_a_element.h index 8687ee3..a060c42 100644 --- a/third_party/blink/renderer/core/svg/svg_a_element.h +++ b/third_party/blink/renderer/core/svg/svg_a_element.h
@@ -43,6 +43,7 @@ void Trace(Visitor*) const override; + KURL Url() const; uint32_t GetLinkRelations() const { return link_relations_; } DOMTokenList& relList() const { return *rel_list_; }
diff --git a/third_party/blink/renderer/core/svg/svg_uri_reference.cc b/third_party/blink/renderer/core/svg/svg_uri_reference.cc index 36b1341..f1537de3 100644 --- a/third_party/blink/renderer/core/svg/svg_uri_reference.cc +++ b/third_party/blink/renderer/core/svg/svg_uri_reference.cc
@@ -86,10 +86,6 @@ return element.getAttribute(xlink_names::kHrefAttr); } -KURL SVGURIReference::LegacyHrefURL(const Document& document) const { - return document.CompleteURL(StripLeadingAndTrailingHtmlSpaces(HrefString())); -} - SVGURLReferenceResolver::SVGURLReferenceResolver(const String& url_string, const Document& document) : relative_url_(url_string),
diff --git a/third_party/blink/renderer/core/svg/svg_uri_reference.h b/third_party/blink/renderer/core/svg/svg_uri_reference.h index 6ef9b58..3c18126 100644 --- a/third_party/blink/renderer/core/svg/svg_uri_reference.h +++ b/third_party/blink/renderer/core/svg/svg_uri_reference.h
@@ -50,10 +50,6 @@ // SVGURIReference. static const AtomicString& LegacyHrefString(const SVGElement&); - // Like above, but for elements that inherit from SVGURIReference. Resolves - // against the base URL of the passed Document. - KURL LegacyHrefURL(const Document&) const; - static AtomicString FragmentIdentifierFromIRIString(const String&, const TreeScope&); static Element* TargetElementFromIRIString(const String&,
diff --git a/third_party/blink/renderer/core/view_transition/view_transition.cc b/third_party/blink/renderer/core/view_transition/view_transition.cc index 849db52..8e55b2c7 100644 --- a/third_party/blink/renderer/core/view_transition/view_transition.cc +++ b/third_party/blink/renderer/core/view_transition/view_transition.cc
@@ -533,6 +533,9 @@ DCHECK_GE(document_->Lifecycle().GetState(), DocumentLifecycle::kCompositingInputsClean); + if (creation_type_ == CreationType::kScript) { + capture_tag_discovery_start_time_ = base::TimeTicks::Now(); + } if (creation_type_ == CreationType::kForSnapshot) { UMA_HISTOGRAM_CUSTOM_MICROSECONDS_TIMES( "Blink.ViewTransitions.InitialFrameDelay", @@ -552,6 +555,13 @@ // Capture request pending -- create the request case State::kCaptureRequestPending: { + if (creation_type_ == CreationType::kScript) { + capture_request_start_time_ = base::TimeTicks::Now(); + UMA_HISTOGRAM_CUSTOM_MICROSECONDS_TIMES( + "Blink.ViewTransitions.CaptureTagDiscoveryDuration", + capture_request_start_time_ - capture_tag_discovery_start_time_, + base::Microseconds(1), base::Seconds(1), 100); + } // If we're capturing during a navigation, browser controls will be // forced to show via animation. Ensure they're fully showing when // performing the capture. @@ -598,6 +608,11 @@ switch (creation_type_) { case CreationType::kScript: { CHECK(script_delegate_); + dom_callback_start_time_ = base::TimeTicks::Now(); + UMA_HISTOGRAM_CUSTOM_MICROSECONDS_TIMES( + "Blink.ViewTransitions.CaptureRequestToDOMCallbackRunningDelay", + dom_callback_start_time_ - capture_request_start_time_, + base::Microseconds(1), base::Seconds(1), 100); script_delegate_->InvokeDOMChangeCallback(); // Since invoking the callback could yield (at least when devtools @@ -657,6 +672,13 @@ break; case State::kDOMCallbackFinished: + if (creation_type_ == CreationType::kScript) { + dom_callback_finished_time_ = base::TimeTicks::Now(); + UMA_HISTOGRAM_CUSTOM_MICROSECONDS_TIMES( + "Blink.ViewTransitions.DOMCallbackRunDuration", + dom_callback_finished_time_ - dom_callback_start_time_, + base::Microseconds(1), base::Seconds(4), 100); + } // For testing check: if the flag is enabled, re-create the style // tracker with the serialized state that the current style tracker // produces. This allows us to use SPA tests for MPA serialization. @@ -706,6 +728,15 @@ break; } + if (creation_type_ == CreationType::kScript) { + animate_request_time_ = base::TimeTicks::Now(); + UMA_HISTOGRAM_CUSTOM_MICROSECONDS_TIMES( + "Blink.ViewTransitions." + "DOMCallbackFinishedToAnimationRequestedDuration", + animate_request_time_ - dom_callback_finished_time_, + base::Microseconds(1), base::Seconds(1), 100); + } + if (RuntimeEnabledFeatures:: ViewTransitionUpdateLifecycleBeforeReadyEnabled()) { document_->View()->UpdateAllLifecyclePhasesExceptPaint( @@ -730,6 +761,12 @@ case State::kAnimating: { if (first_animating_frame_) { first_animating_frame_ = false; + if (creation_type_ == CreationType::kScript) { + UMA_HISTOGRAM_CUSTOM_MICROSECONDS_TIMES( + "Blink.ViewTransitions.AnimateRequestToAnimatingDelay", + base::TimeTicks::Now() - animate_request_time_, + base::Microseconds(1), base::Seconds(1), 100); + } // We need to schedule an animation frame, in case this is the only // kAnimating frame we will get, so that we can clean up in the next // frame.
diff --git a/third_party/blink/renderer/core/view_transition/view_transition.h b/third_party/blink/renderer/core/view_transition/view_transition.h index eacabc0..4aafd59 100644 --- a/third_party/blink/renderer/core/view_transition/view_transition.h +++ b/third_party/blink/renderer/core/view_transition/view_transition.h
@@ -500,6 +500,24 @@ // Time at which we processed the initial state, used for metrics. base::TimeTicks initial_state_processing_time_; + // The following timing variables are only set and used for script-based + // transitions (CreationType::kScript). + + // Time at which we started capture tag discovery, used for metrics. + base::TimeTicks capture_tag_discovery_start_time_; + + // Time at which we started capturing, used for metrics. + base::TimeTicks capture_request_start_time_; + + // Time at which we started running the DOM callback, used for metrics. + base::TimeTicks dom_callback_start_time_; + + // Time at which the DOM callback finished, used for metrics. + base::TimeTicks dom_callback_finished_time_; + + // Time at which we sent the animate request, used for metrics. + base::TimeTicks animate_request_time_; + static int next_id_; };
diff --git a/third_party/blink/renderer/core/view_transition/view_transition_test.cc b/third_party/blink/renderer/core/view_transition/view_transition_test.cc index ea936a5..e0adbe6d 100644 --- a/third_party/blink/renderer/core/view_transition/view_transition_test.cc +++ b/third_party/blink/renderer/core/view_transition/view_transition_test.cc
@@ -9,6 +9,7 @@ #include "base/check_op.h" #include "base/memory/scoped_refptr.h" #include "base/task/single_thread_task_runner.h" +#include "base/test/metrics/histogram_tester.h" #include "base/test/scoped_feature_list.h" #include "cc/view_transition/view_transition_request.h" #include "third_party/blink/public/web/web_settings.h" @@ -269,6 +270,56 @@ finished_tester.WaitUntilSettled(); } +TEST_P(ViewTransitionTest, HistogramsRecorded) { + base::HistogramTester histogram_tester; + + ScriptState* script_state = GetScriptState(); + ScriptState::Scope scope(script_state); + + MockFunctionScope funcs(script_state); + auto* view_transition_callback = V8ViewTransitionCallback::Create( + funcs.ExpectCall()->ToV8Function(script_state)); + + ViewTransitionSupplement::startViewTransition(script_state, GetDocument(), + view_transition_callback, + IGNORE_EXCEPTION_FOR_TESTING); + + // This should trigger kCaptureTagDiscovery and kCaptureRequestPending + UpdateAllLifecyclePhasesForTest(); + + // CaptureTagDiscoveryDuration should be recorded. + histogram_tester.ExpectTotalCount( + "Blink.ViewTransitions.CaptureTagDiscoveryDuration", 1); + + // We need to finish capture to move to kCaptured and then kDOMCallbackRunning + UpdateAllLifecyclePhasesAndFinishDirectives(); + + // At this point, CaptureRequestToDOMCallbackRunningDelay should be recorded. + histogram_tester.ExpectTotalCount( + "Blink.ViewTransitions.CaptureRequestToDOMCallbackRunningDelay", 1); + + // Run pending tasks to let the DOM callback finish + test::RunPendingTasks(); + + // Now DOMCallbackRunDuration should be recorded. + histogram_tester.ExpectTotalCount( + "Blink.ViewTransitions.DOMCallbackRunDuration", 1); + + // Now we are in kAnimating. We need to finish it. + UpdateAllLifecyclePhasesAndFinishDirectives(); + + // DOMCallbackFinishedToAnimationRequestedDuration should be recorded. + histogram_tester.ExpectTotalCount( + "Blink.ViewTransitions.DOMCallbackFinishedToAnimationRequestedDuration", + 1); + + // AnimateRequestToAnimatingDelay should be recorded. + histogram_tester.ExpectTotalCount( + "Blink.ViewTransitions.AnimateRequestToAnimatingDelay", 1); + + FinishTransition(); +} + TEST_P(ViewTransitionTest, TransitionCreatesNewObject) { ScriptState* script_state = GetScriptState(); ScriptState::Scope scope(script_state);
diff --git a/third_party/blink/renderer/modules/accessibility/ax_object_cache_impl.cc b/third_party/blink/renderer/modules/accessibility/ax_object_cache_impl.cc index fc849d3..61cdd38a 100644 --- a/third_party/blink/renderer/modules/accessibility/ax_object_cache_impl.cc +++ b/third_party/blink/renderer/modules/accessibility/ax_object_cache_impl.cc
@@ -5386,7 +5386,7 @@ case ax::mojom::blink::Event::kEnabledChanged: case ax::mojom::blink::Event::kEndOfTest: case ax::mojom::blink::Event::kFocusAfterMenuClose: - case ax::mojom::blink::Event::kFocusContext: + case ax::mojom::blink::Event::kFocusContextDeprecated: case ax::mojom::blink::Event::kHide: case ax::mojom::blink::Event::kHitTestResult: case ax::mojom::blink::Event::kImageFrameUpdated:
diff --git a/third_party/blink/renderer/modules/webaudio/oscillator_handler.cc b/third_party/blink/renderer/modules/webaudio/oscillator_handler.cc index 5260c43..9452675 100644 --- a/third_party/blink/renderer/modules/webaudio/oscillator_handler.cc +++ b/third_party/blink/renderer/modules/webaudio/oscillator_handler.cc
@@ -323,7 +323,7 @@ // Convert from cents to rate scalar. float k = 1.0 / 1200; - vector_math::Vsmul(detune_values.data(), 1, &k, detune_values.data(), 1, + vector_math::Vsmul(detune_values.data(), 1, k, detune_values.data(), 1, frames_to_process); for (unsigned i = 0; i < frames_to_process; ++i) { detune_values[i] = std::exp2(detune_values[i]); @@ -345,7 +345,7 @@ if (has_sample_accurate_values) { ClampFrequency(phase_increments, Context()->sampleRate() / 2); // Convert from frequency to wavetable increment. - vector_math::Vsmul(phase_increments.data(), 1, &final_scale, + vector_math::Vsmul(phase_increments.data(), 1, final_scale, phase_increments.data(), 1, frames_to_process); }
diff --git a/third_party/blink/renderer/modules/webaudio/periodic_wave.cc b/third_party/blink/renderer/modules/webaudio/periodic_wave.cc index 05d7a04..4bc8fa3b 100644 --- a/third_party/blink/renderer/modules/webaudio/periodic_wave.cc +++ b/third_party/blink/renderer/modules/webaudio/periodic_wave.cc
@@ -443,10 +443,10 @@ // arrays. Need to scale the data by fftSize to remove the scaling that the // inverse IFFT would do. float scale = fft_size; - vector_math::Vsmul(real_data.data(), 1, &scale, real.Data(), 1, + vector_math::Vsmul(real_data.data(), 1, scale, real.Data(), 1, number_of_components); scale = -scale; - vector_math::Vsmul(imag_data.data(), 1, &scale, imag.Data(), 1, + vector_math::Vsmul(imag_data.data(), 1, scale, imag.Data(), 1, number_of_components); // Find the starting bin where we should start culling. We need to clear @@ -494,7 +494,7 @@ } // Apply normalization scale. - vector_math::Vsmul(data, 1, &normalization_scale, data, 1, fft_size); + vector_math::Vsmul(data, 1, normalization_scale, data, 1, fft_size); } return true; }
diff --git a/third_party/blink/renderer/modules/xr/xr_frame_provider.cc b/third_party/blink/renderer/modules/xr/xr_frame_provider.cc index 2278a37..cdef666 100644 --- a/third_party/blink/renderer/modules/xr/xr_frame_provider.cc +++ b/third_party/blink/renderer/modules/xr/xr_frame_provider.cc
@@ -673,7 +673,7 @@ XrLayerClient* client, bool was_changed) { CHECK(client); - client->DoneWithSharedBuffer(); + std::unique_ptr<SharedImageHolder> image_ref = client->DoneWithSharedBuffer(); CHECK(immersive_session_); CHECK(client->session()); @@ -700,18 +700,18 @@ } if (frame_transport_->DrawingIntoSharedBuffer()) { - // Image is written to shared buffer already. No need to hold it. + // Image is written to shared buffer already. The layer now takes + // ownership of the SharedImageHolder, which contains the SyncToken. DVLOG(3) << __func__ << ": FrameSubmit for SharedBuffer mode"; any_layer_changed_ = true; - layers_.emplace_back(layer_id, nullptr); + layers_.emplace_back(layer_id, std::move(image_ref)); return; } else { CHECK_NE(client->session()->GraphicsApi(), XRGraphicsBinding::Api::kWebGPU) << "WebGPU layers only support shared buffer submission modes"; } - std::unique_ptr<SharedImageHolder> image_ref = - client->TransferToSharedImageHolder(); + image_ref = client->TransferToSharedImageHolder(); if (!image_ref) { return;
diff --git a/third_party/blink/renderer/modules/xr/xr_layer_client.h b/third_party/blink/renderer/modules/xr/xr_layer_client.h index db37ab26..dfead8eb7 100644 --- a/third_party/blink/renderer/modules/xr/xr_layer_client.h +++ b/third_party/blink/renderer/modules/xr/xr_layer_client.h
@@ -21,7 +21,9 @@ virtual std::unique_ptr<SharedImageHolder> TransferToSharedImageHolder() = 0; virtual XRFrameTransportDelegate* GetTransportDelegate() = 0; - virtual void DoneWithSharedBuffer() {} + virtual std::unique_ptr<SharedImageHolder> DoneWithSharedBuffer() { + return nullptr; + } }; } // namespace blink
diff --git a/third_party/blink/renderer/modules/xr/xr_webgl_layer.cc b/third_party/blink/renderer/modules/xr/xr_webgl_layer.cc index 38e9b2d..92ada0b 100644 --- a/third_party/blink/renderer/modules/xr/xr_webgl_layer.cc +++ b/third_party/blink/renderer/modules/xr/xr_webgl_layer.cc
@@ -319,11 +319,15 @@ return nullptr; } -void XRWebGLLayer::DoneWithSharedBuffer() { +std::unique_ptr<SharedImageHolder> XRWebGLLayer::DoneWithSharedBuffer() { + std::unique_ptr<SharedImageHolder> image_ref; + if (is_direct_draw_frame) { - drawing_buffer_->DoneWithSharedBuffer(); + image_ref = drawing_buffer_->DoneWithSharedBuffer(); is_direct_draw_frame = false; } + + return image_ref; } void XRWebGLLayer::OnFrameStart() {
diff --git a/third_party/blink/renderer/modules/xr/xr_webgl_layer.h b/third_party/blink/renderer/modules/xr/xr_webgl_layer.h index fa24f66..476c784 100644 --- a/third_party/blink/renderer/modules/xr/xr_webgl_layer.h +++ b/third_party/blink/renderer/modules/xr/xr_webgl_layer.h
@@ -48,7 +48,7 @@ XRSession* session() const override; std::unique_ptr<SharedImageHolder> TransferToSharedImageHolder() override; XRFrameTransportDelegate* GetTransportDelegate() override; - void DoneWithSharedBuffer() override; + std::unique_ptr<SharedImageHolder> DoneWithSharedBuffer() override; WebGLFramebuffer* framebuffer() const { return framebuffer_.Get(); } uint32_t framebufferWidth() const;
diff --git a/third_party/blink/renderer/platform/audio/audio_bus.cc b/third_party/blink/renderer/platform/audio/audio_bus.cc index 4679460c..9fd4d7e94 100644 --- a/third_party/blink/renderer/platform/audio/audio_bus.cc +++ b/third_party/blink/renderer/platform/audio/audio_bus.cc
@@ -77,8 +77,8 @@ scoped_refptr<AudioBus> bus = base::AdoptRef(new AudioBus(number_of_channels, length, false)); - for (unsigned i = 0; i < number_of_channels; ++i) { - if (!bus->Channel(i)->TryAllocate(length)) { + for (AudioChannel& channel : bus->channels_) { + if (!channel.TryAllocate(length)) { return nullptr; } } @@ -256,9 +256,8 @@ float AudioBus::MaxAbsValue() const { float max = 0.0f; - for (unsigned i = 0; i < NumberOfChannels(); ++i) { - const AudioChannel* channel = Channel(i); - max = std::max(max, channel->MaxAbsValue()); + for (const AudioChannel& channel : channels_) { + max = std::max(max, channel.MaxAbsValue()); } return max; @@ -272,8 +271,8 @@ } void AudioBus::Scale(float scale) { - for (unsigned i = 0; i < NumberOfChannels(); ++i) { - Channel(i)->Scale(scale); + for (AudioChannel& channel : channels_) { + channel.Scale(scale); } } @@ -323,19 +322,10 @@ } void AudioBus::DiscreteSumFrom(const AudioBus& source_bus) { - unsigned number_of_source_channels = source_bus.NumberOfChannels(); - unsigned number_of_destination_channels = NumberOfChannels(); - - if (number_of_destination_channels < number_of_source_channels) { - // Down-mix by summing channels and dropping the remaining. - for (unsigned i = 0; i < number_of_destination_channels; ++i) { - Channel(i)->SumFrom(source_bus.Channel(i)); - } - } else if (number_of_destination_channels > number_of_source_channels) { - // Up-mix by summing as many channels as we have. - for (unsigned i = 0; i < number_of_source_channels; ++i) { - Channel(i)->SumFrom(source_bus.Channel(i)); - } + DCHECK_NE(NumberOfChannels(), source_bus.NumberOfChannels()); + unsigned count = std::min(NumberOfChannels(), source_bus.NumberOfChannels()); + for (unsigned i = 0; i < count; ++i) { + Channel(i)->SumFrom(source_bus.Channel(i)); } } @@ -416,8 +406,8 @@ float* destination = ChannelByType(kChannelLeft)->MutableData(); float scale = 0.5; - Vsma(source_l, 1, &scale, destination, 1, length()); - Vsma(source_r, 1, &scale, destination, 1, length()); + Vsma(source_l, 1, scale, destination, 1, length()); + Vsma(source_r, 1, scale, destination, 1, length()); } else if (number_of_source_channels == 4 && number_of_destination_channels == 1) { // Down-mixing: 4 -> 1 @@ -432,10 +422,10 @@ float* destination = ChannelByType(kChannelLeft)->MutableData(); float scale = 0.25; - Vsma(source_l, 1, &scale, destination, 1, length()); - Vsma(source_r, 1, &scale, destination, 1, length()); - Vsma(source_sl, 1, &scale, destination, 1, length()); - Vsma(source_sr, 1, &scale, destination, 1, length()); + Vsma(source_l, 1, scale, destination, 1, length()); + Vsma(source_r, 1, scale, destination, 1, length()); + Vsma(source_sl, 1, scale, destination, 1, length()); + Vsma(source_sr, 1, scale, destination, 1, length()); } else if (number_of_source_channels == 6 && number_of_destination_channels == 1) { // Down-mixing: 5.1 -> 1 @@ -453,11 +443,11 @@ float scale_sqrt_half = sqrtf(0.5); float scale_half = 0.5; - Vsma(source_l, 1, &scale_sqrt_half, destination, 1, length()); - Vsma(source_r, 1, &scale_sqrt_half, destination, 1, length()); + Vsma(source_l, 1, scale_sqrt_half, destination, 1, length()); + Vsma(source_r, 1, scale_sqrt_half, destination, 1, length()); Vadd(source_c, 1, destination, 1, destination, 1, length()); - Vsma(source_sl, 1, &scale_half, destination, 1, length()); - Vsma(source_sr, 1, &scale_half, destination, 1, length()); + Vsma(source_sl, 1, scale_half, destination, 1, length()); + Vsma(source_sr, 1, scale_half, destination, 1, length()); } else if (number_of_source_channels == 4 && number_of_destination_channels == 2) { // Down-mixing: 4 -> 2 @@ -474,10 +464,10 @@ float* destination_r = ChannelByType(kChannelRight)->MutableData(); float scale_half = 0.5; - Vsma(source_l, 1, &scale_half, destination_l, 1, length()); - Vsma(source_sl, 1, &scale_half, destination_l, 1, length()); - Vsma(source_r, 1, &scale_half, destination_r, 1, length()); - Vsma(source_sr, 1, &scale_half, destination_r, 1, length()); + Vsma(source_l, 1, scale_half, destination_l, 1, length()); + Vsma(source_sl, 1, scale_half, destination_l, 1, length()); + Vsma(source_r, 1, scale_half, destination_r, 1, length()); + Vsma(source_sr, 1, scale_half, destination_r, 1, length()); } else if (number_of_source_channels == 6 && number_of_destination_channels == 2) { // Down-mixing: 5.1 -> 2 @@ -496,11 +486,11 @@ float scale_sqrt_half = sqrtf(0.5); Vadd(source_l, 1, destination_l, 1, destination_l, 1, length()); - Vsma(source_c, 1, &scale_sqrt_half, destination_l, 1, length()); - Vsma(source_sl, 1, &scale_sqrt_half, destination_l, 1, length()); + Vsma(source_c, 1, scale_sqrt_half, destination_l, 1, length()); + Vsma(source_sl, 1, scale_sqrt_half, destination_l, 1, length()); Vadd(source_r, 1, destination_r, 1, destination_r, 1, length()); - Vsma(source_c, 1, &scale_sqrt_half, destination_r, 1, length()); - Vsma(source_sr, 1, &scale_sqrt_half, destination_r, 1, length()); + Vsma(source_c, 1, scale_sqrt_half, destination_r, 1, length()); + Vsma(source_sr, 1, scale_sqrt_half, destination_r, 1, length()); } else if (number_of_source_channels == 6 && number_of_destination_channels == 4) { // Down-mixing: 5.1 -> 4 @@ -517,9 +507,9 @@ float scale_sqrt_half = sqrtf(0.5); Vadd(source_l, 1, destination_l, 1, destination_l, 1, length()); - Vsma(source_c, 1, &scale_sqrt_half, destination_l, 1, length()); + Vsma(source_c, 1, scale_sqrt_half, destination_l, 1, length()); Vadd(source_r, 1, destination_r, 1, destination_r, 1, length()); - Vsma(source_c, 1, &scale_sqrt_half, destination_r, 1, length()); + Vsma(source_c, 1, scale_sqrt_half, destination_r, 1, length()); Channel(2)->SumFrom(source_bus.Channel(4)); Channel(3)->SumFrom(source_bus.Channel(5)); } else { @@ -577,7 +567,7 @@ } else { for (unsigned channel_index = 0; channel_index < number_of_channels; ++channel_index) { - vector_math::Vsmul(sources[channel_index].data(), 1, &gain, + vector_math::Vsmul(sources[channel_index].data(), 1, gain, destinations[channel_index].data(), 1, frames_to_process); }
diff --git a/third_party/blink/renderer/platform/audio/audio_channel.cc b/third_party/blink/renderer/platform/audio/audio_channel.cc index 6e21e59c..fe1d702 100644 --- a/third_party/blink/renderer/platform/audio/audio_channel.cc +++ b/third_party/blink/renderer/platform/audio/audio_channel.cc
@@ -59,7 +59,7 @@ return; } - vector_math::Vsmul(Data(), 1, &scale, MutableData(), 1, length()); + vector_math::Vsmul(Data(), 1, scale, MutableData(), 1, length()); } void AudioChannel::CopyFrom(const AudioChannel* source_channel) {
diff --git a/third_party/blink/renderer/platform/audio/fft_frame.cc b/third_party/blink/renderer/platform/audio/fft_frame.cc index d3e9d0a..da5d372 100644 --- a/third_party/blink/renderer/platform/audio/fft_frame.cc +++ b/third_party/blink/renderer/platform/audio/fft_frame.cc
@@ -74,9 +74,9 @@ } void FFTFrame::ScaleFFT(float factor) { - vector_math::Vsmul(real_data_.Data(), 1, &factor, real_data_.Data(), 1, + vector_math::Vsmul(real_data_.Data(), 1, factor, real_data_.Data(), 1, real_data_.size()); - vector_math::Vsmul(imag_data_.Data(), 1, &factor, imag_data_.Data(), 1, + vector_math::Vsmul(imag_data_.Data(), 1, factor, imag_data_.Data(), 1, imag_data_.size()); }
diff --git a/third_party/blink/renderer/platform/audio/fft_frame.h b/third_party/blink/renderer/platform/audio/fft_frame.h index f68495e..316f58c 100644 --- a/third_party/blink/renderer/platform/audio/fft_frame.h +++ b/third_party/blink/renderer/platform/audio/fft_frame.h
@@ -61,10 +61,10 @@ // have platform-dependent implementations. explicit FFTFrame(unsigned fft_size); - // creates a blank/empty frame for later use with createInterpolatedFrame() - FFTFrame(); - FFTFrame(const FFTFrame& frame); - ~FFTFrame(); + FFTFrame() = delete; + FFTFrame(const FFTFrame&) = delete; + FFTFrame& operator=(const FFTFrame&) = delete; + ~FFTFrame() = default; // Returns the smallest and largest supported FFT lengths. static unsigned MinFFTSize();
diff --git a/third_party/blink/renderer/platform/audio/mac/fft_frame_mac.cc b/third_party/blink/renderer/platform/audio/mac/fft_frame_mac.cc index 42bb20c..84fe9f2 100644 --- a/third_party/blink/renderer/platform/audio/mac/fft_frame_mac.cc +++ b/third_party/blink/renderer/platform/audio/mac/fft_frame_mac.cc
@@ -132,34 +132,6 @@ frame_.imagp = imag_data_.Data(); } -// Creates a blank/empty frame (interpolate() must later be called). -FFTFrame::FFTFrame() : real_data_(0), imag_data_(0) { - // Later will be set to correct values when interpolate() is called. - frame_.realp = nullptr; - frame_.imagp = nullptr; - - fft_size_ = 0; - log2fft_size_ = 0; -} - -// Copy constructor. -FFTFrame::FFTFrame(const FFTFrame& frame) - : fft_size_(frame.fft_size_), - log2fft_size_(frame.log2fft_size_), - real_data_(frame.fft_size_), - imag_data_(frame.fft_size_), - fft_setup_(frame.fft_setup_) { - // Setup frame data. - frame_.realp = real_data_.Data(); - frame_.imagp = imag_data_.Data(); - - // Copy/setup frame data. - real_data_.as_span().copy_from(frame.real_data_.as_span()); - imag_data_.as_span().copy_from(frame.imag_data_.as_span()); -} - -FFTFrame::~FFTFrame() {} - void FFTFrame::DoFFT(const float* data) { vDSP_ctoz((DSPComplex*)data, 2, &frame_, 1, fft_size_ / 2); vDSP_fft_zrip(fft_setup_, &frame_, 1, log2fft_size_, FFT_FORWARD); @@ -171,8 +143,8 @@ // the correct scaling. float scale = 0.5f; - vector_math::Vsmul(frame_.realp, 1, &scale, frame_.realp, 1, fft_size_ / 2); - vector_math::Vsmul(frame_.imagp, 1, &scale, frame_.imagp, 1, fft_size_ / 2); + vector_math::Vsmul(frame_.realp, 1, scale, frame_.realp, 1, fft_size_ / 2); + vector_math::Vsmul(frame_.imagp, 1, scale, frame_.imagp, 1, fft_size_ / 2); } void FFTFrame::DoInverseFFT(float* data) { @@ -181,7 +153,7 @@ // Do final scaling so that x == IFFT(FFT(x)). float scale = 1.0f / fft_size_; - vector_math::Vsmul(data, 1, &scale, data, 1, fft_size_); + vector_math::Vsmul(data, 1, scale, data, 1, fft_size_); } unsigned FFTFrame::MinFFTSize() {
diff --git a/third_party/blink/renderer/platform/audio/pffft/fft_frame_pffft.cc b/third_party/blink/renderer/platform/audio/pffft/fft_frame_pffft.cc index 9bb0db64..9818d90d 100644 --- a/third_party/blink/renderer/platform/audio/pffft/fft_frame_pffft.cc +++ b/third_party/blink/renderer/platform/audio/pffft/fft_frame_pffft.cc
@@ -147,26 +147,6 @@ InitializeFFTSetupForSize(fft_size); } -// Creates a blank/empty frame (interpolate() must later be called). -FFTFrame::FFTFrame() : fft_size_(0), log2fft_size_(0) {} - -// Copy constructor. -FFTFrame::FFTFrame(const FFTFrame& frame) - : fft_size_(frame.fft_size_), - log2fft_size_(frame.log2fft_size_), - real_data_(frame.fft_size_ / 2), - imag_data_(frame.fft_size_ / 2), - complex_data_(frame.fft_size_), - pffft_work_(frame.fft_size_) { - // Initialize the PFFFT_Setup object here so that it will be ready when we - // compute FFTs. - InitializeFFTSetupForSize(fft_size_); - - // Copy/setup frame data. - real_data_.as_span().copy_from(frame.real_data_.as_span()); - imag_data_.as_span().copy_from(frame.imag_data_.as_span()); -} - unsigned FFTFrame::MinFFTSize() { return 1u << kMinFFTPow2Size; } @@ -202,9 +182,6 @@ } } -FFTFrame::~FFTFrame() { -} - void FFTFrame::DoFFT(const float* data) { DCHECK_EQ(pffft_work_.size(), fft_size_); @@ -247,7 +224,7 @@ // The inverse transform needs to be scaled because PFFFT doesn't. float scale = 1.0 / fft_size_; - vector_math::Vsmul(data, 1, &scale, data, 1, fft_size_); + vector_math::Vsmul(data, 1, scale, data, 1, fft_size_); } } // namespace blink
diff --git a/third_party/blink/renderer/platform/audio/reverb_convolver_stage.cc b/third_party/blink/renderer/platform/audio/reverb_convolver_stage.cc index 4b0a5d6..6b3ea062 100644 --- a/third_party/blink/renderer/platform/audio/reverb_convolver_stage.cc +++ b/third_party/blink/renderer/platform/audio/reverb_convolver_stage.cc
@@ -82,8 +82,8 @@ .copy_from(impulse_response.first(stage_length)); // Account for the normalization (if any) of the convolver node. if (scale != 1) { - vector_math::Vsmul(direct_kernel->Data(), 1, &scale, - direct_kernel->Data(), 1, stage_length); + vector_math::Vsmul(direct_kernel->Data(), 1, scale, direct_kernel->Data(), + 1, stage_length); } direct_convolver_ = std::make_unique<DirectConvolver>( render_slice_size, std::move(direct_kernel));
diff --git a/third_party/blink/renderer/platform/audio/vector_math.cc b/third_party/blink/renderer/platform/audio/vector_math.cc index 86ecade..99a7362 100644 --- a/third_party/blink/renderer/platform/audio/vector_math.cc +++ b/third_party/blink/renderer/platform/audio/vector_math.cc
@@ -172,61 +172,21 @@ void Vsma(const float* source_p, int source_stride, - const float* scale, - float* dest_p, - int dest_stride, - uint32_t frames_to_process) { - const float k = *scale; - - impl::Vsma(source_p, source_stride, &k, dest_p, dest_stride, - frames_to_process); -} - -void Vsma(const float* source_p, - int source_stride, float scale, float* dest_p, int dest_stride, uint32_t frames_to_process) { - const float k = scale; - - impl::Vsma(source_p, source_stride, &k, dest_p, dest_stride, + impl::Vsma(source_p, source_stride, &scale, dest_p, dest_stride, frames_to_process); } void Vsmul(const float* source_p, int source_stride, - const float* scale, - float* dest_p, - int dest_stride, - uint32_t frames_to_process) { - const float k = *scale; - - impl::Vsmul(source_p, source_stride, &k, dest_p, dest_stride, - frames_to_process); -} - -void Vsmul(const float* source_p, - int source_stride, float scale, float* dest_p, int dest_stride, uint32_t frames_to_process) { - const float k = scale; - - impl::Vsmul(source_p, source_stride, &k, dest_p, dest_stride, - frames_to_process); -} - -void Vsadd(const float* source_p, - int source_stride, - const float* addend, - float* dest_p, - int dest_stride, - uint32_t frames_to_process) { - const float k = *addend; - - impl::Vsadd(source_p, source_stride, &k, dest_p, dest_stride, + impl::Vsmul(source_p, source_stride, &scale, dest_p, dest_stride, frames_to_process); } @@ -236,9 +196,7 @@ float* dest_p, int dest_stride, uint32_t frames_to_process) { - const float k = addend; - - impl::Vsadd(source_p, source_stride, &k, dest_p, dest_stride, + impl::Vsadd(source_p, source_stride, &addend, dest_p, dest_stride, frames_to_process); }
diff --git a/third_party/blink/renderer/platform/audio/vector_math.h b/third_party/blink/renderer/platform/audio/vector_math.h index ef96067..244d13d 100644 --- a/third_party/blink/renderer/platform/audio/vector_math.h +++ b/third_party/blink/renderer/platform/audio/vector_math.h
@@ -60,13 +60,6 @@ // different results from what linux and windows would do. PLATFORM_EXPORT void Vsma(const float* source_p, int source_stride, - const float* scale, - float* dest_p, - int dest_stride, - uint32_t frames_to_process); - -PLATFORM_EXPORT void Vsma(const float* source_p, - int source_stride, float scale, float* dest_p, int dest_stride, @@ -77,13 +70,6 @@ // dest[k*dest_stride] = scale * source[k*source_stride] PLATFORM_EXPORT void Vsmul(const float* source_p, int source_stride, - const float* scale, - float* dest_p, - int dest_stride, - uint32_t frames_to_process); - -PLATFORM_EXPORT void Vsmul(const float* source_p, - int source_stride, float scale, float* dest_p, int dest_stride, @@ -91,13 +77,6 @@ PLATFORM_EXPORT void Vsadd(const float* source_p, int source_stride, - const float* addend, - float* dest_p, - int dest_stride, - uint32_t frames_to_process); - -PLATFORM_EXPORT void Vsadd(const float* source_p, - int source_stride, float addend, float* dest_p, int dest_stride,
diff --git a/third_party/blink/renderer/platform/audio/vector_math_test.cc b/third_party/blink/renderer/platform/audio/vector_math_test.cc index 91868b6..fd17ec6 100644 --- a/third_party/blink/renderer/platform/audio/vector_math_test.cc +++ b/third_party/blink/renderer/platform/audio/vector_math_test.cc
@@ -415,7 +415,7 @@ } for (auto& dest : GetSecondaryVectors(GetDestination(1u), source)) { std::ranges::copy(dest_source, dest.begin()); - Vsma(source.p(), source.stride(), &scale, dest.p(), dest.stride(), + Vsma(source.p(), source.stride(), scale, dest.p(), dest.stride(), source.size()); // Different optimizations may use different precisions for intermediate // results which may result in different rounding errors thus let's @@ -446,7 +446,7 @@ expected_dest[i] = scale * source[i]; } for (auto& dest : GetSecondaryVectors(GetDestination(1u), source)) { - Vsmul(source.p(), source.stride(), &scale, dest.p(), dest.stride(), + Vsmul(source.p(), source.stride(), scale, dest.p(), dest.stride(), source.size()); EXPECT_EQ(expected_dest, dest); } @@ -461,7 +461,7 @@ expected_dest[i] = addend + source[i]; } for (auto& dest : GetSecondaryVectors(GetDestination(1u), source)) { - Vsadd(source.p(), source.stride(), &addend, dest.p(), dest.stride(), + Vsadd(source.p(), source.stride(), addend, dest.p(), dest.stride(), source.size()); EXPECT_EQ(expected_dest, dest); }
diff --git a/third_party/blink/renderer/platform/blink_platform_unittests_bundle_data.filelist b/third_party/blink/renderer/platform/blink_platform_unittests_bundle_data.filelist index acb3f24..12b9294 100644 --- a/third_party/blink/renderer/platform/blink_platform_unittests_bundle_data.filelist +++ b/third_party/blink/renderer/platform/blink_platform_unittests_bundle_data.filelist
@@ -161,7 +161,6 @@ ../../web_tests/external/wpt/jpegxl/resources/green_queen_modular_e3.jxl ../../web_tests/external/wpt/jpegxl/resources/green_queen_vardct_e3.jxl ../../web_tests/external/wpt/jpegxl/resources/has_permutation.jxl -../../web_tests/external/wpt/jpegxl/resources/hdr_alpha.jxl ../../web_tests/external/wpt/jpegxl/resources/hdr_hlg_test.jxl ../../web_tests/external/wpt/jpegxl/resources/hdr_pq_test.jxl ../../web_tests/external/wpt/jpegxl/resources/issue648_palette0.jxl @@ -170,7 +169,6 @@ ../../web_tests/external/wpt/jpegxl/resources/orientation8_rotate_90_ccw.jxl ../../web_tests/external/wpt/jpegxl/resources/progressive-lf.jxl ../../web_tests/external/wpt/jpegxl/resources/progressive_ac.jxl -../../web_tests/external/wpt/jpegxl/resources/sdr_alpha.jxl ../../web_tests/external/wpt/jpegxl/resources/smallest_valid.jxl ../../web_tests/external/wpt/jpegxl/resources/spline_on_first_frame.jxl ../../web_tests/external/wpt/jpegxl/resources/with_icc.jxl
diff --git a/third_party/blink/renderer/platform/graphics/canvas_resource_dispatcher.cc b/third_party/blink/renderer/platform/graphics/canvas_resource_dispatcher.cc index 41beb45..6b757ae 100644 --- a/third_party/blink/renderer/platform/graphics/canvas_resource_dispatcher.cc +++ b/third_party/blink/renderer/platform/graphics/canvas_resource_dispatcher.cc
@@ -159,26 +159,8 @@ const gfx::Rect& damage_rect, bool is_opaque) { TRACE_EVENT0("blink", "CanvasResourceDispatcher::DispatchFrame"); - viz::CompositorFrame frame; - if (!PrepareFrame(std::move(canvas_resource), damage_rect, is_opaque, - &frame)) { - return; - } - - pending_compositor_frames_++; - sink_->SubmitCompositorFrame( - parent_local_surface_id_allocator_.GetCurrentLocalSurfaceId(), - std::move(frame), std::nullopt, 0); -} - -bool CanvasResourceDispatcher::PrepareFrame( - scoped_refptr<CanvasResource>&& canvas_resource, - const gfx::Rect& damage_rect, - bool is_opaque, - viz::CompositorFrame* frame) { - TRACE_EVENT0("blink", "CanvasResourceDispatcher::PrepareFrame"); if (!canvas_resource) { - return false; + return; } auto exported_resource = @@ -191,9 +173,25 @@ // For frameless canvas, we don't get a valid frame_sink_id and should drop. if (!frame_sink_id_.is_valid()) { - return false; + return; } + viz::CompositorFrame frame; + PrepareFrame(std::move(exported_resource), damage_rect, is_opaque, &frame); + + pending_compositor_frames_++; + sink_->SubmitCompositorFrame( + parent_local_surface_id_allocator_.GetCurrentLocalSurfaceId(), + std::move(frame), std::nullopt, 0); +} + +void CanvasResourceDispatcher::PrepareFrame( + scoped_refptr<ExportedCanvasResource>&& exported_resource, + const gfx::Rect& damage_rect, + bool is_opaque, + viz::CompositorFrame* frame) { + TRACE_EVENT0("blink", "CanvasResourceDispatcher::PrepareFrame"); + // TODO(crbug.com/652931): update the device_scale_factor frame->metadata.device_scale_factor = 1.0f; if (!current_begin_frame_ack_.frame_id.IsSequenceValid()) { @@ -271,8 +269,6 @@ parent_local_surface_id_allocator_.GetCurrentLocalSurfaceId()); change_size_for_next_commit_ = false; } - - return true; } void CanvasResourceDispatcher::DidReceiveCompositorFrameAck(
diff --git a/third_party/blink/renderer/platform/graphics/canvas_resource_dispatcher.h b/third_party/blink/renderer/platform/graphics/canvas_resource_dispatcher.h index 59cad30e..3c4e41c 100644 --- a/third_party/blink/renderer/platform/graphics/canvas_resource_dispatcher.h +++ b/third_party/blink/renderer/platform/graphics/canvas_resource_dispatcher.h
@@ -120,7 +120,7 @@ using ExportedResourceMap = HashMap<viz::ResourceId, scoped_refptr<ExportedCanvasResource>>; - bool PrepareFrame(scoped_refptr<CanvasResource>&&, + void PrepareFrame(scoped_refptr<ExportedCanvasResource>&&, const gfx::Rect& damage_rect, bool is_opaque, viz::CompositorFrame* frame);
diff --git a/third_party/blink/renderer/platform/graphics/compositing/paint_artifact_compositor_test.cc b/third_party/blink/renderer/platform/graphics/compositing/paint_artifact_compositor_test.cc index 05880650..ce275e4 100644 --- a/third_party/blink/renderer/platform/graphics/compositing/paint_artifact_compositor_test.cc +++ b/third_party/blink/renderer/platform/graphics/compositing/paint_artifact_compositor_test.cc
@@ -93,7 +93,7 @@ // the compositor to run and submit frames. layer_tree_ = std::make_unique<LayerTreeHostEmbedder>( &layer_tree_host_delegate_, - /*single_thread_client=*/nullptr); + /*single_thread_delegate=*/nullptr); layer_tree_host_delegate_.SetLayerTreeHost(layer_tree_->layer_tree_host()); layer_tree_->layer_tree_host()->SetRootLayer( paint_artifact_compositor_->RootLayer());
diff --git a/third_party/blink/renderer/platform/graphics/gpu/xr_webgl_drawing_buffer.cc b/third_party/blink/renderer/platform/graphics/gpu/xr_webgl_drawing_buffer.cc index b07684b1..11d49c2 100644 --- a/third_party/blink/renderer/platform/graphics/gpu/xr_webgl_drawing_buffer.cc +++ b/third_party/blink/renderer/platform/graphics/gpu/xr_webgl_drawing_buffer.cc
@@ -4,6 +4,7 @@ #include "third_party/blink/renderer/platform/graphics/gpu/xr_webgl_drawing_buffer.h" +#include "base/functional/callback_helpers.h" #include "base/logging.h" #include "base/memory/raw_ptr.h" #include "build/build_config.h" @@ -276,6 +277,7 @@ shared_buffer_scoped_access_ = shared_buffer_texture_->BeginAccess(buffer_sync_token, /*readonly=*/false); + buffer_shared_image_ = buffer_shared_image; if (WantExplicitResolve()) { // Bind the shared texture to the destination framebuffer of @@ -319,7 +321,8 @@ client->DrawingBufferClientRestoreFramebufferBinding(); } -void XRWebGLDrawingBuffer::DoneWithSharedBuffer() { +std::unique_ptr<SharedImageHolder> +XRWebGLDrawingBuffer::DoneWithSharedBuffer() { DVLOG(3) << __func__; ScopedPixelLocalStorageInterrupt scoped_pls_interrupt( @@ -344,14 +347,17 @@ // Done with the texture created by CreateAndTexStorage2DSharedImageCHROMIUM // finish accessing and delete it. DCHECK(shared_buffer_texture_); - gpu::SharedImageTexture::ScopedAccess::EndAccess( + gpu::SyncToken sync_token = gpu::SharedImageTexture::ScopedAccess::EndAccess( std::move(shared_buffer_scoped_access_)); shared_buffer_texture_.reset(); DrawingBuffer::Client* client = drawing_buffer_->client(); - if (!client) - return; - client->DrawingBufferClientRestoreFramebufferBinding(); + if (client) { + client->DrawingBufferClientRestoreFramebufferBinding(); + } + + return std::make_unique<SharedImageHolder>(std::move(buffer_shared_image_), + sync_token, base::DoNothing()); } GLuint XRWebGLDrawingBuffer::GetCurrentColorBufferTextureId() {
diff --git a/third_party/blink/renderer/platform/graphics/gpu/xr_webgl_drawing_buffer.h b/third_party/blink/renderer/platform/graphics/gpu/xr_webgl_drawing_buffer.h index d199596..535444c3 100644 --- a/third_party/blink/renderer/platform/graphics/gpu/xr_webgl_drawing_buffer.h +++ b/third_party/blink/renderer/platform/graphics/gpu/xr_webgl_drawing_buffer.h
@@ -49,7 +49,7 @@ void UseSharedBuffer( const scoped_refptr<gpu::ClientSharedImage>& buffer_shared_image, const gpu::SyncToken& buffer_sync_token); - void DoneWithSharedBuffer(); + std::unique_ptr<SharedImageHolder> DoneWithSharedBuffer(); GLuint GetCurrentColorBufferTextureId(); @@ -142,6 +142,8 @@ GLuint depth_stencil_buffer_ = 0; gfx::Size size_; + scoped_refptr<gpu::ClientSharedImage> buffer_shared_image_; + // Valid for shared buffer mode from UseSharedBuffer until // DoneWithSharedBuffer. std::unique_ptr<gpu::SharedImageTexture> shared_buffer_texture_;
diff --git a/third_party/blink/renderer/platform/graphics/paint/display_item_raster_invalidator.cc b/third_party/blink/renderer/platform/graphics/paint/display_item_raster_invalidator.cc index e2d06238..46cbdba 100644 --- a/third_party/blink/renderer/platform/graphics/paint/display_item_raster_invalidator.cc +++ b/third_party/blink/renderer/platform/graphics/paint/display_item_raster_invalidator.cc
@@ -80,13 +80,12 @@ } // Invalidate remaining unmatched (disappeared or uncacheable) old items. - for (auto it = old_display_items_.begin(); it != old_display_items_.end(); - UNSAFE_TODO(++it)) { - if (old_display_items_matched[static_cast<wtf_size_t>( - it - old_display_items_.begin())]) + wtf_size_t i = 0; + for (const auto& old_item : old_display_items_) { + if (old_display_items_matched[i++]) { continue; + } - const auto& old_item = *it; if (old_item.DrawsContent() || old_item.IsTombstone()) { clients_to_invalidate.insert(old_item.ClientId(), OldAndNewDisplayItems()) .stored_value->value.old_visual_rect.Union(old_item.VisualRect()); @@ -104,13 +103,18 @@ DisplayItemIterator& next_old_item_to_match) { if (!new_item.IsCacheable()) return old_display_items_.end(); + // SAFETY: next_old_item_to_match is checked against end() in the loop + // condition. for (; next_old_item_to_match != old_display_items_.end(); - UNSAFE_TODO(next_old_item_to_match++)) { + UNSAFE_BUFFERS(next_old_item_to_match++)) { const auto& old_item = *next_old_item_to_match; if (!old_item.IsCacheable()) continue; - if (old_item.GetId() == new_item.GetId()) - return UNSAFE_TODO(next_old_item_to_match++); + if (old_item.GetId() == new_item.GetId()) { + // SAFETY: next_old_item_to_match is not at end() because we just accessed + // it above. + return UNSAFE_BUFFERS(next_old_item_to_match++); + } // Add the skipped old item into index. old_display_items_index_ .insert(old_item.ClientId(), Vector<DisplayItemIterator>())
diff --git a/third_party/blink/renderer/platform/media/multi_buffer_data_source.cc b/third_party/blink/renderer/platform/media/multi_buffer_data_source.cc index ea0a0599..0903296 100644 --- a/third_party/blink/renderer/platform/media/multi_buffer_data_source.cc +++ b/third_party/blink/renderer/platform/media/multi_buffer_data_source.cc
@@ -804,9 +804,11 @@ base::DoNothing(), tick_clock); } -void MultiBufferDataSource::Factory::Create(const GURL& uri, - DataSource::CacheMode cache_mode, - DataSourceCb cb) { +void MultiBufferDataSource::Factory::Create( + const GURL& uri, + media::DataSource::CacheMode cache_mode, + media::DataSource::EncodingMode encoding_mode, + DataSourceCb cb) { DCHECK(main_task_runner_->BelongsToCurrentThread()); auto download_cb = #if DCHECK_IS_ON() @@ -820,7 +822,7 @@ #endif get_url_data_.Run( - uri, cache_mode, + uri, cache_mode, encoding_mode, blink::BindOnce(&Factory::OnUrlData, weak_factory_.GetWeakPtr(), std::move(cb), std::move(download_cb))); }
diff --git a/third_party/blink/renderer/platform/media/multi_buffer_data_source.h b/third_party/blink/renderer/platform/media/multi_buffer_data_source.h index b4a05b9..97936e9 100644 --- a/third_party/blink/renderer/platform/media/multi_buffer_data_source.h +++ b/third_party/blink/renderer/platform/media/multi_buffer_data_source.h
@@ -51,7 +51,8 @@ public: using UrlDataCb = base::RepeatingCallback<void( const GURL& url, - DataSource::CacheMode cache_mode, + media::DataSource::CacheMode cache_mode, + media::DataSource::EncodingMode encoding_mode, base::OnceCallback<void(scoped_refptr<UrlData>)>)>; ~Factory() override; @@ -64,7 +65,8 @@ scoped_refptr<base::SingleThreadTaskRunner> main_task_runner); void Create(const GURL& uri, - DataSource::CacheMode cache_mode, + media::DataSource::CacheMode cache_mode, + media::DataSource::EncodingMode encoding_mode, DataSourceCb cb) override; private:
diff --git a/third_party/blink/renderer/platform/media/multi_buffer_data_source_unittest.cc b/third_party/blink/renderer/platform/media/multi_buffer_data_source_unittest.cc index d23776ad..f6582d1 100644 --- a/third_party/blink/renderer/platform/media/multi_buffer_data_source_unittest.cc +++ b/third_party/blink/renderer/platform/media/multi_buffer_data_source_unittest.cc
@@ -131,8 +131,15 @@ CorsMode cors_mode, base::WeakPtr<UrlIndex> url_index, UrlData::CacheMode cache_mode, - scoped_refptr<base::SingleThreadTaskRunner> task_runner) - : UrlData(url, cors_mode, url_index, cache_mode, task_runner), + scoped_refptr<base::SingleThreadTaskRunner> task_runner, + media::DataSource::EncodingMode encoding_mode = + media::DataSource::EncodingMode::kIdentity) + : UrlData(url, + cors_mode, + encoding_mode, + url_index, + cache_mode, + task_runner), block_shift_(url_index->block_shift()), task_runner_(std::move(task_runner)) {} @@ -167,12 +174,15 @@ : UrlIndex(fetch_context, task_runner), task_runner_(std::move(task_runner)) {} - scoped_refptr<UrlData> NewUrlData(const KURL& url, - UrlData::CorsMode cors_mode, - UrlData::CacheMode cache_mode) override { + scoped_refptr<UrlData> NewUrlData( + const KURL& url, + UrlData::CorsMode cors_mode, + UrlData::CacheMode cache_mode, + media::DataSource::EncodingMode encoding_mode) override { NotifyNewUrlData(url, cors_mode, cache_mode); last_url_data_ = base::MakeRefCounted<TestUrlData>( - url, cors_mode, weak_factory_.GetWeakPtr(), cache_mode, task_runner_); + url, cors_mode, weak_factory_.GetWeakPtr(), cache_mode, task_runner_, + encoding_mode); return last_url_data_; } @@ -2117,12 +2127,14 @@ base::BindRepeating( [](UrlIndex* url_index, const GURL& url, media::DataSource::CacheMode cache_mode, + media::DataSource::EncodingMode encoding_mode, base::OnceCallback<void(scoped_refptr<UrlData>)> cb) { std::move(cb).Run(url_index->GetByUrl( KURL(url), UrlData::CORS_UNSPECIFIED, cache_mode == media::DataSource::CacheMode::kBypassCache ? UrlData::kCacheDisabled - : UrlData::kNormal)); + : UrlData::kNormal, + encoding_mode)); }, base::Unretained(&url_index_)), /*is_audio_element=*/true, @@ -2134,6 +2146,7 @@ std::unique_ptr<media::DataSource> created_source; factory.Create(GURL(kHttpUrl), media::DataSource::CacheMode::kHitCache, + media::DataSource::EncodingMode::kIdentity, base::BindOnce( [](std::unique_ptr<media::DataSource>* out_source, std::unique_ptr<media::DataSource> source) { @@ -2163,12 +2176,14 @@ base::BindRepeating( [](UrlIndex* url_index, const GURL& url, media::DataSource::CacheMode cache_mode, + media::DataSource::EncodingMode encoding_mode, base::OnceCallback<void(scoped_refptr<UrlData>)> cb) { std::move(cb).Run(url_index->GetByUrl( KURL(url), UrlData::CORS_UNSPECIFIED, cache_mode == media::DataSource::CacheMode::kBypassCache ? UrlData::kCacheDisabled - : UrlData::kNormal)); + : UrlData::kNormal, + encoding_mode)); }, base::Unretained(&url_index_)), /*is_audio_element=*/false, @@ -2177,6 +2192,7 @@ std::unique_ptr<media::DataSource> created_source; factory.Create(GURL(kHttpUrl), media::DataSource::CacheMode::kHitCache, + media::DataSource::EncodingMode::kIdentity, base::BindOnce( [](std::unique_ptr<media::DataSource>* out_source, std::unique_ptr<media::DataSource> source) {
diff --git a/third_party/blink/renderer/platform/media/resource_multi_buffer_data_provider.cc b/third_party/blink/renderer/platform/media/resource_multi_buffer_data_provider.cc index 5af3708..ee35534 100644 --- a/third_party/blink/renderer/platform/media/resource_multi_buffer_data_provider.cc +++ b/third_party/blink/renderer/platform/media/resource_multi_buffer_data_provider.cc
@@ -93,6 +93,7 @@ is_client_audio_element_ ? network::mojom::blink::RequestDestination::kAudio : network::mojom::blink::RequestDestination::kVideo); + request.SetHttpHeaderField( WebString::FromUtf8(net::HttpRequestHeaders::kRange), WebString::FromUtf8( @@ -105,10 +106,18 @@ // along the way. See crbug.com/41185060 and crbug.com/41300485 for more // information. - // Disable compression, compression for audio/video doesn't make sense... - request.SetHttpHeaderField( - WebString::FromUtf8(net::HttpRequestHeaders::kAcceptEncoding), - WebString("identity;q=1, *;q=0")); + if (url_data_->encoding_mode() == + media::DataSource::EncodingMode::kAllowGzip) { + // Allow gzip for manifests or when explicitly requested. + request.SetHttpHeaderField( + WebString::FromUtf8(net::HttpRequestHeaders::kAcceptEncoding), + WebString("gzip, identity;q=1, *;q=0")); + } else { + // Disable compression, compression for audio/video doesn't make sense... + request.SetHttpHeaderField( + WebString::FromUtf8(net::HttpRequestHeaders::kAcceptEncoding), + WebString("identity;q=1, *;q=0")); + } // Start resource loading. WebAssociatedURLLoaderOptions options;
diff --git a/third_party/blink/renderer/platform/media/resource_multi_buffer_data_provider_unittest.cc b/third_party/blink/renderer/platform/media/resource_multi_buffer_data_provider_unittest.cc index 6431a01..c245eb39 100644 --- a/third_party/blink/renderer/platform/media/resource_multi_buffer_data_provider_unittest.cc +++ b/third_party/blink/renderer/platform/media/resource_multi_buffer_data_provider_unittest.cc
@@ -89,10 +89,13 @@ ResourceMultiBufferDataProviderTest& operator=( const ResourceMultiBufferDataProviderTest&) = delete; - void Initialize(const char* url, int first_position) { + void Initialize(const char* url, + int first_position, + media::DataSource::EncodingMode encoding_mode = + media::DataSource::EncodingMode::kIdentity) { url_ = KURL(url); - url_data_ = - url_index_->GetByUrl(url_, UrlData::CORS_UNSPECIFIED, UrlData::kNormal); + url_data_ = url_index_->GetByUrl(url_, UrlData::CORS_UNSPECIFIED, + UrlData::kNormal, encoding_mode); url_data_->set_etag(kEtag); DCHECK(url_data_); url_data_->OnRedirect( @@ -438,4 +441,27 @@ StopWhenLoad(); } +TEST_F(ResourceMultiBufferDataProviderTest, AllowGzip) { + Initialize(kHttpUrl, 0, media::DataSource::EncodingMode::kAllowGzip); + + auto url_loader = std::make_unique<NiceMock<MockWebAssociatedURLLoader>>(); + EXPECT_CALL(*url_loader, + LoadAsynchronously( + Truly([](const WebURLRequest& request) { + std::string value = + request + .HttpHeaderField(WebString::FromUtf8( + net::HttpRequestHeaders::kAcceptEncoding)) + .Utf8(); + return value.contains("gzip"); + }), + loader_.get())); + + EXPECT_CALL(fetch_context_, CreateUrlLoader(_)) + .WillOnce(testing::Return(testing::ByMove(std::move(url_loader)))); + + Start(); + StopWhenLoad(); +} + } // namespace blink
diff --git a/third_party/blink/renderer/platform/media/url_index.cc b/third_party/blink/renderer/platform/media/url_index.cc index 2ec380e..6bbf6c4 100644 --- a/third_party/blink/renderer/platform/media/url_index.cc +++ b/third_party/blink/renderer/platform/media/url_index.cc
@@ -54,17 +54,20 @@ UrlData::UrlData(base::PassKey<UrlIndex>, const KURL& url, CorsMode cors_mode, + media::DataSource::EncodingMode encoding_mode, base::WeakPtr<UrlIndex> url_index, CacheMode cache_lookup_mode, scoped_refptr<base::SingleThreadTaskRunner> task_runner) : UrlData(url, cors_mode, + encoding_mode, url_index, cache_lookup_mode, std::move(task_runner)) {} UrlData::UrlData(const KURL& url, CorsMode cors_mode, + media::DataSource::EncodingMode encoding_mode, base::WeakPtr<UrlIndex> url_index, CacheMode cache_lookup_mode, scoped_refptr<base::SingleThreadTaskRunner> task_runner) @@ -76,7 +79,8 @@ range_supported_(false), cacheable_(false), cache_lookup_mode_(cache_lookup_mode), - multibuffer_(this, url_index_->block_shift_, std::move(task_runner)) {} + encoding_mode_(encoding_mode), + multibuffer_(this, url_index_->block_shift(), std::move(task_runner)) {} UrlData::~UrlData() = default; @@ -274,9 +278,11 @@ } } -scoped_refptr<UrlData> UrlIndex::GetByUrl(const KURL& url, - UrlData::CorsMode cors_mode, - UrlData::CacheMode cache_mode) { +scoped_refptr<UrlData> UrlIndex::GetByUrl( + const KURL& url, + UrlData::CorsMode cors_mode, + UrlData::CacheMode cache_mode, + media::DataSource::EncodingMode encoding_mode) { if (cache_mode == UrlData::kNormal) { auto i = indexed_data_.find(std::make_pair(url, cors_mode)); if (i != indexed_data_.end() && i->value->Valid()) { @@ -284,16 +290,17 @@ } } - return NewUrlData(url, cors_mode, cache_mode); + return NewUrlData(url, cors_mode, cache_mode, encoding_mode); } scoped_refptr<UrlData> UrlIndex::NewUrlData( const KURL& url, UrlData::CorsMode cors_mode, - UrlData::CacheMode cache_lookup_mode) { - return base::MakeRefCounted<UrlData>(base::PassKey<UrlIndex>(), url, - cors_mode, weak_factory_.GetWeakPtr(), - cache_lookup_mode, task_runner_); + UrlData::CacheMode cache_lookup_mode, + media::DataSource::EncodingMode encoding_mode) { + return base::MakeRefCounted<UrlData>( + base::PassKey<UrlIndex>(), url, cors_mode, encoding_mode, + weak_factory_.GetWeakPtr(), cache_lookup_mode, task_runner_); } namespace {
diff --git a/third_party/blink/renderer/platform/media/url_index.h b/third_party/blink/renderer/platform/media/url_index.h index 5271b6d0..afb4aad9 100644 --- a/third_party/blink/renderer/platform/media/url_index.h +++ b/third_party/blink/renderer/platform/media/url_index.h
@@ -18,6 +18,7 @@ #include "base/threading/thread_checker.h" #include "base/time/time.h" #include "base/types/pass_key.h" +#include "media/base/data_source.h" #include "third_party/blink/renderer/platform/media/multi_buffer.h" #include "third_party/blink/renderer/platform/platform_export.h" #include "third_party/blink/renderer/platform/weborigin/kurl.h" @@ -81,6 +82,7 @@ UrlData(base::PassKey<UrlIndex>, const KURL& url, CorsMode cors_mode, + media::DataSource::EncodingMode encoding_mode, base::WeakPtr<UrlIndex> url_index, CacheMode cache_lookup_mode, scoped_refptr<base::SingleThreadTaskRunner> task_runner); @@ -112,6 +114,10 @@ // lookups, regardless of disk cache or response status. CacheMode cache_lookup_mode() const { return cache_lookup_mode_; } + media::DataSource::EncodingMode encoding_mode() const { + return encoding_mode_; + } + // Last used time. base::Time last_used() const { return last_used_; } @@ -193,6 +199,7 @@ protected: UrlData(const KURL& url, CorsMode cors_mode, + media::DataSource::EncodingMode encoding_mode, base::WeakPtr<UrlIndex> url_index, CacheMode cache_lookup_mode, scoped_refptr<base::SingleThreadTaskRunner> task_runner); @@ -244,6 +251,8 @@ // UrlData should use existing underlying cached data. CacheMode cache_lookup_mode_; + media::DataSource::EncodingMode encoding_mode_; + // https://html.spec.whatwg.org/#cors-cross-origin bool is_cors_cross_origin_ = false; @@ -287,9 +296,12 @@ // ranges and it's last modified time. // Because the returned UrlData has a raw reference to |this|, it must be // released before |this| is destroyed. - scoped_refptr<UrlData> GetByUrl(const KURL& url, - UrlData::CorsMode cors_mode, - UrlData::CacheMode cache_mode); + scoped_refptr<UrlData> GetByUrl( + const KURL& url, + UrlData::CorsMode cors_mode, + UrlData::CacheMode cache_mode, + media::DataSource::EncodingMode encoding_mode = + media::DataSource::EncodingMode::kIdentity); // Add the given UrlData to the index if possible. If a better UrlData // is already present in the index, return it instead. (If not, we just @@ -325,7 +337,8 @@ virtual scoped_refptr<UrlData> NewUrlData( const KURL& url, UrlData::CorsMode cors_mode, - UrlData::CacheMode cache_lookup_mode); + UrlData::CacheMode cache_lookup_mode, + media::DataSource::EncodingMode encoding_mode); raw_ptr<ResourceFetchContext> fetch_context_; using UrlDataMap = HashMap<UrlData::KeyType, scoped_refptr<UrlData>>;
diff --git a/third_party/blink/renderer/platform/media/url_index_unittest.cc b/third_party/blink/renderer/platform/media/url_index_unittest.cc index a83c2d63..e2498bd7 100644 --- a/third_party/blink/renderer/platform/media/url_index_unittest.cc +++ b/third_party/blink/renderer/platform/media/url_index_unittest.cc
@@ -224,4 +224,17 @@ EXPECT_TRUE(b->is_cors_cross_origin()); } +TEST_F(UrlIndexTest, AllowGzip) { + KURL url("http://foo.bar.com"); + scoped_refptr<UrlData> a = + url_index_->GetByUrl(url, UrlData::CORS_UNSPECIFIED, UrlData::kNormal, + media::DataSource::EncodingMode::kAllowGzip); + EXPECT_EQ(a->encoding_mode(), media::DataSource::EncodingMode::kAllowGzip); + + scoped_refptr<UrlData> b = + url_index_->GetByUrl(url, UrlData::CORS_UNSPECIFIED, UrlData::kNormal, + media::DataSource::EncodingMode::kIdentity); + EXPECT_EQ(b->encoding_mode(), media::DataSource::EncodingMode::kIdentity); +} + } // namespace blink
diff --git a/third_party/blink/renderer/platform/media/web_media_player_impl.cc b/third_party/blink/renderer/platform/media/web_media_player_impl.cc index 0e0f38e..b3f4c69 100644 --- a/third_party/blink/renderer/platform/media/web_media_player_impl.cc +++ b/third_party/blink/renderer/platform/media/web_media_player_impl.cc
@@ -1745,11 +1745,12 @@ void WebMediaPlayerImpl::GetUrlData( const GURL& gurl, media::DataSource::CacheMode cache_mode, + media::DataSource::EncodingMode encoding_mode, base::OnceCallback<void(scoped_refptr<UrlData>)> cb) { DCHECK(main_task_runner_->BelongsToCurrentThread()); auto url_data = url_index_->GetByUrl( KURL(gurl), static_cast<UrlData::CorsMode>(cors_mode_), - TranslateCacheMode(is_cache_disabled_, cache_mode)); + TranslateCacheMode(is_cache_disabled_, cache_mode), encoding_mode); std::move(cb).Run(std::move(url_data)); }
diff --git a/third_party/blink/renderer/platform/media/web_media_player_impl.h b/third_party/blink/renderer/platform/media/web_media_player_impl.h index 83cefbc8..fa33855 100644 --- a/third_party/blink/renderer/platform/media/web_media_player_impl.h +++ b/third_party/blink/renderer/platform/media/web_media_player_impl.h
@@ -458,6 +458,7 @@ #if BUILDFLAG(ENABLE_HLS_DEMUXER) void GetUrlData(const GURL& gurl, media::DataSource::CacheMode cache_mode, + media::DataSource::EncodingMode encoding_mode, base::OnceCallback<void(scoped_refptr<UrlData>)> cb); base::SequenceBound<media::HlsDataSourceProvider> GetHlsDataSourceProvider() override;
diff --git a/third_party/blink/renderer/platform/runtime_enabled_features.json5 b/third_party/blink/renderer/platform/runtime_enabled_features.json5 index c83eb8d1..fa5c50e 100644 --- a/third_party/blink/renderer/platform/runtime_enabled_features.json5 +++ b/third_party/blink/renderer/platform/runtime_enabled_features.json5
@@ -2429,6 +2429,13 @@ status: "stable", }, { + // Enables JS-constructed File objects (e.g. new File([bytes], 'photo.jpg')) + // to carry their blob contents through the drag-and-drop pipeline. + // When disabled, only the filename is sent as text/plain (legacy behavior). + name: "DragAndDropJSFileObjects", + status: "test", + }, + { // See https://crbug.com/443612843 name: "EditContextAssignmentAsPerSpec", status: "test" @@ -2988,8 +2995,7 @@ { // https://open-ui.org/components/scoped-focusgroup.explainer/ name: "Focusgroup", - status: "experimental", - origin_trial_feature_name: "Focusgroup", + status: "stable", }, { // 2-D grid navigation functionality of focusgroup is considered out of
diff --git a/third_party/blink/renderer/platform/testing/layer_tree_host_embedder.cc b/third_party/blink/renderer/platform/testing/layer_tree_host_embedder.cc index c2255e3a..fc10ae0 100644 --- a/third_party/blink/renderer/platform/testing/layer_tree_host_embedder.cc +++ b/third_party/blink/renderer/platform/testing/layer_tree_host_embedder.cc
@@ -10,11 +10,11 @@ LayerTreeHostEmbedder::LayerTreeHostEmbedder() : LayerTreeHostEmbedder(/*client=*/nullptr, - /*single_thread_client=*/nullptr) {} + /*single_thread_delegate=*/nullptr) {} LayerTreeHostEmbedder::LayerTreeHostEmbedder( cc::LayerTreeHostDelegate* client, - cc::LayerTreeHostSingleThreadClient* single_thread_client) { + cc::LayerTreeHostSingleThreadDelegate* single_thread_delegate) { cc::LayerTreeSettings settings; settings.single_thread_proxy_scheduler = false; settings.use_layer_lists = true; @@ -27,8 +27,8 @@ params.mutator_host = animation_host_.get(); layer_tree_host_ = cc::LayerTreeHost::CreateSingleThreaded( - single_thread_client ? single_thread_client - : &layer_tree_host_single_thread_client_, + single_thread_delegate ? single_thread_delegate + : &layer_tree_host_single_thread_delegate_, std::move(params)); }
diff --git a/third_party/blink/renderer/platform/testing/layer_tree_host_embedder.h b/third_party/blink/renderer/platform/testing/layer_tree_host_embedder.h index 0a63773..d86a9b1 100644 --- a/third_party/blink/renderer/platform/testing/layer_tree_host_embedder.h +++ b/third_party/blink/renderer/platform/testing/layer_tree_host_embedder.h
@@ -7,7 +7,7 @@ #include "cc/animation/animation_host.h" #include "cc/test/stub_layer_tree_host_delegate.h" -#include "cc/test/stub_layer_tree_host_single_thread_client.h" +#include "cc/test/stub_layer_tree_host_single_thread_delegate.h" #include "cc/test/test_task_graph_runner.h" #include "cc/trees/layer_tree_host.h" #include "cc/trees/layer_tree_settings.h" @@ -29,13 +29,14 @@ // overrides of LayerTreeSettings. LayerTreeHostEmbedder( cc::LayerTreeHostDelegate* client, - cc::LayerTreeHostSingleThreadClient* single_thread_client); + cc::LayerTreeHostSingleThreadDelegate* single_thread_delegate); cc::LayerTreeHost* layer_tree_host() { return layer_tree_host_.get(); } cc::AnimationHost* animation_host() { return animation_host_.get(); } private: - cc::StubLayerTreeHostSingleThreadClient layer_tree_host_single_thread_client_; + cc::StubLayerTreeHostSingleThreadDelegate + layer_tree_host_single_thread_delegate_; cc::StubLayerTreeHostDelegate layer_tree_host_delegate_; cc::TestTaskGraphRunner task_graph_runner_; std::unique_ptr<cc::AnimationHost> animation_host_;
diff --git a/third_party/blink/renderer/platform/text/text_run.h b/third_party/blink/renderer/platform/text/text_run.h index 665dacf..d639232d 100644 --- a/third_party/blink/renderer/platform/text/text_run.h +++ b/third_party/blink/renderer/platform/text/text_run.h
@@ -63,14 +63,7 @@ TextRun(TextRun&&) = default; TextRun& operator=(TextRun&&) = delete; - UChar operator[](unsigned i) const { return UNSAFE_TODO(text_[i]); } - - base::span<const LChar> Span8() const { return text_.Span8(); } - base::span<const UChar> Span16() const { return text_.Span16(); } - const StringView& ToStringView() const { return text_; } - - bool Is8Bit() const { return text_.Is8Bit(); } unsigned length() const { return text_.length(); } TextDirection Direction() const {
diff --git a/third_party/blink/renderer/platform/widget/compositing/layer_tree_view.h b/third_party/blink/renderer/platform/widget/compositing/layer_tree_view.h index 82c61ed4..cc031904 100644 --- a/third_party/blink/renderer/platform/widget/compositing/layer_tree_view.h +++ b/third_party/blink/renderer/platform/widget/compositing/layer_tree_view.h
@@ -15,7 +15,7 @@ #include "base/time/time.h" #include "cc/input/browser_controls_state.h" #include "cc/trees/layer_tree_host_delegate.h" -#include "cc/trees/layer_tree_host_single_thread_client.h" +#include "cc/trees/layer_tree_host_single_thread_delegate.h" #include "cc/trees/paint_holding_reason.h" #include "third_party/blink/renderer/platform/platform_export.h" #include "third_party/blink/renderer/platform/widget/compositing/layer_tree_view_delegate.h" @@ -39,7 +39,7 @@ class PLATFORM_EXPORT LayerTreeView : public cc::LayerTreeHostDelegate, - public cc::LayerTreeHostSingleThreadClient, + public cc::LayerTreeHostSingleThreadDelegate, public cc::LayerTreeHostSchedulingDelegate { public: LayerTreeView(LayerTreeViewDelegate* delegate, @@ -126,7 +126,7 @@ cc::PaintBenchmarkResult& result) override; std::string GetPausedDebuggerLocalizedMessage() override; - // cc::LayerTreeHostSingleThreadClient implementation. + // cc::LayerTreeHostSingleThreadDelegate implementation. void DidSubmitCompositorFrame() override; void DidLoseLayerTreeFrameSink() override; void ScheduleAnimationForWebTests() override;
diff --git a/third_party/blink/tools/blinkpy/w3c/test_importer_unittest.py b/third_party/blink/tools/blinkpy/w3c/test_importer_unittest.py index d5ca655..cd7b6db0d 100644 --- a/third_party/blink/tools/blinkpy/w3c/test_importer_unittest.py +++ b/third_party/blink/tools/blinkpy/w3c/test_importer_unittest.py
@@ -40,6 +40,8 @@ MANIFEST_INSTALL_CMD = [ 'python3', '/mock-checkout/third_party/wpt_tools/wpt/wpt', + '--venv=/mock-checkout/third_party/wpt_tools/wpt/_venv3', + '--skip-venv-setup', 'manifest', '-v', '--no-download',
diff --git a/third_party/blink/tools/blinkpy/w3c/wpt_manifest.py b/third_party/blink/tools/blinkpy/w3c/wpt_manifest.py index dbf70ff..e560632 100644 --- a/third_party/blink/tools/blinkpy/w3c/wpt_manifest.py +++ b/third_party/blink/tools/blinkpy/w3c/wpt_manifest.py
@@ -448,12 +448,17 @@ url_base: str = '/', test_paths: Optional[List[str]] = None): """Generates MANIFEST.json on the specified directory.""" - wpt_exec_path = PathFinder( - port.host.filesystem).path_from_chromium_base( - 'third_party', 'wpt_tools', 'wpt', 'wpt') + fs = port.host.filesystem + wpt_tools_dir = PathFinder(fs).path_from_chromium_base( + 'third_party', 'wpt_tools', 'wpt') cmd = [ port.python3_command(), - wpt_exec_path, + fs.join(wpt_tools_dir, 'wpt'), + # Third-party packages are vended through vpython instead of plain + # virtualenv. We still need to specify `wpt --venv`, which doubles + # as a gitignored scratch directory. + f'--venv={fs.join(wpt_tools_dir, "_venv3")}', + '--skip-venv-setup', 'manifest', '-v', '--no-download',
diff --git a/third_party/blink/tools/blinkpy/w3c/wpt_manifest_unittest.py b/third_party/blink/tools/blinkpy/w3c/wpt_manifest_unittest.py index 327c5e8..5803fc3 100644 --- a/third_party/blink/tools/blinkpy/w3c/wpt_manifest_unittest.py +++ b/third_party/blink/tools/blinkpy/w3c/wpt_manifest_unittest.py
@@ -27,6 +27,8 @@ self.assertEqual(host.executive.calls, [[ port.python3_command(), '/mock-checkout/third_party/wpt_tools/wpt/wpt', + '--venv=/mock-checkout/third_party/wpt_tools/wpt/_venv3', + '--skip-venv-setup', 'manifest', '-v', '--no-download', @@ -52,6 +54,8 @@ self.assertEqual(host.executive.calls, [[ port.python3_command(), '/mock-checkout/third_party/wpt_tools/wpt/wpt', + '--venv=/mock-checkout/third_party/wpt_tools/wpt/_venv3', + '--skip-venv-setup', 'manifest', '-v', '--no-download', @@ -74,6 +78,8 @@ self.assertEqual(host.executive.calls, [[ port.python3_command(), '/mock-checkout/third_party/wpt_tools/wpt/wpt', + '--venv=/mock-checkout/third_party/wpt_tools/wpt/_venv3', + '--skip-venv-setup', 'manifest', '-v', '--no-download',
diff --git a/third_party/blink/web_tests/TestExpectations b/third_party/blink/web_tests/TestExpectations index 8f2dec0..b96a68a 100644 --- a/third_party/blink/web_tests/TestExpectations +++ b/third_party/blink/web_tests/TestExpectations
@@ -53,6 +53,12 @@ external/wpt/infrastructure/reftest/reftest_timeout.html [ Timeout ] wpt_internal/infrastructure/fail-before-timeout.html [ Timeout ] +# Fallback-only test should fail in default runs (feature enabled). +crbug.com/510410319 external/wpt/html/editing/dnd/js-file-fallback-text-only.html [ Failure ] + +# Drag-and-drop for JS-constructed files is currently unsupported. +crbug.com/40183464 [ Mac ] external/wpt/html/editing/dnd/js-file-image-drag.html [ Failure ] + # Temporary until we roll a newer jxl-rs version with updated decoder output. [ Linux ] virtual/jxl-enabled/external/wpt/jpegxl/jxl-art-basic-reftest.html [ Failure ] @@ -998,10 +1004,6 @@ crbug.com/40110559 external/wpt/css/css-lists/marker-counter.html [ Failure ] crbug.com/40110559 external/wpt/css/css-lists/marker-quotes.html [ Failure ] -crbug.com/1330383 external/wpt/css/css-lists/counter-order-display-contents.html [ Failure ] -crbug.com/1330383 external/wpt/css/css-lists/counter-slot-order-scoping.html [ Failure ] -crbug.com/1330383 external/wpt/css/css-lists/counter-slot-order.html [ Failure ] - # [css-counter-styles-3] crbug.com/1176323 external/wpt/css/css-counter-styles/counter-style-at-rule/prefix-suffix-syntax.html [ Failure ] crbug.com/1176323 external/wpt/css/css-counter-styles/counter-style-at-rule/symbols-syntax.html [ Failure ] @@ -2764,6 +2766,7 @@ crbug.com/507904104 virtual/produce-compile-hints/external/wpt/fetch/fetch-later/quota/same-origin-iframe/empty-payload.https.window.html [ Failure ] # ====== New tests from wpt-importer added here ====== +crbug.com/510924299 external/wpt/html/semantics/forms/the-select-element/customizable-select/select-appearance-base-display.html [ Failure ] crbug.com/510499376 external/wpt/content-security-policy/inheritance/auxiliary-blank-document.html [ Timeout ] crbug.com/510491337 external/wpt/fullscreen/api/keyboard-lock-cross-origin-iframe.tentative.sub.html [ Skip Timeout ] crbug.com/510497811 [ Win ] external/wpt/navigation-api/ordering-and-transition/anchor-download-intercept-reject.html?currententrychange [ Pass Timeout ] @@ -9807,4 +9810,4 @@ # Gardener 2026-05-07 crbug.com/510507820 [ Win10.20h2 ] external/wpt/speech-api/SpeechRecognition-installOnDevice.https.html [ Failure Pass Timeout ] -crbug.com/510507820 [ Win10.20h2 ] virtual/speech-with-unified-autoplay/external/wpt/speech-api/SpeechRecognition-installOnDevice.https.html [ Failure Pass Timeout ] \ No newline at end of file +crbug.com/510507820 [ Win10.20h2 ] virtual/speech-with-unified-autoplay/external/wpt/speech-api/SpeechRecognition-installOnDevice.https.html [ Failure Pass Timeout ]
diff --git a/third_party/blink/web_tests/TestLists/content_shell.filter b/third_party/blink/web_tests/TestLists/content_shell.filter index 13a6626c..79a1782f 100644 --- a/third_party/blink/web_tests/TestLists/content_shell.filter +++ b/third_party/blink/web_tests/TestLists/content_shell.filter
@@ -379,9 +379,6 @@ external/wpt/css/css-transitions/transition-behavior.html external/wpt/css/css-ui/compute-kind-widget-no-fallback-props-001.html external/wpt/css/css-ui/outline-020.html -external/wpt/css/css-ui/text-overflow-027.html -external/wpt/css/css-ui/text-overflow-028.html -external/wpt/css/css-ui/text-overflow-029.html external/wpt/css/css-values/viewport-units-scrollbars* external/wpt/css/css-variables/variable-presentation-attribute.html external/wpt/css/css-will-change/will-change-fixedpos-cb-*
diff --git a/third_party/blink/web_tests/VirtualTestSuites b/third_party/blink/web_tests/VirtualTestSuites index 5606ca2..0ef2f26 100644 --- a/third_party/blink/web_tests/VirtualTestSuites +++ b/third_party/blink/web_tests/VirtualTestSuites
@@ -6368,6 +6368,18 @@ ] }, { + "prefix": "disable-drag-drop-js-file-objects", + "platforms": ["Linux", "Mac", "Win"], + "bases": [ + "external/wpt/html/editing/dnd/js-file-fallback-text-only.html" + ], + "args": [ + "--disable-blink-features=DragAndDropJSFileObjects" + ], + "owners": ["joonehur@microsoft.com"], + "expires": "Dec 1, 2026" + }, + { "prefix": "webmcp-kill-switch", "platforms": ["Linux", "Mac", "Win"], "bases": [
diff --git a/third_party/blink/web_tests/external/WPT_BASE_MANIFEST_8.json b/third_party/blink/web_tests/external/WPT_BASE_MANIFEST_8.json index 4aae36d..0e0f998 100644 --- a/third_party/blink/web_tests/external/WPT_BASE_MANIFEST_8.json +++ b/third_party/blink/web_tests/external/WPT_BASE_MANIFEST_8.json
@@ -6647,6 +6647,13 @@ {} ] ], + "input-range-content-url.html": [ + "87840a95cd90719749156d7466019a31d4c97b17", + [ + null, + {} + ] + ], "outline-scrollIntoView-crash.html": [ "58bbd4c6b4f1b634400b5c37c06f743f4dda852d", [ @@ -93612,6 +93619,19 @@ } ] ], + "border-shape-outline-invalidation.html": [ + "33f5964b82cbeb291ee9caac3ecba7e19d19c832", + [ + null, + [ + [ + "/css/css-borders/border-shape/border-shape-outline-invalidation-ref.html", + "==" + ] + ], + {} + ] + ], "border-shape-outline-offset.html": [ "a2d66d4a48265e3e9297c10dd3883f6b0b8eb949", [ @@ -189775,6 +189795,19 @@ {} ] ], + "max-height-stretch-relayout.html": [ + "eda960a575084dff09f478df460a3c4729dae731", + [ + null, + [ + [ + "/css/reference/ref-filled-green-100px-square.xht", + "==" + ] + ], + {} + ] + ], "move-with-text-after-paint.html": [ "1ea3d4b58bce1fe07a57faa05b008e426368925a", [ @@ -264584,6 +264617,84 @@ {} ] ], + "caret-color-bar-shape-text-color.html": [ + "0efaf096481e6d74378a3cfe63285aefa9ad0632", + [ + null, + [ + [ + "/css/css-ui/caret-color-non-block-shape-text-color-ref.html", + "==" + ] + ], + {} + ] + ], + "caret-color-block-shape-text-color-001.html": [ + "cd6d9e18b69788ad1bc8cdd58a6f6249fad48e47", + [ + null, + [ + [ + "/css/css-ui/caret-color-block-shape-text-color-001-ref.html", + "==" + ] + ], + {} + ] + ], + "caret-color-block-shape-text-color-002.html": [ + "8bd1e92b633513468257d3f27777345611ce9b92", + [ + null, + [ + [ + "/css/css-ui/caret-color-block-shape-text-color-002-ref.html", + "==" + ] + ], + {} + ] + ], + "caret-color-block-shape-text-color-003.html": [ + "4b8804ea01ac854c8228adf92c4343701d1780dc", + [ + null, + [ + [ + "/css/css-ui/caret-color-block-shape-text-color-003-ref.html", + "==" + ] + ], + {} + ] + ], + "caret-color-block-shape-text-color-004.html": [ + "6d8b350beec5dbf00eb3d423b3661e2e547c2d5d", + [ + null, + [ + [ + "/css/css-ui/caret-color-block-shape-text-color-004-ref.html", + "==" + ] + ], + {} + ] + ], + "caret-color-underscore-shape-text-color.html": [ + "806e32037efb020ddfd9eccae443526530a5e936", + [ + null, + [ + [ + "/css/css-ui/caret-color-non-block-shape-text-color-ref.html", + "==" + ] + ], + {} + ] + ], "caret-eol-001.html": [ "456c38643975f8bb311a3210cac940cc7369fd59", [ @@ -305294,6 +305405,35 @@ {} ] ], + "backdrop-filter-3d-transform-perspective.html": [ + "1df16d5773c6c56e1b2c0e8be8db14e67047722f", + [ + null, + [ + [ + "/css/filter-effects/backdrop-filter-3d-transform-perspective-ref.html", + "==" + ] + ], + { + "fuzzy": [ + [ + null, + [ + [ + 0, + 150 + ], + [ + 0, + 400 + ] + ] + ] + ] + } + ] + ], "backdrop-filter-backdrop-root-animation-in-effect.html": [ "111cafe96d90c10ffdfb5498edc0895650263c4c", [ @@ -306021,6 +306161,35 @@ {} ] ], + "backdrop-filter-nested-3d-transform-perspective.html": [ + "6bd22e83e94fbcf051b2ae169c6d5a39d06c645d", + [ + null, + [ + [ + "/css/filter-effects/backdrop-filter-nested-3d-transform-perspective-ref.html", + "==" + ] + ], + { + "fuzzy": [ + [ + null, + [ + [ + 0, + 150 + ], + [ + 0, + 400 + ] + ] + ] + ] + } + ] + ], "backdrop-filter-nested-border-radius-clip-2.html": [ "51fc9bbc26ea95230a0577ca10a202392217b9cd", [ @@ -332177,6 +332346,19 @@ ] }, "the-video-element": { + "video-poster-clone-template.html": [ + "6825635499b3b4ebf85c2f77420f4393e3a13627", + [ + null, + [ + [ + "/html/semantics/embedded-content/the-video-element/video_dynamic_poster-ref.htm", + "==" + ] + ], + {} + ] + ], "video-poster-shown-preload-auto.html": [ "95e32bb77ce1f7642a881edc1c61ae4d6879cea2", [ @@ -332948,6 +333130,19 @@ } ] ], + "select-appearance-base-display.html": [ + "70bd8717745a57af0cae7762e0937ef235820fa3", + [ + null, + [ + [ + "/html/semantics/forms/the-select-element/customizable-select/select-appearance-base-display-ref.html", + "==" + ] + ], + {} + ] + ], "select-appearance-button-after-option.html": [ "c29c0e827f13c7fcfe22fc3ad3cd92a0df030e0f", [ @@ -342508,19 +342703,6 @@ {} ] ], - "hdr-alpha-reftest.html": [ - "90c50ec37d49cad95366c491ef4894ecc6d8bd44", - [ - null, - [ - [ - "/jpegxl/hdr-alpha-reftest-ref.html", - "==" - ] - ], - {} - ] - ], "html-embed-object-iframe.html": [ "a4fc7f549de5c92d642ea34da5608ed4a1c76bff", [ @@ -342709,19 +342891,6 @@ } ] ], - "sdr-alpha-reftest.html": [ - "5e7dc266f87beed6318d797e447822b743268694", - [ - null, - [ - [ - "/jpegxl/sdr-alpha-reftest-ref.html", - "==" - ] - ], - {} - ] - ], "smallest-valid-reftest.html": [ "2fcf7e9b28616eed4c55afcc6f46898f937b88a5", [ @@ -353732,39 +353901,52 @@ {} ] ], - "svg-filter-render-local-frame.tentative.https.html": [ - "1ef2ef0e90830695cb884ce7e7a457ed5cf41747", + "svg-filter-render-cross-origin-frame-in-same-origin-frame.tentative.https.html": [ + "1f2aa4aa8aa93adfc5954f61b5834b904ade8965", [ null, [ [ - "/svg/styling/svg-filter-render-local-frame-ref.tentative.html", + "/svg/styling/svg-filter-render-cross-origin-frame-in-same-origin-frame-ref.tentative.html", "==" ] ], {} ] ], - "svg-filter-render-remote-frame-in-local-frame.tentative.https.html": [ - "bb7d00d027a6f71817580866aa7248ce26a051c7", + "svg-filter-render-cross-origin-frame.tentative.sub.https.html": [ + "8bd318d663a538b6f583e950eaa196533b95a8a5", [ null, [ [ - "/svg/styling/svg-filter-render-remote-frame-in-local-frame-ref.tentative.html", + "/svg/styling/svg-filter-render-cross-origin-frame-ref.tentative.sub.html", "==" ] ], {} ] ], - "svg-filter-render-remote-frame.tentative.sub.https.html": [ - "cf0e7272987d6df135315a3cdc9367d16dfc35b8", + "svg-filter-render-same-origin-frame.tentative.https.html": [ + "e1518e6dd958c77abf9c34c772fcd40aed694e8a", [ null, [ [ - "/svg/styling/svg-filter-render-remote-frame-ref.tentative.sub.html", + "/svg/styling/svg-filter-render-same-origin-frame-ref.tentative.html", + "==" + ] + ], + {} + ] + ], + "svg-filter-render-sandbox-frame.tentative.https.html": [ + "c4cdf31e7e4d355ea1ba8f4137faaa5c46d9f7c4", + [ + null, + [ + [ + "/svg/styling/svg-filter-render-sandbox-frame-ref.tentative.html", "==" ] ], @@ -377075,6 +377257,10 @@ "6fd6516e469233e7ac7b201a49c0874ca498c657", [] ], + "border-shape-outline-invalidation-ref.html": [ + "950931a3baafb282c6bba4eb084c849696902d4c", + [] + ], "border-shape-outline-offset-ref.html": [ "685473672470385739dc4ba2145d81e682ed80f9", [] @@ -417764,6 +417950,26 @@ "5304352e6bafd1d25436babfc2afc0ba1aa26fa3", [] ], + "caret-color-block-shape-text-color-001-ref.html": [ + "8bb15a550f4d58658cbd2df55000392913599c31", + [] + ], + "caret-color-block-shape-text-color-002-ref.html": [ + "95240c0ef8f200e0a3e7074d242a5be83cc7b923", + [] + ], + "caret-color-block-shape-text-color-003-ref.html": [ + "39c75ef10bc2a2fd7f6a0d3065a6856eef406a6f", + [] + ], + "caret-color-block-shape-text-color-004-ref.html": [ + "f58619754d1e8dd00710bd7ddc4fd878cd691014", + [] + ], + "caret-color-non-block-shape-text-color-ref.html": [ + "27e03bcd744ffff6191f701adeb804d5115e8d93", + [] + ], "caret-shape-block-001-ref.html": [ "2e6f802c638a4e4cc2fd9ae70efd523ab13defae", [] @@ -424818,6 +425024,10 @@ [] ] }, + "backdrop-filter-3d-transform-perspective-ref.html": [ + "323f5aa42b57298fa9c709763ef96afc23c4b2a6", + [] + ], "backdrop-filter-backdrop-root-animation-in-effect-ref.html": [ "336f76866e938887410b3cc33ea6691bd4d04d01", [] @@ -424910,6 +425120,10 @@ "28f3c415f94ab6dd6e63a061e984475593400fd8", [] ], + "backdrop-filter-nested-3d-transform-perspective-ref.html": [ + "d62a223566cc27cbfc7a5393657e1a54a3a1c215", + [] + ], "backdrop-filter-nested-border-radius-clip-2-ref.html": [ "fba477b2f8e2881d97ee6010e2abd7ceeb0d9bca", [] @@ -426599,14 +426813,6 @@ "ff7a2c894a85507b73b85bb2056682b02633bf08", [] ], - "media-loading-pseudo-classes-in-has.sub-expected.txt": [ - "065681d3a804ee058f031e369180e9b025517057", - [] - ], - "media-pseudo-classes-in-has-expected.txt": [ - "1c4bd18666556fd94694e97d1662f92fab8218f0", - [] - ], "negated-always-matches-negated-first-of-type-when-ancestor-changes-ref.html": [ "2ddb130e05f5fe92afb1450de69894b60beb9fad", [] @@ -426769,24 +426975,8 @@ "4cd0fc2726292091a0f7f98f47934afc1578b60b", [] ], - "media-loading-state-timing.sub-expected.txt": [ - "66520084a2e9e81749108de5b636199f055b99aa", - [] - ], - "media-loading-state.sub-expected.txt": [ - "4de3618e7de779ac4480e3d92db3375b77332e9b", - [] - ], - "media-playback-state-expected.txt": [ - "8b1bbd1ced2e5c32b4d0b43cdc43be910ace79c9", - [] - ], - "media-playback-state-timing-expected.txt": [ - "7d6f7c49b05061ae527de7a324d9f56be06eaf09", - [] - ], "sound-state-expected.txt": [ - "eaa0be79b8668b177382a4f4ece7e59ccb00b0ba", + "f80c43e0dfad2650427c86877b256ba662874a67", [] ], "support": { @@ -450633,18 +450823,10 @@ "b2153184c497d4d4ccb2eeca7b522ac1902fb9ff", [] ], - "load-events-networkState-expected.txt": [ - "a68c278688e6d8af08db9907120fe45f74b7cefa", - [] - ], "resource-selection-candidate-moved-expected.txt": [ "4b7ec31280cf04cb5c6087c288a781293b75964f", [] ], - "resource-selection-invoke-insert-into-iframe-expected.txt": [ - "fed0105b593455c14cdb552bbbb9f35a42ef7175", - [] - ], "resource-selection-pointer-control-expected.txt": [ "073d4cd784f67e513f7534fe0d423f71f12c80fd", [] @@ -452527,6 +452709,10 @@ "b6b93cd8ae2152e317e6c10b0a828a5c70ee245d", [] ], + "select-appearance-base-display-ref.html": [ + "909459a042b613e23d3010eb755b5f548907d769", + [] + ], "select-appearance-button-after-option-ref.html": [ "74dd6e933707b6defea0567c7dda307386574b2b", [] @@ -461471,10 +461657,6 @@ "cf2a9a5ef403c4415fe74bf8fae2c7bd2d3ba24c", [] ], - "hdr-alpha-reftest-ref.html": [ - "26e0d3dac0ba2a06c6f74230c6b102ed1be349e4", - [] - ], "html-embed-object-iframe-ref.html": [ "acaa6a238497686ca164dbed17cde78ad0359dc0", [] @@ -461660,14 +461842,6 @@ "c5def3bbe5f2de1cfed122c6709c8807a92e227f", [] ], - "hdr_alpha.jxl": [ - "68226864c8b81fef4facad21f9c602e1d5d1d8c9", - [] - ], - "hdr_alpha.png": [ - "320c0f4843bc913f3a838e1b87c7e8e19ea6fc96", - [] - ], "hdr_hlg_test.jxl": [ "c7a1f342d7c39bf4f376a93ef888cfc45ad22158", [] @@ -461736,14 +461910,6 @@ "3cf484d968fff55fb51cfe6c4b24a386bde21c6f", [] ], - "sdr_alpha.jxl": [ - "9982d7295dc3a9a5480a38a7b0e16bf11f38eec0", - [] - ], - "sdr_alpha.png": [ - "37c22d64e65ba3916125b7017923dc11aed13be5", - [] - ], "smallest_valid.jxl": [ "615989a5ca12529c5a5566d73f0b9ba5a2364f2c", [] @@ -461769,10 +461935,6 @@ [] ] }, - "sdr-alpha-reftest-ref.html": [ - "2b22c7a2984ee76528b2a987018ccbb94b91db99", - [] - ], "smallest-valid-reftest-ref.html": [ "1c72b20dc82bb9f8f309da2273393b5a117bef81", [] @@ -463479,6 +463641,10 @@ "9b050b0119a74e73e091723fdd5ae843db94dc26", [] ], + "html-or-svg-or-mathml-element-interfaces-expected.txt": [ + "4b9dce23aa0ebf1dbe49767abdde58d88eaca190", + [] + ], "integration-point-1-ref.html": [ "49877549670ae9fdd81e24801c74756465c4c595", [] @@ -463515,11 +463681,11 @@ "f885eaec428b72e767ee167834e5371c3486476c", [] ], - "tabindex-001.tentative-expected.txt": [ + "tabindex-001-expected.txt": [ "643140aff548bb9a4b93193a847fd1b277323e54", [] ], - "tabindex-focus-001.tentative-expected.txt": [ + "tabindex-focus-001-expected.txt": [ "1229e9ec3e8ce5076dcdfdc9e0ace3367f39a4a7", [] ], @@ -464010,11 +464176,11 @@ [] ], "decodingInfo-webrtc.any-expected.txt": [ - "6a336652b2f1bafc67168972fba097d22f0e84fa", + "1b0704feba1e815f2134b9a4639dc84101ce9bcd", [] ], "decodingInfo-webrtc.any.worker-expected.txt": [ - "6a336652b2f1bafc67168972fba097d22f0e84fa", + "1b0704feba1e815f2134b9a4639dc84101ce9bcd", [] ], "decodingInfo.any-expected.txt": [ @@ -464026,19 +464192,19 @@ [] ], "encodingInfo-webrtc.any-expected.txt": [ - "bc95729a63f6eb0bdae97f901e861eb3298b2fdd", + "610e9047ca0b7e7dc907d1c728d34272b0e5979b", [] ], "encodingInfo-webrtc.any.worker-expected.txt": [ - "bc95729a63f6eb0bdae97f901e861eb3298b2fdd", + "610e9047ca0b7e7dc907d1c728d34272b0e5979b", [] ], "encodingInfo.any-expected.txt": [ - "7314b25c538792f217f05101d475e4ece09ded53", + "23cf6b2532b6c7534b0777d6f5352cb56c77da66", [] ], "encodingInfo.any.worker-expected.txt": [ - "7314b25c538792f217f05101d475e4ece09ded53", + "23cf6b2532b6c7534b0777d6f5352cb56c77da66", [] ] }, @@ -479267,25 +479433,29 @@ "8d897abee303ea0707c60ef85ebe3b46114e493b", [] ], + "svg-filter-render-cross-origin-iframe.sub.html": [ + "6c26cf90d24ac4ba8d99395c683ec4ec1dc3b137", + [] + ], "svg-filter-render-iframe.html": [ "e29630d6f0fe437e1e5934f8a8738b048c234ec7", [] - ], - "svg-filter-render-remote-iframe.sub.html": [ - "6c26cf90d24ac4ba8d99395c683ec4ec1dc3b137", - [] ] }, - "svg-filter-render-local-frame-ref.tentative.html": [ + "svg-filter-render-cross-origin-frame-in-same-origin-frame-ref.tentative.html": [ + "660b8b90ee90314e62e3df2750d437430b07a65c", + [] + ], + "svg-filter-render-cross-origin-frame-ref.tentative.sub.html": [ + "a54aabc2520c85da8d5283055c02fd043590ea9b", + [] + ], + "svg-filter-render-same-origin-frame-ref.tentative.html": [ "a68b2434b5f9c3013e6172f0b68279c8f7a87a78", [] ], - "svg-filter-render-remote-frame-in-local-frame-ref.tentative.html": [ - "7aafa025defbe7b4067bc975d27f6d4148b80cdd", - [] - ], - "svg-filter-render-remote-frame-ref.tentative.sub.html": [ - "a54aabc2520c85da8d5283055c02fd043590ea9b", + "svg-filter-render-sandbox-frame-ref.tentative.html": [ + "16575f040baec42397aeec7175a84fe3de65d910", [] ], "svg-filter-render-web-plugin-ref.tentative.html": [ @@ -487629,7 +487799,7 @@ }, "resources": { "minimum_datatype_set.json": [ - "5dbc5f531bd6a9184aad3b0ea4d8b938a7cad1b8", + "218975d47c11e9a9495ba39e51959d7431898b63", [] ], "utils.js": [ @@ -554865,6 +555035,13 @@ {} ] ], + "transform-017.html": [ + "45e4a30bf690ff22a9aafe09baace72b653ed1fb", + [ + null, + {} + ] + ], "try-tactic-alignment.html": [ "9f4d6078d2e0ccf0e38e2b1d23c268763cad6227", [ @@ -559492,7 +559669,7 @@ ] ], "container-rule-cssom.html": [ - "4563044357433ade0d2f9f4ab2cd7e802adbd96e", + "4479faf290a47effb96c62b31f9af1dacfc60313", [ null, {} @@ -578579,6 +578756,15 @@ ] ], "input": { + "keyboard-snap-interruption.html": [ + "6e0aa362c0433fe6d9ff0f28b0c29f3ba140c8c9", + [ + null, + { + "testdriver": true + } + ] + ], "keyboard.html": [ "25f430b705e885b780bb6aa2ed2cb74cc1e7040f", [ @@ -591448,21 +591634,21 @@ ] ], "caret-color-computed.html": [ - "0428c783f5b0b0efc664d18529f8cae22acf72e2", + "aa17b64fe5960d6486f4837f6e78d83b74650e41", [ null, {} ] ], "caret-color-invalid.html": [ - "a59b01ec2bb6d2ada5acef7ac7ec68417d79fa03", + "120b3ef14befc2eaa71c52547897fe0349ded2ef", [ null, {} ] ], "caret-color-valid.html": [ - "dd35cf8e903f544b284ebd2b7d83fb6b5e989274", + "8a5ab945bd398a438938623056eb5ed347e2e15b", [ null, {} @@ -593366,7 +593552,7 @@ ] }, "typed_arithmetic.html": [ - "948ba20d6279e6c19f699a8cce2cf298d5393d65", + "f988e40bd7a8d46479798831393c2080b9cdc556", [ null, {} @@ -593846,6 +594032,13 @@ {} ] ], + "variable-css-wide-keywords-after-substitution.html": [ + "78b4851ed860ef73fb28729018e174c39ec58c11", + [ + null, + {} + ] + ], "variable-css-wide-keywords.html": [ "4666729da514e23824cb9697befa4a975df2b093", [ @@ -601398,7 +601591,7 @@ ] ], "media-loading-state.sub.html": [ - "53cb9b4f67f44d4e7449c205123a184050165e9a", + "05639e174d45fc9fdce96c654b4316539a52d798", [ null, { @@ -602899,7 +603092,7 @@ ] ], "scoped-custom-element-registry-customelementregistry-attribute.html": [ - "c29ad6b636f696ff3ee018880cd3d968dba23b97", + "ed553db5b722f41cc97537617cf9454ae017f3ed", [ null, {} @@ -701950,7 +702143,7 @@ ] ], "dataset.html": [ - "a4a16d014d9b6de9c5fa5523a0fc11b801383931", + "3a97454ccfc83f4bb0bd3cff70e7a3f1bbe8ce29", [ null, {} @@ -717994,6 +718187,15 @@ } ] ], + "select-multiple-resize-does-not-autoscroll.html": [ + "18f5e1e93ff2b0892c535919a6f02f3b58fcb9d0", + [ + null, + { + "testdriver": true + } + ] + ], "select-multiple.html": [ "e348064151a6ae2928e8c27c95c4bd0b25e4c8ac", [ @@ -738318,7 +738520,7 @@ ] ], "longtask-in-childiframe-crossorigin.html": [ - "03f3dbf337d357da8c862aa9d150e91992623d31", + "7ab9d2c38ede4f24d769468df936c37625e34333", [ null, {} @@ -739822,8 +740024,8 @@ } ] ], - "html-or-foreign-element-interfaces.tentative.html": [ - "028428cd758d66c787f0bf4a90b13b980eab9b80", + "html-or-svg-or-mathml-element-interfaces.html": [ + "83518bafb252d42f1b233e0725a3cd393555a525", [ null, {} @@ -739852,14 +740054,14 @@ } ] ], - "tabindex-001.tentative.html": [ + "tabindex-001.html": [ "5da95646af89d0c8c09bdf46f6d4f935537797cb", [ null, {} ] ], - "tabindex-002.tentative.html": [ + "tabindex-002.html": [ "4f27df5a993a743633bb02a13d03691882499400", [ null, @@ -739869,7 +740071,7 @@ } ] ], - "tabindex-focus-001.tentative.html": [ + "tabindex-focus-001.html": [ "dbd21a9af167bb42d6d836ff364733572739e064", [ null, @@ -740313,7 +740515,7 @@ }, "media-capabilities": { "decodingInfo-webrtc.any.js": [ - "34ce5fb2921810df672b4a90e678d05f7bdfc8d1", + "69dd288600d23761512ac9b26ab0cb45f47cbb02", [ "media-capabilities/decodingInfo-webrtc.any.html", { @@ -740340,7 +740542,7 @@ ] ], "decodingInfo.any.js": [ - "510a59d9d781d8b400ad429bbeccd94b319ae3d5", + "852e723224074588691c3644ea8e6ccf39747bd0", [ "media-capabilities/decodingInfo.any.html", { @@ -740383,7 +740585,7 @@ ] ], "encodingInfo-webrtc.any.js": [ - "1369941a0a5d52bdcbcc3ffe6ba0feaddce8329c", + "696483d3f1d5984df2986e1a7aa15857e09a0547", [ "media-capabilities/encodingInfo-webrtc.any.html", { @@ -740410,7 +740612,7 @@ ] ], "encodingInfo.any.js": [ - "97b6c196f09d3c4fc3d46f92a916e4020c39d646", + "a12d49dca4396d1ae56c3e00f314722593ae1f69", [ "media-capabilities/encodingInfo.any.html", {} @@ -753574,6 +753776,13 @@ ] ], "crashtests": { + "permissions-query-worker.window.js": [ + "915e62e9f16dd0b404ea33006ed451c4a7dd8485", + [ + "permissions/crashtests/permissions-query-worker.window.html", + {} + ] + ], "permissions-query.any.js": [ "2e207fa2f226fc1dd3501c56e5c2ffaaab206f60", [ @@ -782853,6 +783062,20 @@ {} ] ], + "shadowrootadoptedstylesheets-clonenode-async-fetch.html": [ + "94b83b5828e82b75bca5b4b5d3b7cffa2e46bbfe", + [ + null, + {} + ] + ], + "shadowrootadoptedstylesheets-clonenode.html": [ + "85a5222fdfdd21054930c95c0d3554613a702ecd", + [ + null, + {} + ] + ], "shadowrootadoptedstylesheets-fetched-module.html": [ "bdcd229d8b536da0250e64316310cb54ec4fa2ef", [ @@ -782973,7 +783196,7 @@ ] ], "shadowrootadoptedstylesheets-serialization.html": [ - "e36882d8a33685fa0dce2a8559c738f56e549bc2", + "743ffd8142615e7515d2b3ad0ea795f5eba7280e", [ null, {} @@ -783191,6 +783414,13 @@ } ] ], + "focus-preserved-on-slot-reorder.html": [ + "74f9dc9dae47acfa7f8067c51cf9f3875780d1aa", + [ + null, + {} + ] + ], "focus-pseudo-matches-on-shadow-host.html": [ "34f8c0129456b38fc46a10c9247ed304abfd9715", [ @@ -804759,6 +804989,13 @@ {} ] ], + "animate-path-additive-sum-both-empty-over-base.tentative.html": [ + "3398f9eee725051815879edf7f61412a8e4a7024", + [ + null, + {} + ] + ], "animate-path-animation-Cc-Ss.tentative.html": [ "69b3cc7af4cf00c301c1e2e6d86641181b356b5a", [ @@ -804843,6 +805080,13 @@ {} ] ], + "animate-path-by-animation-empty-base.tentative.html": [ + "c674aa1f1471e755c40f50de6ac8fed07cb4f42b", + [ + null, + {} + ] + ], "animate-path-by-animation-mismatch.tentative.html": [ "6e9973d39830dfed33afcf26c6332ed54d96868a", [ @@ -804857,6 +805101,27 @@ {} ] ], + "animate-path-from-by-animation-both-empty.tentative.html": [ + "9a1b4c875f72c36193ee5183df2114e2a541485a", + [ + null, + {} + ] + ], + "animate-path-from-by-animation-empty-by.tentative.html": [ + "907996524ceec73ea66ddff3141163b608effc56", + [ + null, + {} + ] + ], + "animate-path-from-by-animation-empty-from.tentative.html": [ + "ac38041596ee134839af3efca8e7a8f5b38f04dd", + [ + null, + {} + ] + ], "animate-path-from-by-animation-mismatch.tentative.html": [ "512859bea9f67b29a4dde067fe1be85b5b35deb0", [ @@ -804871,6 +805136,27 @@ {} ] ], + "animate-path-from-to-animation-both-empty.tentative.html": [ + "82c4c96df64e0bf6d26ec9afbca96a80788d88d8", + [ + null, + {} + ] + ], + "animate-path-from-to-animation-empty-from.tentative.html": [ + "37ce54171d030f06a9fcdfe9ad4252fd7da43f7e", + [ + null, + {} + ] + ], + "animate-path-from-to-animation-empty-to.tentative.html": [ + "4598acfae3521a4ad4b28f2e897eab5479f9fbc2", + [ + null, + {} + ] + ], "animate-path-from-to-animation-mismatch-diff-seg-counts.tentative.html": [ "6ff0f1dffcdda1f03266caaeb2d2291a6d05d268", [ @@ -804885,6 +805171,13 @@ {} ] ], + "animate-path-to-animation-empty-base.tentative.html": [ + "c40478c449f8add0259f965a6ab8f4b1b433c2f8", + [ + null, + {} + ] + ], "animate-path-to-animation.tentative.html": [ "a20a33f6a48c4349d947c1872efac9a25864fb8b", [ @@ -830161,7 +830454,7 @@ ] ], "contention.https.window.js": [ - "e305b9cfe600d870cec83d1707d86bb23c7e26c5", + "ea8d95fb370b25dc16ea6d8f41db00623c60349c", [ "web-locks/bfcache/contention.https.window.html?context=document&contention=query", { @@ -830192,6 +830485,22 @@ ], [ "variant", + "?context=document&contention=request-if-available" + ], + [ + "variant", + "?context=worker&contention=request-if-available" + ], + [ + "variant", + "?context=nested-worker&contention=request-if-available" + ], + [ + "variant", + "?context=shared-worker&contention=request-if-available" + ], + [ + "variant", "?context=document&contention=query" ], [ @@ -830260,6 +830569,106 @@ ], [ "variant", + "?context=document&contention=request-if-available" + ], + [ + "variant", + "?context=worker&contention=request-if-available" + ], + [ + "variant", + "?context=nested-worker&contention=request-if-available" + ], + [ + "variant", + "?context=shared-worker&contention=request-if-available" + ], + [ + "variant", + "?context=document&contention=query" + ], + [ + "variant", + "?context=worker&contention=query" + ], + [ + "variant", + "?context=nested-worker&contention=query" + ], + [ + "variant", + "?context=shared-worker&contention=query" + ], + [ + "script", + "/common/utils.js" + ], + [ + "script", + "/common/dispatcher/dispatcher.js" + ], + [ + "script", + "/web-locks/resources/helpers.js" + ], + [ + "script", + "/html/browsers/browsing-the-web/back-forward-cache/resources/helper.sub.js" + ], + [ + "script", + "./helpers.js" + ] + ], + "timeout": "long" + } + ], + [ + "web-locks/bfcache/contention.https.window.html?context=document&contention=request-if-available", + { + "script_metadata": [ + [ + "title", + "Web Locks API: bfcache contention test" + ], + [ + "timeout", + "long" + ], + [ + "variant", + "?context=document&contention=request" + ], + [ + "variant", + "?context=worker&contention=request" + ], + [ + "variant", + "?context=nested-worker&contention=request" + ], + [ + "variant", + "?context=shared-worker&contention=request" + ], + [ + "variant", + "?context=document&contention=request-if-available" + ], + [ + "variant", + "?context=worker&contention=request-if-available" + ], + [ + "variant", + "?context=nested-worker&contention=request-if-available" + ], + [ + "variant", + "?context=shared-worker&contention=request-if-available" + ], + [ + "variant", "?context=document&contention=query" ], [ @@ -830328,6 +830737,22 @@ ], [ "variant", + "?context=document&contention=request-if-available" + ], + [ + "variant", + "?context=worker&contention=request-if-available" + ], + [ + "variant", + "?context=nested-worker&contention=request-if-available" + ], + [ + "variant", + "?context=shared-worker&contention=request-if-available" + ], + [ + "variant", "?context=document&contention=query" ], [ @@ -830396,6 +830821,106 @@ ], [ "variant", + "?context=document&contention=request-if-available" + ], + [ + "variant", + "?context=worker&contention=request-if-available" + ], + [ + "variant", + "?context=nested-worker&contention=request-if-available" + ], + [ + "variant", + "?context=shared-worker&contention=request-if-available" + ], + [ + "variant", + "?context=document&contention=query" + ], + [ + "variant", + "?context=worker&contention=query" + ], + [ + "variant", + "?context=nested-worker&contention=query" + ], + [ + "variant", + "?context=shared-worker&contention=query" + ], + [ + "script", + "/common/utils.js" + ], + [ + "script", + "/common/dispatcher/dispatcher.js" + ], + [ + "script", + "/web-locks/resources/helpers.js" + ], + [ + "script", + "/html/browsers/browsing-the-web/back-forward-cache/resources/helper.sub.js" + ], + [ + "script", + "./helpers.js" + ] + ], + "timeout": "long" + } + ], + [ + "web-locks/bfcache/contention.https.window.html?context=nested-worker&contention=request-if-available", + { + "script_metadata": [ + [ + "title", + "Web Locks API: bfcache contention test" + ], + [ + "timeout", + "long" + ], + [ + "variant", + "?context=document&contention=request" + ], + [ + "variant", + "?context=worker&contention=request" + ], + [ + "variant", + "?context=nested-worker&contention=request" + ], + [ + "variant", + "?context=shared-worker&contention=request" + ], + [ + "variant", + "?context=document&contention=request-if-available" + ], + [ + "variant", + "?context=worker&contention=request-if-available" + ], + [ + "variant", + "?context=nested-worker&contention=request-if-available" + ], + [ + "variant", + "?context=shared-worker&contention=request-if-available" + ], + [ + "variant", "?context=document&contention=query" ], [ @@ -830464,6 +830989,22 @@ ], [ "variant", + "?context=document&contention=request-if-available" + ], + [ + "variant", + "?context=worker&contention=request-if-available" + ], + [ + "variant", + "?context=nested-worker&contention=request-if-available" + ], + [ + "variant", + "?context=shared-worker&contention=request-if-available" + ], + [ + "variant", "?context=document&contention=query" ], [ @@ -830532,6 +831073,106 @@ ], [ "variant", + "?context=document&contention=request-if-available" + ], + [ + "variant", + "?context=worker&contention=request-if-available" + ], + [ + "variant", + "?context=nested-worker&contention=request-if-available" + ], + [ + "variant", + "?context=shared-worker&contention=request-if-available" + ], + [ + "variant", + "?context=document&contention=query" + ], + [ + "variant", + "?context=worker&contention=query" + ], + [ + "variant", + "?context=nested-worker&contention=query" + ], + [ + "variant", + "?context=shared-worker&contention=query" + ], + [ + "script", + "/common/utils.js" + ], + [ + "script", + "/common/dispatcher/dispatcher.js" + ], + [ + "script", + "/web-locks/resources/helpers.js" + ], + [ + "script", + "/html/browsers/browsing-the-web/back-forward-cache/resources/helper.sub.js" + ], + [ + "script", + "./helpers.js" + ] + ], + "timeout": "long" + } + ], + [ + "web-locks/bfcache/contention.https.window.html?context=shared-worker&contention=request-if-available", + { + "script_metadata": [ + [ + "title", + "Web Locks API: bfcache contention test" + ], + [ + "timeout", + "long" + ], + [ + "variant", + "?context=document&contention=request" + ], + [ + "variant", + "?context=worker&contention=request" + ], + [ + "variant", + "?context=nested-worker&contention=request" + ], + [ + "variant", + "?context=shared-worker&contention=request" + ], + [ + "variant", + "?context=document&contention=request-if-available" + ], + [ + "variant", + "?context=worker&contention=request-if-available" + ], + [ + "variant", + "?context=nested-worker&contention=request-if-available" + ], + [ + "variant", + "?context=shared-worker&contention=request-if-available" + ], + [ + "variant", "?context=document&contention=query" ], [ @@ -830600,6 +831241,22 @@ ], [ "variant", + "?context=document&contention=request-if-available" + ], + [ + "variant", + "?context=worker&contention=request-if-available" + ], + [ + "variant", + "?context=nested-worker&contention=request-if-available" + ], + [ + "variant", + "?context=shared-worker&contention=request-if-available" + ], + [ + "variant", "?context=document&contention=query" ], [ @@ -830668,6 +831325,106 @@ ], [ "variant", + "?context=document&contention=request-if-available" + ], + [ + "variant", + "?context=worker&contention=request-if-available" + ], + [ + "variant", + "?context=nested-worker&contention=request-if-available" + ], + [ + "variant", + "?context=shared-worker&contention=request-if-available" + ], + [ + "variant", + "?context=document&contention=query" + ], + [ + "variant", + "?context=worker&contention=query" + ], + [ + "variant", + "?context=nested-worker&contention=query" + ], + [ + "variant", + "?context=shared-worker&contention=query" + ], + [ + "script", + "/common/utils.js" + ], + [ + "script", + "/common/dispatcher/dispatcher.js" + ], + [ + "script", + "/web-locks/resources/helpers.js" + ], + [ + "script", + "/html/browsers/browsing-the-web/back-forward-cache/resources/helper.sub.js" + ], + [ + "script", + "./helpers.js" + ] + ], + "timeout": "long" + } + ], + [ + "web-locks/bfcache/contention.https.window.html?context=worker&contention=request-if-available", + { + "script_metadata": [ + [ + "title", + "Web Locks API: bfcache contention test" + ], + [ + "timeout", + "long" + ], + [ + "variant", + "?context=document&contention=request" + ], + [ + "variant", + "?context=worker&contention=request" + ], + [ + "variant", + "?context=nested-worker&contention=request" + ], + [ + "variant", + "?context=shared-worker&contention=request" + ], + [ + "variant", + "?context=document&contention=request-if-available" + ], + [ + "variant", + "?context=worker&contention=request-if-available" + ], + [ + "variant", + "?context=nested-worker&contention=request-if-available" + ], + [ + "variant", + "?context=shared-worker&contention=request-if-available" + ], + [ + "variant", "?context=document&contention=query" ], [ @@ -832563,7 +833320,7 @@ ] ], "audiocontext-sinkid-state-change.https.html": [ - "8462b5261981f2b33de5682ce3e6130d69c01122", + "d9369e3347c2d645c68bb790c09ae35dcb80abde", [ null, {} @@ -854628,7 +855385,7 @@ ] ], "prelu.https.any.js": [ - "3de7353d62d0fd07278e63a3ad87f54a43f822b9", + "c3c18659c55b5df5410004877d15438f2a6c178f", [ "webnn/conformance_tests/prelu.https.any.html?cpu", {
diff --git a/third_party/blink/web_tests/external/wpt/css/css-anchor-position/auto-inset-margin-getComputedStyle-expected.txt b/third_party/blink/web_tests/external/wpt/css/css-anchor-position/auto-inset-margin-getComputedStyle-expected.txt deleted file mode 100644 index 5276b715..0000000 --- a/third_party/blink/web_tests/external/wpt/css/css-anchor-position/auto-inset-margin-getComputedStyle-expected.txt +++ /dev/null
@@ -1,5 +0,0 @@ -This is a testharness.js-based test. -[FAIL] align-self: anchor-center with invalid anchor does not set insets to zero - assert_not_equals: got disallowed value "0px" -Harness: the test ran to completion. -
diff --git a/third_party/blink/web_tests/external/wpt/css/css-anchor-position/auto-inset-margin-getComputedStyle.html b/third_party/blink/web_tests/external/wpt/css/css-anchor-position/auto-inset-margin-getComputedStyle.html index 17d0cff..b7a5217 100644 --- a/third_party/blink/web_tests/external/wpt/css/css-anchor-position/auto-inset-margin-getComputedStyle.html +++ b/third_party/blink/web_tests/external/wpt/css/css-anchor-position/auto-inset-margin-getComputedStyle.html
@@ -15,7 +15,7 @@ anchor-scope: all; display: inline-block; /* Nudge static position against padding box. */ - padding: 10px; + padding: 15px; } .anchor {
diff --git a/third_party/blink/web_tests/external/wpt/css/css-anchor-position/transform-017.html b/third_party/blink/web_tests/external/wpt/css/css-anchor-position/transform-017.html new file mode 100644 index 0000000..45e4a30b --- /dev/null +++ b/third_party/blink/web_tests/external/wpt/css/css-anchor-position/transform-017.html
@@ -0,0 +1,61 @@ +<!DOCTYPE html> +<title>Anchor with transformed opacity and translate</title> +<link rel="author" title="Morten Stenshorne" href="mailto:mstensho@chromium.org"> +<link rel="author" title="Kiet Ho" href="mailto:kiet.ho@apple.com"> +<link rel="help" href="https://drafts.csswg.org/css-anchor-position-1/#determining-position"> +<style> + #anchor { + anchor-name: --a; + width: 200px; + height: 200px; + transform: translateX(0); + opacity: 0.2; + background: hotpink; + } + #anchored { + position: absolute; + position-anchor: --a; + position-area: bottom right; + width: 100%; + height: 100%; + background: cyan; + } + @keyframes anim { + from { + transform: translateX(0); + opacity: 0.2; + } + + to { + transform: translateX(200px); + opacity: 1; + } + } +</style> +<div style="contain:layout; width:400px; height:400px;"> + <div id="anchor"></div> + <div id="anchored"></div> +</div> + +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script> + const done = Promise.withResolvers(); + let got_halfway = false; + const observer = new ResizeObserver((entries)=> { + assert_equals(entries.length, 1); + const inlineSize = entries[0].contentBoxSize[0].inlineSize; + if (inlineSize > 30 && inlineSize < 170) { + got_halfway = true; + done.resolve(); + } + }); + anchor.onanimationend = ()=> { done.reject(); } + + promise_test(async t => { + anchor.style.animation = "2000ms linear anim"; + observer.observe(anchored); + try { await done.promise; } catch(e) {} + assert_true(got_halfway, "animation frame somewhere in the middle"); + }, "Animation being updated"); +</script>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-conditional/container-queries/container-rule-cssom.html b/third_party/blink/web_tests/external/wpt/css/css-conditional/container-queries/container-rule-cssom.html index 4563044..4479faf 100644 --- a/third_party/blink/web_tests/external/wpt/css/css-conditional/container-queries/container-rule-cssom.html +++ b/third_party/blink/web_tests/external/wpt/css/css-conditional/container-queries/container-rule-cssom.html
@@ -66,6 +66,16 @@ }, ".conditions without query"); test(() => { + const rule = insertRule("@container style(--x > 0) {}"); + assert_true(!!rule, "Rule successfully parsed"); + assert_equals(rule.conditions.length, 1); + assert_equals(rule.conditions[0].name, ""); + assert_equals(rule.conditions[0].query, "style(--x > 0)"); + assert_equals(rule.containerName, ""); + assert_equals(rule.containerQuery, "style(--x > 0)"); + }, "style()"); + + test(() => { const rule = insertRule("@container (width > 300px), Name (width < 1000px) {}"); assert_true(!!rule, "Rule successfully parsed"); assert_equals(rule.conditions.length, 2);
diff --git a/third_party/blink/web_tests/external/wpt/css/css-contain/content-visibility/resources/text-fragment-target-auto.html b/third_party/blink/web_tests/external/wpt/css/css-contain/content-visibility/resources/text-fragment-target-auto.html index 53a22f5..2ce2406 100644 --- a/third_party/blink/web_tests/external/wpt/css/css-contain/content-visibility/resources/text-fragment-target-auto.html +++ b/third_party/blink/web_tests/external/wpt/css/css-contain/content-visibility/resources/text-fragment-target-auto.html
@@ -28,13 +28,14 @@ let key = (new URL(document.location)).searchParams.get("key"); stashResultsThenClose(key, results); } -function doubleRafCheckScroll() { - requestAnimationFrame(() => { - requestAnimationFrame(() => { - checkScroll(); - }); - }); + +function waitAndCheckScroll() { + requestAnimationFrame( + () => requestAnimationFrame( + () => requestAnimationFrame( + () => requestAnimationFrame(checkScroll)))); } + </script> <style> @@ -46,7 +47,7 @@ } </style> -<body onload="doubleRafCheckScroll()"> +<body onload="waitAndCheckScroll()"> <div class=spacer></div> <div class=auto> <div id=text>hiddentext</div>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-grid/grid-lanes/animation/grid-template-columns-001-ref.html b/third_party/blink/web_tests/external/wpt/css/css-grid/grid-lanes/animation/grid-template-columns-001-ref.html new file mode 100644 index 0000000..7b432ace --- /dev/null +++ b/third_party/blink/web_tests/external/wpt/css/css-grid/grid-lanes/animation/grid-template-columns-001-ref.html
@@ -0,0 +1,20 @@ +<!DOCTYPE html> +<html> +<head> + <style> + .grid-lanes { + display: grid-lanes; + width: 400px; + gap: 10px; + grid-template-columns: 60px 1fr; + } + span { border: 1px solid; } + </style> +</head> +<body> + <div class="grid-lanes"> + <span>column1</span> + <span>column2</span> + </div> +</body> +</html>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-grid/grid-lanes/animation/grid-template-columns-001.html b/third_party/blink/web_tests/external/wpt/css/css-grid/grid-lanes/animation/grid-template-columns-001.html new file mode 100644 index 0000000..bdb69e0 --- /dev/null +++ b/third_party/blink/web_tests/external/wpt/css/css-grid/grid-lanes/animation/grid-template-columns-001.html
@@ -0,0 +1,38 @@ +<!DOCTYPE html> +<html class="reftest-wait"> +<head> + <title>CSS Grid Lanes Test: grid-template-columns interpolation</title> + <link rel="author" title="Yanling Wang" href="mailto:yanlingwang@microsoft.com"> + <link rel="help" href="https://drafts.csswg.org/css-grid-3"> + <link rel="match" href="grid-template-columns-001-ref.html"> + <meta name="assert" content="grid-template-columns interpolates from 20px to 100px, reaching 60px at 50% animation progress on a grid-lanes container."> + <style> + @keyframes anim { + from { + grid-template-columns: 20px 1fr; + } + to { + grid-template-columns: 100px 1fr; + } + } + .grid-lanes { + display: grid-lanes; + width: 400px; + gap: 10px; + animation: anim 10s -5s paused linear; + } + span { border: 1px solid; } + </style> +</head> +<body> + <div class="grid-lanes"> + <span>column1</span> + <span>column2</span> + </div> + <script> + requestAnimationFrame(() => { + document.documentElement.classList.remove('reftest-wait'); + }); + </script> +</body> +</html>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-grid/grid-lanes/animation/grid-template-columns-interpolation.html b/third_party/blink/web_tests/external/wpt/css/css-grid/grid-lanes/animation/grid-template-columns-interpolation.html new file mode 100644 index 0000000..23d0aa46 --- /dev/null +++ b/third_party/blink/web_tests/external/wpt/css/css-grid/grid-lanes/animation/grid-template-columns-interpolation.html
@@ -0,0 +1,239 @@ +<!DOCTYPE html> +<html> + <head> + <meta charset="utf-8"> + <title>CSS Grid Lanes Test: grid-template-columns interpolation</title> + <link rel="author" title="Yanling Wang" href="mailto:yanlingwang@microsoft.com"> + <link rel="help" href="https://drafts.csswg.org/css-grid-3"> + <meta name="assert" content="grid-template-columns supports interpolation on a grid-lanes container: matching track counts interpolate smoothly, mismatched counts use discrete animation."> + <script src="/resources/testharness.js"></script> + <script src="/resources/testharnessreport.js"></script> + <script src="/css/support/interpolation-testcommon.js"></script> + <style> + .parent { + display: grid-lanes; + grid-template-columns: 10px 20px 30px; + } + </style> + </head> + <body> + <script> + 'use strict'; + + // Exercise <explicit-track-list> + // Mismatched track count — discrete animation + test_no_interpolation({ + property: 'grid-template-columns', + from: "1fr 1fr 1fr", + to: "2fr 2fr" + }); + + test_no_interpolation({ + property: 'grid-template-columns', + from: "10px 20px 30px", + to: "20px 30px" + }); + + test_no_interpolation({ + property: 'grid-template-columns', + from: "1fr 1fr 1fr", + to: "none" + }); + + test_no_interpolation({ + property: 'grid-template-columns', + from: "none", + to: "20px 30px" + }); + + // Matching track count — interpolation + test_interpolation({ + property: 'grid-template-columns', + from: "10px 20px 30px", + to: "20px 30px 40px" + }, [ + {at: -1, expect: "0px 10px 20px"}, + {at: 0, expect: "10px 20px 30px"}, + {at: 0.4, expect: "14px 24px 34px"}, + {at: 0.6, expect: "16px 26px 36px"}, + {at: 1, expect: "20px 30px 40px"}, + {at: 2, expect: "30px 40px 50px"} + ]); + + test_interpolation({ + property: 'grid-template-columns', + from: "10px 20px 30px", + to: "20% 30% 40px" + }, [ + {at: -1, expect: "calc(20px + -20%) calc(40px + -30%) 20px"}, + {at: 0, expect: "calc(10px + 0%) calc(20px + 0%) 30px"}, + {at: 0.4, expect: "calc(6px + 8%) calc(12px + 12%) 34px"}, + {at: 0.6, expect: "calc(4px + 12%) calc(8px + 18%) 36px"}, + {at: 1, expect: "20% 30% 40px"}, + {at: 2, expect: "calc(-10px + 40%) calc(-20px + 60%) 50px"} + ]); + + test_interpolation({ + property: 'grid-template-columns', + from: "1fr 1fr 1fr", + to: "2fr auto 2fr" + }, [ + {at: -1, expect: "0fr 1fr 0fr"}, + {at: 0, expect: "1fr 1fr 1fr"}, + {at: 0.4, expect: "1.4fr 1fr 1.4fr"}, + {at: 0.6, expect: "1.6fr auto 1.6fr"}, + {at: 1, expect: "2fr auto 2fr"}, + {at: 2, expect: "3fr auto 3fr"} + ]); + + test_interpolation({ + property: 'grid-template-columns', + from: "1fr [a b] 1fr [d] 1fr", + to: "2fr [c] auto 2fr" + }, [ + {at: -1, expect: "0fr [a b] 1fr [d] 0fr"}, + {at: 0, expect: "1fr [a b] 1fr [d] 1fr"}, + {at: 0.4, expect: "1.4fr [a b] 1fr [d] 1.4fr"}, + {at: 0.6, expect: "1.6fr [c] auto 1.6fr"}, + {at: 1, expect: "2fr [c] auto 2fr"}, + {at: 2, expect: "3fr [c] auto 3fr"} + ]); + + test_interpolation({ + property: 'grid-template-columns', + from: "fit-content(10px) fit-content(20px)", + to: "fit-content(20px) max-content" + }, [ + {at: -1, expect: "fit-content(0px) fit-content(20px)"}, + {at: 0, expect: "fit-content(10px) fit-content(20px)"}, + {at: 0.4, expect: "fit-content(14px) fit-content(20px)"}, + {at: 0.6, expect: "fit-content(16px) max-content"}, + {at: 1, expect: "fit-content(20px) max-content"}, + {at: 2, expect: "fit-content(30px) max-content"} + ]); + + test_interpolation({ + property: 'grid-template-columns', + from: "minmax(10px, 1fr) minmax(20px, 2fr)", + to: "minmax(20px, 2fr) minmax(30px, auto)" + }, [ + {at: -1, expect: "minmax(0px, 0fr) minmax(10px, 2fr)"}, + {at: 0, expect: "minmax(10px, 1fr) minmax(20px, 2fr)"}, + {at: 0.4, expect: "minmax(14px, 1.4fr) minmax(24px, 2fr)"}, + {at: 0.6, expect: "minmax(16px, 1.6fr) minmax(26px, auto)"}, + {at: 1, expect: "minmax(20px, 2fr) minmax(30px, auto)"}, + {at: 2, expect: "minmax(30px, 3fr) minmax(40px, auto)"} + ]); + + test_interpolation({ + property: 'grid-template-columns', + from: "10px 10px 10px", + to: "1fr fit-content(20px) minmax(20px, 2fr)" + }, [ + {at: -1, expect: "10px 10px 10px"}, + {at: 0, expect: "10px 10px 10px"}, + {at: 0.4, expect: "10px 10px 10px"}, + {at: 0.6, expect: "1fr fit-content(20px) minmax(20px, 2fr)"}, + {at: 1, expect: "1fr fit-content(20px) minmax(20px, 2fr)"}, + {at: 2, expect: "1fr fit-content(20px) minmax(20px, 2fr)"} + ]); + + test_interpolation({ + property: 'grid-template-columns', + from: "1fr 1fr 1fr", + to: "20px fit-content(20px) minmax(20px, 2fr)" + }, [ + {at: -1, expect: "1fr 1fr 1fr"}, + {at: 0, expect: "1fr 1fr 1fr"}, + {at: 0.4, expect: "1fr 1fr 1fr"}, + {at: 0.6, expect: "20px fit-content(20px) minmax(20px, 2fr)"}, + {at: 1, expect: "20px fit-content(20px) minmax(20px, 2fr)"}, + {at: 2, expect: "20px fit-content(20px) minmax(20px, 2fr)"} + ]); + + test_interpolation({ + property: 'grid-template-columns', + from: "fit-content(10px)", + to: "minmax(20px, 2fr)" + }, [ + {at: -1, expect: "fit-content(10px)"}, + {at: 0, expect: "fit-content(10px)"}, + {at: 0.4, expect: "fit-content(10px)"}, + {at: 0.6, expect: "minmax(20px, 2fr)"}, + {at: 1, expect: "minmax(20px, 2fr)"}, + {at: 2, expect: "minmax(20px, 2fr)"} + ]); + + test_interpolation({ + property: 'grid-template-columns', + from: "inherit", + to: "20px 30px 40px" + }, [ + {at: -1, expect: "0px 10px 20px"}, + {at: 0, expect: "10px 20px 30px"}, + {at: 0.4, expect: "14px 24px 34px"}, + {at: 0.6, expect: "16px 26px 36px"}, + {at: 1, expect: "20px 30px 40px"}, + {at: 2, expect: "30px 40px 50px"} + ]); + + // Exercise <track-list> (with <track-repeat>) + // https://drafts.csswg.org/css-grid/#repeat-interpolation + test_no_interpolation({ + property: 'grid-template-columns', + from: "1fr repeat(2, 2fr 30px) 1fr", + to: "2fr repeat(2, 3fr 40px 50px) 2fr" + }); + + test_no_interpolation({ + property: 'grid-template-columns', + from: "1fr repeat(2, 2fr 30px) 1fr", + to: "2fr repeat(3, 3fr 40px) 2fr" + }); + + test_no_interpolation({ + property: 'grid-template-columns', + from: "repeat(2, 2fr 30px)", + to: "repeat(4, 40px)" + }); + + test_interpolation({ + property: 'grid-template-columns', + from: "1fr repeat(2, 2fr auto 30px) 1fr", + to: "2fr repeat(2, 3fr 30px 40px) 2fr" + }, [ + {at: -1, expect: "0fr repeat(2, 1fr auto 20px) 0fr"}, + {at: 0, expect: "1fr repeat(2, 2fr auto 30px) 1fr"}, + {at: 0.4, expect: "1.4fr repeat(2, 2.4fr auto 34px) 1.4fr"}, + {at: 0.6, expect: "1.6fr repeat(2, 2.6fr 30px 36px) 1.6fr"}, + {at: 1, expect: "2fr repeat(2, 3fr 30px 40px) 2fr"}, + {at: 2, expect: "3fr repeat(2, 4fr 30px 50px) 3fr"} + ]); + + // Exercise <auto-track-list> + test_no_interpolation({ + property: 'grid-template-columns', + from: "10px repeat(auto-fill, minmax(25px, 1fr)) 10px", + to: "20px 20px repeat(auto-fill, minmax(30px, 1fr))" + }); + + test_no_interpolation({ + property: 'grid-template-columns', + from: "10px repeat(auto-fill, minmax(25px, 1fr)) 10px", + to: "20px repeat(auto-fit, minmax(30px, 1fr)) 20px" + }); + + test_no_interpolation({ + property: 'grid-template-columns', + from: "10px repeat(auto-fill, minmax(25px, 1fr)) 10px", + to: "20px repeat(auto-fill, minmax(35px, auto)) 20px" + }); + + test_no_interpolation({ + property: 'grid-template-columns', + from: "repeat(auto-fill, auto)", + to: "repeat(auto-fill, 100px)" + }); + </script> + </body> +</html>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-grid/grid-lanes/animation/grid-template-rows-001-ref.html b/third_party/blink/web_tests/external/wpt/css/css-grid/grid-lanes/animation/grid-template-rows-001-ref.html new file mode 100644 index 0000000..64d93e6 --- /dev/null +++ b/third_party/blink/web_tests/external/wpt/css/css-grid/grid-lanes/animation/grid-template-rows-001-ref.html
@@ -0,0 +1,21 @@ +<!DOCTYPE html> +<html> +<head> + <style> + .grid-lanes { + display: grid-lanes; + width: 400px; + height: 400px; + gap: 10px; + grid-template-rows: 60px 1fr; + } + span { border: 1px solid; } + </style> +</head> +<body> + <div class="grid-lanes"> + <span>row1</span> + <span>row2</span> + </div> +</body> +</html>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-grid/grid-lanes/animation/grid-template-rows-001.html b/third_party/blink/web_tests/external/wpt/css/css-grid/grid-lanes/animation/grid-template-rows-001.html new file mode 100644 index 0000000..495756ad --- /dev/null +++ b/third_party/blink/web_tests/external/wpt/css/css-grid/grid-lanes/animation/grid-template-rows-001.html
@@ -0,0 +1,39 @@ +<!DOCTYPE html> +<html class="reftest-wait"> +<head> + <title>CSS Grid Lanes Test: grid-template-rows interpolation</title> + <link rel="author" title="Yanling Wang" href="mailto:yanlingwang@microsoft.com"> + <link rel="help" href="https://drafts.csswg.org/css-grid-3"> + <link rel="match" href="grid-template-rows-001-ref.html"> + <meta name="assert" content="grid-template-rows interpolates from 20px to 100px, reaching 60px at 50% animation progress on a grid-lanes container."> + <style> + @keyframes anim { + from { + grid-template-rows: 20px 1fr; + } + to { + grid-template-rows: 100px 1fr; + } + } + .grid-lanes { + display: grid-lanes; + width: 400px; + height: 400px; + gap: 10px; + animation: anim 10s -5s paused linear; + } + span { border: 1px solid; } + </style> +</head> +<body> + <div class="grid-lanes"> + <span>row1</span> + <span>row2</span> + </div> + <script> + requestAnimationFrame(() => { + document.documentElement.classList.remove('reftest-wait'); + }); + </script> +</body> +</html>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-grid/grid-lanes/animation/grid-template-rows-interpolation.html b/third_party/blink/web_tests/external/wpt/css/css-grid/grid-lanes/animation/grid-template-rows-interpolation.html new file mode 100644 index 0000000..6dd238d --- /dev/null +++ b/third_party/blink/web_tests/external/wpt/css/css-grid/grid-lanes/animation/grid-template-rows-interpolation.html
@@ -0,0 +1,239 @@ +<!DOCTYPE html> +<html> + <head> + <meta charset="utf-8"> + <title>CSS Grid Lanes Test: grid-template-rows interpolation</title> + <link rel="author" title="Yanling Wang" href="mailto:yanlingwang@microsoft.com"> + <link rel="help" href="https://drafts.csswg.org/css-grid-3"> + <meta name="assert" content="grid-template-rows supports interpolation on a grid-lanes container: matching track counts interpolate smoothly, mismatched counts use discrete animation."> + <script src="/resources/testharness.js"></script> + <script src="/resources/testharnessreport.js"></script> + <script src="/css/support/interpolation-testcommon.js"></script> + <style> + .parent { + display: grid-lanes; + grid-template-rows: 10px 20px 30px; + } + </style> + </head> + <body> + <script> + 'use strict'; + + // Exercise <explicit-track-list> + // Mismatched track count — discrete animation + test_no_interpolation({ + property: 'grid-template-rows', + from: "1fr 1fr 1fr", + to: "2fr 2fr" + }); + + test_no_interpolation({ + property: 'grid-template-rows', + from: "10px 20px 30px", + to: "20px 30px" + }); + + test_no_interpolation({ + property: 'grid-template-rows', + from: "1fr 1fr 1fr", + to: "none" + }); + + test_no_interpolation({ + property: 'grid-template-rows', + from: "none", + to: "20px 30px" + }); + + // Matching track count — interpolation + test_interpolation({ + property: 'grid-template-rows', + from: "10px 20px 30px", + to: "20px 30px 40px" + }, [ + {at: -1, expect: "0px 10px 20px"}, + {at: 0, expect: "10px 20px 30px"}, + {at: 0.4, expect: "14px 24px 34px"}, + {at: 0.6, expect: "16px 26px 36px"}, + {at: 1, expect: "20px 30px 40px"}, + {at: 2, expect: "30px 40px 50px"} + ]); + + test_interpolation({ + property: 'grid-template-rows', + from: "10px 20px 30px", + to: "20% 30% 40px" + }, [ + {at: -1, expect: "calc(20px + -20%) calc(40px + -30%) 20px"}, + {at: 0, expect: "calc(10px + 0%) calc(20px + 0%) 30px"}, + {at: 0.4, expect: "calc(6px + 8%) calc(12px + 12%) 34px"}, + {at: 0.6, expect: "calc(4px + 12%) calc(8px + 18%) 36px"}, + {at: 1, expect: "20% 30% 40px"}, + {at: 2, expect: "calc(-10px + 40%) calc(-20px + 60%) 50px"} + ]); + + test_interpolation({ + property: 'grid-template-rows', + from: "1fr 1fr 1fr", + to: "2fr auto 2fr" + }, [ + {at: -1, expect: "0fr 1fr 0fr"}, + {at: 0, expect: "1fr 1fr 1fr"}, + {at: 0.4, expect: "1.4fr 1fr 1.4fr"}, + {at: 0.6, expect: "1.6fr auto 1.6fr"}, + {at: 1, expect: "2fr auto 2fr"}, + {at: 2, expect: "3fr auto 3fr"} + ]); + + test_interpolation({ + property: 'grid-template-rows', + from: "1fr [a b] 1fr [d] 1fr", + to: "2fr [c] auto 2fr" + }, [ + {at: -1, expect: "0fr [a b] 1fr [d] 0fr"}, + {at: 0, expect: "1fr [a b] 1fr [d] 1fr"}, + {at: 0.4, expect: "1.4fr [a b] 1fr [d] 1.4fr"}, + {at: 0.6, expect: "1.6fr [c] auto 1.6fr"}, + {at: 1, expect: "2fr [c] auto 2fr"}, + {at: 2, expect: "3fr [c] auto 3fr"} + ]); + + test_interpolation({ + property: 'grid-template-rows', + from: "fit-content(10px) fit-content(20px)", + to: "fit-content(20px) max-content" + }, [ + {at: -1, expect: "fit-content(0px) fit-content(20px)"}, + {at: 0, expect: "fit-content(10px) fit-content(20px)"}, + {at: 0.4, expect: "fit-content(14px) fit-content(20px)"}, + {at: 0.6, expect: "fit-content(16px) max-content"}, + {at: 1, expect: "fit-content(20px) max-content"}, + {at: 2, expect: "fit-content(30px) max-content"} + ]); + + test_interpolation({ + property: 'grid-template-rows', + from: "minmax(10px, 1fr) minmax(20px, 2fr)", + to: "minmax(20px, 2fr) minmax(30px, auto)" + }, [ + {at: -1, expect: "minmax(0px, 0fr) minmax(10px, 2fr)"}, + {at: 0, expect: "minmax(10px, 1fr) minmax(20px, 2fr)"}, + {at: 0.4, expect: "minmax(14px, 1.4fr) minmax(24px, 2fr)"}, + {at: 0.6, expect: "minmax(16px, 1.6fr) minmax(26px, auto)"}, + {at: 1, expect: "minmax(20px, 2fr) minmax(30px, auto)"}, + {at: 2, expect: "minmax(30px, 3fr) minmax(40px, auto)"} + ]); + + test_interpolation({ + property: 'grid-template-rows', + from: "10px 10px 10px", + to: "1fr fit-content(20px) minmax(20px, 2fr)" + }, [ + {at: -1, expect: "10px 10px 10px"}, + {at: 0, expect: "10px 10px 10px"}, + {at: 0.4, expect: "10px 10px 10px"}, + {at: 0.6, expect: "1fr fit-content(20px) minmax(20px, 2fr)"}, + {at: 1, expect: "1fr fit-content(20px) minmax(20px, 2fr)"}, + {at: 2, expect: "1fr fit-content(20px) minmax(20px, 2fr)"} + ]); + + test_interpolation({ + property: 'grid-template-rows', + from: "1fr 1fr 1fr", + to: "20px fit-content(20px) minmax(20px, 2fr)" + }, [ + {at: -1, expect: "1fr 1fr 1fr"}, + {at: 0, expect: "1fr 1fr 1fr"}, + {at: 0.4, expect: "1fr 1fr 1fr"}, + {at: 0.6, expect: "20px fit-content(20px) minmax(20px, 2fr)"}, + {at: 1, expect: "20px fit-content(20px) minmax(20px, 2fr)"}, + {at: 2, expect: "20px fit-content(20px) minmax(20px, 2fr)"} + ]); + + test_interpolation({ + property: 'grid-template-rows', + from: "fit-content(10px)", + to: "minmax(20px, 2fr)" + }, [ + {at: -1, expect: "fit-content(10px)"}, + {at: 0, expect: "fit-content(10px)"}, + {at: 0.4, expect: "fit-content(10px)"}, + {at: 0.6, expect: "minmax(20px, 2fr)"}, + {at: 1, expect: "minmax(20px, 2fr)"}, + {at: 2, expect: "minmax(20px, 2fr)"} + ]); + + test_interpolation({ + property: 'grid-template-rows', + from: "inherit", + to: "20px 30px 40px" + }, [ + {at: -1, expect: "0px 10px 20px"}, + {at: 0, expect: "10px 20px 30px"}, + {at: 0.4, expect: "14px 24px 34px"}, + {at: 0.6, expect: "16px 26px 36px"}, + {at: 1, expect: "20px 30px 40px"}, + {at: 2, expect: "30px 40px 50px"} + ]); + + // Exercise <track-list> (with <track-repeat>) + // https://drafts.csswg.org/css-grid/#repeat-interpolation + test_no_interpolation({ + property: 'grid-template-rows', + from: "1fr repeat(2, 2fr 30px) 1fr", + to: "2fr repeat(2, 3fr 40px 50px) 2fr" + }); + + test_no_interpolation({ + property: 'grid-template-rows', + from: "1fr repeat(2, 2fr 30px) 1fr", + to: "2fr repeat(3, 3fr 40px) 2fr" + }); + + test_no_interpolation({ + property: 'grid-template-rows', + from: "repeat(2, 2fr 30px)", + to: "repeat(4, 40px)" + }); + + test_interpolation({ + property: 'grid-template-rows', + from: "1fr repeat(2, 2fr auto 30px) 1fr", + to: "2fr repeat(2, 3fr 30px 40px) 2fr" + }, [ + {at: -1, expect: "0fr repeat(2, 1fr auto 20px) 0fr"}, + {at: 0, expect: "1fr repeat(2, 2fr auto 30px) 1fr"}, + {at: 0.4, expect: "1.4fr repeat(2, 2.4fr auto 34px) 1.4fr"}, + {at: 0.6, expect: "1.6fr repeat(2, 2.6fr 30px 36px) 1.6fr"}, + {at: 1, expect: "2fr repeat(2, 3fr 30px 40px) 2fr"}, + {at: 2, expect: "3fr repeat(2, 4fr 30px 50px) 3fr"} + ]); + + // Exercise <auto-track-list> + test_no_interpolation({ + property: 'grid-template-rows', + from: "10px repeat(auto-fill, minmax(25px, 1fr)) 10px", + to: "20px 20px repeat(auto-fill, minmax(30px, 1fr))" + }); + + test_no_interpolation({ + property: 'grid-template-rows', + from: "10px repeat(auto-fill, minmax(25px, 1fr)) 10px", + to: "20px repeat(auto-fit, minmax(30px, 1fr)) 20px" + }); + + test_no_interpolation({ + property: 'grid-template-rows', + from: "10px repeat(auto-fill, minmax(25px, 1fr)) 10px", + to: "20px repeat(auto-fill, minmax(35px, auto)) 20px" + }); + + test_no_interpolation({ + property: 'grid-template-rows', + from: "repeat(auto-fill, auto)", + to: "repeat(auto-fill, 100px)" + }); + </script> + </body> +</html>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-ui/crashtests/input-range-content-url.html b/third_party/blink/web_tests/external/wpt/css/css-ui/crashtests/input-range-content-url.html new file mode 100644 index 0000000..87840a9 --- /dev/null +++ b/third_party/blink/web_tests/external/wpt/css/css-ui/crashtests/input-range-content-url.html
@@ -0,0 +1,3 @@ +<!DOCTYPE html> +<link rel="help" href="https://github.com/servo/servo/issues/44540"> +<input style="content: url()" type="range">
diff --git a/third_party/blink/web_tests/external/wpt/css/css-variables/variable-css-wide-keywords-after-substitution.html b/third_party/blink/web_tests/external/wpt/css/css-variables/variable-css-wide-keywords-after-substitution.html new file mode 100644 index 0000000..78b4851 --- /dev/null +++ b/third_party/blink/web_tests/external/wpt/css/css-variables/variable-css-wide-keywords-after-substitution.html
@@ -0,0 +1,88 @@ +<!DOCTYPE html> +<title>CSS Custom Properties: CSS-wide keyword detected after var() substitution</title> +<link rel="help" href="https://drafts.csswg.org/css-variables-2/#defining-variables"> +<link rel="help" href="https://drafts.csswg.org/css-values-5/#substitute-arbitrary-substitution-function"> +<meta name="assert" content="A CSS-wide keyword as the sole non-whitespace token of a custom property's value after arbitrary substitution is treated as that keyword, equivalent to specifying the keyword directly."> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> + +<style> + #parent > .direct, + #parent > .substituted, + #parent > .use-as-sibling { + --empty: ; + } + + /* initial */ + #parent > .direct.initial { --x: initial; } + #parent > .substituted.initial { --x: var(--empty) initial; } + + /* inherit */ + #parent > .direct.inherit { --x: inherit; } + #parent > .substituted.inherit { --x: var(--empty) inherit; } + + /* unset */ + #parent > .direct.unset { --x: unset; } + #parent > .substituted.unset { --x: var(--empty) unset; } + + /* revert */ + #parent > .direct.revert { --x: revert; } + #parent > .substituted.revert { --x: var(--empty) revert; } + + /* revert-layer (layered) */ + #parent > .direct.revert-layer { + @layer a { --x: layer-a-value; } + @layer b { & { --x: revert-layer; } } + } + #parent > .substituted.revert-layer { + @layer a { --x: layer-a-value; } + @layer b { & { --x: var(--empty) revert-layer; } } + } + + /* Use --x as a sibling of a literal (exercises the bug's failure mode too). */ + #parent > .use-as-sibling { + @layer a { --x: ; } + @layer b { & { --x: var(--empty) revert-layer; } } + background: var(--x) green; + } +</style> + +<div id="parent"> + <div class="direct initial">direct initial</div> + <div class="substituted initial">substituted initial</div> + + <div class="direct inherit">direct inherit</div> + <div class="substituted inherit">substituted inherit</div> + + <div class="direct unset">direct unset</div> + <div class="substituted unset">substituted unset</div> + + <div class="direct revert">direct revert</div> + <div class="substituted revert">substituted revert</div> + + <div class="direct revert-layer">direct revert-layer</div> + <div class="substituted revert-layer">substituted revert-layer</div> + + <div class="use-as-sibling">use-as-sibling</div> +</div> + +<script> + const keywords = ['initial', 'inherit', 'unset', 'revert', 'revert-layer']; + for (const keyword of keywords) { + test(() => { + const direct = document.querySelector('#parent > .direct.' + CSS.escape(keyword)); + const substituted = document.querySelector('#parent > .substituted.' + CSS.escape(keyword)); + const directValue = getComputedStyle(direct).getPropertyValue('--x'); + const substitutedValue = getComputedStyle(substituted).getPropertyValue('--x'); + assert_equals(substitutedValue, directValue, + '`var(--empty) ' + keyword + '` must resolve identically to `' + keyword + '`'); + }, 'CSS-wide keyword `' + keyword + '` after var() substitution'); + } + + test(() => { + // `--x: var(--empty) revert-layer;` in layer b must revert to layer a's empty value, + // so `background: var(--x) green` resolves to `background: green`. + const el = document.querySelector('#parent > .use-as-sibling'); + assert_equals(getComputedStyle(el).backgroundColor, 'rgb(0, 128, 0)'); + }, 'revert-layer after var() substitution takes effect on the cascade'); +</script>
diff --git a/third_party/blink/web_tests/external/wpt/html/dom/elements/global-attributes/dataset.html b/third_party/blink/web_tests/external/wpt/html/dom/elements/global-attributes/dataset.html index a4a16d0..3a97454cc 100644 --- a/third_party/blink/web_tests/external/wpt/html/dom/elements/global-attributes/dataset.html +++ b/third_party/blink/web_tests/external/wpt/html/dom/elements/global-attributes/dataset.html
@@ -35,4 +35,8 @@ var svg = document.createElementNS("http://www.w3.org/2000/svg", "svg") assert_true(svg.dataset instanceof DOMStringMap); }, "SVG elements should have a .dataset"); +test(function() { + var mathml = document.createElementNS("http://www.w3.org/1998/Math/MathML", "math"); + assert_true(mathml.dataset instanceof DOMStringMap); +}, "MathML elements should have a .dataset"); </script>
diff --git a/third_party/blink/web_tests/external/wpt/html/editing/dnd/js-file-fallback-text-only.html b/third_party/blink/web_tests/external/wpt/html/editing/dnd/js-file-fallback-text-only.html new file mode 100644 index 0000000..494f05a --- /dev/null +++ b/third_party/blink/web_tests/external/wpt/html/editing/dnd/js-file-fallback-text-only.html
@@ -0,0 +1,99 @@ +<!doctype html> +<meta charset="utf-8"> +<title>Drag and Drop: JS File Object (fallback to text/plain when feature disabled)</title> +<link rel="author" title="Microsoft" href="http://www.microsoft.com"> +<link rel="help" href="https://html.spec.whatwg.org/multipage/dnd.html#the-datatransfer-interface"> +<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> + +<style> + .box { + display: inline-block; + border: 2px solid black; + width: 100px; + height: 100px; + margin: 20px; + vertical-align: top; + line-height: 100px; + text-align: center; + } + #drag-src { background: lightblue; } + #drop-tgt { background: lightgrey; } +</style> + +<div id="drag-src" class="box" draggable="true">Source</div> +<div id="drop-tgt" class="box">Target</div> + +<script> +'use strict'; + +function makePngBytes() { + const canvas = document.createElement('canvas'); + canvas.width = 1; canvas.height = 1; + const ctx = canvas.getContext('2d'); + ctx.fillStyle = 'red'; + ctx.fillRect(0, 0, 1, 1); + const bin = atob(canvas.toDataURL('image/png').split(',')[1]); + return Uint8Array.from(bin, c => c.charCodeAt(0)); +} + +const PNG_BYTES = makePngBytes(); + +// This test verifies the fallback path when DragAndDropJSFileObjects is +// disabled: a JS-constructed File object should be transferred as text/plain +// containing only the filename, with no binary file content available. +promise_test(async t => { + const dragSrc = document.getElementById('drag-src'); + const dropTgt = document.getElementById('drop-tgt'); + + const dropWatcher = new Promise((resolve, reject) => { + dragSrc.ondragstart = t.step_func(e => { + const file = new File([PNG_BYTES], 'test.png', {type: 'image/png'}); + e.dataTransfer.items.add(file); + e.dataTransfer.effectAllowed = 'copy'; + }); + + dropTgt.ondragenter = e => { + e.preventDefault(); + e.dataTransfer.dropEffect = 'copy'; + }; + dropTgt.ondragover = e => { + e.preventDefault(); + e.dataTransfer.dropEffect = 'copy'; + }; + + dropTgt.ondrop = t.step_func(async e => { + e.preventDefault(); + try { + // When the feature is disabled, no binary file content is transferred. + assert_equals(e.dataTransfer.files.length, 0, + 'No files should be in .files when feature is disabled'); + + // The filename should be available as text/plain. + const text = e.dataTransfer.getData('text/plain'); + assert_equals(text, 'test.png', + 'Filename should be transferred as text/plain fallback'); + resolve(); + } catch (err) { + reject(err); + } + }); + }); + + await new test_driver.Actions() + .pointerMove(0, 0, {origin: dragSrc}) + .pointerDown() + .pause(100) + .pointerMove(15, 15, {origin: dragSrc}) + .pause(100) + .pointerMove(0, 0, {origin: dropTgt}) + .pause(100) + .pointerUp() + .send(); + + await dropWatcher; +}, 'JS File falls back to text/plain filename when DragAndDropJSFileObjects is disabled'); +</script>
diff --git a/third_party/blink/web_tests/external/wpt/html/editing/dnd/js-file-image-drag.html b/third_party/blink/web_tests/external/wpt/html/editing/dnd/js-file-image-drag.html new file mode 100644 index 0000000..0a826fd --- /dev/null +++ b/third_party/blink/web_tests/external/wpt/html/editing/dnd/js-file-image-drag.html
@@ -0,0 +1,94 @@ +<!doctype html> +<meta charset="utf-8"> +<title>Drag and Drop: JS File Object (PNG)</title> +<link rel="author" title="Microsoft" href="http://www.microsoft.com"> +<link rel="help" href="https://html.spec.whatwg.org/multipage/dnd.html#the-datatransfer-interface"> +<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> + +<style> + .box { + display: inline-block; + border: 2px solid black; + width: 100px; + height: 100px; + margin: 20px; + vertical-align: top; + line-height: 100px; + text-align: center; + } + #drag-src { background: lightblue; } + #drop-tgt { background: lightgrey; } +</style> + +<div id="drag-src" class="box" draggable="true">Source</div> +<div id="drop-tgt" class="box">Target</div> + +<script> +'use strict'; + +function makePngBytes() { + const canvas = document.createElement('canvas'); + canvas.width = 1; canvas.height = 1; + const ctx = canvas.getContext('2d'); + ctx.fillStyle = 'red'; + ctx.fillRect(0, 0, 1, 1); + const bin = atob(canvas.toDataURL('image/png').split(',')[1]); + return Uint8Array.from(bin, c => c.charCodeAt(0)); +} + +const PNG_BYTES = makePngBytes(); + +promise_test(async t => { + const dragSrc = document.getElementById('drag-src'); + const dropTgt = document.getElementById('drop-tgt'); + + const dropWatcher = new Promise((resolve, reject) => { + dragSrc.ondragstart = t.step_func(e => { + const file = new File([PNG_BYTES], 'test.png', {type: 'image/png'}); + e.dataTransfer.items.add(file); + e.dataTransfer.effectAllowed = 'copy'; + }); + + dropTgt.ondragenter = e => { + e.preventDefault(); + e.dataTransfer.dropEffect = 'copy'; + }; + dropTgt.ondragover = e => { + e.preventDefault(); + e.dataTransfer.dropEffect = 'copy'; + }; + + dropTgt.ondrop = t.step_func(async e => { + e.preventDefault(); + try { + assert_equals(e.dataTransfer.files.length, 1, "File should be in .files"); + const file = e.dataTransfer.files[0]; + assert_equals(file.name, 'test.png'); + + const buffer = await file.arrayBuffer(); + assert_equals(buffer.byteLength, PNG_BYTES.byteLength, "Content size mismatch"); + resolve(); + } catch (err) { + reject(err); + } + }); + }); + + await new test_driver.Actions() + .pointerMove(0, 0, {origin: dragSrc}) + .pointerDown() + .pause(100) + .pointerMove(15, 15, {origin: dragSrc}) + .pause(100) + .pointerMove(0, 0, {origin: dropTgt}) + .pause(100) + .pointerUp() + .send(); + + await dropWatcher; +}, 'JS File with PNG bytes carries content to drop target'); +</script>
diff --git a/third_party/blink/web_tests/external/wpt/html/semantics/forms/the-select-element/customizable-select/select-appearance-base-display-ref.html b/third_party/blink/web_tests/external/wpt/html/semantics/forms/the-select-element/customizable-select/select-appearance-base-display-ref.html new file mode 100644 index 0000000..909459a --- /dev/null +++ b/third_party/blink/web_tests/external/wpt/html/semantics/forms/the-select-element/customizable-select/select-appearance-base-display-ref.html
@@ -0,0 +1,27 @@ +<!DOCTYPE html> +<title>CSS Test Reference</title> + +<style> +select { + width: 200px; + border-radius: 0; + font: inherit; + color: inherit; + background: transparent; + border: 1px solid; + padding: 0; + min-height: 0; + appearance: none; +} +.right { + text-align: end; +} +</style> + +<select class="right"> + <option>XXX</option> +</select> +<br> +<select> + <option>XXX</option> +</select>
diff --git a/third_party/blink/web_tests/external/wpt/html/semantics/forms/the-select-element/customizable-select/select-appearance-base-display.html b/third_party/blink/web_tests/external/wpt/html/semantics/forms/the-select-element/customizable-select/select-appearance-base-display.html new file mode 100644 index 0000000..70bd8717 --- /dev/null +++ b/third_party/blink/web_tests/external/wpt/html/semantics/forms/the-select-element/customizable-select/select-appearance-base-display.html
@@ -0,0 +1,40 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<title>CSS display is honored on select with appearance: base-select</title> +<link rel=author title="Emilio Cobos Álvarez" href="mailto:emilio@crisal.io"> +<link rel=help href="https://drafts.csswg.org/css-ui-4/#valdef-appearance-base-select"> +<link rel=help href="https://bugzilla.mozilla.org/show_bug.cgi?id=2023450"> +<link rel=match href="select-appearance-base-display-ref.html"> + +<style> +select { + width: 200px; + display: flex; + justify-content: end; + border-radius: 0; + font: inherit; + color: inherit; + background: transparent; + border: 1px solid; + padding: 0; + min-height: 0; +} +.base { + appearance: base-select; + appearance: base; +} +.none { + appearance: none; +} +select::picker-icon { + display: none; +} +</style> + +<select class="base"> + <option>XXX</option> +</select> + +<select class="none"> + <option>XXX</option> +</select>
diff --git a/third_party/blink/web_tests/external/wpt/jpegxl/hdr-alpha-reftest-ref.html b/third_party/blink/web_tests/external/wpt/jpegxl/hdr-alpha-reftest-ref.html deleted file mode 100644 index 26e0d3da..0000000 --- a/third_party/blink/web_tests/external/wpt/jpegxl/hdr-alpha-reftest-ref.html +++ /dev/null
@@ -1,4 +0,0 @@ -<!DOCTYPE html> -<link rel="stylesheet" href="resources/jpegxl-reftest.css"> - -<img src="resources/hdr_alpha.png">
diff --git a/third_party/blink/web_tests/external/wpt/jpegxl/hdr-alpha-reftest.html b/third_party/blink/web_tests/external/wpt/jpegxl/hdr-alpha-reftest.html deleted file mode 100644 index 90c50ec..0000000 --- a/third_party/blink/web_tests/external/wpt/jpegxl/hdr-alpha-reftest.html +++ /dev/null
@@ -1,7 +0,0 @@ -<!DOCTYPE html> -<link rel="help" href="https://github.com/web-platform-tests/interop-jpegxl"> -<link rel="match" href="hdr-alpha-reftest-ref.html"> -<meta name="assert" content="64x64 HDR JPEG XL with alpha channel decode should match PNG reference output."> -<link rel="stylesheet" href="resources/jpegxl-reftest.css"> - -<img src="resources/hdr_alpha.jxl">
diff --git a/third_party/blink/web_tests/external/wpt/jpegxl/resources/hdr_alpha.jxl b/third_party/blink/web_tests/external/wpt/jpegxl/resources/hdr_alpha.jxl deleted file mode 100644 index 6822686..0000000 --- a/third_party/blink/web_tests/external/wpt/jpegxl/resources/hdr_alpha.jxl +++ /dev/null Binary files differ
diff --git a/third_party/blink/web_tests/external/wpt/jpegxl/resources/hdr_alpha.png b/third_party/blink/web_tests/external/wpt/jpegxl/resources/hdr_alpha.png deleted file mode 100644 index 320c0f48..0000000 --- a/third_party/blink/web_tests/external/wpt/jpegxl/resources/hdr_alpha.png +++ /dev/null Binary files differ
diff --git a/third_party/blink/web_tests/external/wpt/jpegxl/resources/sdr_alpha.jxl b/third_party/blink/web_tests/external/wpt/jpegxl/resources/sdr_alpha.jxl deleted file mode 100644 index 9982d729..0000000 --- a/third_party/blink/web_tests/external/wpt/jpegxl/resources/sdr_alpha.jxl +++ /dev/null Binary files differ
diff --git a/third_party/blink/web_tests/external/wpt/jpegxl/resources/sdr_alpha.png b/third_party/blink/web_tests/external/wpt/jpegxl/resources/sdr_alpha.png deleted file mode 100644 index 37c22d6..0000000 --- a/third_party/blink/web_tests/external/wpt/jpegxl/resources/sdr_alpha.png +++ /dev/null Binary files differ
diff --git a/third_party/blink/web_tests/external/wpt/jpegxl/sdr-alpha-reftest-ref.html b/third_party/blink/web_tests/external/wpt/jpegxl/sdr-alpha-reftest-ref.html deleted file mode 100644 index 2b22c7a..0000000 --- a/third_party/blink/web_tests/external/wpt/jpegxl/sdr-alpha-reftest-ref.html +++ /dev/null
@@ -1,4 +0,0 @@ -<!DOCTYPE html> -<link rel="stylesheet" href="resources/jpegxl-reftest.css"> - -<img src="resources/sdr_alpha.png">
diff --git a/third_party/blink/web_tests/external/wpt/jpegxl/sdr-alpha-reftest.html b/third_party/blink/web_tests/external/wpt/jpegxl/sdr-alpha-reftest.html deleted file mode 100644 index 5e7dc266..0000000 --- a/third_party/blink/web_tests/external/wpt/jpegxl/sdr-alpha-reftest.html +++ /dev/null
@@ -1,7 +0,0 @@ -<!DOCTYPE html> -<link rel="help" href="https://github.com/web-platform-tests/interop-jpegxl"> -<link rel="match" href="sdr-alpha-reftest-ref.html"> -<meta name="assert" content="64x64 sRGB JPEG XL with alpha channel decode should match PNG reference output."> -<link rel="stylesheet" href="resources/jpegxl-reftest.css"> - -<img src="resources/sdr_alpha.jxl">
diff --git a/third_party/blink/web_tests/external/wpt/mathml/relations/html5-tree/html-or-svg-or-mathml-element-interfaces-expected.txt b/third_party/blink/web_tests/external/wpt/mathml/relations/html5-tree/html-or-svg-or-mathml-element-interfaces-expected.txt new file mode 100644 index 0000000..4b9dce2 --- /dev/null +++ b/third_party/blink/web_tests/external/wpt/mathml/relations/html5-tree/html-or-svg-or-mathml-element-interfaces-expected.txt
@@ -0,0 +1,5 @@ +This is a testharness.js-based test. +[FAIL] MathML a element should have a tabIndex value of 0 + assert_equals: expected 0 but got -1 +Harness: the test ran to completion. +
diff --git a/third_party/blink/web_tests/external/wpt/mathml/relations/html5-tree/html-or-foreign-element-interfaces.tentative.html b/third_party/blink/web_tests/external/wpt/mathml/relations/html5-tree/html-or-svg-or-mathml-element-interfaces.html similarity index 85% rename from third_party/blink/web_tests/external/wpt/mathml/relations/html5-tree/html-or-foreign-element-interfaces.tentative.html rename to third_party/blink/web_tests/external/wpt/mathml/relations/html5-tree/html-or-svg-or-mathml-element-interfaces.html index 028428cd..83518ba 100644 --- a/third_party/blink/web_tests/external/wpt/mathml/relations/html5-tree/html-or-foreign-element-interfaces.tentative.html +++ b/third_party/blink/web_tests/external/wpt/mathml/relations/html5-tree/html-or-svg-or-mathml-element-interfaces.html
@@ -2,7 +2,7 @@ <html> <head> <meta charset="utf-8" /> - <title>MathML 'HTMLOrForeignElement` Mixin Tests</title> + <title>MathML 'HTMLOrSVGOrMathMLElement` Mixin Tests</title> <link rel="help" href="https://w3c.github.io/mathml-core/#dom-and-javascript"/> <style> mi { @@ -14,24 +14,24 @@ </style> <meta name="assert" - content="MathMLElements incorporate a functional HTMLOrForeignElement interface" + content="MathMLElements incorporate a functional HTMLOrSVGOrMathMLElement interface" /> <script src="/resources/testharness.js"></script> <script src="/resources/testharnessreport.js"></script> </head> <body tabindex="-1"> - <span tabindex="-1" - >This tests the presence and functionality of features of - `HTMLOrForeignElement` (currently `HTMLOrSVGElement`)</span - > + <span tabindex="-1">This tests the presence and functionality of features of + `HTMLOrSVGOrMathMLElement`</span> <math tabindex="-1"> <mi>E</mi> + <a href="https://example.com">Example Link</a> </math> </body> <script> // spot check the functionality of several interfaces let el = document.querySelector("mi"); let mathEl = document.querySelector("math"); + let mathAEl = mathEl.querySelector("a"); // this really belongs in // https://github.com/web-platform-tests/wpt/blob/master/html/dom/elements/global-attributes/dataset.html @@ -78,6 +78,10 @@ assert_equals(mathEl.tabIndex, -1); }, "MathML elements should have a tabIndex property"); + test(function() { + assert_equals(mathAEl.tabIndex, 0); + }, "MathML a element should have a tabIndex value of 0"); + promise_test(function() { function focus() { mathEl.focus();
diff --git a/third_party/blink/web_tests/external/wpt/mathml/relations/html5-tree/tabindex-001.tentative-expected.txt b/third_party/blink/web_tests/external/wpt/mathml/relations/html5-tree/tabindex-001-expected.txt similarity index 100% rename from third_party/blink/web_tests/external/wpt/mathml/relations/html5-tree/tabindex-001.tentative-expected.txt rename to third_party/blink/web_tests/external/wpt/mathml/relations/html5-tree/tabindex-001-expected.txt
diff --git a/third_party/blink/web_tests/external/wpt/mathml/relations/html5-tree/tabindex-001.tentative.html b/third_party/blink/web_tests/external/wpt/mathml/relations/html5-tree/tabindex-001.html similarity index 100% rename from third_party/blink/web_tests/external/wpt/mathml/relations/html5-tree/tabindex-001.tentative.html rename to third_party/blink/web_tests/external/wpt/mathml/relations/html5-tree/tabindex-001.html
diff --git a/third_party/blink/web_tests/external/wpt/mathml/relations/html5-tree/tabindex-002.tentative.html b/third_party/blink/web_tests/external/wpt/mathml/relations/html5-tree/tabindex-002.html similarity index 100% rename from third_party/blink/web_tests/external/wpt/mathml/relations/html5-tree/tabindex-002.tentative.html rename to third_party/blink/web_tests/external/wpt/mathml/relations/html5-tree/tabindex-002.html
diff --git a/third_party/blink/web_tests/external/wpt/mathml/relations/html5-tree/tabindex-focus-001.tentative-expected.txt b/third_party/blink/web_tests/external/wpt/mathml/relations/html5-tree/tabindex-focus-001-expected.txt similarity index 100% rename from third_party/blink/web_tests/external/wpt/mathml/relations/html5-tree/tabindex-focus-001.tentative-expected.txt rename to third_party/blink/web_tests/external/wpt/mathml/relations/html5-tree/tabindex-focus-001-expected.txt
diff --git a/third_party/blink/web_tests/external/wpt/mathml/relations/html5-tree/tabindex-focus-001.tentative.html b/third_party/blink/web_tests/external/wpt/mathml/relations/html5-tree/tabindex-focus-001.html similarity index 100% rename from third_party/blink/web_tests/external/wpt/mathml/relations/html5-tree/tabindex-focus-001.tentative.html rename to third_party/blink/web_tests/external/wpt/mathml/relations/html5-tree/tabindex-focus-001.html
diff --git a/third_party/blink/web_tests/external/wpt/media-capabilities/decodingInfo-webrtc.any-expected.txt b/third_party/blink/web_tests/external/wpt/media-capabilities/decodingInfo-webrtc.any-expected.txt index 6a336652..1b0704f 100644 --- a/third_party/blink/web_tests/external/wpt/media-capabilities/decodingInfo-webrtc.any-expected.txt +++ b/third_party/blink/web_tests/external/wpt/media-capabilities/decodingInfo-webrtc.any-expected.txt
@@ -9,5 +9,9 @@ assert_unreached: Should have rejected: undefined Reached unreachable code [FAIL] Test that decodingInfo rejects for type 'webrtc' if HDR members are set assert_unreached: Should have rejected: undefined Reached unreachable code +[FAIL] Test that decodingInfo rejects if colorGamut is specified for type webrtc + assert_unreached: Should have rejected: undefined Reached unreachable code +[FAIL] Test that decodingInfo rejects if transferFunction is specified for type webrtc + assert_unreached: Should have rejected: undefined Reached unreachable code Harness: the test ran to completion.
diff --git a/third_party/blink/web_tests/external/wpt/media-capabilities/decodingInfo-webrtc.any.js b/third_party/blink/web_tests/external/wpt/media-capabilities/decodingInfo-webrtc.any.js index 34ce5fb..69dd288 100644 --- a/third_party/blink/web_tests/external/wpt/media-capabilities/decodingInfo-webrtc.any.js +++ b/third_party/blink/web_tests/external/wpt/media-capabilities/decodingInfo-webrtc.any.js
@@ -313,5 +313,33 @@ })); }, "Test that decodingInfo rejects for type 'webrtc' if HDR members are set"); +promise_test(t => { + return promise_rejects_js(t, TypeError, navigator.mediaCapabilities.decodingInfo({ + type: 'webrtc', + video: { + contentType: 'video/VP9', + width: 800, + height: 600, + bitrate: 3000, + framerate: 24, + colorGamut: 'srgb', + } + })); +}, "Test that decodingInfo rejects if colorGamut is specified for type webrtc"); + +promise_test(t => { + return promise_rejects_js(t, TypeError, navigator.mediaCapabilities.decodingInfo({ + type: 'webrtc', + video: { + contentType: 'video/VP9', + width: 800, + height: 600, + bitrate: 3000, + framerate: 24, + transferFunction: 'srgb', + } + })); +}, "Test that decodingInfo rejects if transferFunction is specified for type webrtc"); +
diff --git a/third_party/blink/web_tests/external/wpt/media-capabilities/decodingInfo-webrtc.any.worker-expected.txt b/third_party/blink/web_tests/external/wpt/media-capabilities/decodingInfo-webrtc.any.worker-expected.txt index 6a336652..1b0704f 100644 --- a/third_party/blink/web_tests/external/wpt/media-capabilities/decodingInfo-webrtc.any.worker-expected.txt +++ b/third_party/blink/web_tests/external/wpt/media-capabilities/decodingInfo-webrtc.any.worker-expected.txt
@@ -9,5 +9,9 @@ assert_unreached: Should have rejected: undefined Reached unreachable code [FAIL] Test that decodingInfo rejects for type 'webrtc' if HDR members are set assert_unreached: Should have rejected: undefined Reached unreachable code +[FAIL] Test that decodingInfo rejects if colorGamut is specified for type webrtc + assert_unreached: Should have rejected: undefined Reached unreachable code +[FAIL] Test that decodingInfo rejects if transferFunction is specified for type webrtc + assert_unreached: Should have rejected: undefined Reached unreachable code Harness: the test ran to completion.
diff --git a/third_party/blink/web_tests/external/wpt/media-capabilities/decodingInfo.any.js b/third_party/blink/web_tests/external/wpt/media-capabilities/decodingInfo.any.js index 510a59d..852e7232 100644 --- a/third_party/blink/web_tests/external/wpt/media-capabilities/decodingInfo.any.js +++ b/third_party/blink/web_tests/external/wpt/media-capabilities/decodingInfo.any.js
@@ -375,6 +375,17 @@ }, 'Test that decodingInfo with mismatched codec color space is unsupported'); promise_test(t => { + return navigator.mediaCapabilities.decodingInfo({ + type: 'media-source', + video: videoConfigurationWithDynamicRange, + }).then(ability => { + assert_equals(typeof ability.supported, "boolean"); + assert_equals(typeof ability.smooth, "boolean"); + assert_equals(typeof ability.powerEfficient, "boolean"); + }); +}, "Test that decodingInfo with type media-source accepts colorGamut and transferFunction"); + +promise_test(t => { return promise_rejects_js(t, TypeError, navigator.mediaCapabilities.decodingInfo({ type: 'file', video: {
diff --git a/third_party/blink/web_tests/external/wpt/media-capabilities/encodingInfo-webrtc.any-expected.txt b/third_party/blink/web_tests/external/wpt/media-capabilities/encodingInfo-webrtc.any-expected.txt index bc95729a63..610e904 100644 --- a/third_party/blink/web_tests/external/wpt/media-capabilities/encodingInfo-webrtc.any-expected.txt +++ b/third_party/blink/web_tests/external/wpt/media-capabilities/encodingInfo-webrtc.any-expected.txt
@@ -7,5 +7,9 @@ assert_unreached: Should have rejected: undefined Reached unreachable code [FAIL] Test that encodingInfo rejects if the audio configuration contentType has one parameter that isn't codecs assert_unreached: Should have rejected: undefined Reached unreachable code +[FAIL] Test that encodingInfo rejects if colorGamut is specified for type webrtc + assert_unreached: Should have rejected: undefined Reached unreachable code +[FAIL] Test that encodingInfo rejects if transferFunction is specified for type webrtc + assert_unreached: Should have rejected: undefined Reached unreachable code Harness: the test ran to completion.
diff --git a/third_party/blink/web_tests/external/wpt/media-capabilities/encodingInfo-webrtc.any.js b/third_party/blink/web_tests/external/wpt/media-capabilities/encodingInfo-webrtc.any.js index 1369941a..696483d 100644 --- a/third_party/blink/web_tests/external/wpt/media-capabilities/encodingInfo-webrtc.any.js +++ b/third_party/blink/web_tests/external/wpt/media-capabilities/encodingInfo-webrtc.any.js
@@ -266,3 +266,46 @@ }, "Test that encodingInfo returns supported true for the codec " + codec + (isWorkerEnvironment ? "" : " returned by RTCRtpSender.getCapabilities()"))} ); +promise_test(t => { + return navigator.mediaCapabilities.encodingInfo({ + type: 'webrtc', + video: { + contentType: 'video/VP9', + width: 800, + height: 600, + bitrate: 3000, + framerate: 24, + scalabilityMode: 'L1T2', + } + }).then(ability => { + assert_true(ability.supported); + }); +}, "Test that encodingInfo accepts scalabilityMode for webrtc video configuration"); + +promise_test(t => { + return promise_rejects_js(t, TypeError, navigator.mediaCapabilities.encodingInfo({ + type: 'webrtc', + video: { + contentType: 'video/VP9', + width: 800, + height: 600, + bitrate: 3000, + framerate: 24, + colorGamut: 'srgb', + } + })); +}, "Test that encodingInfo rejects if colorGamut is specified for type webrtc"); + +promise_test(t => { + return promise_rejects_js(t, TypeError, navigator.mediaCapabilities.encodingInfo({ + type: 'webrtc', + video: { + contentType: 'video/VP9', + width: 800, + height: 600, + bitrate: 3000, + framerate: 24, + transferFunction: 'srgb', + } + })); +}, "Test that encodingInfo rejects if transferFunction is specified for type webrtc");
diff --git a/third_party/blink/web_tests/external/wpt/media-capabilities/encodingInfo-webrtc.any.worker-expected.txt b/third_party/blink/web_tests/external/wpt/media-capabilities/encodingInfo-webrtc.any.worker-expected.txt index bc95729a63..610e904 100644 --- a/third_party/blink/web_tests/external/wpt/media-capabilities/encodingInfo-webrtc.any.worker-expected.txt +++ b/third_party/blink/web_tests/external/wpt/media-capabilities/encodingInfo-webrtc.any.worker-expected.txt
@@ -7,5 +7,9 @@ assert_unreached: Should have rejected: undefined Reached unreachable code [FAIL] Test that encodingInfo rejects if the audio configuration contentType has one parameter that isn't codecs assert_unreached: Should have rejected: undefined Reached unreachable code +[FAIL] Test that encodingInfo rejects if colorGamut is specified for type webrtc + assert_unreached: Should have rejected: undefined Reached unreachable code +[FAIL] Test that encodingInfo rejects if transferFunction is specified for type webrtc + assert_unreached: Should have rejected: undefined Reached unreachable code Harness: the test ran to completion.
diff --git a/third_party/blink/web_tests/external/wpt/media-capabilities/encodingInfo.any-expected.txt b/third_party/blink/web_tests/external/wpt/media-capabilities/encodingInfo.any-expected.txt index 7314b25..23cf6b2 100644 --- a/third_party/blink/web_tests/external/wpt/media-capabilities/encodingInfo.any-expected.txt +++ b/third_party/blink/web_tests/external/wpt/media-capabilities/encodingInfo.any-expected.txt
@@ -11,4 +11,9 @@ assert_unreached: Should have rejected: undefined Reached unreachable code [FAIL] Test that encodingInfo rejects if the audio configuration contentType has a codecs parameter that indicates both an audio and a video codec assert_unreached: Should have rejected: undefined Reached unreachable code +[FAIL] Test that encodingInfo rejects if colorGamut is specified for type record + assert_unreached: Should have rejected: undefined Reached unreachable code +[FAIL] Test that encodingInfo rejects if transferFunction is specified for type record + assert_unreached: Should have rejected: undefined Reached unreachable code Harness: the test ran to completion. +
diff --git a/third_party/blink/web_tests/external/wpt/media-capabilities/encodingInfo.any.js b/third_party/blink/web_tests/external/wpt/media-capabilities/encodingInfo.any.js index 97b6c19..a12d49dc 100644 --- a/third_party/blink/web_tests/external/wpt/media-capabilities/encodingInfo.any.js +++ b/third_party/blink/web_tests/external/wpt/media-capabilities/encodingInfo.any.js
@@ -291,3 +291,45 @@ } }), t.unreached_func('Promise.all should not reject for valid types')); }, "Test that encodingInfo rejects if the MediaConfiguration does not have a valid type"); + +promise_test(t => { + return promise_rejects_js(t, TypeError, navigator.mediaCapabilities.encodingInfo({ + type: 'record', + video: { + contentType: 'video/webm; codecs="vp09.00.10.08"', + width: 800, + height: 600, + bitrate: 3000, + framerate: 24, + scalabilityMode: 'L1T2', + } + })); +}, "Test that encodingInfo rejects if scalabilityMode is specified for type record"); + +promise_test(t => { + return promise_rejects_js(t, TypeError, navigator.mediaCapabilities.encodingInfo({ + type: 'record', + video: { + contentType: 'video/webm; codecs="vp09.00.10.08"', + width: 800, + height: 600, + bitrate: 3000, + framerate: 24, + colorGamut: 'srgb', + } + })); +}, "Test that encodingInfo rejects if colorGamut is specified for type record"); + +promise_test(t => { + return promise_rejects_js(t, TypeError, navigator.mediaCapabilities.encodingInfo({ + type: 'record', + video: { + contentType: 'video/webm; codecs="vp09.00.10.08"', + width: 800, + height: 600, + bitrate: 3000, + framerate: 24, + transferFunction: 'srgb', + } + })); +}, "Test that encodingInfo rejects if transferFunction is specified for type record");
diff --git a/third_party/blink/web_tests/external/wpt/media-capabilities/encodingInfo.any.worker-expected.txt b/third_party/blink/web_tests/external/wpt/media-capabilities/encodingInfo.any.worker-expected.txt index 7314b25..23cf6b2 100644 --- a/third_party/blink/web_tests/external/wpt/media-capabilities/encodingInfo.any.worker-expected.txt +++ b/third_party/blink/web_tests/external/wpt/media-capabilities/encodingInfo.any.worker-expected.txt
@@ -11,4 +11,9 @@ assert_unreached: Should have rejected: undefined Reached unreachable code [FAIL] Test that encodingInfo rejects if the audio configuration contentType has a codecs parameter that indicates both an audio and a video codec assert_unreached: Should have rejected: undefined Reached unreachable code +[FAIL] Test that encodingInfo rejects if colorGamut is specified for type record + assert_unreached: Should have rejected: undefined Reached unreachable code +[FAIL] Test that encodingInfo rejects if transferFunction is specified for type record + assert_unreached: Should have rejected: undefined Reached unreachable code Harness: the test ran to completion. +
diff --git a/third_party/blink/web_tests/external/wpt/permissions/crashtests/permissions-query-worker.window.js b/third_party/blink/web_tests/external/wpt/permissions/crashtests/permissions-query-worker.window.js new file mode 100644 index 0000000..915e62e9 --- /dev/null +++ b/third_party/blink/web_tests/external/wpt/permissions/crashtests/permissions-query-worker.window.js
@@ -0,0 +1,16 @@ +promise_test(async () => { + const worker = new Worker(URL.createObjectURL(new Blob([` + postMessage("load"); + while (true) { + navigator.permissions.query({ name: "geolocation" }); + } + `]))); + await new Promise(resolve => { + worker.onmessage = (e) => { + if (e.data === "load") { + worker.terminate(); + resolve(); + } + }; + }); +}, "Terminating worker after permission query should not crash");
diff --git a/third_party/blink/web_tests/external/wpt/scroll-to-text-fragment/resources/popup1.sub.html b/third_party/blink/web_tests/external/wpt/scroll-to-text-fragment/resources/popup1.sub.html new file mode 100644 index 0000000..a76bb19 --- /dev/null +++ b/third_party/blink/web_tests/external/wpt/scroll-to-text-fragment/resources/popup1.sub.html
@@ -0,0 +1,26 @@ +<!doctype html> +<title>Popup 1</title> +<script src="/common/utils.js"></script> +<script src="../stash.js"></script> + +<script> +const urlParams = new URLSearchParams(window.location.search); +const key = urlParams.get("key"); + +window.onload = () => { + // Navigate this window to B.com (cross-origin) with a text fragment directive + const crossOriginURL = new URL("http://{{hosts[alt][www]}}:{{ports[http][0]}}/scroll-to-text-fragment/resources/target.html"); + crossOriginURL.searchParams.set("key", key); + crossOriginURL.hash = "#:~:text=target"; + + // Simultaneously open popup2 on A.com (same-origin) to keep the browsing context group alive + try { + window.open(`popup2.html?key=${key}`, "_blank"); + } catch (e) {} + + // Navigate popup1 to B.com + requestAnimationFrame(() => { + window.location.href = crossOriginURL.href; + }); +}; +</script>
diff --git a/third_party/blink/web_tests/external/wpt/scroll-to-text-fragment/resources/popup2.html b/third_party/blink/web_tests/external/wpt/scroll-to-text-fragment/resources/popup2.html new file mode 100644 index 0000000..426521123 --- /dev/null +++ b/third_party/blink/web_tests/external/wpt/scroll-to-text-fragment/resources/popup2.html
@@ -0,0 +1,3 @@ +<!doctype html> +<title>Popup 2</title> +<body>Popup 2</body>
diff --git a/third_party/blink/web_tests/external/wpt/scroll-to-text-fragment/resources/target.html b/third_party/blink/web_tests/external/wpt/scroll-to-text-fragment/resources/target.html new file mode 100644 index 0000000..c6aa7731 --- /dev/null +++ b/third_party/blink/web_tests/external/wpt/scroll-to-text-fragment/resources/target.html
@@ -0,0 +1,30 @@ +<!doctype html> +<title>Target on B.com</title> +<script src="../stash.js"></script> + +<style> + .spacer { + height: 2000px; + } +</style> + +<body> + <div class="spacer"></div> + <div id="target">target text</div> + + <script> + function checkScroll() { + const did_scroll = window.scrollY > 0; + const urlParams = new URLSearchParams(window.location.search); + const key = urlParams.get("key"); + + stashResultsThenClose(key, { did_scroll: did_scroll }); + } + + window.onload = () => { + requestAnimationFrame(() => requestAnimationFrame(() => { + requestAnimationFrame(() => requestAnimationFrame(checkScroll)) + })); + }; + </script> +</body>
diff --git a/third_party/blink/web_tests/external/wpt/scroll-to-text-fragment/scroll-to-text-fragment-cross-window.html b/third_party/blink/web_tests/external/wpt/scroll-to-text-fragment/scroll-to-text-fragment-cross-window.html new file mode 100644 index 0000000..4d544fb --- /dev/null +++ b/third_party/blink/web_tests/external/wpt/scroll-to-text-fragment/scroll-to-text-fragment-cross-window.html
@@ -0,0 +1,24 @@ +<!doctype html> +<title>Text fragment anchor should not scroll if has other related pages</title> +<meta charset=utf-8> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="/resources/testdriver.js"></script> +<script src="/resources/testdriver-vendor.js"></script> +<script src="/common/utils.js"></script> +<script src="stash.js"></script> + +<script> +async_test(t => { + const key = token(); + + test_driver.bless("Open a popup on A.com", () => { + window.open(`resources/popup1.sub.html?key=${key}`, "_blank", "width=300,height=300"); + }); + + fetchResults(key, t.step_func(data => { + assert_equals(data.did_scroll, false, "scrolled to fragment"); + t.done(); + }), t.unreached_func("Stash fetch failed")); +}, "Text fragment should not scroll if opened with window.open and a simultaneous navigation"); +</script>
diff --git a/third_party/blink/web_tests/external/wpt/shadow-dom/focus/focus-preserved-on-slot-reorder.html b/third_party/blink/web_tests/external/wpt/shadow-dom/focus/focus-preserved-on-slot-reorder.html new file mode 100644 index 0000000..74f9dc9d --- /dev/null +++ b/third_party/blink/web_tests/external/wpt/shadow-dom/focus/focus-preserved-on-slot-reorder.html
@@ -0,0 +1,41 @@ +<!doctype html> +<meta charset="utf-8"> +<title>Reordering a slot wrapper in the shadow tree doesn't drop focus from a slotted element</title> +<link rel="help" href="https://bugzilla.mozilla.org/show_bug.cgi?id=2037337"> +<link rel="author" href="mailto:emilio@crisal.io" title="Emilio Cobos Álvarez"> +<link rel="author" href="https://mozilla.com" title="Mozilla"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<div id="host"> + <div slot="b"><input id="input" value="target"></div> +</div> +<script> +function waitForFrame() { + return new Promise(resolve => requestAnimationFrame(resolve)); +} + +promise_test(async function() { + let host = document.getElementById("host"); + let input = document.getElementById("input"); + let root = host.attachShadow({ mode: "open" }); + root.innerHTML = '<div id="rowA">A</div><div id="rowB"><slot name="b"></slot></div>'; + + input.focus(); + assert_equals(document.activeElement, input, "activeElement after focus"); + + let blurred = false; + input.addEventListener("blur", () => { blurred = true; }); + + let rowA = root.getElementById("rowA"); + let rowB = root.getElementById("rowB"); + root.insertBefore(rowB, rowA); + + assert_equals(document.activeElement, input, "activeElement right after reordering slot wrapper"); + + await waitForFrame(); + await waitForFrame(); + + assert_equals(document.activeElement, input, "activeElement after reordering slot wrapper and waiting for the focus fixup"); + assert_false(blurred, "input did not receive a blur event"); +}, "Reordering a slot wrapper in the shadow tree preserves focus on the slotted element"); +</script>
diff --git a/third_party/blink/web_tests/external/wpt/webmcp/imperative/detached-frame-executeTool.https.html b/third_party/blink/web_tests/external/wpt/webmcp/imperative/detached-frame-executeTool.https.html new file mode 100644 index 0000000..5cf86bf --- /dev/null +++ b/third_party/blink/web_tests/external/wpt/webmcp/imperative/detached-frame-executeTool.https.html
@@ -0,0 +1,47 @@ +<!DOCTYPE html> +<html> +<head> +<title>executeTool() in detached frame</title> +<link rel="author" href="mailto:dom@chromium.org"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +</head> +<body> +<script> +promise_test(async t => { + const iframe = document.createElement('iframe'); + iframe.src = '/common/blank.html'; + + const load_promise = new Promise(resolve => iframe.onload = resolve); + document.body.append(iframe); + await load_promise; + + const iDOMException = iframe.contentWindow.DOMException; + const iframe_modelContext = iframe.contentWindow.navigator.modelContext; + + // Register a dummy tool so we have a valid tool object before detaching the frame. + const toolchange_promise = new Promise(resolve => { + iframe_modelContext.addEventListener('toolchange', resolve, { once: true }); + }); + + iframe_modelContext.registerTool({ + name: 'dummy_tool', + description: 'Dummy tool', + execute: async () => {} + }); + + await toolchange_promise; + + const tools = await iframe_modelContext.getTools(); + const tool = tools.find(t => t.name === 'dummy_tool'); + assert_true(!!tool, 'Tool should be registered'); + + // Detach the iframe context. + iframe.remove(); + + // Calling executeTool() should reject with InvalidStateError. + await promise_rejects_dom(t, 'InvalidStateError', iDOMException, iframe_modelContext.executeTool(tool, '{}')); +}, 'executeTool() throws `InvalidStateError` in detached frame'); +</script> +</body> +</html>
diff --git a/third_party/blink/web_tests/external/wpt/webmcp/imperative/executeTool-abort.https.html b/third_party/blink/web_tests/external/wpt/webmcp/imperative/executeTool-abort.https.html new file mode 100644 index 0000000..ff20930 --- /dev/null +++ b/third_party/blink/web_tests/external/wpt/webmcp/imperative/executeTool-abort.https.html
@@ -0,0 +1,35 @@ +<!DOCTYPE html> +<html> +<head> +<title>WebMCP executeTool with AbortSignal</title> +<link rel="author" href="mailto:dom@chromium.org"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +</head> +<body> +<script> +promise_test(async t => { + const controller = new AbortController(); + + // Register a dummy tool so we have something to execute. + navigator.modelContext.registerTool({ + name: 'dummy_tool', + description: 'Dummy tool', + execute: async () => 'hello' + }); + + // Wait for toolchange to be sure it's registered. + await new Promise(resolve => { + navigator.modelContext.addEventListener('toolchange', resolve, {once: true}); + }); + + const tools = await navigator.modelContext.getTools(); + const tool = tools.find(t => t.name === 'dummy_tool'); + const promise = navigator.modelContext.executeTool(tool, '{}', { signal: controller.signal }); + + controller.abort(); + await promise_rejects_dom(t, 'AbortError', promise, 'Promise should be rejected with AbortError'); +}, 'executeTool() rejects with AbortError when signal is aborted'); +</script> +</body> +</html>
diff --git a/third_party/blink/web_tests/external/wpt/webmcp/imperative/executeTool-across-trees.https.html b/third_party/blink/web_tests/external/wpt/webmcp/imperative/executeTool-across-trees.https.html new file mode 100644 index 0000000..efde5c9d --- /dev/null +++ b/third_party/blink/web_tests/external/wpt/webmcp/imperative/executeTool-across-trees.https.html
@@ -0,0 +1,44 @@ +<!DOCTYPE html> +<html> +<head> +<title>WebMCP executeTool across trees</title> +<link rel="author" href="mailto:dom@chromium.org"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +</head> +<body> +<script> +promise_test(async t => { + const windowB = window.open('/common/blank.html', '_blank'); + t.add_cleanup(() => windowB.close()); + + // Wait for window B to load. + await new Promise(resolve => { + windowB.onload = resolve; + }); + + // Register tool directly in window B using synchronous scripting. + const toolchange_promise = new Promise(resolve => { + windowB.navigator.modelContext.addEventListener('toolchange', resolve, { once: true }); + }); + + windowB.navigator.modelContext.registerTool({ + name: 'target_tool', + description: 'Target tool in other window', + execute: async () => 'test' + }); + + await toolchange_promise; + + const tools = await navigator.modelContext.getTools(); + assert_array_equals(tools, [], "We do not see the other document's tool"); + + const [tool] = await windowB.navigator.modelContext.getTools(); + assert_equals(tool.window, windowB); + const execute_promise = navigator.modelContext.executeTool(tool, '{}'); + + await promise_rejects_dom(t, 'UnknownError', execute_promise); +}, 'executeTool() rejects when the tool is hosted in another frame tree'); +</script> +</body> +</html>
diff --git a/third_party/blink/web_tests/external/wpt/webmcp/imperative/executeTool-caller-navigate-abort.https-expected.txt b/third_party/blink/web_tests/external/wpt/webmcp/imperative/executeTool-caller-navigate-abort.https-expected.txt new file mode 100644 index 0000000..10ca6ca --- /dev/null +++ b/third_party/blink/web_tests/external/wpt/webmcp/imperative/executeTool-caller-navigate-abort.https-expected.txt
@@ -0,0 +1,7 @@ +This is a testharness.js-based test. +[FAIL] executeTool() aborts target signal when the invoker document (same-origin) navigates away + promise_test: Unhandled rejection with value: "signal not provided to execute()" +[FAIL] executeTool() aborts target signal when the invoker document (cross-origin) navigates away + promise_test: Unhandled rejection with value: "signal not provided to execute()" +Harness: the test ran to completion. +
diff --git a/third_party/blink/web_tests/external/wpt/webmcp/imperative/executeTool-caller-navigate-abort.https.html b/third_party/blink/web_tests/external/wpt/webmcp/imperative/executeTool-caller-navigate-abort.https.html new file mode 100644 index 0000000..688cb155 --- /dev/null +++ b/third_party/blink/web_tests/external/wpt/webmcp/imperative/executeTool-caller-navigate-abort.https.html
@@ -0,0 +1,89 @@ +<!DOCTYPE html> +<html> +<head> +<title>WebMCP executeTool with Caller Navigation (Abort)</title> +<link rel="author" href="mailto:dom@chromium.org"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="/common/get-host-info.sub.js"></script> +<script src="resources/helpers.js"></script> +</head> +<body> +<script> +function timeout_promise(t, msg) { + return new Promise((_, reject) => { + t.step_timeout(() => reject(msg), 3000); + }); +} + +const child_origins = [self.origin, get_host_info().HTTPS_REMOTE_ORIGIN]; +for (const origin of child_origins) { + run_test(origin); +} + +function run_test(origin) { + const origin_for_description = + origin === self.origin ? 'same-origin' : 'cross-origin'; + + promise_test(async t => { + const iframe = document.createElement('iframe'); + iframe.src = origin + '/webmcp/imperative/resources/iframe-caller.html'; + iframe.allow = `tools ${origin}`; + + const load_promise = new Promise(resolve => iframe.onload = resolve); + document.body.appendChild(iframe); + t.add_cleanup(() => iframe.remove()); + await load_promise; + + let resolve_tool_started; + const tool_started_promise = + new Promise(resolve => { resolve_tool_started = resolve; }); + + let resolve_signal_aborted, reject_signal_aborted; + const signal_aborted_promise = new Promise((resolve, reject) => { + resolve_signal_aborted = resolve; + reject_signal_aborted = reject; + }); + + const controller = new AbortController(); + navigator.modelContext.registerTool({ + name: 'parent_tool', + description: 'Parent tool', + execute: async (input, opts) => { + resolve_tool_started(); + + // If an `AbortSignal` is not passed into the `execute()` callback, reject + // `signal_aborted_promise`. + if (!opts || !opts.signal) { + reject_signal_aborted('signal not provided to execute()'); + return; + } + + opts.signal.onabort = resolve_signal_aborted; + + // The parent's tool runs "forever", waiting for the signal to abort. + await new Promise(() => {}); + } + }, { exposedTo: [origin], signal: controller.signal }); + t.add_cleanup(() => controller.abort()); + + await new Promise(resolve => { + navigator.modelContext.addEventListener('toolchange', resolve, {once: true}); + }); + + iframe.contentWindow.postMessage({action: 'invoke', name: 'parent_tool'}, '*'); + await Promise.race([tool_started_promise, timeout_promise(t, 'tool never ran')]); + + // The iframe navigates away before the tool has finished executing. Instead + // of navigating the iframe with its `src` attribute, we request it initiate + // its own navigation, just so if the `signal` above aborts, we're sure that + // it's not incidentally aborting due to any bookkeeping `this` Window is + // doing regarding navigations and tool executions. + iframe.contentWindow.postMessage({action: 'navigate'}, '*'); + + await Promise.race([signal_aborted_promise, timeout_promise(t, 'signal never aborted')]); + }, `executeTool() aborts target signal when the invoker document (${origin_for_description}) navigates away`); +} +</script> +</body> +</html>
diff --git a/third_party/blink/web_tests/external/wpt/webmcp/imperative/executeTool-invalid-dictionary.https.html b/third_party/blink/web_tests/external/wpt/webmcp/imperative/executeTool-invalid-dictionary.https.html new file mode 100644 index 0000000..8a6636d --- /dev/null +++ b/third_party/blink/web_tests/external/wpt/webmcp/imperative/executeTool-invalid-dictionary.https.html
@@ -0,0 +1,40 @@ +<!DOCTYPE html> +<html> +<head> +<title>WebMCP: executeTool dictionary member validation checks</title> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +</head> +<body> +<script> +promise_test(async t => { + // Register a legitimate tool first to retrieve a valid template. + navigator.modelContext.registerTool({ + name: "legit_tool", + description: "Legitimate tool description", + execute: async (args) => "success" + }); + + const tools = await navigator.modelContext.getTools(); + const legit = tools.find(t => t.name === "legit_tool"); + assert_true(!!legit, "Legitimate tool must be successfully discovered"); + + // Construct a dictionary containing all required members except the required 'origin' member. + const invalidTool = { + name: legit.name, + description: legit.description, + window: legit.window + // 'origin' is intentionally omitted! + }; + + // Invoking executeTool() must return a Promise rejected with TypeError. + await promise_rejects_js( + t, + TypeError, + navigator.modelContext.executeTool(invalidTool, '{}'), + "executeTool() must reject with TypeError if 'origin' member is omitted" + ); +}, "executeTool() rejects with TypeError when the required 'origin' dictionary member is omitted"); +</script> +</body> +</html>
diff --git a/third_party/blink/web_tests/external/wpt/webmcp/imperative/executeTool-target-detachment.https.html b/third_party/blink/web_tests/external/wpt/webmcp/imperative/executeTool-target-detachment.https.html new file mode 100644 index 0000000..3610821 --- /dev/null +++ b/third_party/blink/web_tests/external/wpt/webmcp/imperative/executeTool-target-detachment.https.html
@@ -0,0 +1,112 @@ +<!DOCTYPE html> +<html> +<head> +<title>WebMCP executeTool with Target Frame Detachment</title> +<link rel="author" href="mailto:dom@chromium.org"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="/common/get-host-info.sub.js"></script> +</head> +<body> +<script> +const host_info = get_host_info(); + +promise_test(async t => { + const iframe = document.createElement('iframe'); + iframe.src = host_info.HTTPS_REMOTE_ORIGIN + '/webmcp/imperative/resources/iframe-register-tool.html'; + iframe.allow = 'tools *'; + + const load_promise = new Promise(resolve => iframe.onload = resolve); + document.body.appendChild(iframe); + t.add_cleanup(() => iframe.remove()); + await load_promise; + + const toolchange_promise = new Promise(resolve => { + navigator.modelContext.addEventListener('toolchange', resolve, { once: true }); + }); + + // Have the iframe register a tool (it will never execute though). + iframe.contentWindow.postMessage({ + action: 'register', + tool: { + name: 'iframe_tool', + description: 'Iframe tool description' + }, + options: { + exposedTo: [self.origin] + } + }, '*'); + + await toolchange_promise; + + const tools = await navigator.modelContext.getTools(); + const tool = tools.find(t => t.name === 'iframe_tool'); + assert_true(!!tool, 'Tool should be retrieved successfully'); + + // Detach the frame before executing the tool. + iframe.remove(); + + // Calling executeTool() on the detached tool will internally fail to locate + // the frame, and thus reject the caller's Promise. + // + // TODO(https://crbug.com/509555636): It should probably reject with a `NotFoundError`. + await promise_rejects_dom( + t, + 'InvalidStateError', + navigator.modelContext.executeTool(tool, '{}'), + 'Promise should be rejected with InvalidStateError when target frame is detached before execution' + ); +}, 'executeTool() rejects when target frame is detached before execution'); + +promise_test(async t => { + const iframe = document.createElement('iframe'); + iframe.src = host_info.HTTPS_REMOTE_ORIGIN + '/webmcp/imperative/resources/iframe-register-tool.html'; + iframe.allow = 'tools *'; + + const load_promise = new Promise(resolve => iframe.onload = resolve); + document.body.appendChild(iframe); + t.add_cleanup(() => iframe.remove()); + await load_promise; + + const toolchange_promise = new Promise(resolve => { + navigator.modelContext.addEventListener('toolchange', resolve, { once: true }); + }); + + // The iframe tool hangs forever, because it must be executing *while* we + // detach the iframe. + iframe.contentWindow.postMessage({ + action: 'register', + hangsForever: true, + tool: { + name: 'iframe_tool_hanging', + description: 'Iframe tool description' + }, + options: { + exposedTo: [self.origin] + } + }, '*'); + + await toolchange_promise; + + const tools = await navigator.modelContext.getTools(); + const tool = tools.find(t => t.name === 'iframe_tool_hanging'); + assert_true(!!tool, 'Tool should be retrieved successfully'); + + // Execute the tool (hangs forever). + const execute_promise = navigator.modelContext.executeTool(tool, '{}'); + + iframe.remove(); + + // The execute promise rejects because the target document died + // mid-execution. See the bug referenced from the above test, about making + // the reason more specific. + await promise_rejects_dom( + t, + 'UnknownError', + execute_promise, + 'Promise should be rejected with UnknownError when target frame is detached mid-execution' + ); +}, 'executeTool() rejects when target frame is detached mid-execution'); +</script> +</body> +</html>
diff --git a/third_party/blink/web_tests/external/wpt/webmcp/imperative/executeTool-target-navigation.https.html b/third_party/blink/web_tests/external/wpt/webmcp/imperative/executeTool-target-navigation.https.html new file mode 100644 index 0000000..c567b518 --- /dev/null +++ b/third_party/blink/web_tests/external/wpt/webmcp/imperative/executeTool-target-navigation.https.html
@@ -0,0 +1,52 @@ +<!DOCTYPE html> +<html> +<head> +<title>WebMCP executeTool with Target Navigation</title> +<link rel="author" href="mailto:dom@chromium.org"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="/common/get-host-info.sub.js"></script> +<script src="resources/helpers.js"></script> +</head> +<body> +<script> +const host_info = get_host_info(); + +promise_test(async t => { + const iframe = document.createElement('iframe'); + iframe.src = 'resources/iframe-register-tool.html'; + iframe.allow = 'tools *'; + + const load_promise = new Promise(resolve => iframe.onload = resolve); + document.body.appendChild(iframe); + t.add_cleanup(() => iframe.remove()); + await load_promise; + + // Tell iframe to register a tool. + iframe.contentWindow.postMessage({ + action: 'register', + hangsForever: true, + tool: { + name: 'iframe_tool', + description: 'Iframe tool' + } + }, '*'); + + // Wait for toolchange. + await new Promise(resolve => { + navigator.modelContext.addEventListener('toolchange', resolve, {once: true}); + }); + + // Invoke tool and immediately navigate iframe away. + const tools = await navigator.modelContext.getTools(); + const tool = tools.find(t => t.name === 'iframe_tool'); + const promise = navigator.modelContext.executeTool(tool, '{}'); + iframe.src = 'about:blank'; + + // The promise should be rejected because the target document died. + // We expect AbortError or similar. Let's assume AbortError for now. + await promise_rejects_dom(t, 'UnknownError', promise, 'Promise should be rejected when target document dies'); +}, 'executeTool() rejects when target document navigates away'); +</script> +</body> +</html>
diff --git a/third_party/blink/web_tests/external/wpt/webmcp/imperative/executeTool-unauthorized-origin.https.html b/third_party/blink/web_tests/external/wpt/webmcp/imperative/executeTool-unauthorized-origin.https.html new file mode 100644 index 0000000..1bdf990 --- /dev/null +++ b/third_party/blink/web_tests/external/wpt/webmcp/imperative/executeTool-unauthorized-origin.https.html
@@ -0,0 +1,62 @@ +<!DOCTYPE html> +<html> +<head> +<title>WebMCP executeTool on unauthorized origin</title> +<link rel="author" href="mailto:dom@chromium.org"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="/common/get-host-info.sub.js"></script> +<script src="resources/helpers.js"></script> +</head> +<body> +<script> +const hostInfo = get_host_info(); + +async function setupCrossOriginIframe(t) { + const iframe = document.createElement('iframe'); + // Load helper iframe from cross-origin remote host. + iframe.src = `${hostInfo.HTTPS_REMOTE_ORIGIN}/webmcp/imperative/resources/iframe-register-tool.html`; + iframe.allow = 'tools *'; + + const load_promise = new Promise(resolve => iframe.onload = resolve); + document.body.appendChild(iframe); + t.add_cleanup(() => iframe.remove()); + await load_promise; + return iframe; +} + +promise_test(async t => { + const iframe = await setupCrossOriginIframe(t); + + // Tell the cross-origin iframe to register a tool with no `exposedTo` array (only visible to itself). + iframe.contentWindow.postMessage({ + action: 'register', + tool: { + name: 'iframe_secure_tool', + description: 'Iframe secure tool description' + } + }, '*'); + + // Wait briefly to ensure registration has completed in the target frame. + // Since it's cross-origin, we can't listen to its `toolchange` event directly, so we wait. + await new Promise(resolve => t.step_timeout(resolve, 1000)); + + // Verify that the parent (unexposed origin) cannot see the tool via getTools(). + const tools = await navigator.modelContext.getTools(); + assert_array_equals(tools, [], 'Parent frame should see no tools'); + + // Manually construct a fake RegisteredTool pointing to the cross-origin iframe window. + const fake_tool = { + name: 'iframe_secure_tool', + description: 'Iframe secure tool description', + window: iframe.contentWindow, + origin: hostInfo.HTTPS_REMOTE_ORIGIN + }; + + // Parent attempts to execute the unexposed tool directly. This should reject with UnknownError. + const promise = navigator.modelContext.executeTool(fake_tool, '{}'); + await promise_rejects_dom(t, 'UnknownError', promise, 'executeTool should reject with UnknownError when parent is not authorized to execute the iframe tool'); +}, 'executeTool() rejects when parent frame attempts unauthorized execution of cross-origin iframe tool'); +</script> +</body> +</html>
diff --git a/third_party/blink/web_tests/external/wpt/webmcp/imperative/exposedTo-cross-origin-child.https.html b/third_party/blink/web_tests/external/wpt/webmcp/imperative/exposedTo-cross-origin-child.https.html index 78e5f3c..b35b35d 100644 --- a/third_party/blink/web_tests/external/wpt/webmcp/imperative/exposedTo-cross-origin-child.https.html +++ b/third_party/blink/web_tests/external/wpt/webmcp/imperative/exposedTo-cross-origin-child.https.html
@@ -43,9 +43,16 @@ assert_true(toolsAreEqual(tool, { name: 'parent_tool_exposed', description: 'Parent tool exposed to child', - inputSchema: JSON.stringify({ type: 'object', properties: { query: { type: 'string' } } }) + inputSchema: JSON.stringify({ type: 'object', properties: { query: { type: 'string' } } }), + origin: self.origin }), 'Tool details should match'); + // Child executes parent's tool. + iframe.contentWindow.postMessage({action: 'execute', name: 'parent_tool_exposed'}, '*'); + const exec_response = await waitForIframeMessage('executeResponse'); + assert_true(exec_response.success, 'Child should successfully execute parent tool'); + assert_equals(exec_response.result, 'hello', 'Child should get correct result from parent tool'); + // Unregister and verify `toolchange` is fired in iframe. iframe.contentWindow.postMessage('listenForToolchange', '*'); await waitForIframeMessage('toolchange_listening_ack'); @@ -76,7 +83,7 @@ description: 'Iframe tool exposed to parent', inputSchema: { type: 'object', properties: { query: { type: 'string' } } } }, - options: { exposedTo: [host_info.HTTPS_ORIGIN] } + options: { exposedTo: [self.origin] } }, '*'); await new Promise(resolve => { @@ -88,9 +95,14 @@ assert_true(toolsAreEqual(tool, { name: 'iframe_tool_exposed', description: 'Iframe tool exposed to parent', - inputSchema: JSON.stringify({ type: 'object', properties: { query: { type: 'string' } } }) + inputSchema: JSON.stringify({ type: 'object', properties: { query: { type: 'string' } } }), + origin: host_info.HTTPS_REMOTE_ORIGIN }), 'Tool details should match'); + // Parent executes iframe's tool. + const result = await navigator.modelContext.executeTool(tool, '{}'); + assert_equals(result, 'hello from iframe', 'Parent should get correct result from iframe tool'); + // Tell the iframe to unregister its tool, and confirm that `toolchange` fires here. const toolchange_promise = new Promise(resolve => { navigator.modelContext.addEventListener('toolchange', resolve, { once: true });
diff --git a/third_party/blink/web_tests/external/wpt/webmcp/imperative/exposedTo-defaults-same-origin.https.html b/third_party/blink/web_tests/external/wpt/webmcp/imperative/exposedTo-defaults-same-origin.https.html index adcef474..6d05ff17 100644 --- a/third_party/blink/web_tests/external/wpt/webmcp/imperative/exposedTo-defaults-same-origin.https.html +++ b/third_party/blink/web_tests/external/wpt/webmcp/imperative/exposedTo-defaults-same-origin.https.html
@@ -41,12 +41,20 @@ let response = await waitForIframeMessage('getToolsResponse'); const [tool] = response.tools; + assert_true(!!tool, 'Same-origin iframe should see parent tool with no exposedTo'); assert_true(toolsAreEqual(tool, { name: 'parent_tool_default', description: 'Parent tool with default exposure', - inputSchema: JSON.stringify({ type: 'object', properties: { query: { type: 'string' } } }) + inputSchema: JSON.stringify({ type: 'object', properties: { query: { type: 'string' } } }), + origin: self.origin }), 'Tool details should match'); + // Iframe executes parent's tool. + iframe.contentWindow.postMessage({action: 'execute', name: 'parent_tool_default'}, '*'); + const exec_response = await waitForIframeMessage('executeResponse'); + assert_true(exec_response.success, 'Iframe should successfully execute parent tool'); + assert_equals(exec_response.result, 'hello', 'Iframe should get correct result from parent tool'); + // Unregister and verify child gets event. iframe.contentWindow.postMessage('listenForToolchange', '*'); await waitForIframeMessage('toolchange_listening_ack'); @@ -80,13 +88,21 @@ iframe.contentWindow.postMessage('getTools', '*'); let response = await waitForIframeMessage('getToolsResponse'); - const [tool2] = response.tools; - assert_true(toolsAreEqual(tool2, { + const [tool] = response.tools; + assert_true(!!tool, 'Same-origin iframe should see parent tool with empty exposedTo'); + assert_true(toolsAreEqual(tool, { name: 'parent_tool_empty', description: 'Parent tool with empty exposedTo', - inputSchema: JSON.stringify({ type: 'object', properties: { query: { type: 'string' } } }) + inputSchema: JSON.stringify({ type: 'object', properties: { query: { type: 'string' } } }), + origin: self.origin }), 'Tool details should match'); + // Iframe executes parent's tool. + iframe.contentWindow.postMessage({action: 'execute', name: 'parent_tool_empty'}, '*'); + const exec_response = await waitForIframeMessage('executeResponse'); + assert_true(exec_response.success, 'Iframe should successfully execute parent tool'); + assert_equals(exec_response.result, 'hello', 'Iframe should get correct result from parent tool'); + // Unregister and verify child gets event. iframe.contentWindow.postMessage('listenForToolchange', '*'); await waitForIframeMessage('toolchange_listening_ack'); @@ -122,7 +138,16 @@ }); let tools = await navigator.modelContext.getTools(); - assert_true(tools.some(t => t.name === 'iframe_tool_default'), 'Same-origin parent should see iframe tool with default exposure'); + const [tool] = tools; + assert_true(toolsAreEqual(tool, { + name: 'iframe_tool_default', + description: 'Iframe tool with default exposure', + origin: iframe.contentWindow.origin + }), 'Tool details should match'); + + // Parent executes iframe's tool. + const result = await navigator.modelContext.executeTool(tool, '{}'); + assert_equals(result, 'hello from iframe', 'Parent should get correct result from iframe tool'); // Unregister tool. const unregister_promise = new Promise(resolve => { @@ -152,7 +177,16 @@ }); let tools = await navigator.modelContext.getTools(); - assert_true(tools.some(t => t.name === 'iframe_tool_empty'), 'Same-origin parent should see iframe tool with empty exposedTo'); + const [tool] = tools; + assert_true(toolsAreEqual(tool, { + name: 'iframe_tool_empty', + description: 'Iframe tool with empty exposedTo', + origin: iframe.contentWindow.origin + }), 'Tool details should match'); + + // Parent executes iframe's tool. + const result = await navigator.modelContext.executeTool(tool, '{}'); + assert_equals(result, 'hello from iframe', 'Parent should get correct result from iframe tool'); // Unregister tool. const unregister_promise = new Promise(resolve => {
diff --git a/third_party/blink/web_tests/external/wpt/webmcp/imperative/exposedTo-multiple-children.https.html b/third_party/blink/web_tests/external/wpt/webmcp/imperative/exposedTo-multiple-children.https.html index efb0b7b4..fbebddb 100644 --- a/third_party/blink/web_tests/external/wpt/webmcp/imperative/exposedTo-multiple-children.https.html +++ b/third_party/blink/web_tests/external/wpt/webmcp/imperative/exposedTo-multiple-children.https.html
@@ -68,7 +68,8 @@ assert_true(toolsAreEqual(tool, { name: 'tool_b_to_c', description: 'Tool B exposed to C', - inputSchema: JSON.stringify({ type: 'object', properties: { query: { type: 'string' } } }) + inputSchema: JSON.stringify({ type: 'object', properties: { query: { type: 'string' } } }), + origin: origin_b }), 'Tool details should match'); // Now, we make B unregister the tool, and assert that:
diff --git a/third_party/blink/web_tests/external/wpt/webmcp/imperative/opaque-origin-tools.https.html b/third_party/blink/web_tests/external/wpt/webmcp/imperative/opaque-origin-tools.https.html new file mode 100644 index 0000000..b1cd083 --- /dev/null +++ b/third_party/blink/web_tests/external/wpt/webmcp/imperative/opaque-origin-tools.https.html
@@ -0,0 +1,41 @@ +<!DOCTYPE html> +<html> +<head> +<title>WebMCP Opaque Origin Tool</title> +<link rel="author" href="mailto:dom@chromium.org"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +</head> +<body> +<script> +promise_test(async t => { + // Assert that our CSP header took effect. + assert_equals(self.origin, 'null'); + + const toolchange_promise = new Promise(resolve => { + navigator.modelContext.addEventListener('toolchange', resolve, { once: true }); + }); + + navigator.modelContext.registerTool({ + name: 'opaque_tool', + description: 'Opaque tool description', + execute: async () => 'success' + }); + + await toolchange_promise; + + // The tool should be available, but its execution is rejected because opaque + // origins lose their same-origin identity during string serialization ("null"). + const tools = await navigator.modelContext.getTools(); + const tool = tools.find(t => t.name === 'opaque_tool'); + + await promise_rejects_dom( + t, + 'UnknownError', + navigator.modelContext.executeTool(tool, '{}'), + 'executeTool() must reject with UnknownError in opaque origin documents' + ); +}, 'An opaque origin document can register but not execute its own tools'); +</script> +</body> +</html>
diff --git a/third_party/blink/web_tests/external/wpt/webmcp/imperative/opaque-origin-tools.https.html.headers b/third_party/blink/web_tests/external/wpt/webmcp/imperative/opaque-origin-tools.https.html.headers new file mode 100644 index 0000000..1efcf8c --- /dev/null +++ b/third_party/blink/web_tests/external/wpt/webmcp/imperative/opaque-origin-tools.https.html.headers
@@ -0,0 +1 @@ +Content-Security-Policy: sandbox allow-scripts
diff --git a/third_party/blink/web_tests/external/wpt/webmcp/imperative/permissions-policy.https.html b/third_party/blink/web_tests/external/wpt/webmcp/imperative/permissions-policy.https.html index 24f25e0..7f3d85e 100644 --- a/third_party/blink/web_tests/external/wpt/webmcp/imperative/permissions-policy.https.html +++ b/third_party/blink/web_tests/external/wpt/webmcp/imperative/permissions-policy.https.html
@@ -45,7 +45,7 @@ if (e.data.startsWith('tool registration failed:')) { resolve(e.data); } - }); + }, {once: true}); }); iframe.contentWindow.postMessage({ @@ -56,6 +56,60 @@ assert_true(failure_msg.includes('SecurityError'), `Expected SecurityError in navigated iframe, got: ${failure_msg}`); }, 'registerTool() throws in iframe, after navigation to an origin not covered by permissions policy'); + +promise_test(async t => { + const iframe = document.createElement('iframe'); + const origin_b = host_info.HTTPS_REMOTE_ORIGIN; + + iframe.src = origin_b + '/webmcp/imperative/resources/iframe-register-tool.html'; + // No `allow` attribute. + + const load_promise = new Promise(resolve => iframe.onload = resolve); + document.body.appendChild(iframe); + t.add_cleanup(() => iframe.remove()); + await load_promise; + + const failure_promise = new Promise(resolve => { + window.addEventListener('message', e => { + if (typeof e.data === 'string' && e.data.startsWith('getTools promise rejected:')) { + resolve(e.data); + } + }, {once: true}); + }); + + iframe.contentWindow.postMessage('getTools', '*'); + const failure_msg = await failure_promise; + + assert_true(failure_msg.includes('SecurityError'), `Expected SecurityError in iframe, got: ${failure_msg}`); +}, 'getTools() throws SecurityError in iframe when permissions policy is disabled'); + +promise_test(async t => { + const iframe = document.createElement('iframe'); + const origin_b = host_info.HTTPS_REMOTE_ORIGIN; + + iframe.src = origin_b + '/webmcp/imperative/resources/iframe-register-tool.html'; + // No `allow` attribute. + + const load_promise = new Promise(resolve => iframe.onload = resolve); + document.body.appendChild(iframe); + t.add_cleanup(() => iframe.remove()); + await load_promise; + + const failure_promise = new Promise(resolve => { + window.addEventListener('message', e => { + if (e.data.action === 'executeFakeToolResponse') { + resolve(e.data); + } + }, {once: true}); + }); + + // Direct the iframe to construct a conceivable `RegisteredTool` and call + // `executeTool()` on it. + iframe.contentWindow.postMessage({ action: 'execute_fake_tool', name: 'dummy_tool' }, '*'); + const response = await failure_promise; + + assert_equals(response.error_name, 'SecurityError', 'Should reject with SecurityError'); +}, 'executeTool() throws SecurityError in iframe when permissions policy is disabled'); </script> </body> </html>
diff --git a/third_party/blink/web_tests/external/wpt/webmcp/imperative/resources/helpers.js b/third_party/blink/web_tests/external/wpt/webmcp/imperative/resources/helpers.js index 458365d..64ec567 100644 --- a/third_party/blink/web_tests/external/wpt/webmcp/imperative/resources/helpers.js +++ b/third_party/blink/web_tests/external/wpt/webmcp/imperative/resources/helpers.js
@@ -14,8 +14,18 @@ // Helper to compare tool objects that should conform to the `RegisteredTool` // dictionary. function toolsAreEqual(actual, expected) { - if (actual.name !== expected.name) return false; - if (actual.description !== expected.description) return false; - if (actual.inputSchema !== expected.inputSchema) return false; + if (actual.name !== expected.name) { + return `names are unequal: ${actual.name} !== ${expected.name}`; + } + if (actual.description !== expected.description) { + return `descriptions are unequal: ${actual.description} !== ${expected.description}`; + } + if (actual.inputSchema !== expected.inputSchema) { + return `inputSchemas are unequal: ${actual.inputSchemas} !== ${expected.inputSchemas}`; + } + if (actual.origin !== expected.origin) { + return `origins are unequal: ${actual.origin} !== ${expected.origin}`; + } + return true; }
diff --git a/third_party/blink/web_tests/external/wpt/webmcp/imperative/resources/iframe-caller.html b/third_party/blink/web_tests/external/wpt/webmcp/imperative/resources/iframe-caller.html new file mode 100644 index 0000000..04ef5b9 --- /dev/null +++ b/third_party/blink/web_tests/external/wpt/webmcp/imperative/resources/iframe-caller.html
@@ -0,0 +1,25 @@ +<!DOCTYPE html> +<html> +<head> +<title>WebMCP Iframe Caller</title> +<script src="/resources/testharness.js"></script> +</head> +<body> +<script> + window.onmessage = e => { + if (e.data.action === 'invoke') { + navigator.modelContext.getTools().then(tools => { + const tool = tools.find(t => t.name === e.data.name); + navigator.modelContext.executeTool(tool, '{}').then(result => { + parent.postMessage({action: 'executeResponse', result: result, success: true}, '*'); + }).catch(err => { + parent.postMessage({action: 'executeResponse', result: String(err), success: false}, '*'); + }); + }); + } else if (e.data.action === 'navigate') { + window.location.href = 'about:blank'; + } + }; +</script> +</body> +</html>
diff --git a/third_party/blink/web_tests/external/wpt/webmcp/imperative/resources/iframe-register-tool.html b/third_party/blink/web_tests/external/wpt/webmcp/imperative/resources/iframe-register-tool.html index 6bb988a..9ef698f 100644 --- a/third_party/blink/web_tests/external/wpt/webmcp/imperative/resources/iframe-register-tool.html +++ b/third_party/blink/web_tests/external/wpt/webmcp/imperative/resources/iframe-register-tool.html
@@ -6,6 +6,17 @@ </head> <body> <script> + // This is a helper function to convert an array of `RegisteredTool` + // dictionaries to objects that can be sent over `postMessage()`. This exists + // because the `RegisteredTool#window` member is not cloneable, so we must + // reduce the tool structure a bit before sending it. + function convertToCloneableTools(tools) { + return tools.map(tool => { + // An array of all relevant key/value pairs EXCEPT `window`. + const entries = Object.entries(tool).filter(([key, _]) => key !== "window") + return Object.fromEntries(entries); + }); + } // Map from tool_name => `AbortController`, so that this iframe document can // manage/unregister any tool that it registers. const controllers = new Map(); @@ -17,7 +28,11 @@ try { const controller = new AbortController(); const tool = e.data.tool; - tool.execute = async () => 'hello from iframe'; + if (e.data.hangsForever) { + tool.execute = () => new Promise(() => {}); + } else { + tool.execute = async () => 'hello from iframe'; + } const givenOptions = e.data.options; const options = { signal: controller.signal, ...givenOptions}; @@ -40,10 +55,33 @@ } } else if (action === 'getTools') { navigator.modelContext.getTools().then(tools => { - parent.postMessage({action: 'getToolsResponse', tools: tools}, '*'); + parent.postMessage({action: 'getToolsResponse', tools: convertToCloneableTools(tools)}, '*'); }).catch(e => { parent.postMessage(`getTools promise rejected: ${e}`, '*'); }); + } else if (action === 'execute') { + const name = e.data.name; + const args = e.data.args || '{}'; + navigator.modelContext.getTools().then(tools => { + const tool = tools.find(t => t.name === name); + navigator.modelContext.executeTool(tool, args).then(result => { + parent.postMessage({action: 'executeResponse', result: result, success: true}, '*'); + }).catch(err => { + parent.postMessage({action: 'executeResponse', result: String(err), success: false}, '*'); + }); + }); + } else if (action === 'execute_fake_tool') { + const fake_tool = { + name: e.data.name || 'dummy', + description: 'fake desc', + window: window, + origin: self.origin + }; + navigator.modelContext.executeTool(fake_tool, '{}').then(result => { + parent.postMessage({action: 'executeFakeToolResponse', result: result, success: true}, '*'); + }).catch(err => { + parent.postMessage({action: 'executeFakeToolResponse', result: String(err), success: false, error_name: err.name}, '*'); + }); } else if (action === 'listenForToolchange') { let timer_id; const listener = () => {
diff --git a/third_party/blink/web_tests/external/wpt/webmcp/imperative/unregister-during-executeTool.https.html b/third_party/blink/web_tests/external/wpt/webmcp/imperative/unregister-during-executeTool.https.html new file mode 100644 index 0000000..a5d7956 --- /dev/null +++ b/third_party/blink/web_tests/external/wpt/webmcp/imperative/unregister-during-executeTool.https.html
@@ -0,0 +1,64 @@ +<!DOCTYPE html> +<html> +<head> +<title>WebMCP executeTool with Unregistered Cross-Origin Tool</title> +<link rel="author" href="mailto:dom@chromium.org"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="/common/get-host-info.sub.js"></script> +<script src="resources/helpers.js"></script> +</head> +<body> +<script> +const host_info = get_host_info(); + +promise_test(async t => { + const iframe = document.createElement('iframe'); + iframe.src = `${host_info.HTTPS_REMOTE_ORIGIN}/webmcp/imperative/resources/iframe-register-tool.html`; + iframe.allow = 'tools *'; + + const load_promise = new Promise(resolve => iframe.onload = resolve); + document.body.appendChild(iframe); + t.add_cleanup(() => iframe.remove()); + await load_promise; + + // 1. Tell the cross-origin iframe to register an endless tool, + // exposing it to the parent origin. + const toolchange_promise = new Promise(resolve => { + navigator.modelContext.addEventListener('toolchange', resolve, { once: true }); + }); + + iframe.contentWindow.postMessage({ + action: 'register', + tool: { + name: 'endless_tool', + description: 'Endless tool in iframe' + }, + options: { exposedTo: [self.origin] }, + hangsForever: true + }, '*'); + + await toolchange_promise; + + const tools = await navigator.modelContext.getTools(); + const tool = tools.find(t => t.name === 'endless_tool'); + assert_true(!!tool, 'Tool should be registered and visible to parent'); + + const execute_promise = navigator.modelContext.executeTool(tool, '{}'); + + // Wait briefly to ensure the execution message has started, and that the + // execution request is now "pending", such that tool unregistration will + // reject `execute_promise`. + await new Promise(resolve => t.step_timeout(resolve, 100)); + + // 3. Unregister the tool in the target iframe while execution is pending. + iframe.contentWindow.postMessage({ + action: 'unregister', + name: 'endless_tool' + }, '*'); + + await promise_rejects_dom(t, 'UnknownError', execute_promise, 'Pending execution Promise should reject with UnknownError when the target tool is unregistered'); +}, 'Pending executeTool() Promise is rejected when the target cross-origin tool is unregistered'); +</script> +</body> +</html>
diff --git a/third_party/blink/web_tests/http/tests/origin_trials/webexposed/focusgroup-origin-trial-interfaces.html b/third_party/blink/web_tests/http/tests/origin_trials/webexposed/focusgroup-origin-trial-interfaces.html deleted file mode 100644 index dc90317..0000000 --- a/third_party/blink/web_tests/http/tests/origin_trials/webexposed/focusgroup-origin-trial-interfaces.html +++ /dev/null
@@ -1,22 +0,0 @@ -<!DOCTYPE html> -<meta charset="utf-8"> -<!-- Generate token with the command: -generate_token.py http://127.0.0.1:8000 Focusgroup --expire-timestamp=2000000000 ---> -<meta http-equiv="origin-trial" content="A9qE0/WDJK/8n0FKJZvc5rYfEUgPo1QIwgYqS15yAumsMKa7ZnoBImBR/5R+EXbWwxVEjGUHx/8nUoBesQDIcwcAAABSeyJvcmlnaW4iOiAiaHR0cDovLzEyNy4wLjAuMTo4MDAwIiwgImZlYXR1cmUiOiAiRm9jdXNncm91cCIsICJleHBpcnkiOiAyMDAwMDAwMDAwfQ==" /> -<title>Focusgroup - interfaces exposed by origin trial</title> -<script src="/resources/testharness.js"></script> -<script src="/resources/testharnessreport.js"></script> -<script src="/resources/origin-trials-helper.js"></script> -<script> -test(t => { - OriginTrialsHelper.check_properties_exist(this, - { - 'HTMLElement': ['focusgroup'], - 'SVGElement': ['focusgroup'], - 'MathMLElement': ['focusgroup'], - }, - ); -}, "focusgroup related interfaces in Origin-Trial enabled document."); -</script> -
diff --git a/third_party/blink/web_tests/virtual/disable-drag-drop-js-file-objects/README.md b/third_party/blink/web_tests/virtual/disable-drag-drop-js-file-objects/README.md new file mode 100644 index 0000000..34e3c51b --- /dev/null +++ b/third_party/blink/web_tests/virtual/disable-drag-drop-js-file-objects/README.md
@@ -0,0 +1,7 @@ +# disable-drag-drop-js-file-objects + +This virtual suite runs drag-and-drop tests with the DragAndDropJSFileObjects +Blink runtime feature disabled. + +It is used to verify the legacy fallback path where JS-constructed File objects +are transferred as text/plain filename data instead of binary file content.
diff --git a/third_party/blink/web_tests/virtual/disable-drag-drop-js-file-objects/external/wpt/html/editing/dnd/README.txt b/third_party/blink/web_tests/virtual/disable-drag-drop-js-file-objects/external/wpt/html/editing/dnd/README.txt new file mode 100644 index 0000000..8e5e1b4 --- /dev/null +++ b/third_party/blink/web_tests/virtual/disable-drag-drop-js-file-objects/external/wpt/html/editing/dnd/README.txt
@@ -0,0 +1,2 @@ +This directory holds virtualized WPT drag-and-drop tests that run with +--disable-blink-features=DragAndDropJSFileObjects.
diff --git a/third_party/blink/web_tests/virtual/stable/webexposed/element-instance-property-listing-expected.txt b/third_party/blink/web_tests/virtual/stable/webexposed/element-instance-property-listing-expected.txt index 3d051854..594ff07 100644 --- a/third_party/blink/web_tests/virtual/stable/webexposed/element-instance-property-listing-expected.txt +++ b/third_party/blink/web_tests/virtual/stable/webexposed/element-instance-property-listing-expected.txt
@@ -120,6 +120,7 @@ property firstChild property firstElementChild property focus + property focusgroup property getAnimations property getAttribute property getAttributeNS @@ -1384,6 +1385,7 @@ property firstChild property firstElementChild property focus + property focusgroup property getAnimations property getAttribute property getAttributeNS
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 1791382..76ad4a2c 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
@@ -3576,6 +3576,7 @@ getter draggable getter editContext getter enterKeyHint + getter focusgroup getter hidden getter inert getter innerText @@ -3719,6 +3720,7 @@ setter draggable setter editContext setter enterKeyHint + setter focusgroup setter hidden setter inert setter innerText @@ -5601,6 +5603,7 @@ getter attributeStyleMap getter autofocus getter dataset + getter focusgroup getter nonce getter onabort getter onanimationcancel @@ -5714,6 +5717,7 @@ method constructor method focus setter autofocus + setter focusgroup setter nonce setter onabort setter onanimationcancel @@ -7897,6 +7901,7 @@ getter autofocus getter className getter dataset + getter focusgroup getter nonce getter onabort getter onanimationcancel @@ -8012,6 +8017,7 @@ method constructor method focus setter autofocus + setter focusgroup setter nonce setter onabort setter onanimationcancel
diff --git a/third_party/blink/web_tests/webexposed/global-interface-listing-expected.txt b/third_party/blink/web_tests/webexposed/global-interface-listing-expected.txt index 7b96ab0..d39b352 100644 --- a/third_party/blink/web_tests/webexposed/global-interface-listing-expected.txt +++ b/third_party/blink/web_tests/webexposed/global-interface-listing-expected.txt
@@ -6763,6 +6763,7 @@ attribute @@toStringTag getter ontoolchange method constructor + method executeTool method getTools method registerTool setter ontoolchange
diff --git a/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/draw-element-image-returned-matrix.html b/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/draw-element-image-returned-matrix.html index fa0756f..8084da53 100644 --- a/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/draw-element-image-returned-matrix.html +++ b/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/draw-element-image-returned-matrix.html
@@ -3,25 +3,21 @@ <link rel="help" href="https://github.com/WICG/html-in-canvas"> <script src="/resources/testharness.js"></script> <script src="/resources/testharnessreport.js"></script> +<script src="/wpt_internal/resources/canvas-draw-element/waitForCanvasPaint.js"></script> <canvas id="canvas" style="width: 100px; height: 100px;" width="100" height="100" layoutsubtree> <div id="element" style="width: 10px; height: 10px;">hello</div> </canvas> <script> -async function waitOneFrame() { - await new Promise(requestAnimationFrame); - await new Promise(setTimeout); -} - function runTest() { promise_test(async function() { - await waitOneFrame(); + await waitForCanvasPaint(canvas); let draw_transform = canvas.getContext('2d').drawElementImage(element, 0, 0); let expected = new DOMMatrix(); assert_array_equals(draw_transform.toFloat64Array(), expected.toFloat64Array()); }, 'Draw transform at the origin should be an identity matrix'); promise_test(async function() { - await waitOneFrame(); + await waitForCanvasPaint(canvas); let draw_transform = canvas.getContext('2d').drawElementImage(element, 17, -9); let expected = new DOMMatrix().translateSelf(17, -9); assert_array_equals(draw_transform.toFloat64Array(), expected.toFloat64Array()); @@ -32,7 +28,7 @@ element.style.transformOrigin = ''; }); element.style.transformOrigin = '0 0'; - await waitOneFrame(); + await waitForCanvasPaint(canvas); let draw_transform = canvas.getContext('2d').drawElementImage(element, 0, 0, 20, 5); let expected = new DOMMatrix().scaleSelf(2, 0.5); assert_array_equals(draw_transform.toFloat64Array(), expected.toFloat64Array()); @@ -43,7 +39,7 @@ element.style.transform = ''; }); element.style.transform = 'rotate(45deg)'; - await waitOneFrame(); + await waitForCanvasPaint(canvas); let draw_transform = canvas.getContext('2d').drawElementImage(element, 0, 0); let expected = new DOMMatrix(); assert_array_equals(draw_transform.toFloat64Array(), expected.toFloat64Array()); @@ -54,7 +50,7 @@ canvas.getContext('2d').reset(); }); canvas.getContext('2d').translate(42, 17); - await waitOneFrame(); + await waitForCanvasPaint(canvas); let draw_transform = canvas.getContext('2d').drawElementImage(element, 0, 0); let expected = new DOMMatrix().translateSelf(42, 17); assert_array_equals(draw_transform.toFloat64Array(), expected.toFloat64Array()); @@ -63,10 +59,9 @@ promise_test(async function(t) { t.add_cleanup(async () => { canvas.style.zoom = ''; - await waitOneFrame(); }); canvas.style.zoom = '1.5'; - await waitOneFrame(); + await waitForCanvasPaint(canvas); let draw_transform = canvas.getContext('2d').drawElementImage(element, 10, 10); let expected = new DOMMatrix().translateSelf(10, 10); assert_array_equals(draw_transform.toFloat64Array(), expected.toFloat64Array()); @@ -78,7 +73,7 @@ canvas.getContext('2d').reset(); }); element.style.transformOrigin = '0 0'; - await waitOneFrame(); + await waitForCanvasPaint(canvas); canvas.getContext('2d').rotate(Math.PI / 4); let draw_transform = canvas.getContext('2d').drawElementImage(element, 0, 0); let expected = new DOMMatrix().rotateSelf(45); @@ -91,7 +86,7 @@ canvas.getContext('2d').reset(); }); element.style.transformOrigin = '5px 5px'; - await waitOneFrame(); + await waitForCanvasPaint(canvas); canvas.getContext('2d').rotate(Math.PI / 4); let draw_transform = canvas.getContext('2d').drawElementImage(element, 0, 0); let expected = new DOMMatrix() @@ -108,6 +103,7 @@ }); canvas.width = 200; canvas.height = 50; + await waitForCanvasPaint(canvas); let draw_transform = canvas.getContext('2d').drawElementImage(element, 0, 0); let expected = new DOMMatrix(); assert_array_equals(draw_transform.toFloat64Array(), expected.toFloat64Array()); @@ -120,7 +116,6 @@ canvas.style.zoom = ''; canvas.getContext('2d').reset(); element.style.transformOrigin = '0 0'; - await waitOneFrame(); }); canvas.width = 200; canvas.height = 50; @@ -129,7 +124,7 @@ canvas.getContext('2d').rotate(Math.PI / 4); element.style.transformOrigin = '5px 5px'; element.style.transformOrigin = '5px 5px'; - await waitOneFrame(); + await waitForCanvasPaint(canvas); let draw_transform = canvas.getContext('2d').drawElementImage(element, 13, -9); let expected = new DOMMatrix() .translateSelf(-5, -5)
diff --git a/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/privacy/backdrop-filter-png-images-ignored.https.sub.html b/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/privacy/backdrop-filter-png-images-ignored.https.sub.html index 83796ef..cddcef8 100644 --- a/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/privacy/backdrop-filter-png-images-ignored.https.sub.html +++ b/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/privacy/backdrop-filter-png-images-ignored.https.sub.html
@@ -28,7 +28,7 @@ <feimage href="https://{{location[host]}}/wpt_internal/html/canvas/drawElementImage/resources/green-100x100.png" /> </filter> <filter id="filter-cross"> - <feimage href="https://{{hosts[alt][www]}}:{{ports[h2][0]}}/wpt_internal/html/canvas/drawElementImage/resources/red-100x100.png" /> + <feimage href="https://{{hosts[alt][www]}}:{{ports[https][0]}}/wpt_internal/html/canvas/drawElementImage/resources/red-100x100.png" /> </filter> </svg>
diff --git a/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/privacy/background-png-images-ignored.https.sub.html b/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/privacy/background-png-images-ignored.https.sub.html index d567aad..317c8f2c 100644 --- a/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/privacy/background-png-images-ignored.https.sub.html +++ b/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/privacy/background-png-images-ignored.https.sub.html
@@ -26,7 +26,7 @@ top: 100px; width: 100px; height: 100px; - background-image: url("https://{{hosts[alt][www]}}:{{ports[h2][0]}}/wpt_internal/html/canvas/drawElementImage/resources/red-100x100.png"); + background-image: url("https://{{hosts[alt][www]}}:{{ports[https][0]}}/wpt_internal/html/canvas/drawElementImage/resources/red-100x100.png"); } </style> </head>
diff --git a/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/privacy/background-svg-images-ignored.https.sub.html b/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/privacy/background-svg-images-ignored.https.sub.html index 67d731b..2d3dc521 100644 --- a/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/privacy/background-svg-images-ignored.https.sub.html +++ b/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/privacy/background-svg-images-ignored.https.sub.html
@@ -26,7 +26,7 @@ top: 100px; width: 100px; height: 100px; - background-image: url("https://{{hosts[alt][www]}}:{{ports[h2][0]}}/wpt_internal/html/canvas/drawElementImage/resources/red-100x100.svg"); + background-image: url("https://{{hosts[alt][www]}}:{{ports[https][0]}}/wpt_internal/html/canvas/drawElementImage/resources/red-100x100.svg"); } </style> </head>
diff --git a/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/privacy/border-png-images-ignored.https.sub.html b/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/privacy/border-png-images-ignored.https.sub.html index 29618efa..1299678 100644 --- a/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/privacy/border-png-images-ignored.https.sub.html +++ b/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/privacy/border-png-images-ignored.https.sub.html
@@ -34,7 +34,7 @@ background-color: green; border-image-width: 25px; border-image-slice: 25; - border-image-source: url("https://{{hosts[alt][www]}}:{{ports[h2][0]}}/wpt_internal/html/canvas/drawElementImage/resources/red-100x100.png"); + border-image-source: url("https://{{hosts[alt][www]}}:{{ports[https][0]}}/wpt_internal/html/canvas/drawElementImage/resources/red-100x100.png"); } </style> </head>
diff --git a/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/privacy/border-svg-images-ignored.https.sub.html b/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/privacy/border-svg-images-ignored.https.sub.html index c8e57eb5..4128d95 100644 --- a/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/privacy/border-svg-images-ignored.https.sub.html +++ b/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/privacy/border-svg-images-ignored.https.sub.html
@@ -34,7 +34,7 @@ background-color: green; border-image-width: 25px; border-image-slice: 25; - border-image-source: url("https://{{hosts[alt][www]}}:{{ports[h2][0]}}/wpt_internal/html/canvas/drawElementImage/resources/red-100x100.svg"); + border-image-source: url("https://{{hosts[alt][www]}}:{{ports[https][0]}}/wpt_internal/html/canvas/drawElementImage/resources/red-100x100.svg"); } </style> </head>
diff --git a/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/privacy/box-mask-png-ignored.https.sub.html b/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/privacy/box-mask-png-ignored.https.sub.html index 56e0a9c..8c0c428 100644 --- a/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/privacy/box-mask-png-ignored.https.sub.html +++ b/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/privacy/box-mask-png-ignored.https.sub.html
@@ -28,7 +28,7 @@ width: 100px; height: 100px; background-color: red; - mask-image: url("https://{{hosts[alt][www]}}:{{ports[h2][0]}}/wpt_internal/html/canvas/drawElementImage/resources/blue-100x50-transparent-100x50.png"); + mask-image: url("https://{{hosts[alt][www]}}:{{ports[https][0]}}/wpt_internal/html/canvas/drawElementImage/resources/blue-100x50-transparent-100x50.png"); } </style> </head>
diff --git a/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/privacy/box-mask-svg-ignored.https.sub.html b/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/privacy/box-mask-svg-ignored.https.sub.html index dc9ba5d..ff124d8 100644 --- a/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/privacy/box-mask-svg-ignored.https.sub.html +++ b/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/privacy/box-mask-svg-ignored.https.sub.html
@@ -28,7 +28,7 @@ width: 100px; height: 100px; background-color: red; - mask-image: url("https://{{hosts[alt][www]}}:{{ports[h2][0]}}/wpt_internal/html/canvas/drawElementImage/resources/blue-100x50-transparent-100x50.svg"); + mask-image: url("https://{{hosts[alt][www]}}:{{ports[https][0]}}/wpt_internal/html/canvas/drawElementImage/resources/blue-100x50-transparent-100x50.svg"); } </style> </head>
diff --git a/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/privacy/box-reflect-images-ignored.https.sub.html b/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/privacy/box-reflect-images-ignored.https.sub.html index 1aaba92..c598046 100644 --- a/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/privacy/box-reflect-images-ignored.https.sub.html +++ b/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/privacy/box-reflect-images-ignored.https.sub.html
@@ -30,7 +30,7 @@ width: 100px; height: 100px; background: green; - -webkit-box-reflect: below 0 url("https://{{hosts[alt][www]}}:{{ports[h2][0]}}/wpt_internal/html/canvas/drawElementImage/resources/left-half-mask-50.svg"); + -webkit-box-reflect: below 0 url("https://{{hosts[alt][www]}}:{{ports[https][0]}}/wpt_internal/html/canvas/drawElementImage/resources/left-half-mask-50.svg"); } </style> </head>
diff --git a/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/privacy/embed-image-ignored.https.sub.html b/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/privacy/embed-image-ignored.https.sub.html index 6781e898..e20cd3ef 100644 --- a/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/privacy/embed-image-ignored.https.sub.html +++ b/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/privacy/embed-image-ignored.https.sub.html
@@ -30,7 +30,7 @@ <embed id=sameOrigin width=100 height=100 type="image/png" src="https://{{location[host]}}/wpt_internal/html/canvas/drawElementImage/resources/green-100x100.png"/> <embed id=crossOrigin width=100 height=100 type="image/png" - src="https://{{hosts[alt][www]}}:{{ports[h2][0]}}/wpt_internal/html/canvas/drawElementImage/resources/red-100x100.png"/> + src="https://{{hosts[alt][www]}}:{{ports[https][0]}}/wpt_internal/html/canvas/drawElementImage/resources/red-100x100.png"/> </div> </canvas> <script>
diff --git a/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/privacy/filtered-div-multi-filters-ignored.https.sub.html b/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/privacy/filtered-div-multi-filters-ignored.https.sub.html index c4fb52b..8cf1268 100644 --- a/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/privacy/filtered-div-multi-filters-ignored.https.sub.html +++ b/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/privacy/filtered-div-multi-filters-ignored.https.sub.html
@@ -29,7 +29,7 @@ <feimage href="https://{{location[host]}}/wpt_internal/html/canvas/drawElementImage/resources/green-100x100.png" /> </filter> <filter id="filter-cross"> - <feImage href="https://{{hosts[alt][www]}}:{{ports[h2][0]}}/wpt_internal/html/canvas/drawElementImage/resources/red-100x100.png"/> + <feImage href="https://{{hosts[alt][www]}}:{{ports[https][0]}}/wpt_internal/html/canvas/drawElementImage/resources/red-100x100.png"/> </filter> </svg>
diff --git a/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/privacy/filtered-div-ref-external-svg-ignored.https.sub.html b/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/privacy/filtered-div-ref-external-svg-ignored.https.sub.html index 6a1d75a..8132b99 100644 --- a/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/privacy/filtered-div-ref-external-svg-ignored.https.sub.html +++ b/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/privacy/filtered-div-ref-external-svg-ignored.https.sub.html
@@ -18,7 +18,7 @@ filter: url("https://{{location[host]}}/wpt_internal/html/canvas/drawElementImage/resources/green-filter.svg#green"); } #child-cross { - filter: url("https://{{hosts[alt][www]}}:{{ports[h2][0]}}/wpt_internal/html/canvas/drawElementImage/resources/red-filter.svg#red"); + filter: url("https://{{hosts[alt][www]}}:{{ports[https][0]}}/wpt_internal/html/canvas/drawElementImage/resources/red-filter.svg#red"); } </style> </head>
diff --git a/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/privacy/filtered-div-ref-svg-feimage-ignored.https.sub.html b/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/privacy/filtered-div-ref-svg-feimage-ignored.https.sub.html index 1dd68d9..9f88122f 100644 --- a/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/privacy/filtered-div-ref-svg-feimage-ignored.https.sub.html +++ b/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/privacy/filtered-div-ref-svg-feimage-ignored.https.sub.html
@@ -28,7 +28,7 @@ <feimage href="https://{{location[host]}}/wpt_internal/html/canvas/drawElementImage/resources/green-100x100.png" /> </filter> <filter id="filter-cross"> - <feImage href="https://{{hosts[alt][www]}}:{{ports[h2][0]}}/wpt_internal/html/canvas/drawElementImage/resources/red-100x100.png"/> + <feImage href="https://{{hosts[alt][www]}}:{{ports[https][0]}}/wpt_internal/html/canvas/drawElementImage/resources/red-100x100.png"/> </filter> </svg>
diff --git a/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/privacy/filtered-div-ref-svg-feimage-svg-ignored.https.sub.html b/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/privacy/filtered-div-ref-svg-feimage-svg-ignored.https.sub.html index 0c9722f..a1cfa9c9 100644 --- a/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/privacy/filtered-div-ref-svg-feimage-svg-ignored.https.sub.html +++ b/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/privacy/filtered-div-ref-svg-feimage-svg-ignored.https.sub.html
@@ -28,7 +28,7 @@ <feImage href="https://{{location[host]}}/wpt_internal/html/canvas/drawElementImage/resources/green-100x100.svg"/> </filter> <filter id="filter-cross"> - <feImage href="https://{{hosts[alt][www]}}:{{ports[h2][0]}}/wpt_internal/html/canvas/drawElementImage/resources/red-100x100.svg"/> + <feImage href="https://{{hosts[alt][www]}}:{{ports[https][0]}}/wpt_internal/html/canvas/drawElementImage/resources/red-100x100.svg"/> </filter> </svg>
diff --git a/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/privacy/filtered-div-ref-svg-mixed-ignored.https.sub.html b/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/privacy/filtered-div-ref-svg-mixed-ignored.https.sub.html index 3d3ab5b..fea6dca 100644 --- a/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/privacy/filtered-div-ref-svg-mixed-ignored.https.sub.html +++ b/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/privacy/filtered-div-ref-svg-mixed-ignored.https.sub.html
@@ -30,7 +30,7 @@ <feGaussianBlur in="sameImageOut" stdDeviation="2" /> </filter> <filter id="filter-cross"> - <feImage href="https://{{hosts[alt][www]}}:{{ports[h2][0]}}/wpt_internal/html/canvas/drawElementImage/resources/red-100x100.png" result="crossImageOut"/> + <feImage href="https://{{hosts[alt][www]}}:{{ports[https][0]}}/wpt_internal/html/canvas/drawElementImage/resources/red-100x100.png" result="crossImageOut"/> <feGaussianBlur in="crossImageOut" stdDeviation="2" /> </filter> </svg>
diff --git a/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/privacy/filtered-svg-ref-svg-feimage-ignored.https.sub.html b/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/privacy/filtered-svg-ref-svg-feimage-ignored.https.sub.html index 3f8c3e7..308f9b8 100644 --- a/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/privacy/filtered-svg-ref-svg-feimage-ignored.https.sub.html +++ b/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/privacy/filtered-svg-ref-svg-feimage-ignored.https.sub.html
@@ -16,7 +16,7 @@ <feImage href="https://{{location[host]}}/wpt_internal/html/canvas/drawElementImage/resources/green-100x100.png"/> </filter> <filter id="filter-cross"> - <feImage href="https://{{hosts[alt][www]}}:{{ports[h2][0]}}/wpt_internal/html/canvas/drawElementImage/resources/red-100x100.png"/> + <feImage href="https://{{hosts[alt][www]}}:{{ports[https][0]}}/wpt_internal/html/canvas/drawElementImage/resources/red-100x100.png"/> </filter> <rect x="0" y="0" width="100" height="100" fill="green" filter="url(#filter-same)"></rect> <rect x="0" y="100" width="100" height="100" fill="green" filter="url(#filter-cross)"></rect>
diff --git a/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/privacy/filtered-svg-ref-svg-feimage-svg-ignored.https.sub.html b/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/privacy/filtered-svg-ref-svg-feimage-svg-ignored.https.sub.html index 2afa25e..2a0bd27a 100644 --- a/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/privacy/filtered-svg-ref-svg-feimage-svg-ignored.https.sub.html +++ b/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/privacy/filtered-svg-ref-svg-feimage-svg-ignored.https.sub.html
@@ -16,7 +16,7 @@ <feImage href="https://{{location[host]}}/wpt_internal/html/canvas/drawElementImage/resources/green-100x100.svg"/> </filter> <filter id="filter-cross"> - <feImage href="https://{{hosts[alt][www]}}:{{ports[h2][0]}}/wpt_internal/html/canvas/drawElementImage/resources/red-100x100.svg"/> + <feImage href="https://{{hosts[alt][www]}}:{{ports[https][0]}}/wpt_internal/html/canvas/drawElementImage/resources/red-100x100.svg"/> </filter> <rect x="0" y="0" width="100" height="100" fill="green" filter="url(#filter-same)"></rect> <rect x="0" y="100" width="100" height="100" fill="green" filter="url(#filter-cross)"></rect>
diff --git a/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/privacy/fragment-mask-png-ignored.https.sub.html b/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/privacy/fragment-mask-png-ignored.https.sub.html index 4fe33ca7..0e4604e 100644 --- a/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/privacy/fragment-mask-png-ignored.https.sub.html +++ b/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/privacy/fragment-mask-png-ignored.https.sub.html
@@ -19,7 +19,7 @@ mask-image: url("https://{{location[host]}}/wpt_internal/html/canvas/drawElementImage/resources/blue-100x50-transparent-100x50.png"); } #crossOrigin { - mask-image: url("https://{{hosts[alt][www]}}:{{ports[h2][0]}}/wpt_internal/html/canvas/drawElementImage/resources/blue-100x50-transparent-100x50.png"); + mask-image: url("https://{{hosts[alt][www]}}:{{ports[https][0]}}/wpt_internal/html/canvas/drawElementImage/resources/blue-100x50-transparent-100x50.png"); } </style> </head>
diff --git a/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/privacy/fragment-mask-svg-ignored.https.sub.html b/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/privacy/fragment-mask-svg-ignored.https.sub.html index 8e21269..7c508a15 100644 --- a/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/privacy/fragment-mask-svg-ignored.https.sub.html +++ b/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/privacy/fragment-mask-svg-ignored.https.sub.html
@@ -19,7 +19,7 @@ mask-image: url("https://{{location[host]}}/wpt_internal/html/canvas/drawElementImage/resources/blue-100x50-transparent-100x50.svg"); } #crossOrigin { - mask-image: url("https://{{hosts[alt][www]}}:{{ports[h2][0]}}/wpt_internal/html/canvas/drawElementImage/resources/blue-100x50-transparent-100x50.svg"); + mask-image: url("https://{{hosts[alt][www]}}:{{ports[https][0]}}/wpt_internal/html/canvas/drawElementImage/resources/blue-100x50-transparent-100x50.svg"); } </style> </head>
diff --git a/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/privacy/html-images-cors-allowed.https.sub.html b/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/privacy/html-images-cors-allowed.https.sub.html new file mode 100644 index 0000000..57798f38 --- /dev/null +++ b/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/privacy/html-images-cors-allowed.https.sub.html
@@ -0,0 +1,53 @@ +<!DOCTYPE HTML> +<html> +<head> + <title>drawElementImage does draws CORS enabled cross-origin HTML images</title> + <script src='/resources/testharness.js'></script> + <script src='/resources/testharnessreport.js'></script> + <script src='../support/getPixel.js'></script> + <script src="/wpt_internal/resources/canvas-draw-element/waitForCanvasPaint.js"></script> + <style> + #child { + width: 10px; + height: 20px; + background: red; + } + #corsPNG { + position: absolute; + left: 0px; + top: 0px; + } + #corsSVG { + position: absolute; + left: 0px; + top: 10px; + } + </style> +</head> +<body> + <canvas id=canvas width="10" height="20" layoutsubtree> + <div id=child> + <img id=corsPNG width=10 height=10 src="https://{{hosts[alt][www]}}:{{ports[https][0]}}/wpt_internal/html/canvas/drawElementImage/privacy/resources/green-100x100.png?pipe=header(Access-Control-Allow-Origin,*)" crossOrigin=anonymous /> + <img id=corsSVG width=10 height=10 src="https://{{hosts[alt][www]}}:{{ports[https][0]}}/wpt_internal/html/canvas/drawElementImage/privacy/resources/green-100x100.svg?pipe=header(Access-Control-Allow-Origin,*)" crossOrigin=anonymous /> + </div> + </canvas> + <script> + window.onload = function() { + promise_test(async function(t) { + await waitForCanvasPaint(canvas); + var context = canvas.getContext("2d", { willReadFrequently: true }); + context.drawElementImage(child, 0, 0); + + // Fetch all pixel data once to avoid multiple slow readbacks. + const imgData = context.getImageData(0, 0, canvas.width, canvas.height); + + let pixel = getPixel(imgData, 5, 5); + assert_array_equals(pixel, [0, 255, 0, 255], "CORS png should draw"); + + pixel = getPixel(imgData, 5, 15); + assert_array_equals(pixel, [0, 255, 0, 255], "CORS svg should draw"); + }); + }; + </script> +</body> +</html>
diff --git a/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/privacy/html-images-ignored.https.sub.html b/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/privacy/html-images-ignored.https.sub.html index 8a6037c..9fa61c6 100644 --- a/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/privacy/html-images-ignored.https.sub.html +++ b/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/privacy/html-images-ignored.https.sub.html
@@ -28,7 +28,7 @@ <canvas id=canvas width="100" height="200" layoutsubtree> <div id=child> <img id=sameOrigin width=100 height=100 src="https://{{location[host]}}/wpt_internal/html/canvas/drawElementImage/resources/green-100x100.png"/> - <img id=crossOrigin width=100 height=100 src="https://{{hosts[alt][www]}}:{{ports[h2][0]}}/wpt_internal/html/canvas/drawElementImage/resources/red-100x100.png"/> + <img id=crossOrigin width=100 height=100 src="https://{{hosts[alt][www]}}:{{ports[https][0]}}/wpt_internal/html/canvas/drawElementImage/resources/red-100x100.png"/> </div> </canvas> <script>
diff --git a/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/privacy/iframes-ignored.https.sub.html b/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/privacy/iframes-ignored.https.sub.html index 9822eb5..0c149f8 100644 --- a/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/privacy/iframes-ignored.https.sub.html +++ b/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/privacy/iframes-ignored.https.sub.html
@@ -28,7 +28,7 @@ <canvas id=canvas width="100" height="200" layoutsubtree> <div id=child> <iframe id=sameOrigin width=100 height=100 src="https://{{location[host]}}/wpt_internal/html/canvas/drawElementImage/resources/iframe-subframe-green.html"></iframe> - <iframe id=crossOrigin width=100 height=100 src="https://{{hosts[alt][www]}}:{{ports[h2][0]}}/wpt_internal/html/canvas/drawElementImage/resources/iframe-subframe-red.html"></iframe> + <iframe id=crossOrigin width=100 height=100 src="https://{{hosts[alt][www]}}:{{ports[https][0]}}/wpt_internal/html/canvas/drawElementImage/resources/iframe-subframe-red.html"></iframe> </div> </canvas>
diff --git a/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/privacy/nested-tainted-canvas-ignored.https.sub.html b/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/privacy/nested-tainted-canvas-ignored.https.sub.html index 6cbb6ff..094eecb 100644 --- a/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/privacy/nested-tainted-canvas-ignored.https.sub.html +++ b/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/privacy/nested-tainted-canvas-ignored.https.sub.html
@@ -43,7 +43,7 @@ }); const sameOriginImg = await preloadImage("https://{{location[host]}}/wpt_internal/html/canvas/drawElementImage/resources/green-100x100.png"); - const crossOriginImg = await preloadImage("https://{{hosts[alt][www]}}:{{ports[h2][0]}}/wpt_internal/html/canvas/drawElementImage/resources/red-100x100.png"); + const crossOriginImg = await preloadImage("https://{{hosts[alt][www]}}:{{ports[https][0]}}/wpt_internal/html/canvas/drawElementImage/resources/red-100x100.png"); const innerSame = document.getElementById('inner-same'); const innerCross = document.getElementById('inner-cross');
diff --git a/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/privacy/object-image-ignored.https.sub.html b/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/privacy/object-image-ignored.https.sub.html index 13062ea..c0167a83 100644 --- a/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/privacy/object-image-ignored.https.sub.html +++ b/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/privacy/object-image-ignored.https.sub.html
@@ -30,7 +30,7 @@ <object id=sameOrigin width=100 height=100 type="image/png" data="https://{{location[host]}}/wpt_internal/html/canvas/drawElementImage/resources/green-100x100.png"/> <object id=crossOrigin width=100 height=100 type="image/png" - data="https://{{hosts[alt][www]}}:{{ports[h2][0]}}/wpt_internal/html/canvas/drawElementImage/resources/red-100x100.png"/> + data="https://{{hosts[alt][www]}}:{{ports[https][0]}}/wpt_internal/html/canvas/drawElementImage/resources/red-100x100.png"/> </div> </canvas> <script>
diff --git a/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/privacy/picture-images-ignored.https.sub.html b/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/privacy/picture-images-ignored.https.sub.html index 59681c4..681f136 100644 --- a/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/privacy/picture-images-ignored.https.sub.html +++ b/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/privacy/picture-images-ignored.https.sub.html
@@ -34,7 +34,7 @@ </picture> <picture id=crossOrigin width=100 height=100> <source width=100 height=100 - srcset="https://{{hosts[alt][www]}}:{{ports[h2][0]}}/wpt_internal/html/canvas/drawElementImage/resources/red-100x100.png" /> + srcset="https://{{hosts[alt][www]}}:{{ports[https][0]}}/wpt_internal/html/canvas/drawElementImage/resources/red-100x100.png" /> <img width=100 height=100 /> </picture> </div>
diff --git a/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/privacy/resources/green-100x100.png b/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/privacy/resources/green-100x100.png new file mode 100644 index 0000000..d65838b7 --- /dev/null +++ b/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/privacy/resources/green-100x100.png Binary files differ
diff --git a/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/privacy/resources/green-100x100.svg b/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/privacy/resources/green-100x100.svg new file mode 100644 index 0000000..87fd691 --- /dev/null +++ b/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/privacy/resources/green-100x100.svg
@@ -0,0 +1,4 @@ +<?xml version="1.0"?> +<svg xmlns="http://www.w3.org/2000/svg"> + <rect x="0" y="0" width="100%" height="100%" fill="lime" fill-opacity="1"/> +</svg>
diff --git a/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/privacy/scrollbar-thumb-images-ignored.https.sub.html b/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/privacy/scrollbar-thumb-images-ignored.https.sub.html index 2770aa7..8317e29 100644 --- a/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/privacy/scrollbar-thumb-images-ignored.https.sub.html +++ b/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/privacy/scrollbar-thumb-images-ignored.https.sub.html
@@ -45,7 +45,7 @@ overflow-y: scroll; } #crossOrigin::-webkit-scrollbar-thumb { - background-image: url("https://{{hosts[alt][www]}}:{{ports[h2][0]}}/wpt_internal/html/canvas/drawElementImage/resources/red-100x100.png"); + background-image: url("https://{{hosts[alt][www]}}:{{ports[https][0]}}/wpt_internal/html/canvas/drawElementImage/resources/red-100x100.png"); min-height: 100px; } .scroll-content {
diff --git a/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/privacy/shape-image-png-ignored.https.sub.html b/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/privacy/shape-image-png-ignored.https.sub.html index cca28e5..3f58791 100644 --- a/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/privacy/shape-image-png-ignored.https.sub.html +++ b/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/privacy/shape-image-png-ignored.https.sub.html
@@ -28,7 +28,7 @@ #crossOrigin { float: left; height: 100px; - shape-outside: url("https://{{hosts[alt][www]}}:{{ports[h2][0]}}/wpt_internal/html/canvas/drawElementImage/resources/left-half-rectangle-50.png"); + shape-outside: url("https://{{hosts[alt][www]}}:{{ports[https][0]}}/wpt_internal/html/canvas/drawElementImage/resources/left-half-rectangle-50.png"); shape-image-threshold: 0.6; } img {
diff --git a/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/privacy/shape-image-svg-ignored.https.sub.html b/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/privacy/shape-image-svg-ignored.https.sub.html index 06f14560..cd7621d 100644 --- a/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/privacy/shape-image-svg-ignored.https.sub.html +++ b/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/privacy/shape-image-svg-ignored.https.sub.html
@@ -30,7 +30,7 @@ float: left; height: 100px; background-color: green; - shape-outside: url("https://{{hosts[alt][www]}}:{{ports[h2][0]}}/wpt_internal/html/canvas/drawElementImage/resources/left-half-rectangle-50.svg"); + shape-outside: url("https://{{hosts[alt][www]}}:{{ports[https][0]}}/wpt_internal/html/canvas/drawElementImage/resources/left-half-rectangle-50.svg"); shape-image-threshold: 0.6; } img {
diff --git a/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/privacy/subframe-cross-origin-background-image.https.sub.html b/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/privacy/subframe-cross-origin-background-image.https.sub.html index 7b91ca74..0749e57 100644 --- a/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/privacy/subframe-cross-origin-background-image.https.sub.html +++ b/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/privacy/subframe-cross-origin-background-image.https.sub.html
@@ -9,7 +9,7 @@ <script src='/resources/testharnessreport.js'></script> </head> <body> - <iframe id=crossOrigin width=100 height=100 src="https://{{hosts[alt][www]}}:{{ports[h2][0]}}/wpt_internal/html/canvas/drawElementImage/privacy/support/subframe-with-canvas-background-png-images.https.sub.html"></iframe> + <iframe id=crossOrigin width=100 height=100 src="https://{{hosts[alt][www]}}:{{ports[https][0]}}/wpt_internal/html/canvas/drawElementImage/privacy/support/subframe-with-canvas-background-png-images.https.sub.html"></iframe> <script> fetch_tests_from_window(crossOrigin.contentWindow);
diff --git a/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/privacy/subframe-cross-origin-frame.https.sub.html b/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/privacy/subframe-cross-origin-frame.https.sub.html index 156d5174..171dd1e 100644 --- a/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/privacy/subframe-cross-origin-frame.https.sub.html +++ b/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/privacy/subframe-cross-origin-frame.https.sub.html
@@ -9,7 +9,7 @@ <script src='/resources/testharnessreport.js'></script> </head> <body> - <iframe id=crossOrigin width=100 height=200 src="https://{{hosts[alt][www]}}:{{ports[h2][0]}}/wpt_internal/html/canvas/drawElementImage/privacy/support/subframe-with-canvas-iframe.https.sub.html"></iframe> + <iframe id=crossOrigin width=100 height=200 src="https://{{hosts[alt][www]}}:{{ports[https][0]}}/wpt_internal/html/canvas/drawElementImage/privacy/support/subframe-with-canvas-iframe.https.sub.html"></iframe> <script> fetch_tests_from_window(crossOrigin.contentWindow);
diff --git a/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/privacy/subframe-cross-origin-html-images.https.sub.html b/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/privacy/subframe-cross-origin-html-images.https.sub.html index d518474..2835577 100644 --- a/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/privacy/subframe-cross-origin-html-images.https.sub.html +++ b/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/privacy/subframe-cross-origin-html-images.https.sub.html
@@ -9,7 +9,7 @@ <script src='/resources/testharnessreport.js'></script> </head> <body> - <iframe id=crossOrigin width=100 height=100 src="https://{{hosts[alt][www]}}:{{ports[h2][0]}}/wpt_internal/html/canvas/drawElementImage/privacy/support/subframe-with-canvas-html-images.https.sub.html"></iframe> + <iframe id=crossOrigin width=100 height=100 src="https://{{hosts[alt][www]}}:{{ports[https][0]}}/wpt_internal/html/canvas/drawElementImage/privacy/support/subframe-with-canvas-html-images.https.sub.html"></iframe> <script> fetch_tests_from_window(crossOrigin.contentWindow);
diff --git a/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/privacy/support/subframe-with-canvas-background-png-images.https.sub.html b/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/privacy/support/subframe-with-canvas-background-png-images.https.sub.html index 485bb71..687b67ce 100644 --- a/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/privacy/support/subframe-with-canvas-background-png-images.https.sub.html +++ b/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/privacy/support/subframe-with-canvas-background-png-images.https.sub.html
@@ -18,7 +18,7 @@ top: 0px; width: 10px; height: 10px; - background-image: url("https://{{hosts[alt][www]}}:{{ports[h2][0]}}/wpt_internal/html/canvas/drawElementImage/resources/green-100x100.png"); + background-image: url("https://{{hosts[alt][www]}}:{{ports[https][0]}}/wpt_internal/html/canvas/drawElementImage/resources/green-100x100.png"); } #crossOrigin { position: absolute; @@ -26,7 +26,7 @@ top: 10px; width: 10px; height: 10px; - background-image: url("https://{{location[host]}}/wpt_internal/html/canvas/drawElementImage/resources/red-100x100.png"); + background-image: url("https://{{host}}:{{ports[https][0]}}/wpt_internal/html/canvas/drawElementImage/resources/red-100x100.png"); } </style> </head> @@ -44,10 +44,10 @@ // Wait for both background-images to load. const img1 = new Image(); const loadPromise1 = new Promise(resolve => img1.onload = resolve); - img1.src = "https://{{hosts[alt][www]}}:{{ports[h2][0]}}/wpt_internal/html/canvas/drawElementImage/resources/green-100x100.png"; + img1.src = "https://{{hosts[alt][www]}}:{{ports[https][0]}}/wpt_internal/html/canvas/drawElementImage/resources/green-100x100.png"; const img2 = new Image(); const loadPromise2 = new Promise(resolve => img2.onload = resolve); - img2.src = "https://{{location[host]}}/wpt_internal/html/canvas/drawElementImage/resources/red-100x100.png"; + img2.src = "https://{{host}}:{{ports[https][0]}}/wpt_internal/html/canvas/drawElementImage/resources/red-100x100.png"; await Promise.all([loadPromise1, loadPromise2]); // Wait for the window to have a non-zero size.
diff --git a/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/privacy/support/subframe-with-canvas-html-images.https.sub.html b/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/privacy/support/subframe-with-canvas-html-images.https.sub.html index 52a1a79..0f7e997 100644 --- a/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/privacy/support/subframe-with-canvas-html-images.https.sub.html +++ b/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/privacy/support/subframe-with-canvas-html-images.https.sub.html
@@ -27,8 +27,8 @@ <body> <canvas id=canvas width="10" height="20" layoutsubtree> <div id=child> - <img id=sameOrigin width=10 height=10 src="https://{{hosts[alt][www]}}:{{ports[h2][0]}}/wpt_internal/html/canvas/drawElementImage/resources/green-100x100.png"/> - <img id=crossOrigin width=10 height=10 src="https://{{location[host]}}/wpt_internal/html/canvas/drawElementImage/resources/red-100x100.png"/> + <img id=sameOrigin width=10 height=10 src="https://{{hosts[alt][www]}}:{{ports[https][0]}}/wpt_internal/html/canvas/drawElementImage/resources/green-100x100.png"/> + <img id=crossOrigin width=10 height=10 src="https://{{host}}:{{ports[https][0]}}/wpt_internal/html/canvas/drawElementImage/resources/red-100x100.png"/> </div> </canvas> <script>
diff --git a/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/privacy/support/subframe-with-canvas-iframe.https.sub.html b/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/privacy/support/subframe-with-canvas-iframe.https.sub.html index e80a49b..37c5923 100644 --- a/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/privacy/support/subframe-with-canvas-iframe.https.sub.html +++ b/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/privacy/support/subframe-with-canvas-iframe.https.sub.html
@@ -29,10 +29,10 @@ <div id=child> <!-- This frame is loading from the same origin as the main frame, which is not the origin of this frame. --> - <iframe id=crossOrigin width=10 height=10 src="https://{{location[host]}}/wpt_internal/html/canvas/drawElementImage/resources/iframe-subframe-red.html"></iframe> + <iframe id=crossOrigin width=10 height=10 src="https://{{host}}:{{ports[https][0]}}/wpt_internal/html/canvas/drawElementImage/resources/iframe-subframe-red.html"></iframe> <!-- This frame is loading from the same origin as this frame, which is not the origin of the main frame. --> - <iframe id=sameOrigin width=10 height=10 src="https://{{hosts[alt][www]}}:{{ports[h2][0]}}/wpt_internal/html/canvas/drawElementImage/resources/iframe-subframe-green.html"></iframe> + <iframe id=sameOrigin width=10 height=10 src="https://{{hosts[alt][www]}}:{{ports[https][0]}}/wpt_internal/html/canvas/drawElementImage/resources/iframe-subframe-green.html"></iframe> </div> </canvas>
diff --git a/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/privacy/svg-images-ignored.https.sub.html b/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/privacy/svg-images-ignored.https.sub.html index 1fa0e20..15a626d 100644 --- a/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/privacy/svg-images-ignored.https.sub.html +++ b/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/privacy/svg-images-ignored.https.sub.html
@@ -12,7 +12,7 @@ <svg id=child width=100 height=200> <rect fill="green" x=0 y=0 width=100 height=200 /> <image id="sameOrigin" href="https://{{location[host]}}/wpt_internal/html/canvas/drawElementImage/resources/red-100x100.png" x=10 y=10 height=100 width=100 /> - <image id="crossOrigin" href="https://{{hosts[alt][www]}}:{{ports[h2][0]}}/wpt_internal/html/canvas/drawElementImage/resources/red-100x100.png" x=0 y=100 height=100 width=100 /> + <image id="crossOrigin" href="https://{{hosts[alt][www]}}:{{ports[https][0]}}/wpt_internal/html/canvas/drawElementImage/resources/red-100x100.png" x=0 y=100 height=100 width=100 /> </svg> </canvas>
diff --git a/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/privacy/svg-pattern-cross-origin-image.https.sub.html b/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/privacy/svg-pattern-cross-origin-image.https.sub.html index 012c017..d7086ff1 100644 --- a/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/privacy/svg-pattern-cross-origin-image.https.sub.html +++ b/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/privacy/svg-pattern-cross-origin-image.https.sub.html
@@ -16,7 +16,7 @@ <image href="https://{{location[host]}}/wpt_internal/html/canvas/drawElementImage/resources/green-100x100.png" x="0" y="0" width="100" height="50" preserveAspectRatio="none"/> </pattern> <pattern id="pattern-cross" patternUnits="userSpaceOnUse" x="0" y="0" width="100" height="50"> - <image href="https://{{hosts[alt][www]}}:{{ports[h2][0]}}/wpt_internal/html/canvas/drawElementImage/resources/red-100x100.png" x="0" y="0" width="100" height="50" preserveAspectRatio="none"/> + <image href="https://{{hosts[alt][www]}}:{{ports[https][0]}}/wpt_internal/html/canvas/drawElementImage/resources/red-100x100.png" x="0" y="0" width="100" height="50" preserveAspectRatio="none"/> </pattern> </defs> <rect x="0" y="0" width="100" height="50" fill="url(#pattern-same)"/>
diff --git a/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/privacy/svg-use-ignored.https.sub.html b/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/privacy/svg-use-ignored.https.sub.html index cbfee060..e2a0149d 100644 --- a/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/privacy/svg-use-ignored.https.sub.html +++ b/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/privacy/svg-use-ignored.https.sub.html
@@ -15,7 +15,7 @@ <use x=0 y=0 href="https://{{location[host]}}/wpt_internal/html/canvas/drawElementImage/resources/green-100x100.svg" /> </svg> <svg id="child-cross" x=0 y=0 width=100 height=100> - <use x=0 y=0 href="https://{{hosts[alt][www]}}:{{ports[h2][0]}}/wpt_internal/html/canvas/drawElementImage/resources/red-100x100.svg" /> + <use x=0 y=0 href="https://{{hosts[alt][www]}}:{{ports[https][0]}}/wpt_internal/html/canvas/drawElementImage/resources/red-100x100.svg" /> </svg> </canvas>
diff --git a/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/privacy/video-ignored.https.sub.html b/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/privacy/video-ignored.https.sub.html index 958ee15..5c1940c 100644 --- a/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/privacy/video-ignored.https.sub.html +++ b/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/privacy/video-ignored.https.sub.html
@@ -34,8 +34,8 @@ <video id=sameOrigin poster="https://{{location[host]}}/wpt_internal/html/canvas/drawElementImage/resources/green-100x100.png" > <source src="https://{{location[host]}}/wpt_internal/html/canvas/drawElementImage/resources/white.webm" type="video/webm" /> </video> - <video id=crossOrigin poster="https://{{hosts[alt][www]}}:{{ports[h2][0]}}/wpt_internal/html/canvas/drawElementImage/resources/red-100x100.png" > - <source src="https://{{hosts[alt][www]}}:{{ports[h2][0]}}/wpt_internal/html/canvas/drawElementImage/resources/white.webm" type="video/webm"/> + <video id=crossOrigin poster="https://{{hosts[alt][www]}}:{{ports[https][0]}}/wpt_internal/html/canvas/drawElementImage/resources/red-100x100.png" > + <source src="https://{{hosts[alt][www]}}:{{ports[https][0]}}/wpt_internal/html/canvas/drawElementImage/resources/white.webm" type="video/webm"/> </video> </div> </canvas>
diff --git a/third_party/boringssl/README.chromium b/third_party/boringssl/README.chromium index b324f0903..c7151f0 100644 --- a/third_party/boringssl/README.chromium +++ b/third_party/boringssl/README.chromium
@@ -1,7 +1,7 @@ Name: BoringSSL URL: https://boringssl.googlesource.com/boringssl Version: N/A -Revision: 0e71ca41ee051e666a507033ecf30460fda01583 +Revision: d03b45948ec9af4ec01f3a9b5adc381c01966659 Update Mechanism: Autoroll License: Apache-2.0, BSD-3-Clause License File: src/LICENSE
diff --git a/third_party/boringssl/src b/third_party/boringssl/src index 0e71ca4..d03b459 160000 --- a/third_party/boringssl/src +++ b/third_party/boringssl/src
@@ -1 +1 @@ -Subproject commit 0e71ca41ee051e666a507033ecf30460fda01583 +Subproject commit d03b45948ec9af4ec01f3a9b5adc381c01966659
diff --git a/third_party/cardboard/BUILD.gn b/third_party/cardboard/BUILD.gn index dce4e96..fe10c4ac 100644 --- a/third_party/cardboard/BUILD.gn +++ b/third_party/cardboard/BUILD.gn
@@ -15,12 +15,12 @@ android_resources("cardboard_resources") { sources = [ - "src/sdk/qrcode/android/res/drawable-xxhdpi/qr_sample.png", - "src/sdk/qrcode/android/res/drawable-xxhdpi/tick_marks.png", - "src/sdk/qrcode/android/res/layout/qr_code_capture.xml", - "src/sdk/qrcode/android/res/values/colors.xml", - "src/sdk/qrcode/android/res/values/strings.xml", - "src/sdk/qrcode/android/res/values/styles.xml", + "src_overrides/sdk/qrcode/android/res/drawable-xxhdpi/qr_sample.png", + "src_overrides/sdk/qrcode/android/res/drawable-xxhdpi/tick_marks.png", + "src_overrides/sdk/qrcode/android/res/layout/qr_code_capture.xml", + "src_overrides/sdk/qrcode/android/res/values/colors.xml", + "src_overrides/sdk/qrcode/android/res/values/strings.xml", + "src_overrides/sdk/qrcode/android/res/values/styles.xml", ] custom_package = "com.google.cardboard.sdk" @@ -32,7 +32,6 @@ "src/sdk/device_params/android/java/com/google/cardboard/sdk/deviceparams/DeviceParamsUtils.java", "src/sdk/java_utils/android/java/com/google/cardboard/sdk/UsedByNative.java", "src/sdk/qrcode/android/java/com/google/cardboard/sdk/HeadsetDetectionActivity.java", - "src/sdk/qrcode/android/java/com/google/cardboard/sdk/QrCodeCaptureActivity.java", "src/sdk/qrcode/android/java/com/google/cardboard/sdk/qrcode/AsyncTask.java", "src/sdk/qrcode/android/java/com/google/cardboard/sdk/qrcode/CardboardParamsUtils.java", "src/sdk/qrcode/android/java/com/google/cardboard/sdk/qrcode/InputStreamProvider.java", @@ -44,6 +43,7 @@ "src/sdk/qrcode/android/java/com/google/cardboard/sdk/qrcode/camera/CameraSource.java", "src/sdk/qrcode/android/java/com/google/cardboard/sdk/qrcode/camera/CameraSourcePreview.java", "src/sdk/screen_params/android/java/com/google/cardboard/sdk/screenparams/ScreenParamsUtils.java", + "src_overrides/sdk/qrcode/android/java/com/google/cardboard/sdk/QrCodeCaptureActivity.java", ] resources_package = "com.google.cardboard.sdk" @@ -79,7 +79,6 @@ "src/sdk/head_tracker.cc", "src/sdk/head_tracker.h", "src/sdk/include/cardboard.h", - "src/sdk/jni_utils/android/jni_utils.cc", "src/sdk/jni_utils/android/jni_utils.h", "src/sdk/lens_distortion.cc", "src/sdk/lens_distortion.h", @@ -134,6 +133,7 @@ "src/sdk/util/vector.h", "src/sdk/util/vectorutils.cc", "src/sdk/util/vectorutils.h", + "src_overrides/sdk/jni_utils/android/jni_utils.cc", ] deps = [
diff --git a/third_party/cardboard/README.chromium b/third_party/cardboard/README.chromium index 4928261..eaf7cbd3 100644 --- a/third_party/cardboard/README.chromium +++ b/third_party/cardboard/README.chromium
@@ -16,4 +16,7 @@ Local Modifications: * Created local top-level BUILD.gn based on src/sdk/build.gradle * Created local proguard-rules.pro to create more scoped proto-specific rules +* Created local override for LoadJClass in jni_utils to leaverage //base JNI loading due to splits. * Created local configuration header file with the OpenGL ES 2.0 binding customization required by setting the CARDBOARD_USE_CUSTOM_GL_BINDINGS buildflag. +* Created local override of QrCodeCaptureActivity in preperation for edge-to-edge rendering + This should be removed once the Cardboard SDK is upgraded to version v1.29.0
diff --git a/third_party/cardboard/src_overrides/DEPS b/third_party/cardboard/src_overrides/DEPS new file mode 100644 index 0000000..a2357e8e --- /dev/null +++ b/third_party/cardboard/src_overrides/DEPS
@@ -0,0 +1,5 @@ +include_rules = [ + "+base/android/jni_android.h", + "+third_party/jni_zero/jni_zero.h", + '+third_party/jni_zero/jni_zero_helper.h', +]
diff --git a/third_party/cardboard/src_overrides/README.md b/third_party/cardboard/src_overrides/README.md new file mode 100644 index 0000000..b3a1babb --- /dev/null +++ b/third_party/cardboard/src_overrides/README.md
@@ -0,0 +1,18 @@ +# Cardboard src_overrides + +This folder is intended to contain forked files of changes that we have made to +cardboard files. The goal should be to eventually upstream these changes. + +`patches/` contains .patch files generated via `git format-patch` and should be +generated based off of commits that *only* have changes to the `src_overrides` +file and should be generated *after* the file is forked and formatted. +Sequential ordering should be maintained (renaming the leading number if +needed). Patches and files should be removed when they no longer need to be +forked due to consuming an upstreamed version of the change. After a roll, the +new version of the files can be copied into `src_overrides`, `git cl format` +run and the patches can be applied via `git am`. If there are merge conflicts, +patches should be re-exported, with care taken to *try* to maintain only one +upstreamable change per patch. + +Note: When overriding a "res" folder, all files must be copied, even the ones +that need no patching.
diff --git a/third_party/cardboard/src_overrides/patches/0001-Adjust-Cardboard-JNI-Util-for-Chrome-build-config.patch b/third_party/cardboard/src_overrides/patches/0001-Adjust-Cardboard-JNI-Util-for-Chrome-build-config.patch new file mode 100644 index 0000000..977c17db --- /dev/null +++ b/third_party/cardboard/src_overrides/patches/0001-Adjust-Cardboard-JNI-Util-for-Chrome-build-config.patch
@@ -0,0 +1,41 @@ +From 3af7b78665624fbf819c87aed65d87dc22921b6c Mon Sep 17 00:00:00 2001 +From: Alexander Cooper <alcooper@chromium.org> +Date: Thu, 27 Feb 2025 12:59:57 -0800 +Subject: [PATCH 1/2] Adjust Cardboard JNI Util for Chrome build config + +--- + .../src_overrides/sdk/jni_utils/android/jni_utils.cc | 9 +++++---- + 1 file changed, 5 insertions(+), 4 deletions(-) + +diff --git a/third_party/cardboard/src_overrides/sdk/jni_utils/android/jni_utils.cc b/third_party/cardboard/src_overrides/sdk/jni_utils/android/jni_utils.cc +index fe6c8cd45e577..8a1f29405f42a 100644 +--- a/third_party/cardboard/src_overrides/sdk/jni_utils/android/jni_utils.cc ++++ b/third_party/cardboard/src_overrides/sdk/jni_utils/android/jni_utils.cc +@@ -13,9 +13,10 @@ + * See the License for the specific language governing permissions and + * limitations under the License. + */ +-#include "jni_utils/android/jni_utils.h" ++#include "third_party/cardboard/src/sdk/jni_utils/android/jni_utils.h" + +-#include "util/logging.h" ++#include "base/android/jni_android.h" ++#include "third_party/cardboard/src/sdk/util/logging.h" + + namespace cardboard::jni { + namespace { +@@ -60,9 +61,9 @@ void LoadJNIEnv(JavaVM* vm, JNIEnv** env) { + } + + jclass LoadJClass(JNIEnv* env, const char* class_name) { +- jclass local = env->FindClass(class_name); ++ auto local_ref = base::android::GetClass(env, class_name); + CheckExceptionInJava(env); +- return static_cast<jclass>(env->NewGlobalRef(local)); ++ return static_cast<jclass>(env->NewGlobalRef(local_ref.obj())); + } + + void ThrowJavaRuntimeException(JNIEnv* env, const char* msg) { +-- +2.48.1.711.g2feabab25a-goog +
diff --git a/third_party/cardboard/src_overrides/patches/0002-Adapt-QRCode-activity-to-edge-to-edge.patch b/third_party/cardboard/src_overrides/patches/0002-Adapt-QRCode-activity-to-edge-to-edge.patch new file mode 100644 index 0000000..195eb5e --- /dev/null +++ b/third_party/cardboard/src_overrides/patches/0002-Adapt-QRCode-activity-to-edge-to-edge.patch
@@ -0,0 +1,67 @@ +From 9a23096f9364d9a423323238e3f4548c4d3b6d68 Mon Sep 17 00:00:00 2001 +From: Alexander Cooper <alcooper@chromium.org> +Date: Thu, 27 Feb 2025 13:01:46 -0800 +Subject: [PATCH 2/2] Adapt QRCode activity to edge-to-edge + +--- + .../cardboard/sdk/QrCodeCaptureActivity.java | 20 +++++++++++++++++++ + .../android/res/layout/qr_code_capture.xml | 1 + + 2 files changed, 21 insertions(+) + +diff --git a/third_party/cardboard/src_overrides/sdk/qrcode/android/java/com/google/cardboard/sdk/QrCodeCaptureActivity.java b/third_party/cardboard/src_overrides/sdk/qrcode/android/java/com/google/cardboard/sdk/QrCodeCaptureActivity.java +index 0e51134060e28..e461e72eec5d1 100644 +--- a/third_party/cardboard/src_overrides/sdk/qrcode/android/java/com/google/cardboard/sdk/QrCodeCaptureActivity.java ++++ b/third_party/cardboard/src_overrides/sdk/qrcode/android/java/com/google/cardboard/sdk/QrCodeCaptureActivity.java +@@ -27,11 +27,15 @@ import android.os.Bundle; + import android.provider.Settings; + import android.util.Log; + import android.view.View; ++import android.view.ViewGroup; + import android.widget.Toast; + + import androidx.annotation.NonNull; + import androidx.appcompat.app.AppCompatActivity; + import androidx.core.app.ActivityCompat; ++import androidx.core.graphics.Insets; ++import androidx.core.view.ViewCompat; ++import androidx.core.view.WindowInsetsCompat; + + import com.google.android.gms.common.ConnectionResult; + import com.google.android.gms.common.GoogleApiAvailability; +@@ -76,6 +80,22 @@ public class QrCodeCaptureActivity extends AppCompatActivity + super.onCreate(icicle); + setContentView(R.layout.qr_code_capture); + ++ // Adds margins to the container to account for edge to edge: ++ // https://developer.android.com/develop/ui/views/layout/edge-to-edge ++ View container = findViewById(R.id.container); ++ ViewCompat.setOnApplyWindowInsetsListener( ++ container, ++ (v, windowInsets) -> { ++ Insets insets = windowInsets.getInsets(WindowInsetsCompat.Type.systemBars()); ++ ViewGroup.MarginLayoutParams mlp = ++ (ViewGroup.MarginLayoutParams) v.getLayoutParams(); ++ mlp.leftMargin = insets.left; ++ mlp.bottomMargin = insets.bottom; ++ mlp.rightMargin = insets.right; ++ v.setLayoutParams(mlp); ++ return WindowInsetsCompat.CONSUMED; ++ }); ++ + cameraSourcePreview = findViewById(R.id.preview); + } + +diff --git a/third_party/cardboard/src_overrides/sdk/qrcode/android/res/layout/qr_code_capture.xml b/third_party/cardboard/src_overrides/sdk/qrcode/android/res/layout/qr_code_capture.xml +index 8aac5fc960446..0fbe9dac77af1 100644 +--- a/third_party/cardboard/src_overrides/sdk/qrcode/android/res/layout/qr_code_capture.xml ++++ b/third_party/cardboard/src_overrides/sdk/qrcode/android/res/layout/qr_code_capture.xml +@@ -1,5 +1,6 @@ + <?xml version="1.0" encoding="utf-8"?> + <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" ++ android:id="@+id/container" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:orientation="vertical"> +-- +2.48.1.711.g2feabab25a-goog +
diff --git a/third_party/cardboard/src_overrides/sdk/jni_utils/android/jni_utils.cc b/third_party/cardboard/src_overrides/sdk/jni_utils/android/jni_utils.cc new file mode 100644 index 0000000..8a1f294 --- /dev/null +++ b/third_party/cardboard/src_overrides/sdk/jni_utils/android/jni_utils.cc
@@ -0,0 +1,74 @@ +/* + * Copyright 2020 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "third_party/cardboard/src/sdk/jni_utils/android/jni_utils.h" + +#include "base/android/jni_android.h" +#include "third_party/cardboard/src/sdk/util/logging.h" + +namespace cardboard::jni { +namespace { + +jclass runtime_excepton_class_; + +void LoadJNIResources(JNIEnv* env) { + runtime_excepton_class_ = + cardboard::jni::LoadJClass(env, "java/lang/RuntimeException"); +} + +} // anonymous namespace + +void initializeAndroid(JavaVM* vm, jobject /*context*/) { + JNIEnv* env; + LoadJNIEnv(vm, &env); + LoadJNIResources(env); +} + +bool CheckExceptionInJava(JNIEnv* env) { + const bool exception_occurred = env->ExceptionOccurred(); + if (exception_occurred) { + env->ExceptionDescribe(); + env->ExceptionClear(); + } + return exception_occurred; +} + +void LoadJNIEnv(JavaVM* vm, JNIEnv** env) { + switch (vm->GetEnv(reinterpret_cast<void**>(env), JNI_VERSION_1_6)) { + case JNI_OK: + break; + case JNI_EDETACHED: + if (vm->AttachCurrentThread(env, nullptr) != 0) { + *env = nullptr; + } + break; + default: + *env = nullptr; + break; + } +} + +jclass LoadJClass(JNIEnv* env, const char* class_name) { + auto local_ref = base::android::GetClass(env, class_name); + CheckExceptionInJava(env); + return static_cast<jclass>(env->NewGlobalRef(local_ref.obj())); +} + +void ThrowJavaRuntimeException(JNIEnv* env, const char* msg) { + CARDBOARD_LOGE("Throw Java RuntimeException: %s", msg); + env->ThrowNew(runtime_excepton_class_, msg); +} + +} // namespace cardboard::jni
diff --git a/third_party/cardboard/src_overrides/sdk/qrcode/android/java/com/google/cardboard/sdk/QrCodeCaptureActivity.java b/third_party/cardboard/src_overrides/sdk/qrcode/android/java/com/google/cardboard/sdk/QrCodeCaptureActivity.java new file mode 100644 index 0000000..e461e72 --- /dev/null +++ b/third_party/cardboard/src_overrides/sdk/qrcode/android/java/com/google/cardboard/sdk/QrCodeCaptureActivity.java
@@ -0,0 +1,307 @@ +/* + * Copyright 2019 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.google.cardboard.sdk; + +import android.Manifest; +import android.app.Dialog; +import android.content.Context; +import android.content.Intent; +import android.content.pm.PackageManager; +import android.net.Uri; +import android.os.Build.VERSION; +import android.os.Build.VERSION_CODES; +import android.os.Bundle; +import android.provider.Settings; +import android.util.Log; +import android.view.View; +import android.view.ViewGroup; +import android.widget.Toast; + +import androidx.annotation.NonNull; +import androidx.appcompat.app.AppCompatActivity; +import androidx.core.app.ActivityCompat; +import androidx.core.graphics.Insets; +import androidx.core.view.ViewCompat; +import androidx.core.view.WindowInsetsCompat; + +import com.google.android.gms.common.ConnectionResult; +import com.google.android.gms.common.GoogleApiAvailability; +import com.google.android.gms.vision.MultiProcessor; +import com.google.android.gms.vision.barcode.Barcode; +import com.google.android.gms.vision.barcode.BarcodeDetector; +import com.google.cardboard.sdk.qrcode.CardboardParamsUtils; +import com.google.cardboard.sdk.qrcode.QrCodeContentProcessor; +import com.google.cardboard.sdk.qrcode.QrCodeTracker; +import com.google.cardboard.sdk.qrcode.QrCodeTrackerFactory; +import com.google.cardboard.sdk.qrcode.camera.CameraSource; +import com.google.cardboard.sdk.qrcode.camera.CameraSourcePreview; + +import java.io.IOException; + +/** + * Manages the QR code capture activity. It scans permanently with the camera until it finds a valid + * QR code. + */ +public class QrCodeCaptureActivity extends AppCompatActivity + implements QrCodeTracker.Listener, QrCodeContentProcessor.Listener { + private static final String TAG = QrCodeCaptureActivity.class.getSimpleName(); + + // Intent request code to handle updating play services if needed. + private static final int RC_HANDLE_GMS = 9001; + + // Permission request codes + private static final int PERMISSIONS_REQUEST_CODE = 2; + + // Min sdk version required for google play services. + private static final int MIN_SDK_VERSION = 23; + + private CameraSource cameraSource; + private CameraSourcePreview cameraSourcePreview; + + // Flag used to avoid saving the device parameters more than once. + private static boolean qrCodeSaved = false; + + /** Initializes the UI and creates the detector pipeline. */ + @Override + public void onCreate(Bundle icicle) { + super.onCreate(icicle); + setContentView(R.layout.qr_code_capture); + + // Adds margins to the container to account for edge to edge: + // https://developer.android.com/develop/ui/views/layout/edge-to-edge + View container = findViewById(R.id.container); + ViewCompat.setOnApplyWindowInsetsListener( + container, + (v, windowInsets) -> { + Insets insets = windowInsets.getInsets(WindowInsetsCompat.Type.systemBars()); + ViewGroup.MarginLayoutParams mlp = + (ViewGroup.MarginLayoutParams) v.getLayoutParams(); + mlp.leftMargin = insets.left; + mlp.bottomMargin = insets.bottom; + mlp.rightMargin = insets.right; + v.setLayoutParams(mlp); + return WindowInsetsCompat.CONSUMED; + }); + + cameraSourcePreview = findViewById(R.id.preview); + } + + /** + * Checks for CAMERA permission. + * + * @return whether CAMERA permission is already granted. + */ + private boolean isCameraEnabled() { + return ActivityCompat.checkSelfPermission(this, Manifest.permission.CAMERA) + == PackageManager.PERMISSION_GRANTED; + } + + /** + * Checks for WRITE_EXTERNAL_STORAGE permission. + * + * @return whether WRITE_EXTERNAL_STORAGE permission is already granted. + */ + private boolean isWriteExternalStoragePermissionsEnabled() { + return ActivityCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE) + == PackageManager.PERMISSION_GRANTED; + } + + /** Handles the requests for activity permissions. */ + private void requestPermissions() { + final String[] permissions = + VERSION.SDK_INT < VERSION_CODES.Q + ? new String[] { + Manifest.permission.CAMERA, Manifest.permission.WRITE_EXTERNAL_STORAGE + } + : new String[] {Manifest.permission.CAMERA}; + ActivityCompat.requestPermissions(this, permissions, PERMISSIONS_REQUEST_CODE); + } + + /** + * Callback for the result from requesting permissions. + * + * <p>When Android SDK version is less than Q, both WRITE_EXTERNAL_STORAGE and CAMERA + * permissions are requested. Otherwise, only CAMERA permission is requested. + */ + @Override + public void onRequestPermissionsResult( + int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { + super.onRequestPermissionsResult(requestCode, permissions, grantResults); + if (VERSION.SDK_INT < VERSION_CODES.Q) { + if (!(isCameraEnabled() && isWriteExternalStoragePermissionsEnabled())) { + Log.i(TAG, getString(R.string.no_permissions)); + Toast.makeText(this, R.string.no_permissions, Toast.LENGTH_LONG).show(); + if (!ActivityCompat.shouldShowRequestPermissionRationale( + this, Manifest.permission.WRITE_EXTERNAL_STORAGE) + || !ActivityCompat.shouldShowRequestPermissionRationale( + this, Manifest.permission.CAMERA)) { + // Permission denied with checking "Do not ask again". + Log.i(TAG, "Permission denied with checking \"Do not ask again\"."); + launchPermissionsSettings(); + } + finish(); + } + } else { + if (!isCameraEnabled()) { + Log.i(TAG, getString(R.string.no_camera_permission)); + Toast.makeText(this, R.string.no_camera_permission, Toast.LENGTH_LONG).show(); + if (!ActivityCompat.shouldShowRequestPermissionRationale( + this, Manifest.permission.CAMERA)) { + // Permission denied with checking "Do not ask again". Note that in Android R + // "Do not ask + // again" is not available anymore. + Log.i(TAG, "Permission denied with checking \"Do not ask again\"."); + launchPermissionsSettings(); + } + finish(); + } + } + } + + private void launchPermissionsSettings() { + Intent intent = new Intent(); + intent.setAction(Settings.ACTION_APPLICATION_DETAILS_SETTINGS); + intent.setData(Uri.fromParts("package", getPackageName(), null)); + startActivity(intent); + } + + /** Creates and starts the camera. */ + private void createCameraSource() { + Context context = getApplicationContext(); + + BarcodeDetector qrCodeDetector = + new BarcodeDetector.Builder(context).setBarcodeFormats(Barcode.QR_CODE).build(); + + QrCodeTrackerFactory qrCodeFactory = new QrCodeTrackerFactory(this); + + qrCodeDetector.setProcessor(new MultiProcessor.Builder<>(qrCodeFactory).build()); + + // Check that native dependencies are downloaded. + if (!qrCodeDetector.isOperational()) { + Toast.makeText(this, R.string.missing_dependencies, Toast.LENGTH_LONG).show(); + Log.w( + TAG, + "QR Code detector is not operational. Try connecting to WiFi and updating" + + " Google Play Services or checking that the device storage isn't low."); + } + + // Creates and starts the camera. + cameraSource = new CameraSource(getApplicationContext(), qrCodeDetector); + } + + /** Restarts the camera. */ + @Override + protected void onResume() { + super.onResume(); + // Checks for CAMERA permission and WRITE_EXTERNAL_STORAGE permission when running on + // Android P + // or below. If needed permissions are not granted, requests them. + if (!(isCameraEnabled() + && (VERSION.SDK_INT >= VERSION_CODES.Q + || isWriteExternalStoragePermissionsEnabled()))) { + requestPermissions(); + return; + } + + createCameraSource(); + qrCodeSaved = false; + startCameraSource(); + } + + /** Stops the camera. */ + @Override + protected void onPause() { + super.onPause(); + if (cameraSourcePreview != null) { + cameraSourcePreview.stop(); + cameraSourcePreview.release(); + } + } + + /** Starts or restarts the camera source, if it exists. */ + private void startCameraSource() { + // Check that the device has play services available. + int code = + GoogleApiAvailability.getInstance() + .isGooglePlayServicesAvailable(getApplicationContext(), MIN_SDK_VERSION); + if (code != ConnectionResult.SUCCESS) { + Log.i(TAG, "isGooglePlayServicesAvailable() returned: " + new ConnectionResult(code)); + Dialog dlg = + GoogleApiAvailability.getInstance().getErrorDialog(this, code, RC_HANDLE_GMS); + dlg.show(); + } + + if (cameraSource != null) { + try { + cameraSourcePreview.start(cameraSource); + } catch (IOException e) { + Log.e(TAG, "Unable to start camera source.", e); + cameraSource.release(); + cameraSource = null; + } catch (SecurityException e) { + Log.e(TAG, "Security exception: ", e); + } + Log.i(TAG, "cameraSourcePreview successfully started."); + } + } + + /** Callback for when "SKIP" is touched */ + public void skipQrCodeCapture(View view) { + Log.d(TAG, "QR code capture skipped"); + + // Check if there are already saved parameters, if not save Cardboard V1 ones. + final Context context = getApplicationContext(); + byte[] deviceParams = CardboardParamsUtils.readDeviceParams(context); + if (deviceParams == null) { + CardboardParamsUtils.saveCardboardV1DeviceParams(context); + } + finish(); + } + + /** + * Callback for when a QR code is detected. + * + * @param qrCode Detected QR code. + */ + @Override + public void onQrCodeDetected(Barcode qrCode) { + if (qrCode != null && !qrCodeSaved) { + qrCodeSaved = true; + QrCodeContentProcessor qrCodeContentProcessor = new QrCodeContentProcessor(this); + qrCodeContentProcessor.processAndSaveQrCode(qrCode, this); + } + } + + /** + * Callback for when a QR code is processed and the parameters are saved in external storage. + * + * @param status Whether the parameters were successfully processed and saved. + */ + @Override + public void onQrCodeSaved(boolean status) { + if (status) { + Log.d(TAG, "Device parameters saved in external storage."); + cameraSourcePreview.stop(); + nativeIncrementDeviceParamsChangedCount(); + finish(); + } else { + Log.e(TAG, "Device parameters not saved in external storage."); + } + qrCodeSaved = false; + } + + private native void nativeIncrementDeviceParamsChangedCount(); +}
diff --git a/third_party/cardboard/src_overrides/sdk/qrcode/android/res/drawable-xxhdpi/qr_sample.png b/third_party/cardboard/src_overrides/sdk/qrcode/android/res/drawable-xxhdpi/qr_sample.png new file mode 100644 index 0000000..5a873a89 --- /dev/null +++ b/third_party/cardboard/src_overrides/sdk/qrcode/android/res/drawable-xxhdpi/qr_sample.png Binary files differ
diff --git a/third_party/cardboard/src_overrides/sdk/qrcode/android/res/drawable-xxhdpi/tick_marks.png b/third_party/cardboard/src_overrides/sdk/qrcode/android/res/drawable-xxhdpi/tick_marks.png new file mode 100644 index 0000000..9ac43518 --- /dev/null +++ b/third_party/cardboard/src_overrides/sdk/qrcode/android/res/drawable-xxhdpi/tick_marks.png Binary files differ
diff --git a/third_party/cardboard/src_overrides/sdk/qrcode/android/res/layout/qr_code_capture.xml b/third_party/cardboard/src_overrides/sdk/qrcode/android/res/layout/qr_code_capture.xml new file mode 100644 index 0000000..0fbe9dac --- /dev/null +++ b/third_party/cardboard/src_overrides/sdk/qrcode/android/res/layout/qr_code_capture.xml
@@ -0,0 +1,113 @@ +<?xml version="1.0" encoding="utf-8"?> +<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:id="@+id/container" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:orientation="vertical"> + + <!-- Camera source preview --> + <com.google.cardboard.sdk.qrcode.camera.CameraSourcePreview + android:id="@+id/preview" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:layout_alignParentStart="true" + android:layout_alignParentTop="true"/> + + <RelativeLayout + android:layout_width="match_parent" + android:layout_height="match_parent" + android:orientation="vertical"> + + <!-- QR code scanning help footer --> + <LinearLayout + android:id="@+id/footer" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:orientation="vertical" + android:layout_alignParentBottom="true"> + + <!-- Instructions bar --> + <LinearLayout + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:orientation="horizontal" + android:paddingTop="32dp" + android:paddingBottom="32dp" + android:paddingLeft="24dp" + android:paddingRight="24dp" + android:background="@color/footer_light_grey"> + + <!-- QR code sample image. Corresponds to g.co/cardboard QR code --> + <ImageView + android:src="@drawable/qr_sample" + android:layout_width="64dp" + android:layout_height="64dp" + android:layout_weight="0"/> + + <TextView + style="@style/text_16sp_sans_serif" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:lineSpacingExtra="4dp" + android:paddingLeft="24dp" + android:paddingTop="10dp" + android:text="@string/reading_qr_code_instructions" + android:textColor="#DE000000"/> + </LinearLayout> + + <!-- Skip bar --> + <LinearLayout + android:layout_width="match_parent" + android:layout_height="56dp" + android:orientation="horizontal" + android:background="@color/footer_medium_grey"> + <View + android:layout_width="0dp" + android:layout_height="0dp" + android:layout_weight="1"/> + <TextView + style="@style/text_14sp_sans_serif_medium" + android:layout_width="wrap_content" + android:layout_height="match_parent" + android:paddingLeft="20dp" + android:paddingRight="20dp" + android:gravity="center_vertical" + android:text="@string/cannot_find_symbol" + android:textColor="@color/darker_gray"/> + <View + style="@style/vertical_divider" + android:background="#1F000000" + android:layout_width="1dp" + android:layout_marginTop="10dp" + android:layout_marginBottom="10dp"/> + <TextView + android:id="@+id/skip" + style="@style/text_14sp_sans_serif_medium" + android:textColor="@color/darker_gray" + android:layout_width="wrap_content" + android:layout_height="match_parent" + android:paddingLeft="20dp" + android:paddingRight="20dp" + android:gravity="center_vertical" + android:text="@string/skip_scanning" + android:background="?android:attr/selectableItemBackground" + android:onClick="skipQrCodeCapture"/> + </LinearLayout> + </LinearLayout> + + <!-- White tick marks --> + <LinearLayout + android:layout_width="match_parent" + android:layout_height="match_parent" + android:orientation="vertical" + android:layout_above="@id/footer" + android:gravity="center"> + + <ImageView + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_gravity="center" + android:src="@drawable/tick_marks" /> + </LinearLayout> + </RelativeLayout> +</RelativeLayout>
diff --git a/third_party/cardboard/src_overrides/sdk/qrcode/android/res/values/colors.xml b/third_party/cardboard/src_overrides/sdk/qrcode/android/res/values/colors.xml new file mode 100644 index 0000000..38f7fcc --- /dev/null +++ b/third_party/cardboard/src_overrides/sdk/qrcode/android/res/values/colors.xml
@@ -0,0 +1,6 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <color name="darker_gray">#555</color> + <color name="footer_light_grey">#FFFAFAFA</color> + <color name="footer_medium_grey">#FFEEEEEE</color> +</resources>
diff --git a/third_party/cardboard/src_overrides/sdk/qrcode/android/res/values/strings.xml b/third_party/cardboard/src_overrides/sdk/qrcode/android/res/values/strings.xml new file mode 100644 index 0000000..b86f7ba --- /dev/null +++ b/third_party/cardboard/src_overrides/sdk/qrcode/android/res/values/strings.xml
@@ -0,0 +1,43 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="no_permissions" + description="Text shown on Android Toast when permissions are not granted. + [CHAR_LIMIT=140]"> + Cardboard SDK requires camera and write to external storage permission to read the QR code and save the encoded device parameters.</string> + <string name="no_camera_permission" + description="Text shown on Android Toast when camera permission is not granted. + [CHAR_LIMIT=70]"> + Camera permission is not granted and is needed to read QR codes</string> + <string name="title_activity_qr_code_capture" + description="QrCodeCapture activity title. [CHAR_LIMIT=30]"> + QrCodeCapture</string> + <string name="reading_qr_code_instructions" + description="Text shown on bottom of QR code capture activity to indicate instructions + to read QR code. [CHAR_LIMIT=50]"> + Find this Cardboard symbol on your viewer</string> + <string name="cannot_find_symbol" + description="Text shown on bottom of QR code capture activity to indicate SKIP button + presence. [CHAR_LIMIT=30]"> + Can\'t find this symbol?</string> + <string name="skip_scanning" + description="Text shown for button to skip the workflow to pair a Cardboard view with + the user's phone. [CHAR_LIMIT=10]"> + SKIP</string> + <string name="headset_detection_activity_title" + description="Headset detection activity title. [CHAR_LIMIT=30]"> + HeadsetDetector</string> + <string name="viewer_detected" + description="Text shown when a device with NFC tag is detected [CHAR_LIMIT=30]"> + Viewer detected</string> + <string name="invalid_qr_code" + description="Text shown when an invalid QR code is scanned. [CHAR_LIMIT=30]"> + Invalid QR Code</string> + <string name="connection_error" + description="Text shown when a connection error occurs when retrieving the QR Code + content. [CHAR_LIMIT=30]"> + Connection error</string> + <string name="missing_dependencies" + description="Text shown when dependencies are not available to scan QR Codes. + [CHAR_LIMIT=40]"> + QR Code detector dependency missing</string> +</resources>
diff --git a/third_party/cardboard/src_overrides/sdk/qrcode/android/res/values/styles.xml b/third_party/cardboard/src_overrides/sdk/qrcode/android/res/values/styles.xml new file mode 100644 index 0000000..c451c230 --- /dev/null +++ b/third_party/cardboard/src_overrides/sdk/qrcode/android/res/values/styles.xml
@@ -0,0 +1,18 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <style name="text_16sp_sans_serif"> + <item name="android:textSize">16sp</item> + <item name="android:fontFamily">sans-serif</item> + </style> + + <style name="text_14sp_sans_serif_medium"> + <item name="android:textSize">14sp</item> + <item name="android:fontFamily">sans-serif-medium</item> + </style> + + <style name="vertical_divider"> + <item name="android:layout_width">1px</item> + <item name="android:layout_height">match_parent</item> + <item name="android:background">@android:color/darker_gray</item> + </style> +</resources>
diff --git a/third_party/depot_tools b/third_party/depot_tools index 8a58b41..86ddfc9 160000 --- a/third_party/depot_tools +++ b/third_party/depot_tools
@@ -1 +1 @@ -Subproject commit 8a58b411c2ee5869edbd00672c9c78b943039ff1 +Subproject commit 86ddfc92bce7b2eebf0107abfbc2cc33a4aae265
diff --git a/third_party/devtools-frontend/README.chromium b/third_party/devtools-frontend/README.chromium index 704521b..89ff93f 100644 --- a/third_party/devtools-frontend/README.chromium +++ b/third_party/devtools-frontend/README.chromium
@@ -1,7 +1,7 @@ Name: Devtools-Frontend URL: https://chromium.googlesource.com/devtools/devtools-frontend Version: N/A -Revision: 47e1cb2b0f26f7a01873359c9f864c5c377f60ae +Revision: 14644b8ee2cb32c76c825c69e77d8a276bc2631b Update Mechanism: Autoroll License: BSD-3-Clause License File: src/LICENSE
diff --git a/third_party/devtools-frontend/src b/third_party/devtools-frontend/src index 47e1cb2..14644b8 160000 --- a/third_party/devtools-frontend/src +++ b/third_party/devtools-frontend/src
@@ -1 +1 @@ -Subproject commit 47e1cb2b0f26f7a01873359c9f864c5c377f60ae +Subproject commit 14644b8ee2cb32c76c825c69e77d8a276bc2631b
diff --git a/third_party/eigen3/README.chromium b/third_party/eigen3/README.chromium index 9ca9713..f404e7f 100644 --- a/third_party/eigen3/README.chromium +++ b/third_party/eigen3/README.chromium
@@ -2,7 +2,7 @@ Short Name: eigen3 URL: https://gitlab.com/libeigen/eigen Version: N/A -Revision: ff72fba837a29c5f7cd032e13bcf9b5270ab99ec +Revision: 83184e67da29564662761cd25d477d8885be13a2 Update Mechanism: Autoroll License: MPL-2.0 License File: LICENSE
diff --git a/third_party/eigen3/src b/third_party/eigen3/src index ff72fba..83184e6 160000 --- a/third_party/eigen3/src +++ b/third_party/eigen3/src
@@ -1 +1 @@ -Subproject commit ff72fba837a29c5f7cd032e13bcf9b5270ab99ec +Subproject commit 83184e67da29564662761cd25d477d8885be13a2
diff --git a/third_party/freetype/README.chromium b/third_party/freetype/README.chromium index 546e6c3..a296a39 100644 --- a/third_party/freetype/README.chromium +++ b/third_party/freetype/README.chromium
@@ -1,7 +1,7 @@ Name: FreeType URL: http://www.freetype.org/ -Version: VER-2-14-3-33-gb6bcd2177 -Revision: b6bcd2177f72bb4842c7701d7b7f633bb3fc951a +Version: VER-2-14-3-39-g7e3750982 +Revision: 7e3750982be8c6c71a572ef6829c89f27f9c989b Update Mechanism: Manual CPEPrefix: cpe:/a:freetype:freetype:2.14.3 License: FTL
diff --git a/third_party/freetype/src b/third_party/freetype/src index b6bcd21..7e37509 160000 --- a/third_party/freetype/src +++ b/third_party/freetype/src
@@ -1 +1 @@ -Subproject commit b6bcd2177f72bb4842c7701d7b7f633bb3fc951a +Subproject commit 7e3750982be8c6c71a572ef6829c89f27f9c989b
diff --git a/third_party/glslang/src b/third_party/glslang/src index 458ff50..1362555 160000 --- a/third_party/glslang/src +++ b/third_party/glslang/src
@@ -1 +1 @@ -Subproject commit 458ff50a67cb69371850068a62b78f1990a1ff9a +Subproject commit 13625556d3ae5fbe198bcebf11878e5b7bf720c1
diff --git a/third_party/llvm-libc/BUILD.gn b/third_party/llvm-libc/BUILD.gn index 53fd4104..1c036893 100644 --- a/third_party/llvm-libc/BUILD.gn +++ b/third_party/llvm-libc/BUILD.gn
@@ -14,3 +14,8 @@ public_configs = [ ":config" ] } + +group("headers") { + visibility = [ "//v8:v8_libbase" ] + public_configs = [ ":config" ] +}
diff --git a/third_party/perfetto b/third_party/perfetto index b6b8fef..1272c9d 160000 --- a/third_party/perfetto +++ b/third_party/perfetto
@@ -1 +1 @@ -Subproject commit b6b8fef3ca7840c15cfb92cd03aea4e10ca39367 +Subproject commit 1272c9dd9ca9b05bc1cb87fad0caebbf37a0ebc1
diff --git a/third_party/rust/chromium_crates_io/gnrt_config.toml b/third_party/rust/chromium_crates_io/gnrt_config.toml index a905ead..a3f68db 100644 --- a/third_party/rust/chromium_crates_io/gnrt_config.toml +++ b/third_party/rust/chromium_crates_io/gnrt_config.toml
@@ -538,6 +538,9 @@ [crate.log.extra_kv] allow_unsafe = true +# TODO(https://crbug.com/374023535): Set +# `override_crate_type_as_dylib_in_component_builds` if the upstream +# https://crrev.com/c/7757704 "lands" and "sticks". [crate.memchr.extra_kv] allow_unsafe = true @@ -661,7 +664,10 @@ [crate.read-fonts] extra_input_roots = ['../generated', '../data/generated'] -extra_kv = { allow_unsafe = false } + +[crate.read-fonts.extra_kv] +allow_unsafe = false +override_crate_type_as_dylib_in_component_builds = true [crate.regex-automata.extra_kv] allow_unsafe = true @@ -702,7 +708,10 @@ [crate.serde_core] build_script_outputs = [ "private.rs" ] -extra_kv = { allow_unsafe = true } + +[crate.serde_core.extra_kv] +allow_unsafe = true +override_crate_type_as_dylib_in_component_builds = true [crate.serde_derive.extra_kv] allow_unsafe = false
diff --git a/third_party/rust/read_fonts/v0_39/BUILD.gn b/third_party/rust/read_fonts/v0_39/BUILD.gn index 4a583e4..1b0b3c4 100644 --- a/third_party/rust/read_fonts/v0_39/BUILD.gn +++ b/third_party/rust/read_fonts/v0_39/BUILD.gn
@@ -12,6 +12,7 @@ crate_name = "read_fonts" epoch = "0.39" crate_type = "rlib" + override_crate_type_as_dylib_in_component_builds = true crate_root = "//third_party/rust/chromium_crates_io/vendor/read-fonts-v0_39/src/lib.rs" sources = [
diff --git a/third_party/rust/serde_core/v1/BUILD.gn b/third_party/rust/serde_core/v1/BUILD.gn index 8667ae08..c349db35 100644 --- a/third_party/rust/serde_core/v1/BUILD.gn +++ b/third_party/rust/serde_core/v1/BUILD.gn
@@ -12,6 +12,7 @@ crate_name = "serde_core" epoch = "1" crate_type = "rlib" + override_crate_type_as_dylib_in_component_builds = true crate_root = "//third_party/rust/chromium_crates_io/vendor/serde_core-v1/src/lib.rs" sources = [
diff --git a/third_party/skia b/third_party/skia index 3c73323..926c097 160000 --- a/third_party/skia +++ b/third_party/skia
@@ -1 +1 @@ -Subproject commit 3c73323d12686f4d9e571f3a9fa02fb76a19b967 +Subproject commit 926c09741ce26563e615e8cc670e0edbd66965c2
diff --git a/third_party/spirv-headers/src b/third_party/spirv-headers/src index 1260380..58006c9 160000 --- a/third_party/spirv-headers/src +++ b/third_party/spirv-headers/src
@@ -1 +1 @@ -Subproject commit 126038020c2bd47efaa942ccc364ca5353ffccde +Subproject commit 58006c901d1d5c37dece6b6610e9af87fa951375
diff --git a/third_party/spirv-tools/src b/third_party/spirv-tools/src index 2ec8457..6337eb6 160000 --- a/third_party/spirv-tools/src +++ b/third_party/spirv-tools/src
@@ -1 +1 @@ -Subproject commit 2ec8457ab33d539b6f1fecc998360c0b8b05ed4f +Subproject commit 6337eb62cadd7d124ac6789bf39c0f71148f0a73
diff --git a/third_party/vulkan-deps b/third_party/vulkan-deps index d234b7b..11781c2 160000 --- a/third_party/vulkan-deps +++ b/third_party/vulkan-deps
@@ -1 +1 @@ -Subproject commit d234b7b29748c07ef389279dd24f533ebd04cadc +Subproject commit 11781c266f0c58d36b0c7a35ebf0b87f2bcdf049
diff --git a/third_party/vulkan-headers/src b/third_party/vulkan-headers/src index f6a6f7a..0e9de56 160000 --- a/third_party/vulkan-headers/src +++ b/third_party/vulkan-headers/src
@@ -1 +1 @@ -Subproject commit f6a6f7ab165cedbfa2a7d0c93fe27a2d01ce09c8 +Subproject commit 0e9de566b7d4051c5cc1b762e242c46565956bdf
diff --git a/third_party/vulkan-loader/src b/third_party/vulkan-loader/src index 15a8465..fc1daa9 160000 --- a/third_party/vulkan-loader/src +++ b/third_party/vulkan-loader/src
@@ -1 +1 @@ -Subproject commit 15a84652b94e465e9a7b25eb507193929863bc2f +Subproject commit fc1daa956375aacd8c7fbdbaa0579f061c932416
diff --git a/third_party/vulkan-tools/src b/third_party/vulkan-tools/src index 7c46da2..d299403 160000 --- a/third_party/vulkan-tools/src +++ b/third_party/vulkan-tools/src
@@ -1 +1 @@ -Subproject commit 7c46da2b39036a80ce088576d5794bf39e667f56 +Subproject commit d2994039d549970c5d8f60603209a947c5241cb2
diff --git a/third_party/vulkan-utility-libraries/src b/third_party/vulkan-utility-libraries/src index 2c909c1..d88097b 160000 --- a/third_party/vulkan-utility-libraries/src +++ b/third_party/vulkan-utility-libraries/src
@@ -1 +1 @@ -Subproject commit 2c909c1ab6f9c6caba39a84a4887186b3fafdead +Subproject commit d88097b51e70f357a96237c4571ded3433ccde99
diff --git a/third_party/vulkan-validation-layers/src b/third_party/vulkan-validation-layers/src index b105d8e..14d771d 160000 --- a/third_party/vulkan-validation-layers/src +++ b/third_party/vulkan-validation-layers/src
@@ -1 +1 @@ -Subproject commit b105d8ea361af258abed65efb5a1565c031dcf1c +Subproject commit 14d771d9b83fa33f4d18d80fb0069f16d945d17e
diff --git a/third_party/webrtc b/third_party/webrtc index 0ea8047..85a228f 160000 --- a/third_party/webrtc +++ b/third_party/webrtc
@@ -1 +1 @@ -Subproject commit 0ea80479e5364ba2ff702d919347ec22f97ab951 +Subproject commit 85a228f95fc2b273f732b8f68c8c966d861a3702
diff --git a/chrome/common/extensions/api/experimental_actor.idl b/tools/json_schema_compiler/test/converted_schemas/experimental_actor.idl similarity index 70% rename from chrome/common/extensions/api/experimental_actor.idl rename to tools/json_schema_compiler/test/converted_schemas/experimental_actor.idl index df72033..f55c571 100644 --- a/chrome/common/extensions/api/experimental_actor.idl +++ b/tools/json_schema_compiler/test/converted_schemas/experimental_actor.idl
@@ -11,23 +11,23 @@ interface Functions { // Stops a task. - // taskId: id of the task to stop. - // stopTaskcallback: a closure that is called when the task is stopped. + // |taskId|: id of the task to stop. + // |callback|: a closure that is called when the task is stopped. static void stopTask(long taskId, - ClosureCallback stopTaskcallback); + ClosureCallback callback); // Creates a new task. The callback will contain the task ID for the newly // created task. static void createTask(TaskIdCallback callback); // Executes one or more actions according to request. - // actionsProto: encoded optimization_guide.proto.Actions - // callback: encoded optimization_guide.proto.ActionsResult + // |actionsProto|: encoded optimization_guide.proto.Actions + // |callback|: encoded optimization_guide.proto.ActionsResult static void performActions(ArrayBuffer actionsProto, DataCallback callback); // Requests a TabObservation for a given tab. - // tabId: The session tabId to observe. - // callback: encoded optimization_guide.proto.TabObservation + // |tabId|: The session tabId to observe. + // |callback|: encoded optimization_guide.proto.TabObservation static void requestTabObservation(long tabId, DataCallback callback); }; };
diff --git a/tools/json_schema_compiler/test/converted_schemas/experimental_actor.webidl b/tools/json_schema_compiler/test/converted_schemas/experimental_actor.webidl new file mode 100644 index 0000000..1286a19cf --- /dev/null +++ b/tools/json_schema_compiler/test/converted_schemas/experimental_actor.webidl
@@ -0,0 +1,35 @@ +// Copyright 2026 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// Experimental API to handle acting and returning new browser state. +[implemented_in="chrome/browser/extensions/api/experimental_actor/experimental_actor_api.h"] +interface ExperimentalActor { + // Stops a task. + // |taskId|: id of the task to stop. + // |Returns|: a closure that is called when the task is stopped. + [requiredCallback] static Promise<undefined> stopTask(long taskId); + + // Creates a new task. The callback will contain the task ID for the newly + // created task. + // |PromiseValue|: taskId + [requiredCallback] static Promise<long> createTask(); + + // Executes one or more actions according to request. + // |actionsProto|: encoded optimization_guide.proto.Actions + // |Returns|: encoded optimization_guide.proto.ActionsResult + // |PromiseValue|: data + [requiredCallback] + static Promise<ArrayBuffer> performActions(ArrayBuffer actionsProto); + + // Requests a TabObservation for a given tab. + // |tabId|: The session tabId to observe. + // |Returns|: encoded optimization_guide.proto.TabObservation + // |PromiseValue|: data + [requiredCallback] + static Promise<ArrayBuffer> requestTabObservation(long tabId); +}; + +partial interface Browser { + static attribute ExperimentalActor experimentalActor; +};
diff --git a/chrome/common/extensions/api/file_manager_private_internal.idl b/tools/json_schema_compiler/test/converted_schemas/file_manager_private_internal.idl similarity index 100% rename from chrome/common/extensions/api/file_manager_private_internal.idl rename to tools/json_schema_compiler/test/converted_schemas/file_manager_private_internal.idl
diff --git a/tools/json_schema_compiler/test/converted_schemas/file_manager_private_internal.webidl b/tools/json_schema_compiler/test/converted_schemas/file_manager_private_internal.webidl new file mode 100644 index 0000000..65876f6 --- /dev/null +++ b/tools/json_schema_compiler/test/converted_schemas/file_manager_private_internal.webidl
@@ -0,0 +1,218 @@ +// Copyright 2026 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +[ExternalExtensionType="fileManagerPrivate.SearchType"] +typedef object FileManagerPrivateSearchType; + +[ExternalExtensionType="fileManagerPrivate.FileCategory"] +typedef object FileManagerPrivateFileCategory; + +[ExternalExtensionType="fileManagerPrivate.EntryProperties"] +typedef object FileManagerPrivateEntryProperties; + +[ExternalExtensionType="fileSystemProvider.Action"] +typedef object FileSystemProviderAction; + +[ExternalExtensionType="fileManagerPrivate.MediaMetadata"] +typedef object FileManagerPrivateMediaMetadata; + +[ExternalExtensionType="fileManagerPrivate.TaskResult"] +typedef object FileManagerPrivateTaskResult; + +[ExternalExtensionType="fileManagerPrivate.ResultingTasks"] +typedef object FileManagerPrivateResultingTasks; + +[ExternalExtensionType="fileManagerPrivate.DlpMetadata"] +typedef object FileManagerPrivateDlpMetadata; + +[ExternalExtensionType="fileManagerPrivate.DriveQuotaMetadata"] +typedef object FileManagerPrivateDriveQuotaMetadata; + +[ExternalExtensionType="fileManagerPrivate.EntryPropertyName"] +typedef object FileManagerPrivateEntryPropertyName; + +[ExternalExtensionType="fileManagerPrivate.FileTaskDescriptor"] +typedef object FileManagerPrivateFileTaskDescriptor; + +[ExternalExtensionType="fileManagerPrivate.GetVolumeRootOptions"] +typedef object FileManagerPrivateGetVolumeRootOptions; + +[ExternalExtensionType="fileManagerPrivate.SourceRestriction"] +typedef object FileManagerPrivateSourceRestriction; + +[ExternalExtensionType="fileManagerPrivate.IoTaskType"] +typedef object FileManagerPrivateIoTaskType; + +// Entry information that renderers need to create an Entry instance. +dictionary EntryDescription { + required DOMString fileSystemName; + required DOMString fileSystemRoot; + required DOMString fileFullPath; + required boolean fileIsDirectory; +}; + +dictionary IOTaskParams { + DOMString destinationFolderUrl; + DOMString password; + boolean showNotification; +}; + +dictionary ParsedTrashInfoFile { + required EntryDescription restoreEntry; + required DOMString trashInfoFileName; + required double deletionDate; +}; + +dictionary SearchFilesParams { + DOMString rootUrl; + required DOMString query; + required FileManagerPrivateSearchType types; + required long maxResults; + required double modifiedTimestamp; + required FileManagerPrivateFileCategory category; +}; + +dictionary CrostiniSharedPathResponse { + required sequence<EntryDescription> entries; + required boolean firstForSession; +}; + +// Internal, used by fileManagerPrivate's custom bindings. +[platforms=("chromeos"), + implemented_in="chrome/browser/ash/extensions/file_manager/file_manager_private_api_functions.h"] +interface FileManagerPrivateInternal { + // |PromiseValue|: entries + [requiredCallback] + static Promise<sequence<EntryDescription>> resolveIsolatedEntries( + sequence<DOMString> urls); + + // |PromiseValue|: entryProperties + [requiredCallback] + static Promise<sequence<FileManagerPrivateEntryProperties>> + getEntryProperties(sequence<DOMString> urls, + sequence<FileManagerPrivateEntryPropertyName> names); + + // |PromiseValue|: success + [requiredCallback] static Promise<boolean?> addFileWatch(DOMString url); + + // |PromiseValue|: success + [requiredCallback] static Promise<boolean?> removeFileWatch(DOMString url); + + // |PromiseValue|: actions + [requiredCallback] + static Promise<sequence<FileSystemProviderAction>> getCustomActions( + sequence<DOMString> urls); + + [requiredCallback] static Promise<undefined> executeCustomAction( + sequence<DOMString> urls, + DOMString actionId); + + // |PromiseValue|: result + [requiredCallback] + static Promise<DOMString> getContentMimeType(DOMString blobUUID); + + // |PromiseValue|: result + [requiredCallback] + static Promise<FileManagerPrivateMediaMetadata> getContentMetadata( + DOMString blobUUID, + DOMString mimeType, + boolean includeImages); + + [requiredCallback] static Promise<undefined> pinDriveFile( + DOMString url, + boolean pin); + + // |PromiseValue|: result + [requiredCallback] static Promise<FileManagerPrivateTaskResult> executeTask( + FileManagerPrivateFileTaskDescriptor descriptor, + sequence<DOMString> urls); + + // |PromiseValue|: entries + [requiredCallback] static Promise<sequence<EntryDescription>> searchFiles( + SearchFilesParams searchParams); + + [requiredCallback] static Promise<undefined> setDefaultTask( + FileManagerPrivateFileTaskDescriptor descriptor, + sequence<DOMString> urls, + sequence<DOMString> mimeTypes); + + // |PromiseValue|: resultingTasks + [requiredCallback] + static Promise<FileManagerPrivateResultingTasks> getFileTasks( + sequence<DOMString> urls, + sequence<DOMString> dlpSourceUrls); + + // |PromiseValue|: entries + [requiredCallback] + static Promise<sequence<EntryDescription>> getDisallowedTransfers( + sequence<DOMString> entries, + DOMString destinationEntry, + boolean isMove); + + // |PromiseValue|: entries + [requiredCallback] + static Promise<sequence<FileManagerPrivateDlpMetadata>> getDlpMetadata( + sequence<DOMString> entries); + + // |PromiseValue|: driveQuotaMetadata + [requiredCallback] + static Promise<FileManagerPrivateDriveQuotaMetadata?> getDriveQuotaMetadata( + DOMString url); + + // |PromiseValue|: result + [requiredCallback] static Promise<boolean> validatePathNameLength( + DOMString parentUrl, + DOMString name); + + // |PromiseValue|: size + [requiredCallback] static Promise<double> getDirectorySize(DOMString url); + + // |PromiseValue|: rootDir + [requiredCallback] static Promise<EntryDescription> getVolumeRoot( + FileManagerPrivateGetVolumeRootOptions options); + + // |PromiseValue|: entries + [requiredCallback] static Promise<sequence<EntryDescription>> getRecentFiles( + FileManagerPrivateSourceRestriction restriction, + DOMString query, + long cutoff_days, + FileManagerPrivateFileCategory file_category, + boolean invalidate_cache); + + [requiredCallback] static Promise<undefined> sharePathsWithCrostini( + DOMString vmName, + sequence<DOMString> urls, + boolean persist); + + [requiredCallback] static Promise<undefined> unsharePathWithCrostini( + DOMString vmName, + DOMString url); + + // |PromiseValue|: response + [requiredCallback] + static Promise<CrostiniSharedPathResponse> getCrostiniSharedPaths( + boolean observeFirstForSession, + DOMString vmName); + + static undefined importCrostiniImage(DOMString url); + + static Promise<undefined> toggleAddedToHoldingSpace( + sequence<DOMString> urls, + boolean add); + + // |PromiseValue|: taskId + static Promise<long> startIOTask( + FileManagerPrivateIoTaskType type, + sequence<DOMString> urls, + IOTaskParams params); + + // |PromiseValue|: files + [requiredCallback] + static Promise<sequence<ParsedTrashInfoFile>> parseTrashInfoFiles( + sequence<DOMString> urls); +}; + +partial interface Browser { + static attribute FileManagerPrivateInternal fileManagerPrivateInternal; +};
diff --git a/tools/json_schema_compiler/web_idl_diff_tool_test.py b/tools/json_schema_compiler/web_idl_diff_tool_test.py index a12cbee1..611c361 100755 --- a/tools/json_schema_compiler/web_idl_diff_tool_test.py +++ b/tools/json_schema_compiler/web_idl_diff_tool_test.py
@@ -52,6 +52,8 @@ ('automation_internal.idl', 'automation_internal.webidl'), ('automation.idl', 'automation.webidl'), ('feedback_private.idl', 'feedback_private.webidl'), + ('file_manager_private_internal.idl', + 'file_manager_private_internal.webidl'), ('media_perception_private.idl', 'media_perception_private.webidl'), ('mojo_private.idl', 'mojo_private.webidl'), ('networking_onc.idl', 'networking_onc.webidl'), @@ -87,6 +89,7 @@ ('enterprise_networking_attributes.idl', 'enterprise_networking_attributes.webidl'), ('enterprise_platform_keys.idl', 'enterprise_platform_keys.webidl'), + ('experimental_actor.idl', 'experimental_actor.webidl'), ] class WebIdlDiffToolTest(unittest.TestCase):
diff --git a/tools/metrics/actions/actions.xml b/tools/metrics/actions/actions.xml index 00072bb7..78fb4ea 100644 --- a/tools/metrics/actions/actions.xml +++ b/tools/metrics/actions/actions.xml
@@ -39767,6 +39767,7 @@ <token key="ActionType"> <variant name="" summary="aggregated"/> <variant name=".AvatarButton" summary="Avatar(profile) button"/> + <variant name=".BatterySaverButton" summary="Battery Saver button"/> <variant name=".ChromeLabsButton" summary="Chrome Labs button"/> <variant name=".DownloadButton" summary="Download button"/> <variant name=".ExtensionsMenuButton" summary="Extensions button"/>
diff --git a/tools/metrics/histograms/enums.xml b/tools/metrics/histograms/enums.xml index bcc9ef9..81a3022 100644 --- a/tools/metrics/histograms/enums.xml +++ b/tools/metrics/histograms/enums.xml
@@ -8901,6 +8901,7 @@ <int value="-1703479539" label="InternalServerSideSpeechRecognition:disabled"/> <int value="-1703308540" label="disable-webaudio"/> + <int value="-1703050195" label="CastStreamingOfferHardwareFirst:enabled"/> <int value="-1702323888" label="MediaAppPdfSignature:disabled"/> <int value="-1701760225" label="ApiPrintingMarginsAndScale:disabled"/> <int value="-1701123067" label="ShowManagedUi:enabled"/> @@ -11536,6 +11537,7 @@ <int value="-842597982" label="ShelfPalmRejectionTouchArea:disabled"/> <int value="-842438090" label="enable-md-feedback"/> <int value="-842236618" label="CrOSSystemVoiceIsolationOption:disabled"/> + <int value="-840851753" label="LongScreenshotsLenientMemoryCheck:disabled"/> <int value="-839828262" label="DesktopUAOnConnectedDisplay:disabled"/> <int value="-839822160" label="WebAppManifestImmediateUpdating:enabled"/> <int value="-839788988" label="EnableToggleCameraShortcut:disabled"/> @@ -12764,6 +12766,7 @@ <int value="-433879402" label="EnableAmbientAuthenticationInIncognito:disabled"/> <int value="-433775463" label="EnforceManagementDisclaimer:enabled"/> + <int value="-431656941" label="LongScreenshotsLenientMemoryCheck:enabled"/> <int value="-431617217" label="ArcGhostWindowNewStyle:disabled"/> <int value="-431553693" label="ChangePasswordAffiliationInfo:disabled"/> <int value="-430369215" label="AssistPersonalInfo:disabled"/> @@ -17835,6 +17838,7 @@ <int value="1253720084" label="CrOSEnforceSystemAecAgc:disabled"/> <int value="1254018350" label="InsecureDownloadWarnings:enabled"/> <int value="1254472299" label="PrivacySettingsRedesign:disabled"/> + <int value="1254742560" label="CastStreamingOfferHardwareFirst:disabled"/> <int value="1254761280" label="LacrosForkZygotesAtLoginScreen:enabled"/> <int value="1255712526" label="FedCmUseOtherAccount:enabled"/> <int value="1255967063" label="IsolatedSandboxedIframesName:enabled"/> @@ -25941,6 +25945,9 @@ <int value="7" label="Abandoned because the content was already painted before profiling started"/> + <int value="8" + label="Abandoned because the launch was done without user interaction + (eg. OS startup)"/> </enum> <!-- LINT.IfChange(EcheStreamStatus) -->
diff --git a/tools/metrics/histograms/metadata/actor/histograms.xml b/tools/metrics/histograms/metadata/actor/histograms.xml index ece134e..979ae0c6 100644 --- a/tools/metrics/histograms/metadata/actor/histograms.xml +++ b/tools/metrics/histograms/metadata/actor/histograms.xml
@@ -500,6 +500,26 @@ </summary> </histogram> +<histogram + name="Actor.PageContext.APC.Comparison.{Source}.IsIdenticalToPreviousFetch" + enum="Boolean" expires_after="2026-11-03"> + <owner>linnan@google.com</owner> + <owner>shivanisha@google.com</owner> + <owner>chrome-synapse-team@google.com</owner> + <summary> + Recorded when a new Annotated Page Content (APC) is fetched by {Source}. + Compares the new APC with the immediately preceding fetch from ANY source + (e.g. Actor or Glic) and records whether they are identical. + + This is used to evaluate the potential benefit of a shared APC cache between + different features. + </summary> + <token key="Source"> + <variant name="Actor"/> + <variant name="Glic"/> + </token> +</histogram> + <histogram name="Actor.PageContext.APC.Duration" units="ms" expires_after="2026-10-11"> <owner>dtapuska@google.com</owner>
diff --git a/tools/metrics/histograms/metadata/android/histograms.xml b/tools/metrics/histograms/metadata/android/histograms.xml index 75af8926..d761542 100644 --- a/tools/metrics/histograms/metadata/android/histograms.xml +++ b/tools/metrics/histograms/metadata/android/histograms.xml
@@ -599,7 +599,7 @@ </histogram> <histogram name="Android.AssistContent.AttachedUrl" enum="BooleanSuccess" - expires_after="2026-09-13"> + expires_after="2026-10-04"> <owner>salg@google.com</owner> <owner>ssid@google.com</owner> <summary> @@ -610,7 +610,7 @@ </histogram> <histogram name="Android.AssistContent.IsEnterpriseInfoCached" enum="Boolean" - expires_after="2026-09-13"> + expires_after="2026-10-04"> <owner>salg@google.com</owner> <owner>ssid@google.com</owner> <summary> @@ -629,7 +629,7 @@ </histogram> <histogram name="Android.AssistContent.StructuredDataAttachedSuccess.Pdf" - enum="BooleanSuccess" expires_after="2026-06-01"> + enum="BooleanSuccess" expires_after="2026-10-04"> <owner>salg@google.com</owner> <owner>ssid@google.com</owner> <summary> @@ -643,7 +643,7 @@ </histogram> <histogram name="Android.AssistContent.StructuredDataAttachedSuccess.WebPage" - enum="BooleanSuccess" expires_after="2026-06-01"> + enum="BooleanSuccess" expires_after="2026-10-04"> <owner>salg@google.com</owner> <owner>ssid@google.com</owner> <summary> @@ -660,7 +660,7 @@ </histogram> <histogram name="Android.AssistContent.WebPage" units="BooleanSuccess" - expires_after="2026-09-06"> + expires_after="2026-10-04"> <owner>salg@google.com</owner> <owner>ssid@google.com</owner> <summary> @@ -674,7 +674,7 @@ </histogram> <histogram name="Android.AssistContent.WebPageContentProvider.Events" - enum="WebPageContentProviderEvent" expires_after="2026-09-13"> + enum="WebPageContentProviderEvent" expires_after="2026-10-04"> <owner>salg@google.com</owner> <owner>ssid@google.com</owner> <summary> @@ -685,7 +685,7 @@ <histogram name="Android.AssistContent.WebPageContentProvider.Events.{RequestType}.{Format}" - enum="WebPageContentProviderEvent" expires_after="2026-09-13"> + enum="WebPageContentProviderEvent" expires_after="2026-10-04"> <owner>salg@google.com</owner> <owner>ssid@google.com</owner> <summary> @@ -729,7 +729,7 @@ <histogram name="Android.AssistContent.WebPageContentProvider.Latency.ExtractionStartToEnd.{RequestType}.{Format}" - units="ms" expires_after="2026-09-13"> + units="ms" expires_after="2026-10-04"> <owner>salg@google.com</owner> <owner>ssid@google.com</owner> <summary> @@ -741,7 +741,7 @@ <histogram name="Android.AssistContent.WebPageContentProvider.Latency.TotalLatency.{RequestType}.{Format}" - units="ms" expires_after="2026-09-06"> + units="ms" expires_after="2026-10-04"> <owner>salg@google.com</owner> <owner>ssid@google.com</owner> <summary> @@ -754,7 +754,7 @@ <histogram name="Android.AssistContent.WebPageContentProvider.TargetPackageProvided" - enum="Boolean" expires_after="2026-09-06"> + enum="Boolean" expires_after="2026-10-04"> <owner>salg@google.com</owner> <owner>tivi@google.com</owner> <summary> @@ -5709,6 +5709,16 @@ </summary> </histogram> +<histogram name="Android.SelectionMenu.TimeToShowMenu" units="ms" + expires_after="2026-11-01"> + <owner>jhimawan@google.com</owner> + <owner>clank-app-team@google.com</owner> + <summary> + Records the time taken to show the text selection popup menu. Recorded when + the menu is shown. + </summary> +</histogram> + <histogram name="Android.SelectionMenu.UsedCachedMenu" enum="Boolean" expires_after="2026-11-01"> <owner>alexmitra@chromium.org</owner>
diff --git a/tools/metrics/histograms/metadata/ash/enums.xml b/tools/metrics/histograms/metadata/ash/enums.xml index 4980584..608b990 100644 --- a/tools/metrics/histograms/metadata/ash/enums.xml +++ b/tools/metrics/histograms/metadata/ash/enums.xml
@@ -3294,11 +3294,6 @@ <int value="3" label="Tile"/> </enum> -<enum name="WallpaperMigrationFailureReason"> - <int value="0" label="Failed to fetch online variants"/> - <int value="1" label="Failed to find a valid online variant"/> -</enum> - <enum name="WallpaperMigrationStatus"> <int value="0" label="Succeeded"/> <int value="1" label="Failed"/>
diff --git a/tools/metrics/histograms/metadata/ash/histograms.xml b/tools/metrics/histograms/metadata/ash/histograms.xml index 431ba44..f9e9305 100644 --- a/tools/metrics/histograms/metadata/ash/histograms.xml +++ b/tools/metrics/histograms/metadata/ash/histograms.xml
@@ -8970,27 +8970,6 @@ </summary> </histogram> -<histogram name="Ash.Wallpaper.MigrationFailureReason" - enum="WallpaperMigrationFailureReason" expires_after="2025-05-30"> - <owner>jasontt@chromium.org</owner> - <owner>cros-p13n-eng@google.com</owner> - <summary> - Tracks the reason for the versioned wallpaper migration failure. Recorded - when the migration fails on user's login. - </summary> -</histogram> - -<histogram name="Ash.Wallpaper.Online.Result" enum="SetWallpaperResult" - expires_after="2023-08-31"> - <owner>jasontt@chromium.org</owner> - <owner>cros-p13n-eng@google.com</owner> - <summary> - Tracks the result of user's attempt to set a wallpaper. Recorded when a user - selects a wallpaper from the personalization hub. b/285387348: Add more - variants and fix incorrect metric recording for online wallpapers. - </summary> -</histogram> - <histogram name="Ash.Wallpaper.Preview.Show" enum="BooleanHit" expires_after="2024-12-30"> <owner>jasontt@chromium.org</owner>
diff --git a/tools/metrics/histograms/metadata/ash_growth/histograms.xml b/tools/metrics/histograms/metadata/ash_growth/histograms.xml index c2ede95..796934d 100644 --- a/tools/metrics/histograms/metadata/ash_growth/histograms.xml +++ b/tools/metrics/histograms/metadata/ash_growth/histograms.xml
@@ -30,7 +30,7 @@ </variants> <histogram name="Ash.Growth.CampaignsComponent.DownloadDurationInOobe" - units="ms" expires_after="2026-09-27"> + units="ms" expires_after="2026-11-02"> <owner>llin@google.com</owner> <owner>cros-growth@google.com</owner> <summary> @@ -42,7 +42,7 @@ </histogram> <histogram name="Ash.Growth.CampaignsComponent.DownloadDurationSessionStart" - units="ms" expires_after="2026-05-22"> + units="ms" expires_after="2026-11-02"> <owner>llin@google.com</owner> <owner>cros-growth@google.com</owner> <summary> @@ -54,7 +54,7 @@ </histogram> <histogram name="Ash.Growth.CampaignsComponent.ParseDuration" units="ms" - expires_after="2026-05-22"> + expires_after="2026-11-02"> <owner>llin@google.com</owner> <owner>cros-growth@google.com</owner> <summary> @@ -64,7 +64,7 @@ </histogram> <histogram name="Ash.Growth.CampaignsManager.Error" - enum="CampaignsManagerError" expires_after="2026-07-26"> + enum="CampaignsManagerError" expires_after="2026-11-02"> <owner>llin@google.com</owner> <owner>cros-growth@google.com</owner> <summary> @@ -74,7 +74,7 @@ </histogram> <histogram name="Ash.Growth.CampaignsManager.GetCampaignBySlot" - enum="CampaignSlot" expires_after="2026-05-24"> + enum="CampaignSlot" expires_after="2026-11-02"> <owner>llin@google.com</owner> <owner>cros-growth@google.com</owner> <summary> @@ -84,7 +84,7 @@ </histogram> <histogram name="Ash.Growth.CampaignsManager.GetCampaignBySlot.Attempt" - enum="CampaignSlot" expires_after="2026-05-22"> + enum="CampaignSlot" expires_after="2026-11-02"> <owner>llin@google.com</owner> <owner>cros-growth@google.com</owner> <summary> @@ -94,7 +94,7 @@ </histogram> <histogram name="Ash.Growth.CampaignsManager.GetCampaignBySlot.Campaigns500" - units="int" expires_after="2026-05-22"> + units="int" expires_after="2026-11-02"> <owner>wutao@chromium.org</owner> <owner>cros-growth@google.com</owner> <summary> @@ -105,7 +105,7 @@ </histogram> <histogram name="Ash.Growth.CampaignsManager.MatchDuration" units="ms" - expires_after="2026-05-22"> + expires_after="2026-11-02"> <owner>llin@google.com</owner> <owner>cros-growth@google.com</owner> <summary> @@ -116,7 +116,7 @@ </histogram> <histogram name="Ash.Growth.Ui.ButtonPressed.Button{ButtonId}.Campaigns500" - units="int" expires_after="2026-09-27"> + units="int" expires_after="2026-11-02"> <owner>wutao@chromium.org</owner> <owner>cros-growth@google.com</owner> <summary> @@ -128,7 +128,7 @@ </histogram> <histogram name="Ash.Growth.Ui.Dismissed.Campaigns500" units="int" - expires_after="2026-05-22"> + expires_after="2026-11-02"> <owner>wutao@chromium.org</owner> <owner>cros-growth@google.com</owner> <summary> @@ -138,7 +138,7 @@ </histogram> <histogram name="Ash.Growth.Ui.Impression.Campaigns500" units="int" - expires_after="2026-09-27"> + expires_after="2026-11-02"> <owner>wutao@chromium.org</owner> <owner>cros-growth@google.com</owner> <summary>
diff --git a/tools/metrics/histograms/metadata/autofill/histograms.xml b/tools/metrics/histograms/metadata/autofill/histograms.xml index d0e35ae..13d20b0db 100644 --- a/tools/metrics/histograms/metadata/autofill/histograms.xml +++ b/tools/metrics/histograms/metadata/autofill/histograms.xml
@@ -7329,17 +7329,6 @@ </token> </histogram> -<histogram name="Autofill.SaveCardCardholderNamePrefilled" enum="Boolean" - expires_after="2026-10-11"> - <owner>osaul@google.com</owner> - <owner>payments-autofill-team@google.com</owner> - <summary> - If the cardholder name fix flow is shown when credit card upload is offered, - records if the cardholder name textfield was prefilled with the name from - the user's Google Account. - </summary> -</histogram> - <histogram name="Autofill.SaveCardCardholderNameWasEdited" enum="Boolean" expires_after="2027-04-30"> <owner>osaul@google.com</owner> @@ -8088,6 +8077,19 @@ </token> </histogram> +<histogram + name="Autofill.StoredCreditCardCount{AutofillCreditCardType}.WithNickname" + units="cards" expires_after="2027-04-30"> + <owner>osaul@google.com</owner> + <owner>battre@chromium.org</owner> + <owner>payments-autofill-team@google.com</owner> + <summary> + The number of credit cards a user has stored with a valid nickname, measured + at Chrome profile launch time. {AutofillCreditCardType} + </summary> + <token key="AutofillCreditCardType" variants="AutofillCreditCardType"/> +</histogram> + <histogram name="Autofill.StoredCreditCardDisusedCount{AutofillCreditCardType}" units="cards" expires_after="2026-12-12"> <owner>battre@chromium.org</owner>
diff --git a/tools/metrics/histograms/metadata/blink/histograms.xml b/tools/metrics/histograms/metadata/blink/histograms.xml index 72709a32..0dfb300 100644 --- a/tools/metrics/histograms/metadata/blink/histograms.xml +++ b/tools/metrics/histograms/metadata/blink/histograms.xml
@@ -5978,6 +5978,73 @@ </summary> </histogram> +<histogram name="Blink.ViewTransitions.AnimateRequestToAnimatingDelay" + units="microseconds" expires_after="2027-01-01"> + <owner>vmpstr@chromium.org</owner> + <owner>rendering-core-dev@chromium.org</owner> + <improvement direction="LOWER_IS_BETTER"/> + <summary> + Elapsed time between sending the animate request and the first animating + frame. This captures the delay waiting for a scheduled frame. Recorded for + script-based (SPA) transitions only. + + This histogram only records metrics on machines with high-resolution clocks. + </summary> +</histogram> + +<histogram name="Blink.ViewTransitions.CaptureRequestToDOMCallbackRunningDelay" + units="microseconds" expires_after="2027-01-01"> + <owner>vmpstr@chromium.org</owner> + <owner>rendering-core-dev@chromium.org</owner> + <improvement direction="LOWER_IS_BETTER"/> + <summary> + Elapsed time between sending a capture request and invoking the DOM change + callback. Recorded for script-based (SPA) transitions only. + + This histogram only records metrics on machines with high-resolution clocks. + </summary> +</histogram> + +<histogram name="Blink.ViewTransitions.CaptureTagDiscoveryDuration" + units="microseconds" expires_after="2027-01-01"> + <owner>vmpstr@chromium.org</owner> + <owner>rendering-core-dev@chromium.org</owner> + <improvement direction="LOWER_IS_BETTER"/> + <summary> + Elapsed time spent in capture tag discovery (adding transition elements from + CSS). Recorded for script-based (SPA) transitions only. + + This histogram only records metrics on machines with high-resolution clocks. + </summary> +</histogram> + +<histogram + name="Blink.ViewTransitions.DOMCallbackFinishedToAnimationRequestedDuration" + units="microseconds" expires_after="2027-01-01"> + <owner>vmpstr@chromium.org</owner> + <owner>rendering-core-dev@chromium.org</owner> + <improvement direction="LOWER_IS_BETTER"/> + <summary> + Elapsed time between the DOM callback finishing and the animation being + requested. Recorded for script-based (SPA) transitions only. + + This histogram only records metrics on machines with high-resolution clocks. + </summary> +</histogram> + +<histogram name="Blink.ViewTransitions.DOMCallbackRunDuration" + units="microseconds" expires_after="2027-01-01"> + <owner>vmpstr@chromium.org</owner> + <owner>rendering-core-dev@chromium.org</owner> + <improvement direction="LOWER_IS_BETTER"/> + <summary> + Elapsed time spent running the DOM change callback. Recorded for + script-based (SPA) transitions only. + + This histogram only records metrics on machines with high-resolution clocks. + </summary> +</histogram> + <histogram name="Blink.ViewTransitions.InitialFrameDelay" units="microseconds" expires_after="2027-01-01"> <owner>vmpstr@chromium.org</owner>
diff --git a/tools/metrics/histograms/metadata/contextual_cueing/enums.xml b/tools/metrics/histograms/metadata/contextual_cueing/enums.xml index 0ee5f119..7fd6a7b 100644 --- a/tools/metrics/histograms/metadata/contextual_cueing/enums.xml +++ b/tools/metrics/histograms/metadata/contextual_cueing/enums.xml
@@ -126,6 +126,9 @@ <int value="26" label="Cue not shown: disabled by enterprise policy"> Cue could not be shown because it is disabled by enterprise policy. </int> + <int value="27" label="Cue not shown: age restriction enforced"> + Cue could not be shown because the user is subject to age restrictions. + </int> </enum> <!-- LINT.ThenChange(//chrome/browser/contextual_cueing/contextual_cueing_enums.h:ContextualCueingDecision) -->
diff --git a/tools/metrics/histograms/metadata/contextual_cueing/histograms.xml b/tools/metrics/histograms/metadata/contextual_cueing/histograms.xml index 594206d..49a50afd 100644 --- a/tools/metrics/histograms/metadata/contextual_cueing/histograms.xml +++ b/tools/metrics/histograms/metadata/contextual_cueing/histograms.xml
@@ -194,6 +194,25 @@ </summary> </histogram> +<histogram name="ContextualCueing.V2.CueInteraction.{InteractionType}" + units="hash" expires_after="2026-09-13"> + <owner>sophiechang@chromium.org</owner> + <owner>pulkitaga@google.com</owner> + <summary> + The hashed string of the CUJ for a contextual cue. Recorded when the + contextual cue is {InteractionType}. + </summary> + <token key="InteractionType"> + <variant name="Clicked" summary="clicked"/> + <variant name="Dismissed" + summary="user opened 3 dot menu and clicked dismiss"/> + <variant name="EditPrompt" + summary="user opened 3 dot menu and clicked to edit the prompt"/> + <variant name="Settings" + summary="user opened 3 dot menu and clicked to go to settings"/> + </token> +</histogram> + <histogram name="ContextualCueing.V2.Decision" enum="ContextualCueingDecision" expires_after="2026-09-13"> <owner>sophiechang@chromium.org</owner>
diff --git a/tools/metrics/histograms/metadata/contextual_tasks/histograms.xml b/tools/metrics/histograms/metadata/contextual_tasks/histograms.xml index 357adc2..0da06105 100644 --- a/tools/metrics/histograms/metadata/contextual_tasks/histograms.xml +++ b/tools/metrics/histograms/metadata/contextual_tasks/histograms.xml
@@ -368,7 +368,7 @@ </token> </histogram> -<histogram name="ContextualTasks.VoiceSearch.State" enum="VoiceSearchState" +<histogram name="ContextualTasks.VoiceSearch.StateV2" enum="VoiceSearchState" expires_after="2026-09-20"> <owner>jamesleung@google.com</owner> <owner>chrome-contextual-tasks-eng@google.com</owner>
diff --git a/tools/metrics/histograms/metadata/enterprise/enums.xml b/tools/metrics/histograms/metadata/enterprise/enums.xml index 2fdcda3..7774eb25 100644 --- a/tools/metrics/histograms/metadata/enterprise/enums.xml +++ b/tools/metrics/histograms/metadata/enterprise/enums.xml
@@ -2426,6 +2426,10 @@ <int value="1445" label="KioskPinchToZoomAllowed"/> <int value="1446" label="SecuritySignalsClientCertificatesSelectors"/> <int value="1447" label="AllowSocketPoolSizeRandomizationForProxies"/> + <int value="1448" label="DefaultSubAppsWithoutPromptsSetting"/> + <int value="1449" label="SubAppsWithoutPromptsAllowedForOrigins"/> + <int value="1450" label="SubAppsWithoutPromptsBlockedForOrigins"/> + <int value="1451" label="IsolatedModeSettings"/> </enum> <enum name="EnterprisePoliciesSources">
diff --git a/tools/metrics/histograms/metadata/extensions/enums.xml b/tools/metrics/histograms/metadata/extensions/enums.xml index 6648727..b35257c 100644 --- a/tools/metrics/histograms/metadata/extensions/enums.xml +++ b/tools/metrics/histograms/metadata/extensions/enums.xml
@@ -3646,6 +3646,20 @@ <int value="3" label="Dialog closed without user action"/> </enum> +<enum name="TabsRemoveActionType"> + <int value="0" label="kOtherRemovals"/> + <int value="1" label="kDSERemovalsWithoutUserGesture"/> + <int value="2" label="kDSERemovalsWithUserGesture"/> + <int value="3" label="kDSERemovalsAfterLandingOnSERP"/> +</enum> + +<enum name="TabsUpdateActionType"> + <int value="0" label="kOtherUpdates"/> + <int value="1" label="kDSERedirectsWithoutUserGesture"/> + <int value="2" label="kDSERedirectsWithUserGesture"/> + <int value="3" label="kDSERedirectsAfterLandingOnSERP"/> +</enum> + <enum name="UpdateDynamicRulesStatus"> <int value="0" label="kSuccess"/> <int value="1" label="kErrorReadJSONRules"/>
diff --git a/tools/metrics/histograms/metadata/extensions/histograms.xml b/tools/metrics/histograms/metadata/extensions/histograms.xml index 12af06d9..a5466302 100644 --- a/tools/metrics/histograms/metadata/extensions/histograms.xml +++ b/tools/metrics/histograms/metadata/extensions/histograms.xml
@@ -1251,7 +1251,7 @@ <histogram name="Extensions.DeclarativeNetRequest.RedirectAction" enum="DeclarativeNetRequestRedirectType" expires_after="2026-12-31"> - <owner>marwant@chromium.org</owner> + <owner>quarz@chromium.org</owner> <owner>src/extensions/OWNERS</owner> <summary> Logs the type of redirect action when a request is redirected by the @@ -4613,6 +4613,20 @@ </summary> </histogram> +<histogram name="Extensions.Tabs.RemoveAction" enum="TabsRemoveActionType" + expires_after="2026-12-31"> + <owner>quarz@chromium.org</owner> + <owner>extensions-core@chromium.org</owner> + <summary>Tracks Tabs API remove actions.</summary> +</histogram> + +<histogram name="Extensions.Tabs.UpdateAction" enum="TabsUpdateActionType" + expires_after="2026-12-31"> + <owner>quarz@chromium.org</owner> + <owner>extensions-core@chromium.org</owner> + <summary>Tracks Tabs API update actions.</summary> +</histogram> + <histogram name="Extensions.Toolbar.ExtensionsActivatedFromRequestAccessButton" units="Extension Count" expires_after="2027-05-03"> <owner>evasu@chromium.org</owner>
diff --git a/tools/metrics/histograms/metadata/feature_engagement/histograms.xml b/tools/metrics/histograms/metadata/feature_engagement/histograms.xml index c85590331..83ef95ab 100644 --- a/tools/metrics/histograms/metadata/feature_engagement/histograms.xml +++ b/tools/metrics/histograms/metadata/feature_engagement/histograms.xml
@@ -722,14 +722,6 @@ summary="Video tutorial card on NTP about voice search in chrome"/> <variant name="IPH_VideoTutorial_TryNow" summary="Video tutorial Try Now feature"/> - <variant name="IPH_WebFeedAwareness" - summary="proactive announcement of web feed on NTP/start surface"/> - <variant name="IPH_WebFeedFollow" - summary="proactive Web Feed follow recommendations"/> - <variant name="IPH_WebFeedPostFollowDialog" - summary="Web Feed successful follow operations"/> - <variant name="IPH_WebFeedPostFollowDialogWithUIUpdate" - summary="Web Feed successful follow operations with UI update"/> <variant name="IPH_WebUiHelpBubbleTest" summary="testing the WebUI help bubble"/> <variant name="IPH_WhatsNewUpdated"
diff --git a/tools/metrics/histograms/metadata/glic/enums.xml b/tools/metrics/histograms/metadata/glic/enums.xml index 79b72d44..b30288d 100644 --- a/tools/metrics/histograms/metadata/glic/enums.xml +++ b/tools/metrics/histograms/metadata/glic/enums.xml
@@ -187,6 +187,16 @@ <!-- LINT.ThenChange(//chrome/browser/glic/glic_metrics.h:EntryPointStatus) --> +<!-- LINT.IfChange(GlicExperimentalTriggeringState) --> + +<enum name="GlicExperimentalTriggeringState"> + <int value="0" label="Unavailable"/> + <int value="1" label="Needs Opt-In"/> + <int value="2" label="Ready"/> +</enum> + +<!-- LINT.ThenChange(//components/sync_device_info/device_info.h:GlicExperimentalTriggeringState) --> + <enum name="GlicFeatureDisabledReason"> <int value="0" label="Feature Flag Disabled"/> <int value="1" label="Country Disabled"/> @@ -278,7 +288,7 @@ <int value="16" label="CaptureScreenshot"/> <int value="17" label="ResizeWindow"/> <int value="18" label="EnableDragResize"/> - <int value="19" label="(obsolete) SetWindowDraggableAreas"/> + <int value="19" label="(Obsolete) SetWindowDraggableAreas"/> <int value="20" label="SetMinimumWidgetSize"/> <int value="21" label="SetMicrophonePermissionState"/> <int value="22" label="SetLocationPermissionState"/> @@ -998,6 +1008,12 @@ <int value="17" label="kFailedTimedOutDidNotCompleteOnboarding"/> <int value="18" label="kFailedLostInstance"/> <int value="19" label="kFailedSawNavigationDidNotCompleteOnboarding"/> + <int value="20" label="kFailedUnknown"/> + <int value="21" label="kFailedInvalidConversationId"/> + <int value="22" label="kFailedInvokeInProgress"/> + <int value="23" label="kFailedInvalidConfiguration"/> + <int value="24" label="kFailedNoClientFrame"/> + <int value="25" label="kFailedNoClipboardMetadata"/> </enum> <!-- LINT.ThenChange(//chrome/browser/glic/glic_metrics.h:ShareImageResult) -->
diff --git a/tools/metrics/histograms/metadata/glic/histograms.xml b/tools/metrics/histograms/metadata/glic/histograms.xml index 6af65fe..1faeb7ae 100644 --- a/tools/metrics/histograms/metadata/glic/histograms.xml +++ b/tools/metrics/histograms/metadata/glic/histograms.xml
@@ -69,7 +69,6 @@ <variant name="OnMicrophoneStatusChange"/> <variant name="OnModeChange"/> <variant name="OnReaction"/> - <variant name="OnRecordUseCounter"/> <variant name="OnResponseRated"/> <variant name="OnResponseStarted"/> <variant name="OnResponseStopped"/> @@ -90,7 +89,7 @@ <variant name="ScrollTo"/> <variant name="SetActuationOnWebSetting"/> <variant name="SetAudioDucking"/> - <variant name="SetClosedCaptioningState"/> + <variant name="SetClosedCaptioningSetting"/> <variant name="SetContextAccessIndicator"/> <variant name="SetLocationPermissionState"/> <variant name="SetMaximumNumberOfPinnedTabs"/> @@ -477,6 +476,16 @@ </summary> </histogram> +<histogram name="Glic.ExperimentalTriggering.StateOnActuationRequest" + enum="GlicExperimentalTriggeringState" expires_after="2027-05-06"> + <owner>sauski@google.com</owner> + <owner>qinmin@chromium.org</owner> + <summary> + Records the GlicExperimentalTriggeringState of the user when an experimental + actuation request is received. + </summary> +</histogram> + <histogram name="Glic.Focus.Settings.Shortcut.Customized" enum="BooleanEnabled" expires_after="2026-05-31"> <owner>bryantchandler@chromium.org</owner>
diff --git a/tools/metrics/histograms/metadata/histogram_suffixes_list.xml b/tools/metrics/histograms/metadata/histogram_suffixes_list.xml index 493cde14..7ce893ec 100644 --- a/tools/metrics/histograms/metadata/histogram_suffixes_list.xml +++ b/tools/metrics/histograms/metadata/histogram_suffixes_list.xml
@@ -185,14 +185,6 @@ <affected-histogram name="Autofill.StoredCreditCardCount.Local"/> </histogram_suffixes> -<histogram_suffixes name="AutofillStoredCardHasNickname" separator="."> - <owner>sujiezhu@google.com</owner> - <owner>osaul@google.com</owner> - <suffix name="WithNickname" label="Card with a valid nickname"/> - <affected-histogram name="Autofill.StoredCreditCardCount.Local"/> - <affected-histogram name="Autofill.StoredCreditCardCount.Server"/> -</histogram_suffixes> - <histogram_suffixes name="AutofillSyncState" separator="."> <suffix name="SignedIn" label="Signed in"/> <suffix name="SignedInAndSyncFeatureEnabled"
diff --git a/tools/metrics/histograms/metadata/omnibox/enums.xml b/tools/metrics/histograms/metadata/omnibox/enums.xml index da8dea82..4557ac7b 100644 --- a/tools/metrics/histograms/metadata/omnibox/enums.xml +++ b/tools/metrics/histograms/metadata/omnibox/enums.xml
@@ -210,6 +210,18 @@ <int value="4" label="SearchTerms"/> </enum> +<!-- LINT.IfChange(MigrationMatch) --> + +<enum name="MigrationMatch"> + <int value="0" label="Exact Match"/> + <int value="1" label="Host Match"/> + <int value="2" label="IDs Don't Match"/> + <int value="3" label="Invalid Checked URL"/> + <int value="4" label="URL Mismatch"/> +</enum> + +<!-- LINT.ThenChange(//components/search_engines/template_url_prepopulate_data_resolver.cc:MigrationMatch) --> + <!-- LINT.IfChange(OmniboxBuiltinEngineType) --> <enum name="OmniboxBuiltinEngineType">
diff --git a/tools/metrics/histograms/metadata/omnibox/histograms.xml b/tools/metrics/histograms/metadata/omnibox/histograms.xml index 0ea1289..54d45a5 100644 --- a/tools/metrics/histograms/metadata/omnibox/histograms.xml +++ b/tools/metrics/histograms/metadata/omnibox/histograms.xml
@@ -4519,6 +4519,36 @@ </summary> </histogram> +<histogram name="Omnibox.TemplateUrl.DBRefresh.MigrationMatch" + enum="MigrationMatch" expires_after="2026-12-31"> + <owner>dgn@google.com</owner> + <owner>chrome-regionalcapabilities@google.com</owner> + <summary> + The match or mismatch reason found while comparing prepopulated engines and + keywords DB engines during prepopulated engines migration. + + A "match" (e.g., exact or host match) indicates that an engine in + the keywords database corresponds to a prepopulated engine definition and + can be migrated. A "mismatch" (e.g., URL mismatch or invalid URL) + indicates that the existing engine does not sufficiently match the + prepopulated one, and thus cannot be safely migrated. + + Recorded for each engine under migration that matches an existing engine, + during the keywords DB refresh that is done on profile load after a database + version update, country change, or migration feature flag flip. + </summary> +</histogram> + +<histogram name="Omnibox.TemplateUrl.DBRefresh.TotalDuplicates" units="units" + expires_after="2026-12-31"> + <owner>dgn@google.com</owner> + <owner>chrome-regionalcapabilities@google.com</owner> + <summary> + The total number of duplicate prepopulated engine IDs found in the search + engine database during a refresh. + </summary> +</histogram> + <histogram name="Omnibox.TemplateUrl.DBRefresh.UnmatchedDefaultSearchCount" units="count" expires_after="2026-09-20"> <owner>dgn@google.com</owner> @@ -4536,6 +4566,17 @@ </summary> </histogram> +<histogram name="Omnibox.TemplateUrl.DseReconciler.MigrationMatch" + enum="MigrationMatch" expires_after="2026-12-31"> + <owner>dgn@google.com</owner> + <owner>chrome-reg-cap@google.com</owner> + <summary> + The type of match found during prepopulated engine migration in the DSE + reconciler. Recorded for each engine under migration that matches an + existing engine. + </summary> +</histogram> + <histogram name="Omnibox.TemplateUrl.Reconciliation.Type" enum="ReconciliationType" expires_after="2026-11-01"> <owner>ender@google.com</owner>
diff --git a/tools/metrics/histograms/metadata/optimization/enums.xml b/tools/metrics/histograms/metadata/optimization/enums.xml index 513ac4c..608bd13 100644 --- a/tools/metrics/histograms/metadata/optimization/enums.xml +++ b/tools/metrics/histograms/metadata/optimization/enums.xml
@@ -889,6 +889,17 @@ <int value="6" label="ContentsDeletedOnTabUpdate"/> </enum> +<!-- LINT.IfChange(PageContentExtractionEnablementSource) --> + +<enum name="PageContentExtractionEnablementSource"> + <int value="0" label="None"/> + <int value="1" label="Feature flag"/> + <int value="2" label="Observer(s) present"/> + <int value="3" label="Both"/> +</enum> + +<!-- LINT.ThenChange(//components/page_content_annotations/content/page_content_extraction_service.cc:EnablementSource) --> + </enums> </histogram-configuration>
diff --git a/tools/metrics/histograms/metadata/optimization/histograms.xml b/tools/metrics/histograms/metadata/optimization/histograms.xml index c7b82e02..6a88b8e5 100644 --- a/tools/metrics/histograms/metadata/optimization/histograms.xml +++ b/tools/metrics/histograms/metadata/optimization/histograms.xml
@@ -1296,7 +1296,7 @@ </histogram> <histogram - name="OptimizationGuide.ModelExecution.OnDeviceModelInstalledAtRegistrationTime" + name="OptimizationGuide.ModelExecution.OnDeviceModelInstalledAtRegistrationTime.{BaseModel}" enum="Boolean" expires_after="2027-04-14"> <owner>holte@chromium.org</owner> <owner>wittman@chromium.org</owner> @@ -1305,6 +1305,7 @@ on-device model installer is registered. This happens at most once per restart, when the device becomes eligible for on-device execution. </summary> + <token key="BaseModel" variants="BaseModel"/> </histogram> <histogram @@ -1806,6 +1807,19 @@ </summary> </histogram> +<histogram name="OptimizationGuide.OnDeviceModel.ManifestBrokerInstantiated" + enum="Boolean" expires_after="2027-04-14"> + <owner>zekunjiang@google.com</owner> + <owner>holte@chromium.org</owner> + <owner>wittman@chromium.org</owner> + <summary> + Only records `true`. Recorded once when `ManifestBrokerState` is + instantiated. This histogram helps compute the number of clients without an + installed model, by subtracting from it the clients that have logged to + `OptimizationGuide.OnDeviceModel.InstalledModel`. + </summary> +</histogram> + <histogram name="OptimizationGuide.OnDeviceModel.NewModelInstalled" enum="InstalledModel" expires_after="2026-10-15"> <owner>zekunjiang@google.com</owner> @@ -2181,6 +2195,17 @@ </summary> </histogram> +<histogram + name="OptimizationGuide.PageContentExtraction.EnablementSourcePerNavigation" + enum="PageContentExtractionEnablementSource" expires_after="2026-11-05"> + <owner>alexmt@chromium.org</owner> + <owner>chrome-intelligence-core@google.com</owner> + <summary> + Records the reason (if any) that page content extraction was enabled. + Recorded once per cross-document navigation. + </summary> +</histogram> + <histogram name="OptimizationGuide.PageContentExtraction.IsCacheHit" enum="BooleanCacheHit" expires_after="2026-09-16"> <owner>alexmt@chromium.org</owner> @@ -2191,6 +2216,17 @@ </summary> </histogram> +<histogram + name="OptimizationGuide.PageContentExtraction.ObserverCountPerNavigation" + units="count" expires_after="2026-11-05"> + <owner>alexmt@chromium.org</owner> + <owner>chrome-intelligence-core@google.com</owner> + <summary> + Records the number of active observers for page content extraction during a + cross-document navigation. Recorded once per cross-document navigation. + </summary> +</histogram> + <histogram name="OptimizationGuide.PageContentExtraction.OnDemand.IsPDF" enum="Boolean" expires_after="2026-09-16"> <owner>alexmt@chromium.org</owner>
diff --git a/tools/metrics/histograms/metadata/others/histograms.xml b/tools/metrics/histograms/metadata/others/histograms.xml index 0cf68c5..2c44caf 100644 --- a/tools/metrics/histograms/metadata/others/histograms.xml +++ b/tools/metrics/histograms/metadata/others/histograms.xml
@@ -7119,17 +7119,6 @@ </summary> </histogram> -<histogram name="SpellCheck.SpellingService.RequestDuration" units="ms" - expires_after="2024-12-01"> - <owner>shend@chromium.org</owner> - <owner>essential-inputs-team@google.com</owner> - <summary> - The elapsed time, in ms, between the moment the SimpleURLLoader starts - downloading the request and the moment the SimpleURLLoader callback is - invoked after the request finishes, whether an error or a success. - </summary> -</histogram> - <histogram name="SpellCheck.SpellingService.RequestHttpResponseCode" enum="HttpResponseCode" expires_after="2025-11-16"> <owner>shend@chromium.org</owner>
diff --git a/tools/metrics/histograms/metadata/persistent_cache/histograms.xml b/tools/metrics/histograms/metadata/persistent_cache/histograms.xml index eb65164e..fe963773 100644 --- a/tools/metrics/histograms/metadata/persistent_cache/histograms.xml +++ b/tools/metrics/histograms/metadata/persistent_cache/histograms.xml
@@ -107,6 +107,17 @@ </summary> </histogram> +<histogram name="SandboxedVfs.GetFileSystemIdError.{VfsClient}" + enum="PlatformFileError" expires_after="2027-05-01"> + <owner>grt@chromium.org</owner> + <owner>catan-team@chromium.org</owner> + <summary> + Reports the error encountered when attempting to retrieve the physical file + system ID for a sandboxed SQLite file set's main database file for the + {VfsClient}. + </summary> +</histogram> + <histogram name="SandboxedVfs.LockResult.{VfsClient}" enum="PlatformFileError" expires_after="2026-11-06"> <owner>grt@chromium.org</owner>
diff --git a/tools/metrics/histograms/metadata/profile/histograms.xml b/tools/metrics/histograms/metadata/profile/histograms.xml index 6eb446a72..fae22a9 100644 --- a/tools/metrics/histograms/metadata/profile/histograms.xml +++ b/tools/metrics/histograms/metadata/profile/histograms.xml
@@ -813,6 +813,17 @@ </summary> </histogram> +<histogram + name="ProfilePicker.FirstProfileTime.FirstWebContentsNonEmptyPaint.AutoLaunchByOs" + units="ms" expires_after="2026-10-25"> + <owner>atharvmaan@google.com</owner> + <owner>chrome-desktop-ui-waterloo@google.com</owner> + <summary> + Same as ProfilePicker.FirstProfileTime.FirstWebContentsNonEmptyPaint, but + recorded only for browser launches initiated automatically by the OS. + </summary> +</histogram> + <histogram name="ProfilePicker.FirstRun.DefaultBrowser" enum="FirstRunDefaultBrowserChoice" expires_after="2026-09-20"> <owner>dgn@chromium.org</owner>
diff --git a/tools/metrics/histograms/metadata/sharing/enums.xml b/tools/metrics/histograms/metadata/sharing/enums.xml index e43f1edf..dbd63ea 100644 --- a/tools/metrics/histograms/metadata/sharing/enums.xml +++ b/tools/metrics/histograms/metadata/sharing/enums.xml
@@ -130,6 +130,16 @@ <int value="12" label="SHARE_AS_TAB_GROUP"/> </enum> +<!-- LINT.IfChange(SharingShareSheetLongScreenshotsBitmapGenerationStatus) --> + +<enum name="SharingShareSheetLongScreenshotsBitmapGenerationStatus"> + <int value="0" label="Capture Completed"/> + <int value="1" label="Insufficient Memory"/> + <int value="2" label="General Generation Error"/> +</enum> + +<!-- LINT.ThenChange(/chrome/browser/share/android/java/src/org/chromium/chrome/browser/share/long_screenshots/LongScreenshotsUtils.java:BitmapGeneratorStatus) --> + </enums> </histogram-configuration>
diff --git a/tools/metrics/histograms/metadata/sharing/histograms.xml b/tools/metrics/histograms/metadata/sharing/histograms.xml index 70031ff..fa164827 100644 --- a/tools/metrics/histograms/metadata/sharing/histograms.xml +++ b/tools/metrics/histograms/metadata/sharing/histograms.xml
@@ -234,6 +234,17 @@ </summary> </histogram> +<histogram name="Sharing.LongScreenshots.MemoryLimitOnCapture" units="%" + expires_after="2026-10-01"> + <owner>jhimawan@google.com</owner> + <owner>src/components/paint_preview/OWNERS</owner> + <summary> + Records the memory limit percentage when a long screenshot capture is + initiated. Recorded for both the system (scroll capture) and share sheet + (compositor) paths. Android only. + </summary> +</histogram> + <histogram name="Sharing.MessageReceivedType" enum="SharingMessageType" expires_after="2026-09-27"> <owner>mvanouwerkerk@chromium.org</owner> @@ -290,6 +301,16 @@ </summary> </histogram> +<histogram name="Sharing.ScrollCapture.MemoryPressureOnSearch" + enum="MemoryPressureLevel" expires_after="2026-10-01"> + <owner>jhimawan@google.com</owner> + <owner>src/components/paint_preview/OWNERS</owner> + <summary> + Records the memory pressure level when a scroll capture search is initiated. + Recorded when the system requests scroll capture support. Android only. + </summary> +</histogram> + <histogram name="Sharing.ScrollCapture.SuccessfulCaptureDuration" units="ms" expires_after="2026-10-01"> <owner>ckitagawa@chromium.org</owner> @@ -480,6 +501,18 @@ </summary> </histogram> +<histogram name="Sharing.ShareSheetLongScreenshots.BitmapGeneratorStatus" + enum="SharingShareSheetLongScreenshotsBitmapGenerationStatus" + expires_after="2026-10-01"> + <owner>jhimawan@google.com</owner> + <owner>src/components/paint_preview/OWNERS</owner> + <summary> + Records the status of the Bitmap Generator when capturing long screenshot + from the share sheet (3-dot menu path). Recorded when the capture completes + or fails due to insufficient memory or other errors. Android only. + </summary> +</histogram> + <histogram name="Sharing.SharingHubAndroid.CustomAction" enum="ShareCustomAction" expires_after="2026-10-25"> <owner>wenyufu@chromium.org</owner>
diff --git a/tools/metrics/histograms/metadata/signin/enums.xml b/tools/metrics/histograms/metadata/signin/enums.xml index 33ef0f7..8d39264 100644 --- a/tools/metrics/histograms/metadata/signin/enums.xml +++ b/tools/metrics/histograms/metadata/signin/enums.xml
@@ -789,6 +789,7 @@ <int value="96" label="Indigo"/> <int value="97" label="Glic Invoke API"/> <int value="98" label="Secure Gateway Service"/> + <int value="99" label="Drive Picker Host"/> </enum> <!-- LINT.ThenChange(//components/signin/public/base/oauth_consumer_id.h:OAuthConsumerId) -->
diff --git a/tools/metrics/histograms/metadata/stability/enums.xml b/tools/metrics/histograms/metadata/stability/enums.xml index ddf4554..741f14c 100644 --- a/tools/metrics/histograms/metadata/stability/enums.xml +++ b/tools/metrics/histograms/metadata/stability/enums.xml
@@ -516,6 +516,7 @@ <int value="347" label="RFHI_WEBMCP_DUPLICATE_SET_RECEIVER"/> <int value="348" label="RFHI_WEBMCP_EXPOSED_NON_HTTPS_ORIGIN"/> <int value="349" label="RFHI_SYNCHONOUS_COMMIT_ORIGIN_MISMATCH"/> + <int value="350" label="RFHI_WEBMCP_INVALID_TOOL_OWNER"/> </enum> <enum name="BadMessageReasonExtensions">
diff --git a/tools/metrics/histograms/metadata/startup/histograms.xml b/tools/metrics/histograms/metadata/startup/histograms.xml index 0abe2a20..aa17d210 100644 --- a/tools/metrics/histograms/metadata/startup/histograms.xml +++ b/tools/metrics/histograms/metadata/startup/histograms.xml
@@ -1043,6 +1043,16 @@ </summary> </histogram> +<histogram name="Startup.FirstWebContents.NonEmptyPaint3.AutoLaunchByOs" + units="ms" expires_after="2026-10-25"> + <owner>atharvmaan@google.com</owner> + <owner>chrome-desktop-ui-waterloo@google.com</owner> + <summary> + Similar to Startup.FirstWebContents.NonEmptyPaint3, but recorded only for + browser launches initiated automatically by the OS. + </summary> +</histogram> + <histogram name="Startup.FirstWebContents.NonEmptyPaint3{StartupTemperature}" units="ms" expires_after="never"> <!-- expires-never: guiding metric (internal: go/chrome-browser-guiding-metrics) -->
diff --git a/tools/metrics/histograms/metadata/storage/histograms.xml b/tools/metrics/histograms/metadata/storage/histograms.xml index 34b9c79..63305b9 100644 --- a/tools/metrics/histograms/metadata/storage/histograms.xml +++ b/tools/metrics/histograms/metadata/storage/histograms.xml
@@ -417,15 +417,17 @@ </summary> </histogram> -<histogram name="IndexedDB.ContextShutdownDuration" units="ms" +<histogram name="IndexedDB.ContextShutdownDuration2" units="ms" expires_after="2026-10-02"> <owner>leimy@chromium.org</owner> <owner>evanstade@microsoft.com</owner> + <owner>abhishek.shanthkumar@microsoft.com</owner> <owner>chrome-owp-storage@google.com</owner> <summary> Logs the time it takes to completely shut down `IndexedDBContextImpl`, - starting from when it's requested on the UI thread to when destruction is - completed on the IDB task runner. + starting from when it's requested on the UI thread to when the last bucket's + destruction is completed. This is only recorded for regular (not incognito) + profiles. </summary> </histogram>
diff --git a/tools/perf/chrome_telemetry_build/BUILD.gn b/tools/perf/chrome_telemetry_build/BUILD.gn index d93d0c5..7a55f582 100644 --- a/tools/perf/chrome_telemetry_build/BUILD.gn +++ b/tools/perf/chrome_telemetry_build/BUILD.gn
@@ -189,6 +189,7 @@ data = [ "//build/android/pylib/", # chromium_config.py uses pylib to look for Java "//components/crash/content/tools/generate_breakpad_symbols.py", + "//third_party/webpagereplay/", "//tools/perf/chrome_telemetry_build/", "//tools/perf/core/", # chrome_telemetry_build/ depends on core/ ]
diff --git a/tools/perf/core/perfetto_binary_roller/binary_deps.json b/tools/perf/core/perfetto_binary_roller/binary_deps.json index d1b9cc7..4df0c42 100644 --- a/tools/perf/core/perfetto_binary_roller/binary_deps.json +++ b/tools/perf/core/perfetto_binary_roller/binary_deps.json
@@ -5,8 +5,8 @@ "full_remote_path": "perfetto-luci-artifacts/v52.0/linux-arm64/trace_processor_shell" }, "win": { - "hash": "4d49d3b8d93338bd138ebe185830801e03780511", - "full_remote_path": "chromium-telemetry/perfetto_binaries/trace_processor_shell/win/8a7e053633f02e811164a64c084ccbc03daa38b9/trace_processor_shell.exe" + "hash": "c3173c771b20aa218ee98d786faef363d6adb65d", + "full_remote_path": "chromium-telemetry/perfetto_binaries/trace_processor_shell/win/9cb76d9d67c05ebb32470bbe4f33bd4f623b8f0a/trace_processor_shell.exe" }, "linux_arm": { "hash": "46d798c1864490cbb2ee053d6eda436184470e69", @@ -21,8 +21,8 @@ "full_remote_path": "perfetto-luci-artifacts/ebf44e57a3b734c5281bdff53d9945805486004e/mac-arm64/trace_processor_shell" }, "linux": { - "hash": "ae04d1efa80fb218af181c1663aa9caf6256c8f5", - "full_remote_path": "chromium-telemetry/perfetto_binaries/trace_processor_shell/linux/8a7e053633f02e811164a64c084ccbc03daa38b9/trace_processor_shell" + "hash": "e2360222fdcbadeb7aeddea9b26a5e77309ef994", + "full_remote_path": "chromium-telemetry/perfetto_binaries/trace_processor_shell/linux/9cb76d9d67c05ebb32470bbe4f33bd4f623b8f0a/trace_processor_shell" } }, "power_profile.sql": {
diff --git a/tools/utr/cipd.py b/tools/utr/cipd.py index c5541f9..42f9c9b 100644 --- a/tools/utr/cipd.py +++ b/tools/utr/cipd.py
@@ -54,5 +54,14 @@ logging.getLogger('basic_logger').info(' '.join(cmd)) # The stdout of `cipd install` seems noisy, and all useful logs appear to go # to stderr anyway. - subprocess.check_call(cmd, stdout=subprocess.DEVNULL) + try: + subprocess.check_call(cmd, stdout=subprocess.DEVNULL) + except subprocess.CalledProcessError: + logging.error( + 'Failed to install recipe bundle via CIPD. Note: If running in ' + 'a non-interactive remote environment, this may be caused ' + 'by Context Aware Access (CAA) / BeyondCorp blocks preventing ' + 'downloads. Please ensure your remote session is explicitly ' + 'authenticated.') + raise return cipd_root_dir
diff --git a/tools/utr/recipe.py b/tools/utr/recipe.py index 050cb7b..65845908 100644 --- a/tools/utr/recipe.py +++ b/tools/utr/recipe.py
@@ -57,6 +57,10 @@ "Please run 'luci-auth login -scopes " "https://www.googleapis.com/auth/userinfo.email' to authenticate, " 'preferring your @google.com account if you have one.') + logging.error( + 'Note: If running in a non-interactive remote environment, ' + 'this failure may be due to Context Aware Access (CAA) / BeyondCorp ' + 'token blocks. You may need to re-authenticate explicitly.') return False return True
diff --git a/tools/utr/run.py b/tools/utr/run.py index c31bd79..0ac6b17 100755 --- a/tools/utr/run.py +++ b/tools/utr/run.py
@@ -267,7 +267,9 @@ cipd_bin_path = _SRC_DIR.joinpath('third_party', 'depot_tools', '.cipd_bin') if not cipd_bin_path.exists(): logging.warning( - ".cipd_bin folder not found. 'gclient sync' may need to be run") + ".cipd_bin folder not found. To resolve missing dependencies in a " + "remote workspace, please run 'gclient sync -D' and ensure " + "your session is authenticated with Context Aware Access (CAA).") else: os.environ["PATH"] = str(cipd_bin_path) + os.pathsep + os.environ["PATH"]
diff --git a/ui/accelerated_widget_mac/BUILD.gn b/ui/accelerated_widget_mac/BUILD.gn index 0f8a6f7..eb90cda4 100644 --- a/ui/accelerated_widget_mac/BUILD.gn +++ b/ui/accelerated_widget_mac/BUILD.gn
@@ -74,6 +74,7 @@ deps = [ "//base", + "//build:ios_buildflags", "//components/metal_util", "//media", "//skia",
diff --git a/ui/accelerated_widget_mac/ca_layer_tree_coordinator.mm b/ui/accelerated_widget_mac/ca_layer_tree_coordinator.mm index 80caa5b4..f11bfee 100644 --- a/ui/accelerated_widget_mac/ca_layer_tree_coordinator.mm +++ b/ui/accelerated_widget_mac/ca_layer_tree_coordinator.mm
@@ -10,6 +10,7 @@ #include "base/mac/mac_util.h" #include "base/task/single_thread_task_runner.h" #include "base/trace_event/trace_event.h" +#include "build/ios_buildflags.h" #include "ui/accelerated_widget_mac/ca_renderer_layer_tree.h" #include "ui/base/cocoa/animation_utils.h" #include "ui/base/cocoa/remote_layer_api.h" @@ -214,6 +215,7 @@ pixel_size_.width()); if (allow_remote_layers_) { +#if !BUILDFLAG(IS_IOS) || BUILDFLAG(IS_IOS_TVOS) if (!ca_context_) { // Create the CAContext to send this to the GPU process, and the layer // for the context. @@ -231,6 +233,7 @@ #endif ca_context_.layer = root_ca_layer_; } +#endif // !BUILDFLAG(IS_IOS) || BUILDFLAG(IS_IOS_TVOS) params.ca_context_id = [ca_context_ contextId]; } else { IOSurfaceRef io_surface = frame.layer_tree->GetContentIOSurface();
diff --git a/ui/accessibility/ax_enum_util.cc b/ui/accessibility/ax_enum_util.cc index 2a63656..1f44b95 100644 --- a/ui/accessibility/ax_enum_util.cc +++ b/ui/accessibility/ax_enum_util.cc
@@ -44,8 +44,8 @@ return "focus"; case ax::mojom::Event::kFocusAfterMenuClose: return "focusAfterMenuClose"; - case ax::mojom::Event::kFocusContext: - return "focusContext"; + case ax::mojom::Event::kFocusContextDeprecated: + return "focusContextDeprecated"; case ax::mojom::Event::kHide: return "hide"; case ax::mojom::Event::kHitTestResult:
diff --git a/ui/accessibility/ax_enums.mojom b/ui/accessibility/ax_enums.mojom index 88638d4..ad71eb96 100644 --- a/ui/accessibility/ax_enums.mojom +++ b/ui/accessibility/ax_enums.mojom
@@ -53,8 +53,7 @@ kFocus = 14, kFocusAfterMenuClose = 15, - // Contextual focus event that must delay the next focus event. - kFocusContext = 16, + kFocusContextDeprecated = 16, // Deprecated: Do not use. kHide = 17, // Remove: http://crbug.com/392502 kHitTestResult = 18, kHover = 19,
diff --git a/ui/accessibility/platform/automation/automation_api_util.cc b/ui/accessibility/platform/automation/automation_api_util.cc index 21555ce..4907320 100644 --- a/ui/accessibility/platform/automation/automation_api_util.cc +++ b/ui/accessibility/platform/automation/automation_api_util.cc
@@ -46,7 +46,7 @@ case ax::mojom::Event::kEndOfTest: case ax::mojom::Event::kFocus: case ax::mojom::Event::kFocusAfterMenuClose: - case ax::mojom::Event::kFocusContext: + case ax::mojom::Event::kFocusContextDeprecated: case ax::mojom::Event::kHide: case ax::mojom::Event::kHitTestResult: case ax::mojom::Event::kHover:
diff --git a/ui/accessibility/platform/ax_platform_node_auralinux.cc b/ui/accessibility/platform/ax_platform_node_auralinux.cc index 75b08b5d..60c2104 100644 --- a/ui/accessibility/platform/ax_platform_node_auralinux.cc +++ b/ui/accessibility/platform/ax_platform_node_auralinux.cc
@@ -4106,7 +4106,6 @@ OnExpandedStateChanged(HasState(ax::mojom::State::kExpanded)); break; case ax::mojom::Event::kFocus: - case ax::mojom::Event::kFocusContext: OnFocused(); break; case ax::mojom::Event::kFocusAfterMenuClose:
diff --git a/ui/accessibility/platform/ax_platform_node_cocoa.mm b/ui/accessibility/platform/ax_platform_node_cocoa.mm index c31fbe1..0a7e1753 100644 --- a/ui/accessibility/platform/ax_platform_node_cocoa.mm +++ b/ui/accessibility/platform/ax_platform_node_cocoa.mm
@@ -142,8 +142,6 @@ NSAccessibilityValueChangedNotification}, {ax::mojom::Event::kFocus, NSAccessibilityFocusedUIElementChangedNotification}, - {ax::mojom::Event::kFocusContext, - NSAccessibilityFocusedUIElementChangedNotification}, // Do not map kMenuStart/End to the Mac's opened/closed notifications. // kMenuStart/End are fired at the start/end of menu interaction on the
diff --git a/ui/accessibility/platform/ax_platform_node_win.cc b/ui/accessibility/platform/ax_platform_node_win.cc index 1f68b3bf6..e56c01331 100644 --- a/ui/accessibility/platform/ax_platform_node_win.cc +++ b/ui/accessibility/platform/ax_platform_node_win.cc
@@ -701,22 +701,25 @@ void AXPlatformNodeWin::NotifyAccessibilityEvent(ax::mojom::Event event_type) { TRACE_EVENT("accessibility", "NotifyAccessibilityEvent", perfetto::Flow::FromPointer(this)); - AXPlatformNodeBase::NotifyAccessibilityEvent(event_type); const bool selection_event_on_unselected_node = event_type == ax::mojom::Event::kSelection && HasBoolAttribute(ax::mojom::BoolAttribute::kSelected) && !GetBoolAttribute(ax::mojom::BoolAttribute::kSelected); // Menu items fire selection events but Windows screen readers work reliably - // with focus events. Remap here if the node is selected. + // with focus events. Remap selected menu items and legacy selection events + // without explicit selected state, but do not remap explicitly unselected + // items. if (event_type == ax::mojom::Event::kSelection && - !HasBoolAttribute(ax::mojom::BoolAttribute::kSelected)) { + !selection_event_on_unselected_node) { // A menu item could have something other than a role of // |ROLE_SYSTEM_MENUITEM|. Zoom modification controls for example have a // role of button. + const bool selection_state_is_unknown = + !HasBoolAttribute(ax::mojom::BoolAttribute::kSelected); if (int role = MSAARole(); role == ROLE_SYSTEM_MENUITEM) { event_type = ax::mojom::Event::kFocus; - } else if (role == ROLE_SYSTEM_LISTITEM) { + } else if (selection_state_is_unknown && role == ROLE_SYSTEM_LISTITEM) { if (const AXPlatformNodeBase* container = GetSelectionContainer()) { if (container->GetRole() == ax::mojom::Role::kListBox && !container->HasState(ax::mojom::State::kMultiselectable) && @@ -724,15 +727,19 @@ event_type = ax::mojom::Event::kFocus; } } - } else if (auto* parent = GetParentPlatformNodeWin(); parent) { - if (int parent_role = parent->MSAARole(); - parent_role == ROLE_SYSTEM_MENUPOPUP || - parent_role == ROLE_SYSTEM_LIST) { - event_type = ax::mojom::Event::kFocus; + } else if (selection_state_is_unknown) { + if (auto* parent = GetParentPlatformNodeWin(); parent) { + if (int parent_role = parent->MSAARole(); + parent_role == ROLE_SYSTEM_MENUPOPUP || + parent_role == ROLE_SYSTEM_LIST) { + event_type = ax::mojom::Event::kFocus; + } } } } + AXPlatformNodeBase::NotifyAccessibilityEvent(event_type); + // TODO(benjamin.beaudry): Uncomment DCHECK once https://crbug.com/331840469 // is fixed. // DCHECK(event_type != ax::mojom::Event::kLiveRegionChanged || @@ -8154,7 +8161,6 @@ case ax::mojom::Event::kStateChanged: return EVENT_OBJECT_STATECHANGE; case ax::mojom::Event::kFocus: - case ax::mojom::Event::kFocusContext: case ax::mojom::Event::kFocusAfterMenuClose: return EVENT_OBJECT_FOCUS; case ax::mojom::Event::kLiveRegionChanged: @@ -8201,7 +8207,6 @@ case ax::mojom::Event::kEndOfTest: return UiaRegistrarWin::GetInstance().GetTestCompleteEventId(); case ax::mojom::Event::kFocus: - case ax::mojom::Event::kFocusContext: case ax::mojom::Event::kFocusAfterMenuClose: return UIA_AutomationFocusChangedEventId; case ax::mojom::Event::kLiveRegionChanged:
diff --git a/ui/accessibility/platform/ax_platform_node_win_unittest.cc b/ui/accessibility/platform/ax_platform_node_win_unittest.cc index 964f060..321ffb6 100644 --- a/ui/accessibility/platform/ax_platform_node_win_unittest.cc +++ b/ui/accessibility/platform/ax_platform_node_win_unittest.cc
@@ -21,6 +21,7 @@ #include "base/run_loop.h" #include "base/strings/string_util_win.h" #include "base/strings/utf_string_conversions.h" +#include "base/test/bind.h" #include "base/test/metrics/histogram_tester.h" #include "base/test/scoped_feature_list.h" #include "base/test/task_environment.h" @@ -7526,6 +7527,40 @@ ASSERT_EQ(nullptr, option4_provider.Get()); } +TEST_F(AXPlatformNodeWinTest, SelectedMenuItemSelectionEventNotifiesFocus) { + AXNodeData root; + root.id = 1; + root.role = ax::mojom::Role::kMenu; + + AXNodeData menu_item; + menu_item.id = 2; + menu_item.role = ax::mojom::Role::kMenuItem; + menu_item.AddBoolAttribute(ax::mojom::BoolAttribute::kSelected, true); + root.child_ids.push_back(menu_item.id); + + Init(root, menu_item); + + bool focus_event_fired = false; + bool selection_event_fired = false; + AXPlatformNodeBase::SetOnNotifyEventCallbackForTesting( + ax::mojom::Event::kFocus, + base::BindLambdaForTesting([&]() { focus_event_fired = true; })); + AXPlatformNodeBase::SetOnNotifyEventCallbackForTesting( + ax::mojom::Event::kSelection, + base::BindLambdaForTesting([&]() { selection_event_fired = true; })); + + AXPlatformNodeFromNode(GetRoot()->children()[0]) + ->NotifyAccessibilityEvent(ax::mojom::Event::kSelection); + + AXPlatformNodeBase::SetOnNotifyEventCallbackForTesting( + ax::mojom::Event::kFocus, base::RepeatingClosure()); + AXPlatformNodeBase::SetOnNotifyEventCallbackForTesting( + ax::mojom::Event::kSelection, base::RepeatingClosure()); + + EXPECT_TRUE(focus_event_fired); + EXPECT_FALSE(selection_event_fired); +} + TEST_F(AXPlatformNodeWinTest, ISelectionItemProviderTable) { AXNodeData root; root.id = 1;
diff --git a/ui/android/BUILD.gn b/ui/android/BUILD.gn index 9505096..ab2270a 100644 --- a/ui/android/BUILD.gn +++ b/ui/android/BUILD.gn
@@ -587,6 +587,7 @@ "java/src/org/chromium/ui/util/MotionEventUtils.java", "java/src/org/chromium/ui/util/RunnableTimer.java", "java/src/org/chromium/ui/util/StyleUtils.java", + "java/src/org/chromium/ui/util/TimeoutRunnable.java", "java/src/org/chromium/ui/util/TokenHolder.java", "java/src/org/chromium/ui/util/ValueUtils.java", "java/src/org/chromium/ui/widget/AnchoredPopupWindow.java", @@ -853,6 +854,7 @@ "junit/src/org/chromium/ui/util/MotionEventUtilsTest.java", "junit/src/org/chromium/ui/util/RunnableTimerTest.java", "junit/src/org/chromium/ui/util/StyleUtilsTest.java", + "junit/src/org/chromium/ui/util/TimeoutRunnableTest.java", "junit/src/org/chromium/ui/util/TokenHolderTest.java", "junit/src/org/chromium/ui/widget/AnchoredPopupWindowTest.java", "junit/src/org/chromium/ui/widget/FlyoutPopupSpecCalculatorTest.java", @@ -878,6 +880,7 @@ "//base:base_java_test_support", "//base:base_junit_test_support", "//base:flagged_apis_java", + "//base:holder_java", "//base:service_loader_java", "//base/test:test_support_java", "//build/android:build_java",
diff --git a/ui/android/java/src/org/chromium/ui/util/TimeoutRunnable.java b/ui/android/java/src/org/chromium/ui/util/TimeoutRunnable.java new file mode 100644 index 0000000..7651890 --- /dev/null +++ b/ui/android/java/src/org/chromium/ui/util/TimeoutRunnable.java
@@ -0,0 +1,120 @@ +// Copyright 2026 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +package org.chromium.ui.util; + +import org.chromium.base.ThreadUtils.ThreadChecker; +import org.chromium.base.task.PostTask; +import org.chromium.base.task.TaskRunner; +import org.chromium.base.task.TaskTraits; +import org.chromium.build.annotations.EnsuresNonNull; +import org.chromium.build.annotations.NullMarked; +import org.chromium.build.annotations.Nullable; + +/** + * A wrapper around a successful execution {@link Runnable} that executes a fallback {@link + * Runnable} after a specified duration if not run early. All operations including the timeout + * runnable must be executed on the same thread. + * + * <p>If the timeout expires, the timeout runnable is executed instead of the primary one. If + * cancelled, neither are executed. Only one of the two runnables will be executed, exactly once. + * + * <p>The timeout countdown must be explicitly started by calling {@link #startTimeout()}. It does + * not start automatically upon construction. + */ +@NullMarked +public class TimeoutRunnable implements Runnable { + private final ThreadChecker mThreadChecker = new ThreadChecker(); + private final long mDurationMs; + + private @Nullable Runnable mRunnable; + private @Nullable Runnable mOnTimeout; + private @Nullable TaskRunner mRunner; + private boolean mTimeoutStarted; + + /** + * Creates a runnable that times out if it is not called within |durationMs| after starting. It + * cannot be called again after the timeout. When it times out, |onTimeout| is called instead. + * + * <p>The runnable will be run on the UI thread. + * + * @param runnable The runnable to run upon successful execution. + * @param onTimeout The runnable to run when time expires. + * @param durationMs The timeout duration in milliseconds. + */ + public TimeoutRunnable(Runnable runnable, Runnable onTimeout, long durationMs) { + this(runnable, onTimeout, durationMs, PostTask.getTaskRunner(TaskTraits.UI_DEFAULT)); + } + + /** + * Creates a runnable that times out if it is not called within |durationMs| after starting. It + * cannot be called again after the timeout. When it times out, |onTimeout| is called instead. + * + * @param runnable The runnable to run upon successful execution. + * @param onTimeout The runnable to run when time expires. + * @param durationMs The timeout duration in milliseconds. + * @param runner The task runner to use for the timeout. + */ + public TimeoutRunnable( + Runnable runnable, Runnable onTimeout, long durationMs, TaskRunner runner) { + mRunnable = runnable; + mOnTimeout = onTimeout; + mDurationMs = durationMs; + mRunner = runner; + } + + @Override + public void run() { + mThreadChecker.assertOnValidThread(); + if (mRunner == null) return; + + assertNotDestroyed(); + mRunnable.run(); + + destroy(); + } + + /** Starts the timeout countdown. */ + public void startTimeout() { + mThreadChecker.assertOnValidThread(); + if (mRunner == null || mTimeoutStarted) return; + mTimeoutStarted = true; + + assertNotDestroyed(); + mRunner.postDelayedTask(this::expire, mDurationMs); + } + + /** Cancels the runnable execution and cleans up internal references. */ + public void cancel() { + mThreadChecker.assertOnValidThread(); + if (mRunner == null) return; + + assertNotDestroyed(); + destroy(); + } + + private void expire() { + assert mTimeoutStarted; + mThreadChecker.assertOnValidThread(); + if (mRunner == null) return; + + assertNotDestroyed(); + mOnTimeout.run(); + + destroy(); + } + + private void destroy() { + mRunnable = null; + mOnTimeout = null; + mRunner = null; + } + + @EnsuresNonNull({"mRunner", "mRunnable", "mOnTimeout"}) + private void assertNotDestroyed() { + assert mRunner != null; + assert mRunnable != null; + assert mOnTimeout != null; + } +}
diff --git a/ui/android/java/src/org/chromium/ui/widget/AnchoredPopupWindow.java b/ui/android/java/src/org/chromium/ui/widget/AnchoredPopupWindow.java index 2e7c000..5fc5b9f4 100644 --- a/ui/android/java/src/org/chromium/ui/widget/AnchoredPopupWindow.java +++ b/ui/android/java/src/org/chromium/ui/widget/AnchoredPopupWindow.java
@@ -903,6 +903,16 @@ } /** + * Sets whether the popup is allowed to be clipped by the screen edges. See {@link + * PopupWindow#setClippingEnabled(boolean)}. + * + * @param enabled True if clipping is enabled, false otherwise. + */ + public void setClippingEnabled(boolean enabled) { + mPopupWindow.setClippingEnabled(enabled); + } + + /** * Changes whether the popup is touch modal or if outside touches will be sent to other windows * behind it. See {@link PopupWindow#setTouchModal(boolean)}. *
diff --git a/ui/android/junit/src/org/chromium/ui/util/TimeoutRunnableTest.java b/ui/android/junit/src/org/chromium/ui/util/TimeoutRunnableTest.java new file mode 100644 index 0000000..247342d --- /dev/null +++ b/ui/android/junit/src/org/chromium/ui/util/TimeoutRunnableTest.java
@@ -0,0 +1,142 @@ +// Copyright 2026 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +package org.chromium.ui.util; + +import org.junit.Assert; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.robolectric.Robolectric; + +import org.chromium.base.Holder; +import org.chromium.base.test.BaseRobolectricTestRunner; +import org.chromium.base.test.util.CallbackHelper; + +import java.util.concurrent.atomic.AtomicInteger; + +/** Unit tests for {@link TimeoutRunnable}. */ +@RunWith(BaseRobolectricTestRunner.class) +public class TimeoutRunnableTest { + @Test + public void testTimeoutFires() throws Exception { + CallbackHelper normalCallback = new CallbackHelper(); + CallbackHelper timeoutCallback = new CallbackHelper(); + TimeoutRunnable wrapped = + new TimeoutRunnable(normalCallback::notifyCalled, timeoutCallback::notifyCalled, 1); + wrapped.startTimeout(); + + Robolectric.flushForegroundThreadScheduler(); + timeoutCallback.waitForOnly(); + + wrapped.run(); + Assert.assertEquals( + "Normal callback should not be called", 0, normalCallback.getCallCount()); + Assert.assertEquals( + "Timeout callback should be called once", 1, timeoutCallback.getCallCount()); + } + + @Test + public void testExplicitRunBeforeTimeout() throws Exception { + AtomicInteger normalRunCount = new AtomicInteger(0); + AtomicInteger timeoutRunCount = new AtomicInteger(0); + TimeoutRunnable wrapped = + new TimeoutRunnable( + normalRunCount::incrementAndGet, timeoutRunCount::incrementAndGet, 1000); + wrapped.startTimeout(); + + wrapped.run(); + Assert.assertEquals(1, normalRunCount.get()); + Assert.assertEquals(0, timeoutRunCount.get()); + + Robolectric.flushForegroundThreadScheduler(); + Assert.assertEquals("Timeout should not fire", 0, timeoutRunCount.get()); + Assert.assertEquals("Normal callback should not run again", 1, normalRunCount.get()); + + wrapped.run(); + Assert.assertEquals( + "Explicit call after first run should be no-op", 1, normalRunCount.get()); + } + + @Test + public void testCancel() throws Exception { + CallbackHelper normalCallback = new CallbackHelper(); + CallbackHelper timeoutCallback = new CallbackHelper(); + TimeoutRunnable wrapped = + new TimeoutRunnable( + normalCallback::notifyCalled, timeoutCallback::notifyCalled, 1000); + wrapped.startTimeout(); + wrapped.cancel(); + + wrapped.run(); + Assert.assertEquals( + "Normal callback should not run after cancel", 0, normalCallback.getCallCount()); + + Robolectric.flushForegroundThreadScheduler(); + Assert.assertEquals( + "Timeout callback should not run after cancel", 0, timeoutCallback.getCallCount()); + } + + @Test + public void testRunWithoutStartingTimeout() throws Exception { + CallbackHelper normalCallback = new CallbackHelper(); + CallbackHelper timeoutCallback = new CallbackHelper(); + TimeoutRunnable wrapped = + new TimeoutRunnable( + normalCallback::notifyCalled, timeoutCallback::notifyCalled, 1000); + + wrapped.run(); + Assert.assertEquals(1, normalCallback.getCallCount()); + + wrapped.startTimeout(); + Robolectric.flushForegroundThreadScheduler(); + Assert.assertEquals(0, timeoutCallback.getCallCount()); + } + + @Test + public void testCancelBeforeStartingTimeout() throws Exception { + CallbackHelper normalCallback = new CallbackHelper(); + CallbackHelper timeoutCallback = new CallbackHelper(); + TimeoutRunnable wrapped = + new TimeoutRunnable( + normalCallback::notifyCalled, timeoutCallback::notifyCalled, 1000); + + wrapped.cancel(); + wrapped.startTimeout(); + + Robolectric.flushForegroundThreadScheduler(); + Assert.assertEquals(0, timeoutCallback.getCallCount()); + + wrapped.run(); + Assert.assertEquals(0, normalCallback.getCallCount()); + } + + @Test + public void testMultipleStartTimeoutCalls() throws Exception { + CallbackHelper normalCallback = new CallbackHelper(); + CallbackHelper timeoutCallback = new CallbackHelper(); + TimeoutRunnable wrapped = + new TimeoutRunnable(normalCallback::notifyCalled, timeoutCallback::notifyCalled, 1); + + wrapped.startTimeout(); + wrapped.startTimeout(); + + Robolectric.flushForegroundThreadScheduler(); + timeoutCallback.waitForOnly(); + Assert.assertEquals(1, timeoutCallback.getCallCount()); + } + + @Test(expected = AssertionError.class) + public void testThreadCheckerCrash() throws Exception { + Holder<TimeoutRunnable> wrappedHolder = new Holder<>(null); + Thread thread = + new Thread( + () -> { + wrappedHolder.onResult(new TimeoutRunnable(() -> {}, () -> {}, 1000)); + }); + thread.start(); + thread.join(); + + wrappedHolder.get().run(); + } +}
diff --git a/ui/base/ui_base_features.cc b/ui/base/ui_base_features.cc index b78b159..1fb0e89d 100644 --- a/ui/base/ui_base_features.cc +++ b/ui/base/ui_base_features.cc
@@ -65,7 +65,7 @@ base::FEATURE_DISABLED_BY_DEFAULT); // Controls replacement of CATransactionCoordinator with a new implementation. -BASE_FEATURE(kCATransactionV2, base::FEATURE_ENABLED_BY_DEFAULT); +BASE_FEATURE(kCATransactionV2, base::FEATURE_DISABLED_BY_DEFAULT); #endif // BUILDFLAG(IS_MAC) #if BUILDFLAG(IS_CHROMEOS)
diff --git a/ui/compositor/compositor.h b/ui/compositor/compositor.h index 7186d67..13b23123 100644 --- a/ui/compositor/compositor.h +++ b/ui/compositor/compositor.h
@@ -29,7 +29,7 @@ #include "cc/paint/element_id.h" #include "cc/trees/layer_tree_host.h" #include "cc/trees/layer_tree_host_delegate.h" -#include "cc/trees/layer_tree_host_single_thread_client.h" +#include "cc/trees/layer_tree_host_single_thread_delegate.h" #include "cc/trees/paint_holding_reason.h" #include "cc/trees/property_tree.h" #include "components/viz/common/frame_sinks/begin_frame_args.h" @@ -152,11 +152,12 @@ // displayable form of pixels comprising a single widget's contents. It draws an // appropriately transformed texture for each transformed view in the widget's // view hierarchy. -class COMPOSITOR_EXPORT Compositor : public base::PowerSuspendObserver, - public cc::LayerTreeHostDelegate, - public cc::LayerTreeHostSingleThreadClient, - public viz::HostFrameSinkClient, - public CompositorMetricsTrackerHost { +class COMPOSITOR_EXPORT Compositor + : public base::PowerSuspendObserver, + public cc::LayerTreeHostDelegate, + public cc::LayerTreeHostSingleThreadDelegate, + public viz::HostFrameSinkClient, + public CompositorMetricsTrackerHost { public: Compositor(const viz::FrameSinkId& frame_sink_id, ui::ContextFactory* context_factory, @@ -443,7 +444,7 @@ base::TimeDelta first_scroll_delay, base::TimeTicks first_scroll_timestamp) override {} - // cc::LayerTreeHostSingleThreadClient implementation. + // cc::LayerTreeHostSingleThreadDelegate implementation. void DidSubmitCompositorFrame() override; void DidLoseLayerTreeFrameSink() override {} void FrameIntervalUpdated(base::TimeDelta interval) override;
diff --git a/ui/display/mac/ca_display_link_mac.mm b/ui/display/mac/ca_display_link_mac.mm index afe8181..7303f7f 100644 --- a/ui/display/mac/ca_display_link_mac.mm +++ b/ui/display/mac/ca_display_link_mac.mm
@@ -129,9 +129,12 @@ CGDirectDisplayID display_id, bool success, bool in_gpu_process) { - // Only record the one from the GPU process as we cannot track the status from - // multi-process in one |globals.recorded_displays|. - if (!in_gpu_process) { + // Only record CADisplayLink creation status from one process to avoid + // duplicate logging as they cannot be tracked across multiple processes using + // a single |recorded_displays| set. If kCADisplayLinkInGpuThenInBrowser + // (disabled by default) is enabled, it should record the ones in_gpu_process + // instead. + if (in_gpu_process) { return; } auto& globals = CADisplayLinkGlobals::Get();
diff --git a/ui/display/mac/display_link_mac.mm b/ui/display/mac/display_link_mac.mm index 0507b2b..b9aebf7 100644 --- a/ui/display/mac/display_link_mac.mm +++ b/ui/display/mac/display_link_mac.mm
@@ -18,6 +18,12 @@ // For testing only. Create CADisplayLink in the GPU process. BASE_FEATURE(kCADisplayLinkInGpu, base::FEATURE_DISABLED_BY_DEFAULT); +// For testing only. If enabled, CADisplayLink is initially created in the GPU +// process, but falls back to the browser process after a power event or +// refresh rate change. +BASE_FEATURE(kCADisplayLinkInGpuThenInBrowser, + base::FEATURE_DISABLED_BY_DEFAULT); + //////////////////////////////////////////////////////////////////////////////// // DisplayLinkMac @@ -50,7 +56,7 @@ CGDirectDisplayID display_id = base::checked_cast<CGDirectDisplayID>(vsync_display_id); - // CADisplayLink is available only for MacOS 14.0+. + // For testing only. CADisplayLink is available only for MacOS 14.0+. if (@available(macos 14.0, *)) { if (base::FeatureList::IsEnabled(kCADisplayLinkInGpu)) { return CADisplayLinkMac::GetForDisplay(display_id, @@ -59,10 +65,14 @@ } if (SupportsDisplayLinkMacInBrowser()) { - if (CADisplayLinkMac::IsValidInGpuProcess(display_id)) { + // For testing only. If kCADisplayLinkInGpuThenInBrowser is enabled, attempt + // to use the GPU process instance if it is still valid. + if (base::FeatureList::IsEnabled(kCADisplayLinkInGpuThenInBrowser) && + CADisplayLinkMac::IsValidInGpuProcess(display_id)) { return CADisplayLinkMac::GetForDisplay(display_id, /*in_gpu_process=*/true); } + return ExternalDisplayLinkMac::GetForDisplay(display_id); }
diff --git a/ui/gl/gl_features.cc b/ui/gl/gl_features.cc index f87f9e7c..2d8772c 100644 --- a/ui/gl/gl_features.cc +++ b/ui/gl/gl_features.cc
@@ -53,9 +53,11 @@ kPassthroughCommandDecoderBlockListByManufacturer{ &kDefaultPassthroughCommandDecoder, "BlockListByManufacturer", ""}; +// b/455412928 flickering issue with WebView on the following XR devices const base::FeatureParam<std::string> kPassthroughCommandDecoderBlockListByModel{ - &kDefaultPassthroughCommandDecoder, "BlockListByModel", ""}; + &kDefaultPassthroughCommandDecoder, "BlockListByModel", + "SM-I610|SM-I610H|Robin XR"}; const base::FeatureParam<std::string> kPassthroughCommandDecoderBlockListByBoard{ @@ -105,18 +107,8 @@ // Feature lives in ui/gl because it affects the GL binding initialization on // platforms that would otherwise not default to using EGL bindings. BASE_FEATURE(kDefaultPassthroughCommandDecoder, - base::FEATURE_DISABLED_BY_DEFAULT); - -// Add a small delay in shader compiling if validating command decoder is used. -// This is to verify if passthrough command decoder impacting negatively top -// level metrics could be due to slower shader compiling. -BASE_FEATURE(kAddDelayToGLCompileShader, base::FEATURE_DISABLED_BY_DEFAULT); -// Histogram |GrCompileShaderUs| mean is 1.8ms (native) vs 3.1ms (ANGLE). -// Therefore, we add a 1.3ms delay to shader compiling. -constexpr base::FeatureParam<base::TimeDelta> kGLCompileShaderDelay = { - &kAddDelayToGLCompileShader, /*name=*/"interval", - /*default_value=*/base::Microseconds(1300)}; -#endif // !defined(PASSTHROUGH_COMMAND_DECODER_LAUNCHED) + base::FEATURE_ENABLED_BY_DEFAULT); +#endif // BUILDFLAG(ENABLE_VALIDATING_COMMAND_DECODER) // Controls whether the GPU process falls back to software if GLES3 is not // supported. @@ -370,20 +362,6 @@ return base::FeatureList::IsEnabled(kAllowSoftwareGLFallbackDueToCrashes); } -base::TimeDelta GetGLCompileShaderDelay() { -#if BUILDFLAG(ENABLE_VALIDATING_COMMAND_DECODER) - if (UsePassthroughCommandDecoder()) { - return base::TimeDelta(); - } - if (!base::FeatureList::IsEnabled(kAddDelayToGLCompileShader)) { - return base::TimeDelta(); - } - return kGLCompileShaderDelay.Get(); -#else - return base::TimeDelta(); -#endif // BUILDFLAG(ENABLE_VALIDATING_COMMAND_DECODER) -} - BASE_FEATURE(kAllowANGLED3D9Fallback, base::FEATURE_DISABLED_BY_DEFAULT); bool IsANGLED3D9FallbackAllowed() {
diff --git a/ui/gl/gl_features.h b/ui/gl/gl_features.h index ca7ce81..40c2fb5d 100644 --- a/ui/gl/gl_features.h +++ b/ui/gl/gl_features.h
@@ -25,7 +25,6 @@ // All features in alphabetical order. The features should be documented // alongside the definition of their values in the .cc file. GL_EXPORT BASE_DECLARE_FEATURE(kDefaultPassthroughCommandDecoder); -GL_EXPORT BASE_DECLARE_FEATURE(kAddDelayToGLCompileShader); #endif GL_EXPORT BASE_DECLARE_FEATURE(kFallbackToSWIfGLES3NotSupported); @@ -81,10 +80,6 @@ GL_EXPORT bool IsSoftwareGLFallbackDueToCrashesAllowed( const base::CommandLine* command_line); -// Query the delay we add to glCompileShader. -// Default is 0 if kAddDelayToGLCompileShader is off. -GL_EXPORT base::TimeDelta GetGLCompileShaderDelay(); - // Check if ANGLE's D3D9 backend is a valid fallback if D3D11 does not // initialize or is disabled. GL_EXPORT bool IsANGLED3D9FallbackAllowed();
diff --git a/ui/gl/init/create_gr_gl_interface.cc b/ui/gl/init/create_gr_gl_interface.cc index 787d1f94..4d43216 100644 --- a/ui/gl/init/create_gr_gl_interface.cc +++ b/ui/gl/init/create_gr_gl_interface.cc
@@ -31,13 +31,6 @@ // synchronization on platforms that does not have GL fences but support EGL namespace { -// If enabled, adds a delay to GL program link whose value is given by the -// feature param. Used for an ablation study. -BASE_FEATURE(kAddDelayToGLProgramLink, base::FEATURE_DISABLED_BY_DEFAULT); -constexpr base::FeatureParam<int> kGLProgramLinkDelayMicroseconds{ - &kAddDelayToGLProgramLink, /*name=*/"GLProgramLinkDelayMicroseconds", - /*default_value=*/1000}; - struct EGLFenceData { EGLSync sync; EGLDisplay display; @@ -188,11 +181,6 @@ gl::ScopedProgressReporter scoped_reporter(progress_reporter); SCOPED_UMA_HISTOGRAM_TIMER_MICROS("Gpu.GrCompileShaderUs"); - base::TimeDelta delay = features::GetGLCompileShaderDelay(); - if (delay.is_positive()) { - base::PlatformThread::Sleep(delay); - } - func(shader, args...); GLint compile_result = 0; @@ -215,11 +203,6 @@ gl::ScopedProgressReporter scoped_reporter(progress_reporter); SCOPED_UMA_HISTOGRAM_TIMER_MICROS("Gpu.GrLinkProgramUs"); - if (base::FeatureList::IsEnabled(kAddDelayToGLProgramLink)) { - base::PlatformThread::Sleep( - base::Microseconds(kGLProgramLinkDelayMicroseconds.Get())); - } - func(program, args...); GLint compile_result = 0;
diff --git a/ui/menus/simple_menu_model.cc b/ui/menus/simple_menu_model.cc index 12e64797..77fdd39 100644 --- a/ui/menus/simple_menu_model.cc +++ b/ui/menus/simple_menu_model.cc
@@ -339,6 +339,12 @@ MenuItemsChanged(); } +void SimpleMenuModel::SetIconForCommandId(int command_id, + const ui::ImageModel& icon) { + std::optional<size_t> index = GetIndexOfCommandId(command_id); + SetIcon(index.value(), icon); +} + void SimpleMenuModel::SetLabel(size_t index, const std::u16string& label) { items_[ValidateItemIndex(index)].label = label; MenuItemsChanged();
diff --git a/ui/menus/simple_menu_model.h b/ui/menus/simple_menu_model.h index cd41d9e..531f44d 100644 --- a/ui/menus/simple_menu_model.h +++ b/ui/menus/simple_menu_model.h
@@ -215,6 +215,9 @@ // Sets the icon for the item at |index|. void SetIcon(size_t index, const ui::ImageModel& icon); + // Sets the icon for the item with the given |command_id|. + void SetIconForCommandId(int command_id, const ui::ImageModel& icon); + // Sets the label for the item at |index|. void SetLabel(size_t index, const std::u16string& label);
diff --git a/ui/menus/simple_menu_model_unittest.cc b/ui/menus/simple_menu_model_unittest.cc index 7dcdf7c..ead69e6 100644 --- a/ui/menus/simple_menu_model_unittest.cc +++ b/ui/menus/simple_menu_model_unittest.cc
@@ -300,6 +300,22 @@ EXPECT_FALSE(simple_menu_model.GetIconAt(1).IsEmpty()); } +TEST(SimpleMenuModelTest, SetIconForCommandId) { + SimpleMenuModel simple_menu_model(nullptr); + simple_menu_model.AddItem(/*command_id*/ 5, u"menu item 0"); + simple_menu_model.AddItem(/*command_id*/ 6, u"menu item 1"); + + EXPECT_TRUE(simple_menu_model.GetIconAt(0).IsEmpty()); + EXPECT_TRUE(simple_menu_model.GetIconAt(1).IsEmpty()); + + const ui::ImageModel icon = + ui::ImageModel::FromImage(gfx::test::CreateImage(16, 16)); + simple_menu_model.SetIconForCommandId(5, icon); + + EXPECT_FALSE(simple_menu_model.GetIconAt(0).IsEmpty()); + EXPECT_TRUE(simple_menu_model.GetIconAt(1).IsEmpty()); +} + TEST(SimpleMenuModelTest, InheritsSubMenuAlert) { DelegateBase delegate; SimpleMenuModel submenu_model(&delegate);
diff --git a/ui/views/accessibility/tree/view_accessibility_ax_tree_source.cc b/ui/views/accessibility/tree/view_accessibility_ax_tree_source.cc index ac382b35..fe96925 100644 --- a/ui/views/accessibility/tree/view_accessibility_ax_tree_source.cc +++ b/ui/views/accessibility/tree/view_accessibility_ax_tree_source.cc
@@ -61,17 +61,19 @@ tree_data->parent_tree_id = parent_tree_id_; tree_data->loaded = true; tree_data->loading_progress = 1.0; - tree_data->focus_id = focused_node_id_; + const ui::AXNodeID focus_id = + transient_focus_id_for_serialization_.value_or(focused_node_id_); + tree_data->focus_id = focus_id; // Populate text selection fields from the focused node's attributes. - if (focused_node_id_ != ui::kInvalidAXNodeID) { - if (ViewAccessibility* focused_node = cache_->Get(focused_node_id_)) { + if (focus_id != ui::kInvalidAXNodeID) { + if (ViewAccessibility* focused_node = cache_->Get(focus_id)) { ui::AXNodeData node_data; focused_node->GetAccessibleNodeData(&node_data); if (node_data.HasIntAttribute(ax::mojom::IntAttribute::kTextSelStart) && node_data.HasIntAttribute(ax::mojom::IntAttribute::kTextSelEnd)) { - tree_data->sel_anchor_object_id = focused_node_id_; - tree_data->sel_focus_object_id = focused_node_id_; + tree_data->sel_anchor_object_id = focus_id; + tree_data->sel_focus_object_id = focus_id; tree_data->sel_anchor_offset = node_data.GetIntAttribute(ax::mojom::IntAttribute::kTextSelStart); tree_data->sel_focus_offset =
diff --git a/ui/views/accessibility/tree/view_accessibility_ax_tree_source.h b/ui/views/accessibility/tree/view_accessibility_ax_tree_source.h index 9c39b933..e8c7e3f 100644 --- a/ui/views/accessibility/tree/view_accessibility_ax_tree_source.h +++ b/ui/views/accessibility/tree/view_accessibility_ax_tree_source.h
@@ -7,6 +7,7 @@ #include <algorithm> #include <functional> +#include <optional> #include <string> #include <vector> @@ -46,6 +47,12 @@ void SetParentTreeId(const ui::AXTreeID& id) { parent_tree_id_ = id; } void SetFocusedNodeId(ui::AXNodeID id) { focused_node_id_ = id; } + void SetTransientFocusIdForNextSerialization(ui::AXNodeID id) { + transient_focus_id_for_serialization_ = id; + } + void ClearTransientFocusIdForNextSerialization() { + transient_focus_id_for_serialization_.reset(); + } // AXTreeSource: bool GetTreeData(ui::AXTreeData* data) const override; @@ -86,6 +93,9 @@ // The AXNodeID of the currently focused node, set by WidgetAXManager. ui::AXNodeID focused_node_id_ = ui::kInvalidAXNodeID; + + // The AXTreeData::focus_id to use for the next transient focus serialization. + std::optional<ui::AXNodeID> transient_focus_id_for_serialization_; }; } // namespace views
diff --git a/ui/views/accessibility/tree/widget_ax_manager.cc b/ui/views/accessibility/tree/widget_ax_manager.cc index 4b08de9..0a9c45d 100644 --- a/ui/views/accessibility/tree/widget_ax_manager.cc +++ b/ui/views/accessibility/tree/widget_ax_manager.cc
@@ -88,7 +88,6 @@ // being addressed incrementally, one event at a time. switch (event_type) { case Event::kFocusAfterMenuClose: - case Event::kFocusContext: case Event::kMenuEnd: case Event::kMenuPopupEnd: case Event::kMenuPopupStart: @@ -167,6 +166,19 @@ } } +void WidgetAXManager::OnTransientFocusRequested(ViewAccessibility& view_ax) { + if (!is_enabled_) { + return; + } + + CHECK(tree_source_); + pending_data_updates_.insert(view_ax.GetUniqueId()); + tree_source_->SetTransientFocusIdForNextSerialization(view_ax.GetUniqueId()); + auto clear_transient_focus = absl::MakeCleanup( + [this] { tree_source_->ClearTransientFocusIdForNextSerialization(); }); + SendPendingUpdate(); +} + void WidgetAXManager::OnDataChanged(ViewAccessibility& view_ax) { if (!is_enabled_) { return;
diff --git a/ui/views/accessibility/tree/widget_ax_manager.h b/ui/views/accessibility/tree/widget_ax_manager.h index 51966ea..2bc4736 100644 --- a/ui/views/accessibility/tree/widget_ax_manager.h +++ b/ui/views/accessibility/tree/widget_ax_manager.h
@@ -72,6 +72,7 @@ void RemoveObserver(WidgetAXManagerObserver* observer); void OnEvent(ViewAccessibility& view_ax, ax::mojom::Event event_type); + void OnTransientFocusRequested(ViewAccessibility& view_ax); void OnDataChanged(ViewAccessibility& view_ax); void OnChildAdded(ViewAccessibility& child, ViewAccessibility& parent);
diff --git a/ui/views/accessibility/tree/widget_ax_manager_unittest.cc b/ui/views/accessibility/tree/widget_ax_manager_unittest.cc index caf21ae..051387d 100644 --- a/ui/views/accessibility/tree/widget_ax_manager_unittest.cc +++ b/ui/views/accessibility/tree/widget_ax_manager_unittest.cc
@@ -1084,6 +1084,115 @@ EXPECT_TRUE(found_v2); } +TEST_F(WidgetAXManagerTest, TransientFocus_SerializesBeforeRestoredFocus) { + struct FocusEvent { + ui::AXNodeID id; + std::string name; + }; + + WidgetAXManagerTestApi api(manager()); + api.Enable(); + + auto* root = widget()->GetRootView(); + auto* focused_view = AddFocusableView(); + api.WaitForNextSerialization(); + + widget()->Show(); + + const ui::AXNodeID root_id = + static_cast<ui::AXNodeID>(root->GetViewAccessibility().GetUniqueId()); + const ui::AXNodeID focused_view_id = static_cast<ui::AXNodeID>( + focused_view->GetViewAccessibility().GetUniqueId()); + + std::vector<FocusEvent> focus_events; + ui::AXTreeManager::SetFocusChangeCallbackForTesting(base::BindRepeating( + [](std::vector<FocusEvent>* focus_events, + ui::BrowserAccessibilityManager* manager) { + ui::BrowserAccessibility* focus = manager->GetFocus(); + focus_events->push_back({focus ? focus->GetId() : ui::kInvalidAXNodeID, + focus ? focus->GetStringAttribute( + ax::mojom::StringAttribute::kName) + : std::string()}); + }, + &focus_events, api.ax_tree_manager())); + base::ScopedClosureRunner reset_focus_callback(base::BindOnce( + []() { ui::AXTreeManager::SetFocusChangeCallbackForTesting({}); })); + + constexpr char kUpdatedTitle[] = "Updated window title"; + root->GetViewAccessibility().SetName(kUpdatedTitle); + root->GetViewAccessibility().NotifyTransientFocus(); + + bool found_context_focus_id = false; + bool found_updated_root = false; + for (const auto& update : api.last_serialization().updates) { + if (update.has_tree_data && update.tree_data.focus_id == root_id) { + found_context_focus_id = true; + } + for (const auto& node : update.nodes) { + if (node.id == root_id && + node.GetStringAttribute(ax::mojom::StringAttribute::kName) == + kUpdatedTitle) { + found_updated_root = true; + } + } + } + EXPECT_TRUE(found_context_focus_id); + EXPECT_TRUE(found_updated_root); + EXPECT_TRUE(api.last_serialization().events.empty()); + ASSERT_EQ(focus_events.size(), 1u); + EXPECT_EQ(focus_events[0].id, root_id); + EXPECT_EQ(focus_events[0].name, kUpdatedTitle); + EXPECT_EQ(api.ax_tree_manager()->GetTreeData().focus_id, root_id); + + focused_view->RequestFocus(); + api.WaitForNextSerialization(); + + ASSERT_GE(focus_events.size(), 2u); + EXPECT_EQ(focus_events[0].id, root_id); + EXPECT_EQ(focus_events[1].id, focused_view_id); + EXPECT_EQ(api.ax_tree_manager()->GetTreeData().focus_id, focused_view_id); +} + +TEST_F(WidgetAXManagerTest, TransientFocus_SerializesVirtualView) { + auto* root = widget()->GetRootView(); + auto* container = root->AddChildView(std::make_unique<View>()); + auto virtual_context = std::make_unique<AXVirtualView>(); + auto* virtual_context_ptr = virtual_context.get(); + virtual_context_ptr->SetRole(ax::mojom::Role::kGroup); + virtual_context_ptr->SetName("Virtual context"); + const ui::AXNodeID virtual_context_id = static_cast<ui::AXNodeID>( + virtual_context_ptr->ViewAccessibility::GetUniqueId()); + container->GetViewAccessibility().AddVirtualChildView( + std::move(virtual_context)); + + WidgetAXManagerTestApi api(manager()); + api.Enable(); + + constexpr char kUpdatedContextName[] = "Updated virtual context"; + virtual_context_ptr->SetName(kUpdatedContextName); + virtual_context_ptr->NotifyTransientFocus(); + + bool found_virtual_focus_id = false; + bool found_updated_virtual_context = false; + for (const auto& update : api.last_serialization().updates) { + if (update.has_tree_data && + update.tree_data.focus_id == virtual_context_id) { + found_virtual_focus_id = true; + } + for (const auto& node : update.nodes) { + if (node.id == virtual_context_id && + node.GetStringAttribute(ax::mojom::StringAttribute::kName) == + kUpdatedContextName) { + found_updated_virtual_context = true; + } + } + } + EXPECT_TRUE(found_virtual_focus_id); + EXPECT_TRUE(found_updated_virtual_context); + EXPECT_TRUE(api.last_serialization().events.empty()); + EXPECT_EQ(api.ax_tree_manager()->GetTreeData().focus_id, virtual_context_id); +} + TEST_F(WidgetAXManagerTest, TextSelection_PopulatesTreeData) { WidgetAXManagerTestApi api(manager()); api.Enable();
diff --git a/ui/views/accessibility/view_accessibility.cc b/ui/views/accessibility/view_accessibility.cc index 1fe6b347..3e6af9b 100644 --- a/ui/views/accessibility/view_accessibility.cc +++ b/ui/views/accessibility/view_accessibility.cc
@@ -294,6 +294,26 @@ NotifyEvent(ax::mojom::Event::kFocusAfterMenuClose, true); } +void ViewAccessibility::NotifyTransientFocus() { + if (IsViewsAccessibilityTreeEnabled()) { + if (!ready_to_notify_events_) { + return; + } + + Widget* const widget = GetWidget(); + if (!widget || !widget->GetNativeView()) { + return; + } + + if (auto* ax_manager = widget->ax_manager()) { + ax_manager->OnTransientFocusRequested(*this); + } + return; + } + + NotifyEvent(ax::mojom::Event::kFocus, true); +} + void ViewAccessibility::SetIsLeaf(bool value) { if (value == ViewAccessibility::IsLeaf()) { return;
diff --git a/ui/views/accessibility/view_accessibility.h b/ui/views/accessibility/view_accessibility.h index af21de3..cecf258 100644 --- a/ui/views/accessibility/view_accessibility.h +++ b/ui/views/accessibility/view_accessibility.h
@@ -173,6 +173,9 @@ // Call when a menu closes, to restore focus to where it was previously. virtual void FireFocusAfterMenuClose(); + // Sends a transient focus notification without changing the focused view. + virtual void NotifyTransientFocus(); + // Sets/gets whether or not this view's descendants should be included in // the accessibility tree. It is the functional equivalent of calling // `SetAccessibleIsIgnored` on each and every view descendant of this
diff --git a/ui/views/accessibility/view_ax_platform_node_delegate.cc b/ui/views/accessibility/view_ax_platform_node_delegate.cc index 70aaaaa..b2a94aff2 100644 --- a/ui/views/accessibility/view_ax_platform_node_delegate.cc +++ b/ui/views/accessibility/view_ax_platform_node_delegate.cc
@@ -217,6 +217,39 @@ } } +void ViewAXPlatformNodeDelegate::NotifyTransientFocus() { + if (ViewAccessibility::IsViewsAccessibilityTreeEnabled()) { + ViewAccessibility::NotifyTransientFocus(); + return; + } + + DCHECK(ax_platform_node_); + if (!IsReadyToNotifyEvents()) { + return; + } + + Widget* const widget = view()->GetWidget(); + if (!widget || !widget->GetNativeView() || widget->IsClosed()) { + return; + } + + if (g_is_flushing) { + return; + } + + if (accessibility_events_callback_) { + accessibility_events_callback_.Run(this, ax::mojom::Event::kFocus); + } + + if (g_is_queueing_events) { + GetEventQueue().emplace_back(ax::mojom::Event::kFocus, GetUniqueId()); + return; + } + + PostFlushEventQueueTaskIfNecessary(); + ax_platform_node_->NotifyAccessibilityEvent(ax::mojom::Event::kFocus); +} + gfx::NativeViewAccessible ViewAXPlatformNodeDelegate::GetNativeObject() const { DCHECK(ax_platform_node_); return ax_platform_node_->GetNativeViewAccessible(); @@ -272,14 +305,6 @@ } break; } - case ax::mojom::Event::kFocusContext: { - // A focus context event is intended to send a focus event and a delay - // before the next focus event. It makes sense to delay the entire next - // synchronous batch of next events so that ordering remains the same. - // Begin queueing subsequent events and flush queue asynchronously. - PostFlushEventQueueTaskIfNecessary(); - break; - } case ax::mojom::Event::kLiveRegionChanged: { // Fire after a delay so that screen readers don't wipe it out when // another user-generated event fires simultaneously.
diff --git a/ui/views/accessibility/view_ax_platform_node_delegate.h b/ui/views/accessibility/view_ax_platform_node_delegate.h index 00a1da56..789268c 100644 --- a/ui/views/accessibility/view_ax_platform_node_delegate.h +++ b/ui/views/accessibility/view_ax_platform_node_delegate.h
@@ -58,6 +58,7 @@ void SetPopupFocusOverride() override; void EndPopupFocusOverride() override; void FireFocusAfterMenuClose() override; + void NotifyTransientFocus() override; gfx::NativeViewAccessible GetNativeObject() const override; void FireNativeEvent(ax::mojom::Event event_type) override; #if BUILDFLAG(IS_MAC)
diff --git a/ui/views/accessibility/view_ax_platform_node_delegate_unittest.cc b/ui/views/accessibility/view_ax_platform_node_delegate_unittest.cc index 279f2e67..cffcc5f2 100644 --- a/ui/views/accessibility/view_ax_platform_node_delegate_unittest.cc +++ b/ui/views/accessibility/view_ax_platform_node_delegate_unittest.cc
@@ -9,9 +9,11 @@ #include <utility> #include <vector> +#include "base/functional/bind.h" #include "base/memory/raw_ptr.h" #include "base/strings/utf_string_conversions.h" #include "base/test/gtest_util.h" +#include "base/test/run_until.h" #include "build/build_config.h" #include "ui/accessibility/ax_action_data.h" #include "ui/accessibility/ax_enums.mojom.h" @@ -1234,6 +1236,29 @@ button_accessibility()->GetFocus()); } +TEST_F(ViewAXPlatformNodeDelegateTest, TransientFocusDelaysNextFocusEvent) { + button_accessibility()->SetName("Button", ax::mojom::NameFrom::kAttribute); + textfield_accessibility()->SetName("Textfield", + ax::mojom::NameFrom::kAttribute); + + int focus_events = 0; + ui::AXPlatformNodeBase::SetOnNotifyEventCallbackForTesting( + ax::mojom::Event::kFocus, + base::BindRepeating([](int* count) { ++*count; }, &focus_events)); + + widget()->GetRootView()->GetViewAccessibility().NotifyTransientFocus(); + EXPECT_EQ(1, focus_events); + + button_->NotifyAccessibilityEventDeprecated(ax::mojom::Event::kFocus, true); + EXPECT_EQ(1, focus_events); + + EXPECT_TRUE(base::test::RunUntil([&]() { return focus_events == 2; })); + EXPECT_EQ(2, focus_events); + + ui::AXPlatformNodeBase::SetOnNotifyEventCallbackForTesting( + ax::mojom::Event::kFocus, {}); +} + TEST_F(ViewAXPlatformNodeDelegateTest, GetUnignoredSelection) { // Initialize the selection to a collapsed selection at the start of the // textfield, as if it was the caret.
diff --git a/ui/views/focus/focus_manager.cc b/ui/views/focus/focus_manager.cc index db37af4..abb407e8 100644 --- a/ui/views/focus/focus_manager.cc +++ b/ui/views/focus/focus_manager.cc
@@ -350,15 +350,16 @@ focus_change_listeners_.Notify(&FocusChangeListener::OnWillChangeFocus, focused_view_, view); - View* old_focused_view = focused_view_; + // Actions below like `Blur()` can destroy `focused_view_`. + ViewTracker old_focused_view_tracker(focused_view_); focused_view_ = view; base::AutoReset<int> entrance_count_resetter( &setting_focused_view_entrance_count_, setting_focused_view_entrance_count_ + 1); - if (old_focused_view) { - old_focused_view->RemoveObserver(this); - old_focused_view->Blur(); + if (old_focused_view_tracker.view()) { + old_focused_view_tracker.view()->RemoveObserver(this); + old_focused_view_tracker.view()->Blur(); } // Also make |focused_view_| the stored focus view. This way the stored focus // view is remembered if focus changes are requested prior to a show or while @@ -373,7 +374,8 @@ } focus_change_listeners_.Notify(&FocusChangeListener::OnDidChangeFocus, - old_focused_view, focused_view_); + old_focused_view_tracker.view(), + focused_view_); } void FocusManager::SetFocusedView(View* view) {
diff --git a/ui/views/focus/focus_manager_unittest.cc b/ui/views/focus/focus_manager_unittest.cc index b9af5fdc..9086d5d3 100644 --- a/ui/views/focus/focus_manager_unittest.cc +++ b/ui/views/focus/focus_manager_unittest.cc
@@ -175,6 +175,75 @@ RemoveFocusChangeListener(&listener); } +namespace { + +class DestructiveView : public View { + METADATA_HEADER(DestructiveView, View) + public: + explicit DestructiveView(View* view_to_delete) { + SetFocusBehavior(FocusBehavior::ALWAYS); + tracker_.SetView(view_to_delete); + } + + void OnFocus() override { + if (View* v = tracker_.view()) { + v->parent()->RemoveChildViewT(v); + } + View::OnFocus(); + } + + private: + ViewTracker tracker_; +}; + +BEGIN_METADATA(DestructiveView) +END_METADATA + +class DidChangeFocusListener : public FocusChangeListener { + public: + void OnWillChangeFocus(View* focused_before, View* focused_now) override {} + void OnDidChangeFocus(View* focused_before, View* focused_now) override { + focus_changes_.emplace_back(focused_before, focused_now); + } + + const std::vector<ViewPair>& focus_changes() const { return focus_changes_; } + void ClearFocusChanges() { focus_changes_.clear(); } + + private: + std::vector<ViewPair> focus_changes_; +}; + +} // namespace + +TEST_F(FocusManagerTest, FocusChangeWithSynchronousDestruction) { + auto view1_ptr = std::make_unique<View>(); + view1_ptr->SetFocusBehavior(View::FocusBehavior::ALWAYS); + View* view1 = GetContentsView()->AddChildView(std::move(view1_ptr)); + + DestructiveView* view2 = + GetContentsView()->AddChildView(std::make_unique<DestructiveView>(view1)); + + DidChangeFocusListener listener; + AddFocusChangeListener(&listener); + + view1->RequestFocus(); + ASSERT_EQ(1u, listener.focus_changes().size()); + EXPECT_EQ(nullptr, listener.focus_changes()[0].first); + EXPECT_EQ(view1, listener.focus_changes()[0].second); + listener.ClearFocusChanges(); + + // This will trigger view2->OnFocus() which will synchronously delete view1. + view2->RequestFocus(); + ASSERT_EQ(1u, listener.focus_changes().size()); + // Since view1 was synchronously destroyed, the old focused view should be + // nullptr. + EXPECT_EQ(nullptr, listener.focus_changes()[0].first); + EXPECT_EQ(view2, listener.focus_changes()[0].second); + listener.ClearFocusChanges(); + + RemoveFocusChangeListener(&listener); +} + TEST_F(FocusManagerTest, NativeViewFocusChangeListener) { // First, ensure the simulator is aware of the Widget created in SetUp() being // currently active.
diff --git a/ui/webui/BUILD.gn b/ui/webui/BUILD.gn index 08200576..60e9ef92 100644 --- a/ui/webui/BUILD.gn +++ b/ui/webui/BUILD.gn
@@ -3,10 +3,11 @@ # found in the LICENSE file. import("//build/buildflag_header.gni") +import("//build/config/features.gni") import("//components/signin/features.gni") import("//ui/webui/webui_features.gni") -if (!is_ios) { +if (use_blink) { static_library("webui") { sources = [ "mojo_web_ui_controller.cc",
diff --git a/ui/webui/resources/cr_components/composebox/composebox.css b/ui/webui/resources/cr_components/composebox/composebox.css index b019771..52ed1cb 100644 --- a/ui/webui/resources/cr_components/composebox/composebox.css +++ b/ui/webui/resources/cr_components/composebox/composebox.css
@@ -117,6 +117,11 @@ border-radius: var(--expanded-border-radius); } +:host([energy-effect-animation-enabled]) { + /* Energy Effect Motion Curves */ + --emphasized-accelerate-curve: cubic-bezier(0.3, 0, 0.8, 0.15); +} + #errorScrim { border-radius: inherit; } @@ -134,7 +139,7 @@ } :host([searchbox-next-enabled]) #composebox { - padding-bottom: 10px; + padding-bottom: var(--cr-composebox-padding-bottom, 10px); } search-animated-glow { @@ -180,6 +185,21 @@ max-height: 254px; } +:host([energy-effect-animation-enabled_][animation-state='expanding']) #composebox { + will-change: opacity; + animation: zero-state-composebox-fade-in 50ms + var(--emphasized-accelerate-curve) both; +} + +@keyframes zero-state-composebox-fade-in { + 0% { + opacity: 0; + } + 100% { + opacity: 1; + } +} + :host([is-collapsible][expanding_][show-file-carousel][carousel-on-top_]) #composebox { min-height: calc(136px + 52px); }
diff --git a/ui/webui/resources/cr_components/composebox/composebox.mojom b/ui/webui/resources/cr_components/composebox/composebox.mojom index 39adf8e..0614618 100644 --- a/ui/webui/resources/cr_components/composebox/composebox.mojom +++ b/ui/webui/resources/cr_components/composebox/composebox.mojom
@@ -47,6 +47,9 @@ // Called when the context menu is opened in the composebox. OnContextMenuOpened(); + + // Notifies the feature engagement tracker that a composebox query with tool/context attached was submitted. + NotifyComposeboxQuerySubmittedWithContext(); }; // NewTabPage WebUI-side handler for requests from the browser.
diff --git a/ui/webui/resources/cr_components/composebox/composebox.ts b/ui/webui/resources/cr_components/composebox/composebox.ts index 27ca03c..cb7bba3 100644 --- a/ui/webui/resources/cr_components/composebox/composebox.ts +++ b/ui/webui/resources/cr_components/composebox/composebox.ts
@@ -752,6 +752,10 @@ } protected onSubmitClick_(e: MouseEvent) { + if (this.hasFiles() || + this.inputState?.activeTool !== ToolMode.kUnspecified) { + this.pageHandler_.notifyComposeboxQuerySubmittedWithContext(); + } this.submitQuery(e); } @@ -995,40 +999,15 @@ } - // TODO(crbug.com/486706573): Refactor this function and move the common logic + // TODO(crbug.com/486706573): common logic is moved // to the mixin class. Move embedder specific logic to the embedder class. override clearAllInputs( querySubmitted: boolean, shouldBlockAutoSuggestedTabs: boolean) { - this.clearInput(); + // Reset side-panel specific suggested tab context URL/Title pointers this.automaticActiveTab = null; this.pendingAutomaticActiveTabUrl_ = ''; this.pendingAutomaticActiveTabTitle_ = ''; - // Let `querySubmit` handle clearing files if the tool mode is a tool mode - // that should be cleared after submitting. For all other general - // clearing, clear input here. - if (!querySubmitted) { - this.resetModes(); - } - const undeletableFiles = - Array.from(this.files.values()).filter(file => !file.isDeletable); - if (undeletableFiles.length !== this.files.size) { - this.files = new Map(undeletableFiles.map(file => [file.uuid, file])); - this.addedTabsIds = new Map(undeletableFiles.filter(file => file.tabId) - .map(file => [file.tabId!, file.uuid])); - } - // Reset files in set to match remaining files in carousel. - this.pendingUploads = new Set([...this.files.keys()]); - this.smartComposeInlineHint = ''; - this.resetSmartComposeStats(); - if (!querySubmitted) { - // If the query was submitted, the searchbox handler will clear its own - // uploaded file state when the query submission is handled. - this.searchboxHandler_.clearFiles(shouldBlockAutoSuggestedTabs); - } - this.fileUploadsComplete = this.pendingUploads.size === 0; - if (this.inVoiceSearchMode) { - this.voiceSearchEndCleanup(); - } + super.clearAllInputs(querySubmitted, shouldBlockAutoSuggestedTabs); } protected shouldDisableFileInputs_() {
diff --git a/ui/webui/resources/cr_components/composebox/composebox_input.css b/ui/webui/resources/cr_components/composebox/composebox_input.css index 019b37e..1e7e713 100644 --- a/ui/webui/resources/cr_components/composebox/composebox_input.css +++ b/ui/webui/resources/cr_components/composebox/composebox_input.css
@@ -273,7 +273,7 @@ --cr-icon-button-icon-size: 20px; position: absolute; top: var(--composebox-cancel-button-top); - inset-inline-end: 12px; + inset-inline-end: var(--composebox-cancel-button-inline-end, 12px); /* Uses the same transitions as the submit button so they are in sync. */ transition: var(--submit-enabled-transition); }
diff --git a/ui/webui/resources/cr_components/composebox/composebox_mixin.ts b/ui/webui/resources/cr_components/composebox/composebox_mixin.ts index ccaa380b..3b4e0fcb 100644 --- a/ui/webui/resources/cr_components/composebox/composebox_mixin.ts +++ b/ui/webui/resources/cr_components/composebox/composebox_mixin.ts
@@ -998,8 +998,36 @@ } clearAllInputs( - _querySubmitted: boolean, _shouldBlockAutoSuggestedTabs: boolean) { - assertNotReached(); + querySubmitted: boolean, shouldBlockAutoSuggestedTabs: boolean) { + this.clearInput(); + // Let `querySubmit` handle clearing files if the tool mode is a tool + // mode that should be cleared after submitting. For all other general + // clearing, clear input here. + if (!querySubmitted) { + this.resetModes(); + } + const undeletableFiles = + Array.from(this.files.values()).filter(file => !file.isDeletable); + if (undeletableFiles.length !== this.files.size) { + this.files = + new Map(undeletableFiles.map(file => [file.uuid, file])); + this.addedTabsIds = + new Map(undeletableFiles.filter(file => file.tabId) + .map(file => [file.tabId!, file.uuid])); + } + // Reset files in set to match remaining files in carousel. + this.pendingUploads = new Set([...this.files.keys()]); + this.smartComposeInlineHint = ''; + this.resetSmartComposeStats(); + if (!querySubmitted) { + // If the query was submitted, the searchbox handler will clear its + // own uploaded file state when the query submission is handled. + this.getSearchboxHandler().clearFiles(shouldBlockAutoSuggestedTabs); + } + this.fileUploadsComplete = this.pendingUploads.size === 0; + if (this.inVoiceSearchMode) { + this.voiceSearchEndCleanup(); + } } handleProcessFilesError(error: ProcessFilesError) { @@ -1857,7 +1885,7 @@ hasContent(): boolean; clearInput(): void; clearAllInputs( - _querySubmitted: boolean, _shouldBlockAutoSuggestedTabs: boolean): void; + querySubmitted: boolean, shouldBlockAutoSuggestedTabs: boolean): void; handleProcessFilesError(error: ProcessFilesError): void; isFileAllowed(fileType: string): boolean; isMimeTypeAllowed(mimeType: string, allowedTypes: string[]): boolean;
diff --git a/ui/webui/resources/cr_components/composebox/composebox_voice_search.html.ts b/ui/webui/resources/cr_components/composebox/composebox_voice_search.html.ts index 7512167..87b8cef0 100644 --- a/ui/webui/resources/cr_components/composebox/composebox_voice_search.html.ts +++ b/ui/webui/resources/cr_components/composebox/composebox_voice_search.html.ts
@@ -42,7 +42,7 @@ ?hidden="${this.shouldShowErrorScrim_()}"> <cr-icon-button id="stopButton" part="voice-stop-button" iron-icon="composebox:stop" - title="Stop" + title="${this.i18n('voiceStop')}" @click="${this.onStopClick_}" > </cr-icon-button>
diff --git a/ui/webui/resources/cr_components/composebox/file_thumbnail.css b/ui/webui/resources/cr_components/composebox/file_thumbnail.css index 5f66f65..f17b48a 100644 --- a/ui/webui/resources/cr_components/composebox/file_thumbnail.css +++ b/ui/webui/resources/cr_components/composebox/file_thumbnail.css
@@ -310,32 +310,6 @@ white-space: nowrap; } -@keyframes thumbnail-fade { - 0%, - 13% { - opacity: 0; - } - 90%, - 100% { - opacity: 1; - } -} - -@keyframes zero-state-thumbnail-fade { - 0%, - 17% { - opacity: 0; - } - 100% { - opacity: 1; - } -} - -.chip, -.img-chip { - animation: - var(--cr-composebox-file-thumbnail-animation-name, thumbnail-fade) var(--cr-composebox-file-thumbnail-animation-duration, 0s) var(--cr-composebox-file-thumbnail-animation-curve, linear) forwards; -} @media (forced-colors: active) { .chip, @@ -349,3 +323,73 @@ box-sizing: border-box; } } + +@keyframes slide-out { + from { + transform: translateY(0); + } + to { + transform: translateY(30px); + } +} + +@keyframes fade-out { + from { + opacity: 1; + } + to { + opacity: 0; + } +} + +@keyframes shrink-out { + 0% { + opacity: 1; + width: var(--file-chip-width, 154px); + margin-inline-end: 0; + } + 100% { + opacity: 0; + width: 0; + /* Offset the gap to avoid a glitch. */ + margin-inline-end: calc(0px - var(--composebox-thumbnail-gap, 6px)); + } +} + +:host(.exiting) { + animation: var(--cr-composebox-file-thumbnail-exit-animation, none); +} + +:host(.exiting:last-of-type) { + animation: var(--cr-composebox-file-thumbnail-exit-animation-last, none); +} + +:host(.exiting:only-of-type) { + animation: none; +} + +@keyframes slide-in { + from { + transform: translateY(30px); + } + to { + transform: translateY(0); + } +} + +@keyframes fade-in { + from { + opacity: 0; + } + to { + opacity: 1; + } +} + +:host(.entering) { + animation: var(--cr-composebox-file-thumbnail-entry-animation, none); +} + +:host(.entering:first-of-type) { + animation: none; +}
diff --git a/ui/webui/resources/cr_components/composebox/file_thumbnail.ts b/ui/webui/resources/cr_components/composebox/file_thumbnail.ts index 23ce6e51..d47d050b 100644 --- a/ui/webui/resources/cr_components/composebox/file_thumbnail.ts +++ b/ui/webui/resources/cr_components/composebox/file_thumbnail.ts
@@ -88,10 +88,38 @@ } } + override firstUpdated(changedProperties: PropertyValues<this>) { + super.firstUpdated(changedProperties); + requestAnimationFrame(() => { + this.classList.add('entering'); + const animations = this.getAnimations(); + if (animations.length > 0) { + Promise.allSettled(animations.map(a => a.finished)).then(() => { + this.classList.remove('entering'); + }); + } else { + this.classList.remove('entering'); + } + }); + } + protected onRemoveButtonClick_() { - // TODO(crbug.com/422559977): Send call to handler to delete file from - // cache. - this.fire('delete-file', {uuid: this.file.uuid, fromUserAction: true}); + if (this.classList.contains('exiting')) { + return; + } + + this.classList.add('exiting'); + const animations = this.getAnimations(); + + if (animations.length === 0) { + this.classList.remove('exiting'); + this.fire('delete-file', {uuid: this.file.uuid, fromUserAction: true}); + } else { + Promise.allSettled(animations.map(a => a.finished)).then(() => { + this.classList.remove('exiting'); + this.fire('delete-file', {uuid: this.file.uuid, fromUserAction: true}); + }); + } } protected getDeleteFileButtonTitle_(): string {
diff --git a/ui/webui/resources/cr_components/help_bubble/help_bubble_controller.ts b/ui/webui/resources/cr_components/help_bubble/help_bubble_controller.ts index 3d89b8e..443d92b5 100644 --- a/ui/webui/resources/cr_components/help_bubble/help_bubble_controller.ts +++ b/ui/webui/resources/cr_components/help_bubble/help_bubble_controller.ts
@@ -19,6 +19,7 @@ interface Options { padding: InsetsF; fixed: boolean; + containerElement?: HTMLElement; } // Return whether the current language is right-to-left @@ -276,7 +277,13 @@ 'fixed') { this.bubble_.fixed = true; } - this.anchor_.parentNode.insertBefore(this.bubble_, this.anchor_); + + const container = this.options_.containerElement; + if (container) { + container.appendChild(this.bubble_); + } else { + this.anchor_.parentNode.insertBefore(this.bubble_, this.anchor_); + } return this.bubble_; }
diff --git a/ui/webui/resources/cr_components/help_bubble/help_bubble_mixin_lit.ts b/ui/webui/resources/cr_components/help_bubble/help_bubble_mixin_lit.ts index c34e6fd5..2d433e8a 100644 --- a/ui/webui/resources/cr_components/help_bubble/help_bubble_mixin_lit.ts +++ b/ui/webui/resources/cr_components/help_bubble/help_bubble_mixin_lit.ts
@@ -588,6 +588,7 @@ anchorPaddingBottom?: number; anchorPaddingRight?: number; fixed?: boolean; + containerElement?: HTMLElement; } export function parseOptions(options: Options) { @@ -599,6 +600,7 @@ return { padding, fixed: !!options.fixed, + containerElement: options.containerElement, }; }
diff --git a/ui/webui/resources/cr_elements/cr_view_manager/cr_view_manager.ts b/ui/webui/resources/cr_elements/cr_view_manager/cr_view_manager.ts index 3ec21f4..c474afe8 100644 --- a/ui/webui/resources/cr_elements/cr_view_manager/cr_view_manager.ts +++ b/ui/webui/resources/cr_elements/cr_view_manager/cr_view_manager.ts
@@ -153,7 +153,7 @@ promises.push(this.enter_( view, enterAnimation || - (previousViews.size === 0 ? 'no-animation' : 'fade-out'))); + (previousViews.size === 0 ? 'no-animation' : 'fade-in'))); } return Promise.all(promises).then(() => {});
diff --git a/ui/webui/resources/js/tracked_element/tracked_element_manager.ts b/ui/webui/resources/js/tracked_element/tracked_element_manager.ts index 2c0b1d39..d9ce2c4 100644 --- a/ui/webui/resources/js/tracked_element/tracked_element_manager.ts +++ b/ui/webui/resources/js/tracked_element/tracked_element_manager.ts
@@ -464,7 +464,32 @@ } } - private clickElement_(nativeId: string): {success: boolean} { + private async waitUntilNotDisabled_(element: HTMLElement, nativeId: string): + Promise<void> { + if (!element.hasAttribute('disabled')) { + return; + } + + console.info( + `TrackedElementManager: Element ${nativeId} is disabled, ` + + `waiting...`); + + return new Promise((resolve) => { + const observer = new MutationObserver(() => { + if (!element.hasAttribute('disabled')) { + observer.disconnect(); + console.info( + `TrackedElementManager: Element ${nativeId} is no ` + + `longer disabled.`); + resolve(); + } + }); + observer.observe( + element, {attributes: true, attributeFilter: ['disabled']}); + }); + } + + private async clickElement_(nativeId: string): Promise<{success: boolean}> { const trackedElement = this.trackedElements_.get(nativeId); if (!trackedElement) { console.error(`TrackedElementManager: Click failed, element not found: ${ @@ -485,14 +510,39 @@ } } + await this.waitUntilNotDisabled_(target, nativeId); + // Some components (like the reload button) listen to pointer events - // instead of click. + // instead of click. We also need to fake pointer capture for some tests. + const oldPointerCapture = { + setPointerCapture: target.setPointerCapture, + hasPointerCapture: target.hasPointerCapture, + releasePointerCapture: target.releasePointerCapture, + }; + { + let hasCapture: number|null = null; + target.setPointerCapture = (id) => { + hasCapture = id; + }; + target.hasPointerCapture = (id) => { + return id === hasCapture; + }; + target.releasePointerCapture = (id) => { + if (id === hasCapture) { + hasCapture = null; + } + }; + } + const bounds = target.getBoundingClientRect(); target.dispatchEvent(new PointerEvent('pointerdown', { bubbles: true, composed: true, button: 0, // Left pointerId: 1, isPrimary: true, + buttons: 1, + clientX: bounds.left + bounds.width / 2, + clientY: bounds.top + bounds.height / 2, })); target.dispatchEvent(new PointerEvent('pointerup', { bubbles: true, @@ -500,13 +550,21 @@ button: 0, // Left pointerId: 1, isPrimary: true, + buttons: 0, + clientX: bounds.left + bounds.width / 2, + clientY: bounds.top + bounds.height / 2, })); target.dispatchEvent(new MouseEvent('click', { bubbles: true, composed: true, button: 0, // Left detail: 1, // Single click + clientX: bounds.left + bounds.width / 2, + clientY: bounds.top + bounds.height / 2, })); + target.setPointerCapture = oldPointerCapture.setPointerCapture; + target.hasPointerCapture = oldPointerCapture.hasPointerCapture; + target.releasePointerCapture = oldPointerCapture.releasePointerCapture; return {success: true}; }
diff --git a/ui/webui/resources/js/util.ts b/ui/webui/resources/js/util.ts index 5a85e12..bf91f56 100644 --- a/ui/webui/resources/js/util.ts +++ b/ui/webui/resources/js/util.ts
@@ -74,7 +74,7 @@ * transitionDuration style value. */ export function ensureTransitionEndEvent( - el: HTMLElement, timeOut?: number): void { + el: HTMLElement, timeOut: number): void { if (timeOut === undefined) { const style = getComputedStyle(el); timeOut = parseFloat(style.transitionDuration) * 1000;
diff --git a/v8 b/v8 index 05fdb31..e05f632b 160000 --- a/v8 +++ b/v8
@@ -1 +1 @@ -Subproject commit 05fdb31f32a0b6e9771eb9ecd088bd1cde6799c5 +Subproject commit e05f632b216d7407e7570cfc88e5391a1850e685