Version in base suite: 147.0.7727.137-1~deb13u1
Version in overlay suite: 149.0.7827.114-1~deb13u1
Base version: chromium_149.0.7827.114-1~deb13u1
Target version: chromium_149.0.7827.155-1~deb13u1
Base file: /srv/ftp-master.debian.org/ftp/pool/main/c/chromium/chromium_149.0.7827.114-1~deb13u1.dsc
Target file: /srv/ftp-master.debian.org/policy/pool/main/c/chromium/chromium_149.0.7827.155-1~deb13u1.dsc
DEPS | 22
ash/strings/ash_strings_fa.xtb | 6
base/files/file_util.h | 6
base/files/file_util_posix.cc | 4
base/files/file_util_win.cc | 14
base/metrics/histogram.h | 8
base/metrics/histogram_base.cc | 63
base/metrics/histogram_base.h | 20
base/metrics/histogram_base_unittest.cc | 78
base/metrics/histogram_delta_serialization.cc | 3
base/metrics/persistent_histogram_allocator.cc | 2
base/metrics/sparse_histogram.h | 2
build/util/LASTCHANGE | 2
build/util/LASTCHANGE.committime | 2
chrome/VERSION | 2
chrome/app/resources/generated_resources_ar.xtb | 4
chrome/app/resources/generated_resources_de.xtb | 2
chrome/app/resources/generated_resources_eu.xtb | 2
chrome/app/resources/generated_resources_fa.xtb | 4
chrome/app/resources/generated_resources_fr-CA.xtb | 2
chrome/app/resources/generated_resources_fr.xtb | 4
chrome/app/resources/generated_resources_iw.xtb | 4
chrome/app/resources/generated_resources_ja.xtb | 2
chrome/app/resources/generated_resources_ko.xtb | 2
chrome/app/resources/generated_resources_pt-BR.xtb | 8
chrome/app/resources/generated_resources_ru.xtb | 2
chrome/app/resources/generated_resources_tr.xtb | 4
chrome/app/resources/google_chrome_strings_ar.xtb | 2
chrome/app/resources/google_chrome_strings_de.xtb | 2
chrome/app/resources/google_chrome_strings_pt-BR.xtb | 2
chrome/app/resources/google_chrome_strings_ru.xtb | 4
chrome/app/resources/google_chrome_strings_vi.xtb | 4
chrome/browser/about_flags.cc | 20
chrome/browser/android/omnibox/composebox_query_controller_bridge.cc | 2
chrome/browser/background/glic/os_icon_provider_mac.mm | 22
chrome/browser/chrome_content_browser_client.cc | 61
chrome/browser/chrome_content_browser_client.h | 6
chrome/browser/contextual_tasks/BUILD.gn | 39
chrome/browser/contextual_tasks/OWNERS | 3
chrome/browser/contextual_tasks/contextual_tasks.mojom | 21
chrome/browser/contextual_tasks/contextual_tasks_composebox_handler.cc | 45
chrome/browser/contextual_tasks/contextual_tasks_composebox_handler.h | 9
chrome/browser/contextual_tasks/contextual_tasks_composebox_handler_unittest.cc | 63
chrome/browser/contextual_tasks/contextual_tasks_interactive_uitest.cc | 840
chrome/browser/contextual_tasks/contextual_tasks_mojom_traits.h | 56
chrome/browser/contextual_tasks/contextual_tasks_navigation_throttle.cc | 19
chrome/browser/contextual_tasks/contextual_tasks_page_handler.cc | 34
chrome/browser/contextual_tasks/contextual_tasks_page_handler.h | 6
chrome/browser/contextual_tasks/contextual_tasks_page_handler_unittest.cc | 74
chrome/browser/contextual_tasks/contextual_tasks_side_panel_coordinator_interactive_uitest.cc | 155
chrome/browser/contextual_tasks/contextual_tasks_types.h | 33
chrome/browser/contextual_tasks/contextual_tasks_ui.cc | 7
chrome/browser/contextual_tasks/contextual_tasks_ui_browsertest.cc | 5
chrome/browser/contextual_tasks/contextual_tasks_ui_service.cc | 864
chrome/browser/contextual_tasks/contextual_tasks_ui_service.h | 131
chrome/browser/contextual_tasks/contextual_tasks_ui_service_browsertest.cc | 10
chrome/browser/contextual_tasks/contextual_tasks_ui_service_factory.cc | 3
chrome/browser/contextual_tasks/contextual_tasks_ui_service_interactive_uitest.cc | 15
chrome/browser/contextual_tasks/contextual_tasks_ui_service_pdf_browsertest.cc | 4
chrome/browser/contextual_tasks/contextual_tasks_ui_service_unittest.cc | 1046
chrome/browser/contextual_tasks/contextual_tasks_utils.cc | 6
chrome/browser/contextual_tasks/contextual_tasks_utils.h | 3
chrome/browser/contextual_tasks/contextual_tasks_window_tracker.cc | 110
chrome/browser/contextual_tasks/contextual_tasks_window_tracker.h | 133
chrome/browser/contextual_tasks/contextual_tasks_window_tracker_manager.cc | 309
chrome/browser/contextual_tasks/contextual_tasks_window_tracker_manager.h | 114
chrome/browser/contextual_tasks/contextual_tasks_window_tracker_manager_unittest.cc | 164
chrome/browser/contextual_tasks/contextual_tasks_window_tracker_unittest.cc | 115
chrome/browser/contextual_tasks/guest_opener_user_data.cc | 22
chrome/browser/contextual_tasks/guest_opener_user_data.h | 30
chrome/browser/contextual_tasks/mock_contextual_tasks_page.h | 5
chrome/browser/contextual_tasks/search_ai_mode_promo_tab_helper.cc | 4
chrome/browser/download/android/dangerous_download_dialog_bridge.cc | 5
chrome/browser/download/android/download_controller.cc | 21
chrome/browser/download/android/download_controller.h | 3
chrome/browser/download/android/policy_warning_download_dialog_bridge.cc | 5
chrome/browser/extensions/api/developer_private/developer_private_functions.cc | 33
chrome/browser/extensions/api/glic_private/glic_messaging_browsertest.cc | 30
chrome/browser/extensions/api/glic_private/glic_private_api.cc | 41
chrome/browser/extensions/api/glic_private/glic_private_apitest.cc | 137
chrome/browser/extensions/api/user_scripts/user_scripts_apitest.cc | 64
chrome/browser/extensions/script_injection_tracker_browsertest.cc | 120
chrome/browser/file_system_access/chrome_file_system_access_permission_context_browsertest.cc | 81
chrome/browser/flag-metadata.json | 10
chrome/browser/flag_descriptions.h | 10
chrome/browser/flags/android/chrome_feature_list.cc | 2
chrome/browser/flags/android/chrome_feature_list.h | 1
chrome/browser/glic/glic_metrics.cc | 26
chrome/browser/glic/glic_metrics.h | 9
chrome/browser/glic/glic_metrics_unittest.cc | 10
chrome/browser/glic/host/glic.mojom | 2
chrome/browser/glic/public/features.cc | 9
chrome/browser/glic/public/features.h | 4
chrome/browser/glic/public/glic_instance.h | 6
chrome/browser/glic/resources/browser_resources.grd | 4
chrome/browser/glic/service/glic_instance_coordinator_impl.cc | 2
chrome/browser/glic/service/glic_instance_impl.cc | 8
chrome/browser/glic/service/glic_instance_impl.h | 2
chrome/browser/glic/service/metrics/glic_instance_coordinator_metrics.cc | 35
chrome/browser/glic/service/metrics/glic_instance_coordinator_metrics.h | 10
chrome/browser/glic/service/metrics/glic_instance_coordinator_metrics_unittest.cc | 32
chrome/browser/glic/service/metrics/metrics_types.h | 8
chrome/browser/glic/suggestions/contextual_cueing_enums.h | 3
chrome/browser/glic/suggestions/contextual_cueing_helper.cc | 8
chrome/browser/glic/suggestions/contextual_cueing_helper_interactive_uitest.cc | 176
chrome/browser/glic/test_support/mock_glic_instance.h | 4
chrome/browser/glic/widget/glic_widget.cc | 11
chrome/browser/glic/widget/glic_widget_unittest.cc | 59
chrome/browser/new_tab_page/new_tab_page_realbox_interactive_uitest.cc | 19
chrome/browser/permissions/BUILD.gn | 2
chrome/browser/permissions/chrome_permissions_client.cc | 30
chrome/browser/permissions/permission_delegation_browsertest.cc | 100
chrome/browser/resources/contextual_tasks/BUILD.gn | 1
chrome/browser/resources/contextual_tasks/app.ts | 43
chrome/browser/resources/contextual_tasks/composebox.ts | 4
chrome/browser/resources/contextual_tasks/window_manager.ts | 57
chrome/browser/resources/glic/glic_api/glic_api.ts | 2
chrome/browser/resources/new_tab_page/app.html | 5
chrome/browser/resources/new_tab_page/app.ts | 10
chrome/browser/resources/new_tab_page/ntp_searchbox.ts | 3
chrome/browser/resources/omnibox_popup/aim_app.css | 14
chrome/browser/resources/omnibox_popup/aim_app.html.ts | 4
chrome/browser/resources/omnibox_popup/aim_app.ts | 23
chrome/browser/safe_browsing/client_side_detection_intelligent_scan_delegate_desktop.cc | 12
chrome/browser/safe_browsing/client_side_detection_intelligent_scan_delegate_desktop.h | 8
chrome/browser/safe_browsing/client_side_detection_intelligent_scan_delegate_desktop_unittest.cc | 42
chrome/browser/safe_browsing/client_side_detection_intelligent_scan_delegate_factory.cc | 6
chrome/browser/touch_to_fill/password_manager/touch_to_fill_controller.cc | 33
chrome/browser/ui/android/strings/translations/android_chrome_strings_fa.xtb | 4
chrome/browser/ui/extensions/reload_page_dialog_controller.cc | 9
chrome/browser/ui/extensions/reload_page_dialog_controller_android_browsertest.cc | 48
chrome/browser/ui/lens/lens_composebox_controller.cc | 22
chrome/browser/ui/lens/lens_composebox_controller.h | 9
chrome/browser/ui/lens/lens_composebox_controller_browsertest.cc | 30
chrome/browser/ui/lens/lens_composebox_handler.cc | 10
chrome/browser/ui/lens/lens_composebox_handler.h | 3
chrome/browser/ui/startup/default_browser_prompt/default_browser_modal_dialog_manager.cc | 34
chrome/browser/ui/startup/default_browser_prompt/default_browser_modal_dialog_manager.h | 8
chrome/browser/ui/startup/default_browser_prompt/default_browser_modal_dialog_manager_interactive_uitest.cc | 134
chrome/browser/ui/views/digital_credentials/digital_identity_multi_step_dialog.cc | 14
chrome/browser/ui/views/digital_credentials/digital_identity_multi_step_dialog.h | 2
chrome/browser/ui/views/passwords/account_chooser_dialog_view.cc | 5
chrome/browser/ui/views/passwords/account_chooser_dialog_view.h | 1
chrome/browser/ui/views/passwords/password_combined_selector_view.cc | 5
chrome/browser/ui/views/passwords/password_combined_selector_view.h | 1
chrome/browser/ui/views/permissions/permission_prompt_bubble.cc | 25
chrome/browser/ui/views/permissions/permission_prompt_bubble_base_view_browsertest.cc | 68
chrome/browser/ui/views/tabs/dragging/tab_drag_controller.cc | 7
chrome/browser/ui/webui/cr_components/composebox/composebox_handler.cc | 12
chrome/browser/ui/webui/cr_components/composebox/composebox_handler.h | 6
chrome/browser/ui/webui/cr_components/composebox/composebox_handler_unittest.cc | 3
chrome/browser/ui/webui/cr_components/searchbox/contextual_searchbox_handler.cc | 20
chrome/browser/ui/webui/cr_components/searchbox/contextual_searchbox_handler.h | 9
chrome/browser/ui/webui/cr_components/searchbox/searchbox_handler.cc | 10
chrome/browser/ui/webui/cr_components/searchbox/searchbox_handler.h | 3
chrome/browser/ui/webui/new_tab_page/new_tab_page_ui.cc | 5
chrome/browser/ui/webui/searchbox/contextual_searchbox_handler_unittest.cc | 21
chrome/browser/ui/webui/searchbox/omnibox_composebox_handler.cc | 2
chrome/browser/ui/webui/searchbox/searchbox_handler_unittest.cc | 63
chrome/browser/ui/webui/updater/updater_page_handler.cc | 6
chrome/browser/webauthn/authenticator_request_dialog_controller.cc | 7
chrome/browser/webauthn/chrome_webauthn_browsertest.cc | 109
chrome/browser/webshare/win/share_operation.cc | 22
chrome/chrome_branch_deps.json | 39
chrome/common/extensions/api/glic_private.webidl | 3
chrome/common/safe_browsing/binary_feature_extractor.cc | 32
chrome/common/safe_browsing/binary_feature_extractor_unittest.cc | 23
chrome/services/file_util/public/cpp/temporary_file_getter.cc | 28
chrome/services/file_util/public/cpp/temporary_file_getter_unittest.cc | 22
chrome/test/BUILD.gn | 1
chrome/test/chromedriver/chrome/web_view_impl.cc | 1
chrome/test/chromedriver/session_commands.cc | 3
chromeos/CHROMEOS_LKGM | 2
chromeos/profiles/arm.afdo.newest.txt | 2
chromeos/profiles/atom.afdo.newest.txt | 2
chromeos/profiles/bigcore.afdo.newest.txt | 2
components/browser_ui/strings/android/translations/browser_ui_strings_fa.xtb | 2
components/browser_ui/strings/android/translations/browser_ui_strings_hu.xtb | 2
components/browser_ui/strings/android/translations/browser_ui_strings_te.xtb | 2
components/certificate_transparency/data/log_list.json | 4
components/contextual_search/contextual_search_context_controller.h | 3
components/contextual_search/internal/composebox_query_controller.cc | 12
components/contextual_search/internal/composebox_query_controller_unittest.cc | 65
components/contextual_tasks/public/features.cc | 12
components/contextual_tasks/public/features.h | 9
components/cronet/gn2bp/templates/Android.extras.bp.template | 1
components/exo/drag_drop_operation.cc | 29
components/exo/drag_drop_operation_unittest.cc | 130
components/exo/seat.cc | 17
components/exo/seat_unittest.cc | 49
components/input/render_widget_host_input_event_router.cc | 6
components/input/render_widget_targeter.cc | 20
components/js_injection/renderer/js_communication.cc | 106
components/js_injection/renderer/js_communication.h | 6
components/lens/lens_features.cc | 2
components/lens/lens_features.h | 4
components/metrics/content/subprocess_metrics_provider.cc | 8
components/mirroring/service/mirroring_features.cc | 3
components/mirroring/service/mirroring_features.h | 5
components/mirroring/service/openscreen_session_host.cc | 43
components/mirroring/service/openscreen_session_host.h | 1
components/mirroring/service/openscreen_session_host_unittest.cc | 87
components/omnibox/browser/searchbox.mojom | 3
components/password_manager/core/browser/password_autofill_manager.cc | 8
components/password_manager/core/browser/password_autofill_manager.h | 6
components/password_manager/core/browser/password_autofill_manager_unittest.cc | 289
components/policy/resources/policy_templates_vi.xtb | 2
components/search_engines/BUILD.gn | 1
components/search_engines/util.cc | 12
components/search_engines/util_unittest.cc | 22
components/strings/components_strings_ar.xtb | 6
components/strings/components_strings_iw.xtb | 4
components/strings/components_strings_ja.xtb | 6
components/strings/components_strings_ru.xtb | 12
components/strings/components_strings_zh-TW.xtb | 4
content/browser/digital_credentials/digital_identity_request_impl.cc | 31
content/browser/file_system_access/file_system_access_file_handle_impl.cc | 11
content/browser/file_system_access/file_system_access_file_handle_impl_unittest.cc | 50
content/browser/file_system_access/file_system_access_file_writer_impl_unittest.cc | 10
content/browser/file_system_access/file_system_access_manager_impl.cc | 9
content/browser/file_system_access/file_system_access_manager_impl_unittest.cc | 99
content/browser/file_system_access/file_system_access_security_browsertest.cc | 55
content/browser/file_system_access/file_system_access_watcher_manager_unittest.cc | 17
content/browser/renderer_host/render_frame_proxy_host.cc | 22
content/browser/renderer_host/render_widget_host_input_event_router_unittest.cc | 97
content/browser/site_per_process_hit_test_browsertest.cc | 4
content/browser/web_contents/web_contents_impl.cc | 28
content/browser/web_contents/web_contents_impl_browsertest.cc | 79
content/child/runtime_features.cc | 2
content/public/android/java/strings/translations/android_content_strings_ar.xtb | 2
content/public/browser/content_browser_client.cc | 8
content/public/browser/content_browser_client.h | 10
content/test/BUILD.gn | 1
debian/changelog | 53
extensions/browser/api/user_scripts/user_scripts_api.cc | 45
extensions/browser/api/user_scripts/user_scripts_api.h | 10
extensions/browser/script_executor.cc | 10
extensions/common/csp_validator.cc | 10
extensions/common/csp_validator_unittest.cc | 28
extensions/common/extension_features.cc | 3
extensions/common/extension_features.h | 2
gpu/command_buffer/service/framebuffer_manager.cc | 15
gpu/command_buffer/service/framebuffer_manager.h | 2
gpu/command_buffer/service/framebuffer_manager_unittest.cc | 93
gpu/command_buffer/service/gles2_cmd_decoder.cc | 19
gpu/config/gpu_lists_version.h | 2
gpu/webgpu/DAWN_VERSION | 2
gpu/webgpu/dawn_commit_hash.h | 2
media/base/media_switches.cc | 12
media/base/media_switches.h | 1
media/base/video_frame_layout.cc | 7
media/gpu/h264_decoder.cc | 53
media/gpu/h264_decoder.h | 2
media/gpu/h264_decoder_unittest.cc | 14
media/mojo/clients/mojo_decryptor_unittest.cc | 93
media/mojo/common/media_type_converters.cc | 2
media/mojo/services/mojo_decryptor_service.cc | 12
remoting/host/BUILD.gn | 7
remoting/host/clipboard_win.cc | 273
remoting/host/clipboard_win.h | 66
remoting/host/clipboard_win_unittest.cc | 238
remoting/host/win/BUILD.gn | 4
remoting/host/win/unprivileged_process_delegate.cc | 9
remoting/host/win/unprivileged_process_delegate.h | 4
remoting/host/win/unprivileged_process_delegate_unittest.cc | 146
remoting/host/win/windows_process_delegate.cc | 8
remoting/host/win/windows_process_delegate.h | 3
services/device/public/cpp/device_features.cc | 4
services/device/public/cpp/device_features.h | 1
services/viz/public/cpp/compositing/bitmap_in_shared_memory_mojom_traits.cc | 21
services/viz/public/mojom/compositing/bitmap_in_shared_memory.mojom | 1
services/webnn/host/weights_file_provider.cc | 27
skia/ext/skia_commit_hash.h | 2
testing/variations/fieldtrial_testing_config.json | 36
third_party/angle/src/compiler/preprocessor/Preprocessor.cpp | 12
third_party/angle/src/compiler/preprocessor/Preprocessor.h | 3
third_party/angle/src/compiler/translator/Compiler.cpp | 72
third_party/angle/src/compiler/translator/Compiler.h | 5
third_party/angle/src/compiler/translator/DirectiveHandler.cpp | 5
third_party/angle/src/compiler/translator/ParseContext.cpp | 96
third_party/angle/src/compiler/translator/ParseContext.h | 5
third_party/angle/src/libANGLE/TransformFeedback.h | 5
third_party/angle/src/libANGLE/renderer/metal/ContextMtl.mm | 17
third_party/angle/src/libANGLE/renderer/metal/DisplayMtl.mm | 1
third_party/angle/src/libANGLE/renderer/vulkan/ContextVk.cpp | 29
third_party/angle/src/libANGLE/renderer/vulkan/ContextVk.h | 18
third_party/angle/src/libANGLE/renderer/vulkan/SamplerVk.cpp | 2
third_party/angle/src/libANGLE/renderer/vulkan/ShareGroupVk.cpp | 4
third_party/angle/src/libANGLE/renderer/vulkan/TextureVk.cpp | 21
third_party/angle/src/libANGLE/renderer/vulkan/UtilsVk.cpp | 3
third_party/angle/src/libANGLE/renderer/vulkan/vk_cache_utils.cpp | 72
third_party/angle/src/libANGLE/renderer/vulkan/vk_cache_utils.h | 4
third_party/angle/src/libANGLE/renderer/vulkan/vk_helpers.cpp | 28
third_party/angle/src/libANGLE/renderer/vulkan/vk_helpers.h | 14
third_party/angle/src/libANGLE/renderer/vulkan/vk_renderer.cpp | 63
third_party/angle/src/libANGLE/renderer/vulkan/vk_renderer.h | 12
third_party/angle/src/tests/angle_end2end_tests_expectations.txt | 10
third_party/angle/src/tests/compiler_tests/Parse_test.cpp | 8
third_party/angle/src/tests/deqp_support/deqp_egl_test_expectations.txt | 4
third_party/angle/src/tests/egl_tests/EGLContextSharingTest.cpp | 199
third_party/angle/src/tests/egl_tests/EGLRobustnessTest.cpp | 40
third_party/angle/src/tests/gl_tests/GLSLValidationTest.cpp | 77
third_party/angle/src/tests/gl_tests/TransformFeedbackTest.cpp | 80
third_party/angle/src/tests/gl_tests/VulkanPerformanceCounterTest.cpp | 133
third_party/blink/renderer/core/html/media/html_media_element.cc | 4
third_party/blink/renderer/core/html/media/html_media_element.h | 1
third_party/blink/renderer/core/html/media/lazy_load_media_observer_test.cc | 6
third_party/blink/renderer/core/html/track/html_track_element.cc | 5
third_party/blink/renderer/core/page/drag_controller.cc | 179
third_party/blink/renderer/modules/serial/serial.cc | 135
third_party/blink/renderer/modules/serial/serial.h | 29
third_party/blink/renderer/modules/serial/serial_port.h | 8
third_party/blink/renderer/modules/serial/serial_unittest.cc | 210
third_party/blink/renderer/modules/webrtc/DEPS | 7
third_party/blink/renderer/modules/webrtc/webrtc_audio_device_impl.cc | 17
third_party/blink/renderer/modules/webrtc/webrtc_audio_device_impl_test.cc | 204
third_party/blink/renderer/modules/webrtc/webrtc_audio_renderer.cc | 4
third_party/blink/renderer/modules/webrtc/webrtc_audio_renderer.h | 9
third_party/blink/renderer/platform/runtime_enabled_features.json5 | 8
third_party/breakpad/README.chromium | 2
third_party/breakpad/breakpad/Makefile.am | 14
third_party/breakpad/breakpad/Makefile.in | 45
third_party/breakpad/breakpad/src/client/windows/crash_generation/minidump_generator.cc | 53
third_party/breakpad/breakpad/src/common/dwarf_cu_to_module.cc | 6
third_party/breakpad/breakpad/src/common/dwarf_cu_to_module_unittest.cc | 39
third_party/breakpad/breakpad/src/common/language.cc | 47
third_party/breakpad/breakpad/src/common/language.h | 4
third_party/breakpad/breakpad/src/common/stabs_reader.cc | 3
third_party/breakpad/breakpad/src/common/stabs_reader_unittest.cc | 3
third_party/dawn/src/dawn/native/Buffer.cpp | 13
third_party/dawn/src/dawn/native/Buffer.h | 2
third_party/dawn/src/dawn/native/Toggles.cpp | 9
third_party/dawn/src/dawn/native/Toggles.h | 2
third_party/dawn/src/dawn/native/d3d12/PhysicalDeviceD3D12.cpp | 8
third_party/dawn/src/dawn/native/d3d12/ShaderModuleD3D12.cpp | 2
third_party/dawn/src/dawn/native/metal/PhysicalDeviceMTL.mm | 5
third_party/dawn/src/dawn/native/metal/ShaderModuleMTL.mm | 4
third_party/dawn/src/dawn/native/opengl/ShaderModuleGL.cpp | 8
third_party/dawn/src/dawn/native/vulkan/PhysicalDeviceVk.cpp | 16
third_party/dawn/src/dawn/native/vulkan/PhysicalDeviceVk.h | 1
third_party/dawn/src/dawn/native/vulkan/RenderPipelineVk.cpp | 30
third_party/dawn/src/dawn/native/vulkan/ShaderModuleVk.cpp | 2
third_party/dawn/src/dawn/native/vulkan/ShaderModuleVk.h | 3
third_party/dawn/src/dawn/tests/BUILD.gn | 1
third_party/dawn/src/dawn/tests/CMakeLists.txt | 1
third_party/dawn/src/dawn/tests/DawnTest.cpp | 30
third_party/dawn/src/dawn/tests/end2end/OpArrayLengthTests.cpp | 87
third_party/dawn/src/dawn/tests/end2end/ShaderIOPolyfillTests.cpp | 111
third_party/dawn/src/dawn/tests/end2end/ShaderTests.cpp | 46
third_party/dawn/src/dawn/tests/end2end/SubgroupsTests.cpp | 47
third_party/dawn/src/tint/lang/core/ir/transform/BUILD.bazel | 3
third_party/dawn/src/tint/lang/core/ir/transform/BUILD.cmake | 3
third_party/dawn/src/tint/lang/core/ir/transform/BUILD.gn | 3
third_party/dawn/src/tint/lang/core/ir/transform/collapse_subgroup_min_max.cc | 86
third_party/dawn/src/tint/lang/core/ir/transform/collapse_subgroup_min_max.h | 49
third_party/dawn/src/tint/lang/core/ir/transform/collapse_subgroup_min_max_test.cc | 191
third_party/dawn/src/tint/lang/hlsl/writer/common/options.h | 6
third_party/dawn/src/tint/lang/hlsl/writer/raise/raise.cc | 5
third_party/dawn/src/tint/lang/hlsl/writer/writer_fuzz.cc | 5
third_party/dawn/src/tint/lang/msl/builtin_fn.cc | 3
third_party/dawn/src/tint/lang/msl/builtin_fn.cc.tmpl | 1
third_party/dawn/src/tint/lang/msl/builtin_fn.h | 1
third_party/dawn/src/tint/lang/msl/intrinsic/data.cc | 17
third_party/dawn/src/tint/lang/msl/msl.def | 1
third_party/dawn/src/tint/lang/msl/writer/common/options.h | 10
third_party/dawn/src/tint/lang/msl/writer/printer/printer.cc | 12
third_party/dawn/src/tint/lang/msl/writer/raise/binary_polyfill.cc | 25
third_party/dawn/src/tint/lang/msl/writer/raise/binary_polyfill.h | 9
third_party/dawn/src/tint/lang/msl/writer/raise/binary_polyfill_test.cc | 127
third_party/dawn/src/tint/lang/msl/writer/raise/raise.cc | 13
third_party/dawn/src/tint/lang/msl/writer/writer_fuzz.cc | 8
third_party/dawn/src/tint/lang/msl/writer/writer_test.cc | 27
third_party/dawn/src/tint/lang/spirv/writer/common/options.h | 11
third_party/dawn/src/tint/lang/spirv/writer/raise/raise.cc | 5
third_party/dawn/src/tint/lang/spirv/writer/raise/shader_io.cc | 32
third_party/dawn/src/tint/lang/spirv/writer/raise/shader_io.h | 2
third_party/dawn/src/tint/lang/spirv/writer/raise/shader_io_test.cc | 16
third_party/dawn/src/tint/lang/spirv/writer/writer_fuzz.cc | 9
third_party/dawn/src/tint/lang/spirv/writer/writer_test.cc | 2
third_party/lens_server_proto/aim_communication.proto | 9
third_party/lens_server_proto/aim_query.proto | 1
third_party/libvpx/README.chromium | 2
third_party/libvpx/source/config/vpx_version.h | 6
third_party/libvpx/source/libvpx/test/encode_api_test.cc | 68
third_party/libvpx/source/libvpx/vpx/src/vpx_encoder.c | 5
third_party/pdfium/core/fpdfdoc/cpdf_nametree.cpp | 2
third_party/pdfium/core/fpdfdoc/cpdf_nametree_unittest.cpp | 43
third_party/skia/src/gpu/ganesh/ops/AtlasTextOp.cpp | 8
third_party/skia/src/gpu/graphite/dawn/DawnGraphicsPipeline.cpp | 22
third_party/skia/src/text/gpu/GlyphVector.h | 12
third_party/skia/src/text/gpu/SubRunContainer.cpp | 193
third_party/skia/src/text/gpu/SubRunContainer.h | 8
third_party/skia/src/text/gpu/VertexFiller.cpp | 31
third_party/skia/src/text/gpu/VertexFiller.h | 11
third_party/skia/tests/SkRemoteGlyphCacheTest.cpp | 222
third_party/skia/tests/SlugTest.cpp | 148
third_party/skia/tools/text/gpu/TextBlobTools.cpp | 13
third_party/skia/tools/text/gpu/TextBlobTools.h | 2
third_party/webrtc/modules/desktop_capture/desktop_frame_rotation.cc | 4
third_party/webrtc/modules/desktop_capture/win/dxgi_output_duplicator.cc | 13
third_party/webrtc/modules/desktop_capture/win/wgc_capture_session.cc | 25
tools/metrics/histograms/enums.xml | 4
tools/metrics/histograms/metadata/contextual_cueing/enums.xml | 1
tools/metrics/histograms/metadata/contextual_tasks/histograms.xml | 139
tools/metrics/histograms/metadata/glic/enums.xml | 11
ui/base/ime/win/tsf_text_store.cc | 12
ui/base/ime/win/tsf_text_store.h | 5
ui/base/ime/win/tsf_text_store_unittest.cc | 117
ui/events/gestures/gesture_recognizer_impl.cc | 9
ui/gfx/canvas.cc | 8
ui/gfx/canvas_unittest.cc | 118
ui/gfx/platform_font.cc | 20
ui/gfx/platform_font.h | 11
ui/gfx/platform_font_ios.mm | 13
ui/gfx/platform_font_mac.mm | 6
ui/gfx/platform_font_skia.cc | 2
ui/gtk/select_file_dialog_linux_gtk.cc | 5
ui/strings/translations/ax_strings_ar.xtb | 2
ui/views/widget/desktop_aura/desktop_drag_drop_client_ozone.cc | 7
ui/views/widget/desktop_aura/desktop_drag_drop_client_ozone.h | 6
ui/views/widget/desktop_aura/desktop_drag_drop_client_ozone_unittest.cc | 10
ui/views/widget/desktop_aura/desktop_window_tree_host_platform.cc | 5
ui/webui/resources/cr_components/composebox/composebox.html.ts | 1
ui/webui/resources/cr_components/composebox/composebox_mixin.ts | 46
ui/webui/resources/cr_components/composebox/composebox_voice_search.css | 37
ui/webui/resources/cr_components/composebox/composebox_voice_search.html.ts | 60
ui/webui/resources/cr_components/composebox/composebox_voice_search.ts | 85
ui/webui/resources/cr_components/search/animated_glow.css | 17
ui/webui/resources/cr_components/search/animated_glow.html.ts | 9
v8/include/v8-version.h | 2
v8/src/compiler/wasm-compiler-definitions.h | 7
v8/src/deoptimizer/deoptimizer.cc | 8
v8/src/inspector/v8-inspector-impl.cc | 14
v8/src/wasm/signature-hashing.h | 44
v8/tools/builtins-pgo/profiles/meta.json | 2
v8/tools/builtins-pgo/profiles/x64-rl.profile |27273 +++----
v8/tools/builtins-pgo/profiles/x64.profile |26147 +++---
v8/tools/builtins-pgo/profiles/x86-rl.profile |38186 +++++-----
v8/tools/builtins-pgo/profiles/x86.profile |28519 +++----
439 files changed, 73027 insertions(+), 62065 deletions(-)
dpkg-source: warning: cannot verify inline signature for /srv/release.debian.org/tmp/tmp1fywjjry/chromium_149.0.7827.114-1~deb13u1.dsc: no acceptable signature found
dpkg-source: warning: cannot verify inline signature for /srv/release.debian.org/tmp/tmp1fywjjry/chromium_149.0.7827.155-1~deb13u1.dsc: no acceptable signature found
diff -Nru chromium-149.0.7827.114/DEPS chromium-149.0.7827.155/DEPS
--- chromium-149.0.7827.114/DEPS 2026-06-10 17:58:03.000000000 +0000
+++ chromium-149.0.7827.155/DEPS 2026-06-16 01:31:02.000000000 +0000
@@ -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': '8a237ab7e3bfa15806008cd9ba10339229780d1b',
+ 'src_internal_revision': '4d7db44c3de9fb626e31e7444bedcfc97c6fc0bd',
# 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': '92a56ebeef43061f4878aa869aa1f2160265c24c',
+ 'skia_revision': '75c589e1f436688fca8f5b7f7a8affeafaa4f923',
# 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': '16ef80c1f5d3cfade812bd1743952a4cfd480a31',
+ 'v8_revision': '6511f6cfab1f24c360d0fb737d205c27da48a623',
# 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': '4b8c7f0f321952bba4f81056b4aa57d0d6428642',
+ 'angle_revision': '591ee1999d950f2bc54be89651eb62c8d7925314',
# Three lines of non-changing comments so that
# the commit queue can handle CLs rolling SwiftShader
# and whatever else without interference from each other.
@@ -336,7 +336,7 @@
# Three lines of non-changing comments so that
# the commit queue can handle CLs rolling PDFium
# and whatever else without interference from each other.
- 'pdfium_revision': '74d747ce1d383caca3ec0e604d77bac35ccd1e58',
+ 'pdfium_revision': 'be702d63baba7507bb1f6f6ff2d35be9c133c08c',
# Three lines of non-changing comments so that
# the commit queue can handle CLs rolling BoringSSL
# and whatever else without interference from each other.
@@ -356,7 +356,7 @@
# Three lines of non-changing comments so that
# the commit queue can handle CLs rolling breakpad
# and whatever else without interference from each other.
- 'breakpad_revision': 'afa2870e449ef33ad41545e7670c574cf70926a4',
+ 'breakpad_revision': '8ef5673404a3bbc192b0997e1c2df559cc5bd79d',
# Three lines of non-changing comments so that
# the commit queue can handle CLs rolling freetype
# and whatever else without interference from each other.
@@ -424,7 +424,7 @@
# Three lines of non-changing comments so that
# the commit queue can handle CLs rolling feed
# and whatever else without interference from each other.
- 'dawn_revision': 'c1179de12ec3ed8feb91e922f12a90ae33f4a8cf',
+ 'dawn_revision': '5f4c5ef509c5ffa65822302341cf9b2ccad471f9',
# Three lines of non-changing comments so that
# the commit queue can handle CLs rolling feed
# and whatever else without interference from each other.
@@ -2504,7 +2504,7 @@
Var('chromium_git') + '/chromiumos/platform/libva-fake-driver.git' + '@' + 'a9bcab9cd6b15d4e3634ca44d5e5f7652c612194',
'src/third_party/libvpx/source/libvpx':
- Var('chromium_git') + '/webm/libvpx.git' + '@' + '640d4ce27ba918783e28a0da46a8a37abe4a65b6',
+ Var('chromium_git') + '/webm/libvpx.git' + '@' + 'd1268a5f3f3553ad7735911c614e8548381ca3cd',
'src/third_party/libwebm/source':
Var('chromium_git') + '/webm/libwebm.git' + '@' + '6184f4484a826724b5293837134ab9492261b941',
@@ -3040,7 +3040,7 @@
Var('chromium_git') + '/webpagereplay.git' + '@' + Var('webpagereplay_revision'),
'src/third_party/webrtc':
- Var('webrtc_git') + '/src.git' + '@' + 'e8b4d4c5952a8fb7b35c2a6cba4e8c3de2ea2e1e',
+ Var('webrtc_git') + '/src.git' + '@' + '28311abc149a9bb7e6761a3d54f362a8d77e6793',
# 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.
@@ -3540,7 +3540,7 @@
'src/chrome/browser/glic/resources/internal': {
'url': Var('chrome_git') + '/chrome/browser/glic/resources/internal.git' + '@' +
- 'd3751ee3dbedb61f6d5db816f90f45f75c1abcd8',
+ '95f6df7236aeabbcc0409bf3f7e4c284ca3f2e72',
'condition': 'checkout_src_internal',
},
@@ -3767,7 +3767,7 @@
'src/components/optimization_guide/internal': {
'url': Var('chrome_git') + '/chrome/components/optimization_guide.git' + '@' +
- '21bed19e78ae5e494cf51be7a0bd81c5869ec014',
+ 'f57f8dd6bbcaad191f8893df49818b64bb706bcc',
'condition': 'checkout_src_internal',
},
diff -Nru chromium-149.0.7827.114/ash/strings/ash_strings_fa.xtb chromium-149.0.7827.155/ash/strings/ash_strings_fa.xtb
--- chromium-149.0.7827.114/ash/strings/ash_strings_fa.xtb 2026-06-10 17:58:03.000000000 +0000
+++ chromium-149.0.7827.155/ash/strings/ash_strings_fa.xtb 2026-06-16 01:31:02.000000000 +0000
@@ -1209,7 +1209,7 @@
صفحه حریم خصوصی خاموش است
بارگیری بعداً انجام خواهد شد. اکنون گفتار برای پردازش به Google ارسال خواهد شد.
وارونگی رنگ
-افزایش نور صفحهکلید
+زیاد کردن نور صفحهکلید
هفته آینده
کابل USB-C شما از USB4 پشتیبانی نمیکند. ممکن است عملکرد دستگاه محدود شود.
مکرراً استفادهشده
@@ -1739,7 +1739,7 @@
1X
گذرواژه باید فقط حاوی نویسههای معتبر باشد
دکمه بالایی
-هربار پرسیده شود
+هربار پرسیدن
باز کردن اعلانها
بهعنوان تکمیلشده علامت زده نشد. وقتی آنلاین شدید دوباره امتحان کنید.
قفل درحالت افقی
@@ -2289,7 +2289,7 @@
بهروزرسانی امنیتی تمدیدشده دردسترس است
میخواهد از شما استفاده کند
پنجرهها بازیابی نشوند
-کم کردن نور صفحهکلید
+کمنور کردن صفحهکلید
حروف بزرگ
وضوح جدید را تأیید میکنید؟
«ذرهبین تمامصفحه» فعال شد. برای خاموش کردن آن، کلیدهای را دوباره فشار دهید.
diff -Nru chromium-149.0.7827.114/base/files/file_util.h chromium-149.0.7827.155/base/files/file_util.h
--- chromium-149.0.7827.114/base/files/file_util.h 2026-06-10 17:58:03.000000000 +0000
+++ chromium-149.0.7827.155/base/files/file_util.h 2026-06-16 01:31:02.000000000 +0000
@@ -407,8 +407,12 @@
// NOTE: Exclusivity is unique to Windows. On Windows, the returned file
// supports File::DeleteOnClose. On other platforms, the caller is responsible
// for deleting the file `temp_file` points to, if appropriate.
+//
+// On Windows, `additional_flags` will be combined with the default flags when
+// opening the file.
BASE_EXPORT File CreateAndOpenTemporaryFileInDir(const FilePath& dir,
- FilePath* temp_file);
+ FilePath* temp_file,
+ uint32_t additional_flags = 0);
// Creates a temporary file. The full path is placed in `path`, and the
// function returns true if was successful in creating the file. The file will
diff -Nru chromium-149.0.7827.114/base/files/file_util_posix.cc chromium-149.0.7827.155/base/files/file_util_posix.cc
--- chromium-149.0.7827.114/base/files/file_util_posix.cc 2026-06-10 17:58:03.000000000 +0000
+++ chromium-149.0.7827.155/base/files/file_util_posix.cc 2026-06-16 01:31:02.000000000 +0000
@@ -850,7 +850,9 @@
}
#endif // !BUILDFLAG(IS_APPLE)
-File CreateAndOpenTemporaryFileInDir(const FilePath& dir, FilePath* temp_file) {
+File CreateAndOpenTemporaryFileInDir(const FilePath& dir,
+ FilePath* temp_file,
+ uint32_t /*additional_flags*/) {
// For call to close() inside ScopedFD.
ScopedBlockingCall scoped_blocking_call(FROM_HERE, BlockingType::MAY_BLOCK);
ScopedFD fd = CreateAndOpenFdForTemporaryFileInDir(dir, temp_file);
diff -Nru chromium-149.0.7827.114/base/files/file_util_win.cc chromium-149.0.7827.155/base/files/file_util_win.cc
--- chromium-149.0.7827.114/base/files/file_util_win.cc 2026-06-10 17:58:03.000000000 +0000
+++ chromium-149.0.7827.155/base/files/file_util_win.cc 2026-06-16 01:31:02.000000000 +0000
@@ -779,15 +779,17 @@
return FilePath(FILE_PATH_LITERAL("C:\\"));
}
-File CreateAndOpenTemporaryFileInDir(const FilePath& dir, FilePath* temp_file) {
+File CreateAndOpenTemporaryFileInDir(const FilePath& dir,
+ FilePath* temp_file,
+ uint32_t additional_flags) {
ScopedBlockingCall scoped_blocking_call(FROM_HERE, BlockingType::MAY_BLOCK);
// Open the file with exclusive r/w/d access, and allow the caller to decide
// to mark it for deletion upon close after the fact.
- constexpr uint32_t kFlags = File::FLAG_CREATE | File::FLAG_READ |
- File::FLAG_WRITE | File::FLAG_WIN_EXCLUSIVE_READ |
- File::FLAG_WIN_EXCLUSIVE_WRITE |
- File::FLAG_CAN_DELETE_ON_CLOSE;
+ uint32_t flags = File::FLAG_CREATE | File::FLAG_READ | File::FLAG_WRITE |
+ File::FLAG_WIN_EXCLUSIVE_READ |
+ File::FLAG_WIN_EXCLUSIVE_WRITE |
+ File::FLAG_CAN_DELETE_ON_CLOSE | additional_flags;
// Use GUID instead of ::GetTempFileName() to generate unique file names.
// "Due to the algorithm used to generate file names, GetTempFileName can
@@ -804,7 +806,7 @@
for (int i = 0; i < 100; ++i) {
temp_name = dir.Append(FormatTemporaryFileName(
UTF8ToWide(Uuid::GenerateRandomV4().AsLowercaseString()), true));
- file.Initialize(temp_name, kFlags);
+ file.Initialize(temp_name, flags);
if (file.IsValid()) {
break;
}
diff -Nru chromium-149.0.7827.114/base/metrics/histogram.h chromium-149.0.7827.155/base/metrics/histogram.h
--- chromium-149.0.7827.114/base/metrics/histogram.h 2026-06-10 17:58:03.000000000 +0000
+++ chromium-149.0.7827.155/base/metrics/histogram.h 2026-06-16 01:31:02.000000000 +0000
@@ -294,7 +294,7 @@
friend class StatisticsRecorder; // To allow it to delete duplicates.
friend class StatisticsRecorderTest;
- friend BASE_EXPORT HistogramBase* DeserializeHistogramInfo(
+ friend HistogramBase* HistogramBase::DeserializeInfo(
base::PickleIterator* iter,
NameMapper mapper);
static HistogramBase* DeserializeInfoImpl(base::PickleIterator* iter,
@@ -431,7 +431,7 @@
HistogramSamples::Metadata* logged_meta);
private:
- friend BASE_EXPORT HistogramBase* DeserializeHistogramInfo(
+ friend HistogramBase* HistogramBase::DeserializeInfo(
base::PickleIterator* iter,
NameMapper mapper);
static HistogramBase* DeserializeInfoImpl(base::PickleIterator* iter,
@@ -563,7 +563,7 @@
HistogramSamples::Metadata* meta,
HistogramSamples::Metadata* logged_meta);
- friend BASE_EXPORT HistogramBase* DeserializeHistogramInfo(
+ friend HistogramBase* HistogramBase::DeserializeInfo(
base::PickleIterator* iter,
NameMapper mapper);
static HistogramBase* DeserializeInfoImpl(base::PickleIterator* iter,
@@ -636,7 +636,7 @@
void SerializeInfoImpl(base::Pickle* pickle) const override;
private:
- friend BASE_EXPORT HistogramBase* DeserializeHistogramInfo(
+ friend HistogramBase* HistogramBase::DeserializeInfo(
base::PickleIterator* iter,
NameMapper mapper);
static HistogramBase* DeserializeInfoImpl(base::PickleIterator* iter,
diff -Nru chromium-149.0.7827.114/base/metrics/histogram_base.cc chromium-149.0.7827.155/base/metrics/histogram_base.cc
--- chromium-149.0.7827.114/base/metrics/histogram_base.cc 2026-06-10 17:58:03.000000000 +0000
+++ chromium-149.0.7827.155/base/metrics/histogram_base.cc 2026-06-16 01:31:02.000000000 +0000
@@ -63,6 +63,46 @@
const HistogramBase::Sample32 HistogramBase::kSampleType_MAX = INT_MAX;
+// static
+HistogramBase* HistogramBase::DeserializeInfo(
+ PickleIterator* iter,
+ HistogramBase::NameMapper mapper) {
+ int type;
+ if (!iter->ReadInt(&type)) {
+ return nullptr;
+ }
+
+ HistogramBase* result;
+ switch (type) {
+ case HISTOGRAM:
+ result = Histogram::DeserializeInfoImpl(iter, mapper);
+ break;
+ case LINEAR_HISTOGRAM:
+ result = LinearHistogram::DeserializeInfoImpl(iter, mapper);
+ break;
+ case BOOLEAN_HISTOGRAM:
+ result = BooleanHistogram::DeserializeInfoImpl(iter, mapper);
+ break;
+ case CUSTOM_HISTOGRAM:
+ result = CustomHistogram::DeserializeInfoImpl(iter, mapper);
+ break;
+ case SPARSE_HISTOGRAM:
+ result = SparseHistogram::DeserializeInfoImpl(iter, mapper);
+ break;
+ default:
+ return nullptr;
+ }
+
+ if (result != nullptr &&
+ result->GetHistogramType() != static_cast(type)) {
+ // If there's a type mismatch, this could be a DummyHistogram returned by
+ // FactoryGetInternal() due to invalid arguments. In this case, return
+ // nullptr to indicate an error.
+ return nullptr;
+ }
+ return result;
+}
+
HistogramBase::HistogramBase(DurableStringView name)
: histogram_name_(name->data()),
histogram_name_length_(base::saturated_cast(name->length())),
@@ -234,27 +274,4 @@
return DurableStringView(*it);
}
-HistogramBase* DeserializeHistogramInfo(PickleIterator* iter,
- HistogramBase::NameMapper mapper) {
- int type;
- if (!iter->ReadInt(&type)) {
- return nullptr;
- }
-
- switch (type) {
- case HISTOGRAM:
- return Histogram::DeserializeInfoImpl(iter, mapper);
- case LINEAR_HISTOGRAM:
- return LinearHistogram::DeserializeInfoImpl(iter, mapper);
- case BOOLEAN_HISTOGRAM:
- return BooleanHistogram::DeserializeInfoImpl(iter, mapper);
- case CUSTOM_HISTOGRAM:
- return CustomHistogram::DeserializeInfoImpl(iter, mapper);
- case SPARSE_HISTOGRAM:
- return SparseHistogram::DeserializeInfoImpl(iter, mapper);
- default:
- return nullptr;
- }
-}
-
} // namespace base
diff -Nru chromium-149.0.7827.114/base/metrics/histogram_base.h chromium-149.0.7827.155/base/metrics/histogram_base.h
--- chromium-149.0.7827.114/base/metrics/histogram_base.h 2026-06-10 17:58:03.000000000 +0000
+++ chromium-149.0.7827.155/base/metrics/histogram_base.h 2026-06-16 01:31:02.000000000 +0000
@@ -115,6 +115,16 @@
NEVER_EXCEEDED_VALUE = 0x10,
};
+ // Create or find existing histogram that matches the pickled info.
+ // The `mapper` callback is invoked with the original histogram name. It can
+ // return a new name to rename the histogram, the original name to keep it,
+ // or an empty string to drop the histogram entirely. This allows renaming
+ // metrics during deserialization, e.g., adding prefixes based on process
+ // type.
+ // Returns nullptr if the pickled data has problems.
+ static HistogramBase* DeserializeInfo(base::PickleIterator* iter,
+ HistogramBase::NameMapper mapper);
+
// Construct the base histogram. The name is not copied; it's up to the
// caller to ensure that it lives at least as long as this object.
explicit HistogramBase(DurableStringView name);
@@ -328,16 +338,6 @@
std::atomic flags_{0};
};
-// Create or find existing histogram that matches the pickled info.
-// The `mapper` callback is invoked with the original histogram name. It can
-// return a new name to rename the histogram, the original name to keep it,
-// or an empty string to drop the histogram entirely. This allows renaming
-// metrics during deserialization, e.g., adding prefixes based on process type.
-// Returns NULL if the pickled data has problems.
-BASE_EXPORT HistogramBase* DeserializeHistogramInfo(
- base::PickleIterator* iter,
- HistogramBase::NameMapper mapper);
-
} // namespace base
#endif // BASE_METRICS_HISTOGRAM_BASE_H_
diff -Nru chromium-149.0.7827.114/base/metrics/histogram_base_unittest.cc chromium-149.0.7827.155/base/metrics/histogram_base_unittest.cc
--- chromium-149.0.7827.114/base/metrics/histogram_base_unittest.cc 2026-06-10 17:58:03.000000000 +0000
+++ chromium-149.0.7827.155/base/metrics/histogram_base_unittest.cc 2026-06-16 01:31:02.000000000 +0000
@@ -53,13 +53,13 @@
PickleIterator iter(pickle);
HistogramBase* deserialized =
- DeserializeHistogramInfo(&iter, base::NullCallback());
+ HistogramBase::DeserializeInfo(&iter, base::NullCallback());
EXPECT_EQ(histogram, deserialized);
ResetStatisticsRecorder();
PickleIterator iter2(pickle);
- deserialized = DeserializeHistogramInfo(&iter2, base::NullCallback());
+ deserialized = HistogramBase::DeserializeInfo(&iter2, base::NullCallback());
EXPECT_TRUE(deserialized);
EXPECT_NE(histogram, deserialized);
EXPECT_EQ("TestHistogram", deserialized->histogram_name());
@@ -78,13 +78,13 @@
PickleIterator iter(pickle);
HistogramBase* deserialized =
- DeserializeHistogramInfo(&iter, base::NullCallback());
+ HistogramBase::DeserializeInfo(&iter, base::NullCallback());
EXPECT_EQ(histogram, deserialized);
ResetStatisticsRecorder();
PickleIterator iter2(pickle);
- deserialized = DeserializeHistogramInfo(&iter2, base::NullCallback());
+ deserialized = HistogramBase::DeserializeInfo(&iter2, base::NullCallback());
EXPECT_TRUE(deserialized);
EXPECT_NE(histogram, deserialized);
EXPECT_EQ("TestHistogram", deserialized->histogram_name());
@@ -101,13 +101,13 @@
PickleIterator iter(pickle);
HistogramBase* deserialized =
- DeserializeHistogramInfo(&iter, base::NullCallback());
+ HistogramBase::DeserializeInfo(&iter, base::NullCallback());
EXPECT_EQ(histogram, deserialized);
ResetStatisticsRecorder();
PickleIterator iter2(pickle);
- deserialized = DeserializeHistogramInfo(&iter2, base::NullCallback());
+ deserialized = HistogramBase::DeserializeInfo(&iter2, base::NullCallback());
EXPECT_TRUE(deserialized);
EXPECT_NE(histogram, deserialized);
EXPECT_EQ("TestHistogram", deserialized->histogram_name());
@@ -129,13 +129,13 @@
PickleIterator iter(pickle);
HistogramBase* deserialized =
- DeserializeHistogramInfo(&iter, base::NullCallback());
+ HistogramBase::DeserializeInfo(&iter, base::NullCallback());
EXPECT_EQ(histogram, deserialized);
ResetStatisticsRecorder();
PickleIterator iter2(pickle);
- deserialized = DeserializeHistogramInfo(&iter2, base::NullCallback());
+ deserialized = HistogramBase::DeserializeInfo(&iter2, base::NullCallback());
EXPECT_TRUE(deserialized);
EXPECT_NE(histogram, deserialized);
EXPECT_EQ("TestHistogram", deserialized->histogram_name());
@@ -152,13 +152,13 @@
PickleIterator iter(pickle);
HistogramBase* deserialized =
- DeserializeHistogramInfo(&iter, base::NullCallback());
+ HistogramBase::DeserializeInfo(&iter, base::NullCallback());
EXPECT_EQ(histogram, deserialized);
ResetStatisticsRecorder();
PickleIterator iter2(pickle);
- deserialized = DeserializeHistogramInfo(&iter2, base::NullCallback());
+ deserialized = HistogramBase::DeserializeInfo(&iter2, base::NullCallback());
EXPECT_TRUE(deserialized);
EXPECT_NE(histogram, deserialized);
EXPECT_EQ("TestHistogram", deserialized->histogram_name());
@@ -248,4 +248,62 @@
EXPECT_EQ(add_count, samples->GetCount(0));
}
+TEST_F(HistogramBaseTest, DeserializeTypeMismatch) {
+ // Create a LinearHistogram and register it in the StatisticsRecorder.
+ HistogramBase* histogram = LinearHistogram::FactoryGet(
+ "TestMismatchedHistogram", 1, 1000, 10, HistogramBase::kNoFlags);
+ Pickle real_pickle;
+ histogram->SerializeInfo(&real_pickle);
+
+ // Read the serialized fields using a PickleIterator.
+ int type;
+ std::string name;
+ int flags;
+ int declared_min;
+ int declared_max;
+ uint32_t bucket_count;
+ uint32_t range_checksum;
+
+ PickleIterator iter(real_pickle);
+ ASSERT_TRUE(iter.ReadInt(&type));
+ ASSERT_TRUE(iter.ReadString(&name));
+ ASSERT_TRUE(iter.ReadInt(&flags));
+ ASSERT_TRUE(iter.ReadInt(&declared_min));
+ ASSERT_TRUE(iter.ReadInt(&declared_max));
+ ASSERT_TRUE(iter.ReadUInt32(&bucket_count));
+ ASSERT_TRUE(iter.ReadUInt32(&range_checksum));
+
+ EXPECT_EQ(LINEAR_HISTOGRAM, type);
+
+ // Verify that deserializing the unmodified base pickle succeeds.
+ PickleIterator real_iter(real_pickle);
+ HistogramBase* deserialized_real =
+ HistogramBase::DeserializeInfo(&real_iter, base::NullCallback());
+ EXPECT_EQ(histogram, deserialized_real);
+ // And that it succeeds a second time too.
+ PickleIterator real_iter2(real_pickle);
+ HistogramBase* deserialized_real2 =
+ HistogramBase::DeserializeInfo(&real_iter2, base::NullCallback());
+ EXPECT_EQ(histogram, deserialized_real2);
+
+ // Construct a modified pickle claiming the type is HISTOGRAM instead of
+ // LINEAR_HISTOGRAM.
+ Pickle mismatched_pickle;
+ mismatched_pickle.WriteInt(HISTOGRAM); // Mismatched type!
+ mismatched_pickle.WriteString(name);
+ mismatched_pickle.WriteInt(flags);
+ mismatched_pickle.WriteInt(declared_min);
+ mismatched_pickle.WriteInt(declared_max);
+ mismatched_pickle.WriteUInt32(bucket_count);
+ mismatched_pickle.WriteUInt32(range_checksum);
+
+ // Deserialize the mismatched pickle. Because "TestMismatchedHistogram"
+ // is already registered as a LINEAR_HISTOGRAM, looking it up as a
+ // HISTOGRAM should detect the type mismatch and return nullptr.
+ PickleIterator mismatched_iter(mismatched_pickle);
+ HistogramBase* deserialized =
+ HistogramBase::DeserializeInfo(&mismatched_iter, base::NullCallback());
+ EXPECT_FALSE(deserialized);
+}
+
} // namespace base
diff -Nru chromium-149.0.7827.114/base/metrics/histogram_delta_serialization.cc chromium-149.0.7827.155/base/metrics/histogram_delta_serialization.cc
--- chromium-149.0.7827.114/base/metrics/histogram_delta_serialization.cc 2026-06-10 17:58:03.000000000 +0000
+++ chromium-149.0.7827.155/base/metrics/histogram_delta_serialization.cc 2026-06-16 01:31:02.000000000 +0000
@@ -22,7 +22,8 @@
// Silently returns when seeing any data problem in the pickle.
void DeserializeHistogramAndAddSamples(PickleIterator* iter,
HistogramBase::NameMapper mapper) {
- HistogramBase* histogram = DeserializeHistogramInfo(iter, std::move(mapper));
+ HistogramBase* histogram =
+ HistogramBase::DeserializeInfo(iter, std::move(mapper));
if (!histogram) {
return;
}
diff -Nru chromium-149.0.7827.114/base/metrics/persistent_histogram_allocator.cc chromium-149.0.7827.155/base/metrics/persistent_histogram_allocator.cc
--- chromium-149.0.7827.114/base/metrics/persistent_histogram_allocator.cc 2026-06-10 17:58:03.000000000 +0000
+++ chromium-149.0.7827.155/base/metrics/persistent_histogram_allocator.cc 2026-06-16 01:31:02.000000000 +0000
@@ -748,7 +748,7 @@
name_override);
}
- existing = DeserializeHistogramInfo(&iter, mapper);
+ existing = HistogramBase::DeserializeInfo(&iter, mapper);
if (!existing) {
return nullptr;
}
diff -Nru chromium-149.0.7827.114/base/metrics/sparse_histogram.h chromium-149.0.7827.155/base/metrics/sparse_histogram.h
--- chromium-149.0.7827.114/base/metrics/sparse_histogram.h 2026-06-10 17:58:03.000000000 +0000
+++ chromium-149.0.7827.155/base/metrics/sparse_histogram.h 2026-06-16 01:31:02.000000000 +0000
@@ -78,7 +78,7 @@
HistogramSamples::Metadata* meta,
HistogramSamples::Metadata* logged_meta);
- friend BASE_EXPORT HistogramBase* DeserializeHistogramInfo(
+ friend HistogramBase* HistogramBase::DeserializeInfo(
base::PickleIterator* iter,
NameMapper mapper);
static HistogramBase* DeserializeInfoImpl(base::PickleIterator* iter,
diff -Nru chromium-149.0.7827.114/build/util/LASTCHANGE chromium-149.0.7827.155/build/util/LASTCHANGE
--- chromium-149.0.7827.114/build/util/LASTCHANGE 2026-06-10 17:58:03.000000000 +0000
+++ chromium-149.0.7827.155/build/util/LASTCHANGE 2026-06-16 01:31:02.000000000 +0000
@@ -1,2 +1,2 @@
-LASTCHANGE=5be7af702aa73ed64f47858cecc86290e42f2a20-refs/branch-heads/7827_102@{#39}
+LASTCHANGE=07b52360cc15066f987c910ab34dfbcd4a8778d2-refs/branch-heads/7827@{#3246}
LASTCHANGE_YEAR=2026
diff -Nru chromium-149.0.7827.114/build/util/LASTCHANGE.committime chromium-149.0.7827.155/build/util/LASTCHANGE.committime
--- chromium-149.0.7827.114/build/util/LASTCHANGE.committime 2026-06-10 17:58:03.000000000 +0000
+++ chromium-149.0.7827.155/build/util/LASTCHANGE.committime 2026-06-16 01:31:02.000000000 +0000
@@ -1 +1 @@
-1781114283
\ No newline at end of file
+1781573462
\ No newline at end of file
diff -Nru chromium-149.0.7827.114/chrome/VERSION chromium-149.0.7827.155/chrome/VERSION
--- chromium-149.0.7827.114/chrome/VERSION 2026-06-10 17:58:03.000000000 +0000
+++ chromium-149.0.7827.155/chrome/VERSION 2026-06-16 01:31:02.000000000 +0000
@@ -1,4 +1,4 @@
MAJOR=149
MINOR=0
BUILD=7827
-PATCH=114
+PATCH=155
diff -Nru chromium-149.0.7827.114/chrome/app/resources/generated_resources_ar.xtb chromium-149.0.7827.155/chrome/app/resources/generated_resources_ar.xtb
--- chromium-149.0.7827.114/chrome/app/resources/generated_resources_ar.xtb 2026-06-10 17:58:03.000000000 +0000
+++ chromium-149.0.7827.155/chrome/app/resources/generated_resources_ar.xtb 2026-06-16 01:31:02.000000000 +0000
@@ -1787,7 +1787,7 @@
حذف السجلّ وملفات تعريف الارتباط وذاكرة التخزين المؤقت وغير ذلك
متوسط
المجلدات التي تمت مشاركتها
-يمكنك التسوّق وترجمة النصوص والتعرّف على ما تراه باستخدام الكاميرا.
+يمكنك تسوّق ما يظهر أمامك أو ترجمته أو التعرّف عليه باستخدام الكاميرا.
{LINE_COUNT,plural, =1{<لم يتم عرض سطر واحد>}zero{<لم يتم عرض سطر>}two{<لم يتم عرض سطرين ()>}few{<لم يتم عرض ) سطور>}many{<لم يتم عرض سطرًا>}other{<لم يتم عرض سطر>}}
لمشاركة الصوت، عليك مشاركة علامة التبويب أو الشاشة بدلاً من ذلك.
حالة موثوقية الشهادة
@@ -7256,7 +7256,7 @@
الحصول على مقاطع شرح لملفات الصوت والفيديو
غير مقروءة
نجوم
-عام
+سنة الصنع
الانتقال إلى المحتوى الإضافي من أصل
كل الأجهزة المجاورة
تأكيد Powerwash بالعودة إلى الإصدار السابق
diff -Nru chromium-149.0.7827.114/chrome/app/resources/generated_resources_de.xtb chromium-149.0.7827.155/chrome/app/resources/generated_resources_de.xtb
--- chromium-149.0.7827.114/chrome/app/resources/generated_resources_de.xtb 2026-06-10 17:58:03.000000000 +0000
+++ chromium-149.0.7827.155/chrome/app/resources/generated_resources_de.xtb 2026-06-16 01:31:02.000000000 +0000
@@ -1779,7 +1779,7 @@
Verlauf, Cookies und andere Daten löschen sowie Cache leeren
Mäßig
Freigegebene Ordner
-Nutze deine Kamera zum Einkaufen, Übersetzen und um mehr über Dinge zu erfahren, die du siehst.
+Über deine Kamera kannst du das, was du siehst, kaufen, übersetzen oder identifizieren.
{LINE_COUNT,plural, =1{<1 Zeile wird nicht angezeigt>}other{< Zeilen werden nicht angezeigt>}}
Zum Teilen von Audio, musst du stattdessen einen Tab oder Bildschirm teilen
Vertrauensstatus
diff -Nru chromium-149.0.7827.114/chrome/app/resources/generated_resources_eu.xtb chromium-149.0.7827.155/chrome/app/resources/generated_resources_eu.xtb
--- chromium-149.0.7827.114/chrome/app/resources/generated_resources_eu.xtb 2026-06-10 17:58:03.000000000 +0000
+++ chromium-149.0.7827.155/chrome/app/resources/generated_resources_eu.xtb 2026-06-16 01:31:02.000000000 +0000
@@ -10244,7 +10244,7 @@
Abrikota
ChromeOS eguneratuta dago
Inaktibo bateria erabili bitartean
-PIN kodeak ez datoz bat
+PINak ez datoz bat
Luzapen baten proxy-ezarpenak ari da erabiltzen
Administratzaileak Zerbitzu-baldintzak onartu behar ditu administrazio-kontsolaren Chrome-ko gailuen zerrendan.
instalatu nahi duzu, eta disko gogorreko eduki guztia ezabatu?
diff -Nru chromium-149.0.7827.114/chrome/app/resources/generated_resources_fa.xtb chromium-149.0.7827.155/chrome/app/resources/generated_resources_fa.xtb
--- chromium-149.0.7827.114/chrome/app/resources/generated_resources_fa.xtb 2026-06-10 17:58:03.000000000 +0000
+++ chromium-149.0.7827.155/chrome/app/resources/generated_resources_fa.xtb 2026-06-16 01:31:02.000000000 +0000
@@ -7586,7 +7586,7 @@
اجازه دارد از جاوا اسکریپت استفاده کند
بار کردن مجدد قاب
مجاز است. دسترسی مکان سیستم را روشن کنید.
-هربار پرسیده شود
+هربار پرسیدن
برای استفاده از «مدیر گذرواژه Google» در سیستمعامل خود، برنامه Chrome را مجدداً راهاندازی کنید و دسترسی به مدیر گذرواژه رایانهتان را مجاز کنید. پساز راهاندازی مجدد، برگههایتان دوباره باز میشوند.
دسترسی به اطلاعات دستگاههای بلوتوث مرتبط شده با سیستم شما و کشف دستگاههای بلوتوث در این نزدیکی.
نیازی به دسترسی نیست
@@ -8046,7 +8046,7 @@
قالببندی نوشتار
ویرایش دادههای ورود به سیستم
محافظت بیشتری دربرابر وبسایتها و بارگیریهای خطرناک دریافت کنید
-همیشه اجازه داشتن
+همیشه اجازه دادن
نمیتوان میانبر را برداشت
Chromebook شما دیگر بهروزرسانیهای امنیتی و نرمافزاری را دریافت نمیکند. برای داشتن بهترین تجربه، Chromebook جدیدی دریافت کنید.
&بارگیری مجدد این صفحه
diff -Nru chromium-149.0.7827.114/chrome/app/resources/generated_resources_fr-CA.xtb chromium-149.0.7827.155/chrome/app/resources/generated_resources_fr-CA.xtb
--- chromium-149.0.7827.114/chrome/app/resources/generated_resources_fr-CA.xtb 2026-06-10 17:58:03.000000000 +0000
+++ chromium-149.0.7827.155/chrome/app/resources/generated_resources_fr-CA.xtb 2026-06-16 01:31:02.000000000 +0000
@@ -2606,7 +2606,7 @@
Bougez votre bouche vers la droite
souhaite connaître le numéro d'identifiant de votre clé de sécurité. Le site saura exactement quelle clé de sécurité vous utilisez.
Votre carte est enregistrée
-Utiliser l'historique d'entrée
+Utiliser l'historique d'entrées
Recherche d'appareils à association rapide enregistrés avec
De la productivité au divertissement, trouvez les applications dont vous avez besoin dans la boutique Google Play Store. Vous pouvez installer des applications à tout moment.
Réinitialiser ce
diff -Nru chromium-149.0.7827.114/chrome/app/resources/generated_resources_fr.xtb chromium-149.0.7827.155/chrome/app/resources/generated_resources_fr.xtb
--- chromium-149.0.7827.114/chrome/app/resources/generated_resources_fr.xtb 2026-06-10 17:58:03.000000000 +0000
+++ chromium-149.0.7827.155/chrome/app/resources/generated_resources_fr.xtb 2026-06-16 01:31:02.000000000 +0000
@@ -5632,7 +5632,7 @@
Sélectionne le parent qui t'autorise à ajouter un compte scolaire.
(bloqué)
Mémoire économisée
-Enregistrez et saisissez automatiquement vos mots de passe pour vous connecter facilement à des sites et applis
+Enregistrez et saisissez automatiquement vos mots de passe pour vous connecter facilement à des sites et applis.
Ouvrir automatiquement certains types de fichiers après leur téléchargement
Les parents peuvent approuver ou bloquer des applis, définir des limites de temps d'utilisation et contrôler la navigation sur le Web. Un compte scolaire peut être ajouté plus tard pour accéder à la plupart des ressources scolaires.
· Onglet récent
@@ -11312,7 +11312,7 @@
Masquer les autres
Fond d'écran
Fenêtre déplacée vers la gauche
-Ajoutez un niveau de sécurité supplémentaire contre les menaces en ligne
+Ajoutez un niveau de sécurité accru contre les menaces en ligne.
L'EID de votre appareil est , son code IMEI est et son numéro de série est . Ces numéros peuvent servir à activer le service.
Favoris
Configurer l'adresse IP automatiquement
diff -Nru chromium-149.0.7827.114/chrome/app/resources/generated_resources_iw.xtb chromium-149.0.7827.155/chrome/app/resources/generated_resources_iw.xtb
--- chromium-149.0.7827.114/chrome/app/resources/generated_resources_iw.xtb 2026-06-10 17:58:03.000000000 +0000
+++ chromium-149.0.7827.155/chrome/app/resources/generated_resources_iw.xtb 2026-06-16 01:31:02.000000000 +0000
@@ -699,7 +699,7 @@
{NUM_TABS,plural, =1{הוספת הכרטיסייה לקבוצה חדשה}one{הוספת כרטיסיות לקבוצה חדשה}two{הוספת כרטיסיות לקבוצה חדשה}other{הוספת כרטיסיות לקבוצה חדשה}}
כדי להפעיל או להשבית או הגלישה באמצעות סמן הטקסט, אפשר להשתמש במקשי הקיצור Ctrl+Search+7
למצב הקריאה יש בעיה באיתור התוכן הראשי בדף הזה
-הצגת יותר מידע
+עוד
קביעת ההגדרות האישיות לשיתוף אינטרנט בין מכשירים (tethering)
&פריים
אפשר לנסות שוב או להתחבר לרשת באופן ידני
@@ -6029,7 +6029,7 @@
יש לוודא ששני המכשירים אינם נעולים, שהם קרובים זה לזה ושבשניהם מופעל Bluetooth. מידע נוסף
F7
החיבור לשיתוף אינטרנט מיידי בין מכשירים נכשל
-פחות מידע
+פחות
יש להזין את קוד האימות של מפתח האבטחה. אם לא ידוע לך מהו קוד האימות, יהיה עליך לאפס את מפתח האבטחה.
&פתיחה
מקשים לשימוש
diff -Nru chromium-149.0.7827.114/chrome/app/resources/generated_resources_ja.xtb chromium-149.0.7827.155/chrome/app/resources/generated_resources_ja.xtb
--- chromium-149.0.7827.114/chrome/app/resources/generated_resources_ja.xtb 2026-06-10 17:58:03.000000000 +0000
+++ chromium-149.0.7827.155/chrome/app/resources/generated_resources_ja.xtb 2026-06-16 01:31:02.000000000 +0000
@@ -2492,7 +2492,7 @@
デバイスをサーバーに登録するときにエラーが発生しました: 。
このパスワードを覚えておく必要はありません。パスワードは、 の Google パスワード マネージャーに保存されます。
言語オプションを表示
-機種
+車種
要素タイプと書式付きテキストを読み上げるときにピッチを変更する
アクア
充電開始の音
diff -Nru chromium-149.0.7827.114/chrome/app/resources/generated_resources_ko.xtb chromium-149.0.7827.155/chrome/app/resources/generated_resources_ko.xtb
--- chromium-149.0.7827.114/chrome/app/resources/generated_resources_ko.xtb 2026-06-10 17:58:03.000000000 +0000
+++ chromium-149.0.7827.155/chrome/app/resources/generated_resources_ko.xtb 2026-06-16 01:31:02.000000000 +0000
@@ -1793,7 +1793,7 @@
방문 기록, 쿠키, 캐시 등 삭제
보통
공유 폴더
-카메라를 사용해 지금 보고 있는 항목을 쇼핑하고, 번역하고, 알아보세요
+카메라를 사용해 지금 보고 있는 항목을 쇼핑하고, 번역하고, 무엇인지 알아보세요
{LINE_COUNT,plural, =1{<1줄이 표시되지 않음>}other{<줄이 표시되지 않음>}}
오디오를 공유하려면 탭이나 화면을 대신 공유하세요.
신뢰 상태
diff -Nru chromium-149.0.7827.114/chrome/app/resources/generated_resources_pt-BR.xtb chromium-149.0.7827.155/chrome/app/resources/generated_resources_pt-BR.xtb
--- chromium-149.0.7827.114/chrome/app/resources/generated_resources_pt-BR.xtb 2026-06-10 17:58:03.000000000 +0000
+++ chromium-149.0.7827.155/chrome/app/resources/generated_resources_pt-BR.xtb 2026-06-16 01:31:02.000000000 +0000
@@ -5343,7 +5343,7 @@
Compartilhar arquivos com Chromebooks e dispositivos Androids próximos
Todas
Sem interesse
-Pesquisa de guias
+Pesquisa em todas as guias
Velocidade do TrackPoint
Manter a privacidade da sua tela
O app não está disponível off-line.
@@ -5648,7 +5648,7 @@
Selecione o familiar responsável que está autorizando adicionar a conta escolar.
(sensor bloqueado)
Memória economizada
-Salve e preencha automaticamente suas senhas para fazer login em sites e apps com facilidade
+Salve e preencha automaticamente suas senhas para fazer login em sites e apps rapidamente.
Abrir alguns tipos de arquivo automaticamente depois do download
O familiar responsável pode aprovar ou bloquear apps, definir limites de tempo e controlar a navegação na Web. Uma conta escolar pode ser adicionada mais tarde para acessar a maioria dos recursos escolares.
· Guia recente
@@ -8043,7 +8043,7 @@
Os sites podem usar esse recurso para buscar e se conectar a qualquer dispositivo na sua rede local
Crie o que quiser
Leia o QR code com seu celular para salvar e preencher automaticamente suas senhas, facilitando o login em sites e apps
-Pesquisa de guias
+Pesquisa em todas as guias
Economia média
Usar um dispositivo diferente
É preciso ter permissão para fazer o download
@@ -11329,7 +11329,7 @@
Ocultar outros
Plano de fundo
Janela movida para a esquerda
-Adicione mais uma camada de proteção contra ameaças on-line
+Mais proteção contra ameaças on-line.
O EID do dispositivo é , o IMEI é e o número de série é . Esses números podem ser usados para ativar o serviço.
Favoritos
Configurar endereço IP automaticamente
diff -Nru chromium-149.0.7827.114/chrome/app/resources/generated_resources_ru.xtb chromium-149.0.7827.155/chrome/app/resources/generated_resources_ru.xtb
--- chromium-149.0.7827.114/chrome/app/resources/generated_resources_ru.xtb 2026-06-10 17:58:03.000000000 +0000
+++ chromium-149.0.7827.155/chrome/app/resources/generated_resources_ru.xtb 2026-06-16 01:31:02.000000000 +0000
@@ -10365,7 +10365,7 @@
Контент
{NUM_TABS,plural, =1{Добавить вкладку в список для чтения}one{Добавить вкладки в список для чтения}few{Добавить вкладки в список для чтения}many{Добавить вкладки в список для чтения}other{Добавить вкладки в список для чтения}}
Слева
-Сведения об устройстве
+Информация об устройстве
Розовый с белым
Чтобы предоставить доступ к микрофону, используйте специальный физический переключатель на устройстве
Сведения о сети
diff -Nru chromium-149.0.7827.114/chrome/app/resources/generated_resources_tr.xtb chromium-149.0.7827.155/chrome/app/resources/generated_resources_tr.xtb
--- chromium-149.0.7827.114/chrome/app/resources/generated_resources_tr.xtb 2026-06-10 17:58:03.000000000 +0000
+++ chromium-149.0.7827.155/chrome/app/resources/generated_resources_tr.xtb 2026-06-16 01:31:02.000000000 +0000
@@ -5630,7 +5630,7 @@
Okul hesabı eklenmesine izin veren ebeveyni seçin.
(engellendi)
Anı Kaydedildi
-Sitelerde ve uygulamalarda kolayca oturum açmak için şifrelerinizi kaydedin ve otomatik olarak doldurun
+Şifrelerinizi kaydedip otomatik doldurarak site ve uygulamalarda kolayca oturum açın
Belirli türdeki dosyaları indirildikten sonra otomatik olarak aç
Ebeveynler uygulamaları onaylayabilir ya da engelleyebilir, zaman sınırları belirleyebilir ve web'de gezinmeyi kontrol edebilirler. Çoğu okul kaynağına erişmek için daha sonra okul hesabı eklenebilir.
· Son Kullanılanlar sekmesi
@@ -11306,7 +11306,7 @@
Diğerlerini Gizle
Duvar kağıdı
Pencere sola taşındı
-İnternet ortamındaki tehditlere karşı ek koruma katmanı sağlayın
+İnternet ortamındaki tehditlere karşı ek koruma katmanı
Cihazınızın SIM kimliği , IMEI numarası ve seri numarası . Bu numaralar, hizmeti etkinleştirmek için kullanılabilir.
Yer işaretleri
IP adresini otomatik olarak yapılandır
diff -Nru chromium-149.0.7827.114/chrome/app/resources/google_chrome_strings_ar.xtb chromium-149.0.7827.155/chrome/app/resources/google_chrome_strings_ar.xtb
--- chromium-149.0.7827.114/chrome/app/resources/google_chrome_strings_ar.xtb 2026-06-10 17:58:03.000000000 +0000
+++ chromium-149.0.7827.155/chrome/app/resources/google_chrome_strings_ar.xtb 2026-06-16 01:31:02.000000000 +0000
@@ -652,7 +652,7 @@
لحماية بياناتك، يمكنك السماح لمتصفِّح Chrome بإزالة الأذونات من المواقع الإلكترونية التي لم تتم زيارتها مؤخرًا.
يتم إعلامك من خلال Chrome في حال تم اختراق كلمات المرور في أي وقت.
ضَبَط مشرف النظام Google Chrome لفتح للوصول إلى .
-نصيحة من Chrome: حان وقت "الحماية المحسّنة للتصفّح الآمن"
+نصيحة من Chrome: ميزة "الحماية المحسّنة للتصفّح الآمن" تحميك على الإنترنت
مساعدة
شعار Chrome Enterprise
حَظَر Chrome عملية التنزيل هذه لأنّك أوقفت ميزة "التصفُّح الآمن" ولا يمكن التحقّق من الملف.
diff -Nru chromium-149.0.7827.114/chrome/app/resources/google_chrome_strings_de.xtb chromium-149.0.7827.155/chrome/app/resources/google_chrome_strings_de.xtb
--- chromium-149.0.7827.114/chrome/app/resources/google_chrome_strings_de.xtb 2026-06-10 17:58:03.000000000 +0000
+++ chromium-149.0.7827.155/chrome/app/resources/google_chrome_strings_de.xtb 2026-06-16 01:31:02.000000000 +0000
@@ -647,7 +647,7 @@
Erlaube Chrome zum Schutz deiner Daten, Berechtigungen von Websites zu entfernen, die du in letzter Zeit nicht besucht hast.
Chrome wird dich informieren, sollten deine Passwörter nicht mehr sicher sein
Dein Systemadministrator hat Google Chrome so konfiguriert, dass für den Zugriff auf geöffnet wird.
-Chrome-Tipp: erweitertes Safe Browsing
+Chrome-Tipp: Erweitertes Safe Browsing
Hilfe
Logo von Chrome Enterprise
Chrome hat diesen Download blockiert, weil du Safe Browsing deaktiviert hast und die Datei nicht überprüft werden kann
diff -Nru chromium-149.0.7827.114/chrome/app/resources/google_chrome_strings_pt-BR.xtb chromium-149.0.7827.155/chrome/app/resources/google_chrome_strings_pt-BR.xtb
--- chromium-149.0.7827.114/chrome/app/resources/google_chrome_strings_pt-BR.xtb 2026-06-10 17:58:03.000000000 +0000
+++ chromium-149.0.7827.155/chrome/app/resources/google_chrome_strings_pt-BR.xtb 2026-06-16 01:31:02.000000000 +0000
@@ -648,7 +648,7 @@
Para proteger seus dados, permita que o Chrome remova permissões de sites que você não visitou recentemente.
O Chrome avisa se suas senhas forem comprometidas
O administrador do seu sistema configurou o Google Chrome para abrir o ao acessar .
-Dica do Chrome: Navegação Segura com Maior Proteção
+Dica do Chrome: navegação segura com maior proteção
Ajuda
Logotipo do Chrome Enterprise
O Chrome bloqueou o download porque você desativou o recurso Navegação segura, e não é possível verificar o arquivo
diff -Nru chromium-149.0.7827.114/chrome/app/resources/google_chrome_strings_ru.xtb chromium-149.0.7827.155/chrome/app/resources/google_chrome_strings_ru.xtb
--- chromium-149.0.7827.114/chrome/app/resources/google_chrome_strings_ru.xtb 2026-06-10 17:58:03.000000000 +0000
+++ chromium-149.0.7827.155/chrome/app/resources/google_chrome_strings_ru.xtb 2026-06-16 01:31:02.000000000 +0000
@@ -281,7 +281,7 @@
Окно выбора языка
Удалить Google Chrome
Скопировать текст
-Совет по работе с Chrome: используйте Объектив для поиска информации
+Объектив для поиска
Произошла ошибка. Название файла установщика недействительно или не поддерживается.
Невозможно получить доступ к защищенным страницам. Попробуйте использовать функцию на другой странице.
Введите кодовую фразу, чтобы использовать и сохранять данные Chrome в аккаунте .
@@ -647,7 +647,7 @@
Чтобы защитить свои данные, разрешите Chrome отзывать разрешения у сайтов, которые вы давно не посещали.
Chrome сообщит вам, если возникнет проблема с безопасностью ваших паролей.
Ваш системный администратор задал настройки, согласно которым Google Chrome должен запускать для доступа к сайту .
-Совет по работе с Chrome: используйте улучшенный Безопасный просмотр
+Используйте улучшенный Безопасный просмотр
Справка
Логотип Chrome Enterprise
Chrome заблокировал скачивание, поскольку Безопасный просмотр отключен и файл невозможно проверить
diff -Nru chromium-149.0.7827.114/chrome/app/resources/google_chrome_strings_vi.xtb chromium-149.0.7827.155/chrome/app/resources/google_chrome_strings_vi.xtb
--- chromium-149.0.7827.114/chrome/app/resources/google_chrome_strings_vi.xtb 2026-06-10 17:58:03.000000000 +0000
+++ chromium-149.0.7827.155/chrome/app/resources/google_chrome_strings_vi.xtb 2026-06-16 01:31:02.000000000 +0000
@@ -503,7 +503,7 @@
- Google Chrome phiên bản beta
Thẻ này đang sử dụng thêm tài nguyên. Để cải thiện hiệu suất, hãy cho phép Chrome dừng hoạt động của thẻ này.
Không cài đặt được do lỗi nội bộ của máy chủ cập nhật.
-Mẹo khi dùng Chrome: Lưu mật khẩu
+Mẹo dùng Chrome: Lưu mật khẩu
Tài khoản của bạn trong Chrome
đã thêm một tiện ích có thể thay đổi cách Chrome hoạt động.
@@ -652,7 +652,7 @@
Để bảo vệ dữ liệu của bạn, hãy cho phép Chrome loại bỏ quyền của các trang web mà gần đây bạn không truy cập.
Chrome cho bạn biết mật khẩu của bạn có bị đánh cắp hay không
Quản trị viên hệ thống đã định cấu hình Google Chrome để mở khi truy cập vào .
-Mẹo khi dùng Chrome: Duyệt web an toàn có tăng cường bảo vệ
+Mẹo dùng Chrome: Duyệt web an toàn có tăng cường bảo vệ
Trợ giúp
Biểu trưng Chrome Enterprise
Chrome đã chặn tệp tải xuống này do không thể xác minh tệp (vì bạn đã tắt tính năng Duyệt web an toàn)
diff -Nru chromium-149.0.7827.114/chrome/browser/about_flags.cc chromium-149.0.7827.155/chrome/browser/about_flags.cc
--- chromium-149.0.7827.114/chrome/browser/about_flags.cc 2026-06-10 17:58:03.000000000 +0000
+++ chromium-149.0.7827.155/chrome/browser/about_flags.cc 2026-06-16 01:31:02.000000000 +0000
@@ -3838,6 +3838,16 @@
{"Pre-prod", kGlicGuestUrlPresetTypePreprod, nullptr},
{"Prod", kGlicGuestUrlPresetTypeProd, nullptr}};
+const FeatureEntry::FeatureParam kGlicOSIconVariant_0[] = {{"variant", "0"}};
+const FeatureEntry::FeatureParam kGlicOSIconVariant_1[] = {{"variant", "1"}};
+const FeatureEntry::FeatureParam kGlicOSIconVariant_2[] = {{"variant", "2"}};
+
+const FeatureEntry::FeatureVariation kGlicOSIconVariantVariations[] = {
+ {"Variant 0 (Tab)", kGlicOSIconVariant_0, nullptr},
+ {"Variant 1 (Ball)", kGlicOSIconVariant_1, nullptr},
+ {"Variant 2 (Square)", kGlicOSIconVariant_2, nullptr},
+};
+
const FeatureEntry::Choice kGlicSelectionPromptChoices[] = {
{flags_ui::kGenericExperimentChoiceDefault, "", ""},
{"Enabled with Updates Only", switches::kEnableFeatures,
@@ -5670,6 +5680,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)},
@@ -10833,6 +10848,11 @@
{"glic-mi-tab-context-menu", flag_descriptions::kGlicMITabContextMenuName,
flag_descriptions::kGlicMITabContextMenuDescription, kOsDesktop,
FEATURE_VALUE_TYPE(features::kGlicMITabContextMenu)},
+ {"glic-os-icon-variant", flag_descriptions::kGlicOSIconVariantName,
+ flag_descriptions::kGlicOSIconVariantDescription, kOsMac,
+ FEATURE_WITH_PARAMS_VALUE_TYPE(features::kGlicOSIconVariant,
+ kGlicOSIconVariantVariations,
+ "GlicOSIconVariant")},
{"glic-share-image", flag_descriptions::kGlicShareImageName,
flag_descriptions::kGlicShareImageDescription, kOsDesktop,
FEATURE_VALUE_TYPE(features::kGlicShareImage)},
diff -Nru chromium-149.0.7827.114/chrome/browser/android/omnibox/composebox_query_controller_bridge.cc chromium-149.0.7827.155/chrome/browser/android/omnibox/composebox_query_controller_bridge.cc
--- chromium-149.0.7827.114/chrome/browser/android/omnibox/composebox_query_controller_bridge.cc 2026-06-10 17:58:03.000000000 +0000
+++ chromium-149.0.7827.155/chrome/browser/android/omnibox/composebox_query_controller_bridge.cc 2026-06-16 01:31:02.000000000 +0000
@@ -732,7 +732,7 @@
query, self->session_handle_.get(),
self->contextual_tasks_web_ui_interface_, active_tool, active_model,
/*active_tab_context_id=*/std::nullopt,
- /*overlay_token=*/std::nullopt);
+ /*overlay_token=*/std::nullopt, /*is_voice_search=*/false);
contextual_tasks::FinalizeAndSendAimQuery(
std::move(request_info), self->session_handle_.get(),
diff -Nru chromium-149.0.7827.114/chrome/browser/background/glic/os_icon_provider_mac.mm chromium-149.0.7827.155/chrome/browser/background/glic/os_icon_provider_mac.mm
--- chromium-149.0.7827.114/chrome/browser/background/glic/os_icon_provider_mac.mm 2026-06-10 17:58:03.000000000 +0000
+++ chromium-149.0.7827.155/chrome/browser/background/glic/os_icon_provider_mac.mm 2026-06-16 01:31:02.000000000 +0000
@@ -161,6 +161,28 @@
OSIconProviderMac::~OSIconProviderMac() = default;
gfx::ImageSkia OSIconProviderMac::GetIcon() const {
+ if (base::FeatureList::IsEnabled(features::kGlicOSIconVariant)) {
+ int variant = features::kGlicOSIconVariantParam.Get();
+ int resource_id = IDR_GLIC_OS_ICON_VARIANT_0;
+ switch (variant) {
+ case 0:
+ resource_id = IDR_GLIC_OS_ICON_VARIANT_0;
+ break;
+ case 1:
+ resource_id = IDR_GLIC_OS_ICON_VARIANT_1;
+ break;
+ case 2:
+ resource_id = IDR_GLIC_OS_ICON_VARIANT_2;
+ break;
+ default:
+ // Fallback to variant 0 for unexpected values.
+ resource_id = IDR_GLIC_OS_ICON_VARIANT_0;
+ break;
+ }
+ return gfx::CreateVectorIcon(
+ glic::GlicVectorIconManager::GetVectorIcon(resource_id),
+ features::kGlicChromeStatusIconSizePx.Get(), SK_ColorWHITE);
+ }
if (GetUseAltIcon() && !features::kGlicChromeStatusIconLogOnly.Get()) {
// Despite the similar name, this feature param now decides which alt icon
// to use, not whether to use an alt icon at all.
diff -Nru chromium-149.0.7827.114/chrome/browser/chrome_content_browser_client.cc chromium-149.0.7827.155/chrome/browser/chrome_content_browser_client.cc
--- chromium-149.0.7827.114/chrome/browser/chrome_content_browser_client.cc 2026-06-10 17:58:03.000000000 +0000
+++ chromium-149.0.7827.155/chrome/browser/chrome_content_browser_client.cc 2026-06-16 01:31:02.000000000 +0000
@@ -76,6 +76,7 @@
#include "chrome/browser/contextual_tasks/contextual_tasks_ui_service.h"
#include "chrome/browser/contextual_tasks/contextual_tasks_ui_service_factory.h"
#include "chrome/browser/contextual_tasks/contextual_tasks_url_loader_factory_interceptor.h"
+#include "chrome/browser/contextual_tasks/guest_opener_user_data.h"
#include "chrome/browser/custom_handlers/protocol_handler_registry_factory.h"
#include "chrome/browser/data_saver/data_saver.h"
#include "chrome/browser/defaults.h"
@@ -337,6 +338,7 @@
#include "content/public/browser/client_certificate_delegate.h"
#include "content/public/browser/digital_identity_provider.h"
#include "content/public/browser/file_url_loader.h"
+#include "content/public/browser/global_routing_id.h"
#include "content/public/browser/internal_webui_config.h"
#include "content/public/browser/isolated_web_apps_policy.h"
#include "content/public/browser/legacy_tech_cookie_issue_details.h"
@@ -4506,6 +4508,7 @@
content::OpenURLParams url_params(
target_url, referrer, disposition,
ui::PageTransition::PAGE_TRANSITION_AUTO_TOPLEVEL, true);
+ url_params.user_gesture = user_gesture;
content::WebContents* responsible_web_contents =
web_contents->GetResponsibleWebContents();
bool is_from_embedded_page =
@@ -4517,8 +4520,11 @@
contextual_tasks_ui_service->HandleNavigation(
std::move(url_params), responsible_web_contents,
is_from_embedded_page,
- /*is_to_new_tab=*/true,
- /*is_same_site_or_from_ui=*/is_same_site_or_from_ui)) {
+ /*from_can_create_window=*/true, is_same_site_or_from_ui,
+ /*is_mobile_ua=*/false,
+ /*initiator_origin=*/opener->GetLastCommittedOrigin(),
+ /*initiator_frame_token=*/opener->GetGlobalFrameToken(),
+ features)) {
return false;
}
}
@@ -4871,6 +4877,20 @@
}
#endif
+ contextual_tasks::ContextualTasksUiService* ui_service =
+ contextual_tasks::ContextualTasksUiServiceFactory::GetForBrowserContext(
+ profile);
+ if (ui_service && ui_service->IsTrackedWindow(web_contents)) {
+ // This preference must be set here because OverrideWebPreferences is
+ // the central place in Chrome to modify WebPreferences for renderers.
+ // There is no component-specific hook in
+ // chrome/browser/contextual_tasks that allows overriding these
+ // preferences directly. We need to allow scripts to close windows for
+ // tracked guest windows so that the page that was opened via
+ // window.open can close itself if needed (e.g., via window.close()).
+ web_prefs->allow_scripts_to_close_windows = true;
+ }
+
web_prefs->is_initial_profile =
profile->GetOriginalProfile()->GetBaseName() ==
ProfileManager::GetInitialProfileDir();
@@ -8642,6 +8662,43 @@
#endif // BUILDFLAG(ENABLE_EXTENSIONS_CORE)
}
+content::RenderFrameHost*
+ChromeContentBrowserClient::GetPostMessageTargetOverride(
+ content::RenderFrameHost* target_rfh,
+ const std::optional& source_frame_token,
+ const url::Origin& source_origin,
+ const std::optional& target_origin) {
+ content::WebContents* web_contents =
+ content::WebContents::FromRenderFrameHost(target_rfh);
+ if (!web_contents) {
+ return nullptr;
+ }
+
+ // Don't proceed to looking up the ContextualTasksUiService if the WebContents
+ // is not marked as a guest opener. In other words, this post message is
+ // unrelated to Contextual Tasks.
+ if (!contextual_tasks::GuestOpenerUserData::IsGuestOpener(web_contents)) {
+ return nullptr;
+ }
+
+ Profile* profile =
+ Profile::FromBrowserContext(web_contents->GetBrowserContext());
+ if (!profile) {
+ return nullptr;
+ }
+
+ // The ContextualTasks feature manually tracks window opens to be able to
+ // route messages back to the appropriate RenderFrameHost. If that feature
+ // returns a frame, use that instead.
+ contextual_tasks::ContextualTasksUiService* service = contextual_tasks::
+ ContextualTasksUiServiceFactory::GetForBrowserContextIfExists(profile);
+ if (service) {
+ return service->GetGuestForMessage(target_rfh, source_origin);
+ }
+
+ return nullptr;
+}
+
std::unique_ptr
ChromeContentBrowserClient::CreateResponsivenessCalculatorDelegate() {
#if !BUILDFLAG(IS_ANDROID)
diff -Nru chromium-149.0.7827.114/chrome/browser/chrome_content_browser_client.h chromium-149.0.7827.155/chrome/browser/chrome_content_browser_client.h
--- chromium-149.0.7827.114/chrome/browser/chrome_content_browser_client.h 2026-06-10 17:58:03.000000000 +0000
+++ chromium-149.0.7827.155/chrome/browser/chrome_content_browser_client.h 2026-06-16 01:31:02.000000000 +0000
@@ -50,6 +50,7 @@
#include "services/network/public/cpp/permissions_policy/permissions_policy_declaration.h"
#include "services/network/public/mojom/network_context.mojom.h"
#include "services/network/public/mojom/url_loader_factory.mojom.h"
+#include "third_party/blink/public/common/tokens/tokens.h"
#include "third_party/blink/public/mojom/on_device_translation/translation_manager.mojom-forward.h"
#include "third_party/blink/public/mojom/worker/shared_worker_info.mojom.h"
#include "ui/base/clipboard/clipboard_metadata.h"
@@ -1065,6 +1066,11 @@
content::WebContents* web_contents) override;
bool ShouldUseFirstPartyStorageKey(const url::Origin& origin) override;
+ content::RenderFrameHost* GetPostMessageTargetOverride(
+ content::RenderFrameHost* target_rfh,
+ const std::optional& source_frame_token,
+ const url::Origin& source_origin,
+ const std::optional& target_origin) override;
bool ShouldSkipBeforeUnloadDialog(content::RenderFrameHost* rfh) override;
diff -Nru chromium-149.0.7827.114/chrome/browser/contextual_tasks/BUILD.gn chromium-149.0.7827.155/chrome/browser/contextual_tasks/BUILD.gn
--- chromium-149.0.7827.114/chrome/browser/contextual_tasks/BUILD.gn 2026-06-10 17:58:03.000000000 +0000
+++ chromium-149.0.7827.155/chrome/browser/contextual_tasks/BUILD.gn 2026-06-16 01:31:02.000000000 +0000
@@ -168,6 +168,8 @@
"contextual_tasks_ui.h",
"contextual_tasks_ui_service.h",
"contextual_tasks_ui_service_factory.h",
+ "contextual_tasks_window_tracker.h",
+ "contextual_tasks_window_tracker_manager.h",
"entry_point_eligibility_manager.h",
]
@@ -193,6 +195,7 @@
public_deps = [
":contextual_tasks",
":mojo_bindings",
+ ":mojom_traits",
"//base",
"//chrome/browser/tab_list",
"//chrome/common:url_constants",
@@ -278,7 +281,11 @@
"contextual_tasks_ui_service_factory.cc",
"contextual_tasks_url_loader_factory_interceptor.cc",
"contextual_tasks_url_loader_factory_interceptor.h",
+ "contextual_tasks_window_tracker.cc",
+ "contextual_tasks_window_tracker_manager.cc",
"entry_point_eligibility_manager.cc",
+ "guest_opener_user_data.cc",
+ "guest_opener_user_data.h",
]
if (is_android) {
@@ -390,6 +397,19 @@
}
}
+source_set("mojom_traits") {
+ sources = [
+ "contextual_tasks_mojom_traits.h",
+ "contextual_tasks_types.h",
+ ]
+ public_deps = [
+ ":mojo_bindings_shared_cpp_sources",
+ "//base",
+ "//mojo/public/cpp/base:shared_typemap_traits",
+ "//mojo/public/cpp/bindings",
+ ]
+}
+
mojom("mojo_bindings") {
sources = [
"contextual_tasks.mojom",
@@ -406,6 +426,23 @@
public_deps +=
[ "//ui/webui/resources/cr_components/composebox:mojo_bindings" ]
}
+
+ cpp_typemaps = [
+ {
+ types = [
+ {
+ mojom = "contextual_tasks.mojom.ContextualTaskId"
+ cpp = "::contextual_tasks::ContextualTaskId"
+ },
+ {
+ mojom = "contextual_tasks.mojom.ContextualWindowId"
+ cpp = "::contextual_tasks::ContextualWindowId"
+ },
+ ]
+ traits_headers = [ "contextual_tasks_mojom_traits.h" ]
+ traits_public_deps = [ ":mojom_traits" ]
+ },
+ ]
}
if (!is_android) {
@@ -484,6 +521,8 @@
"contextual_tasks_tab_visit_tracker_unittest.cc",
"contextual_tasks_ui_service_unittest.cc",
"contextual_tasks_ui_unittest.cc",
+ "contextual_tasks_window_tracker_manager_unittest.cc",
+ "contextual_tasks_window_tracker_unittest.cc",
"entry_point_eligibility_manager_unittest.cc",
]
diff -Nru chromium-149.0.7827.114/chrome/browser/contextual_tasks/OWNERS chromium-149.0.7827.155/chrome/browser/contextual_tasks/OWNERS
--- chromium-149.0.7827.114/chrome/browser/contextual_tasks/OWNERS 2026-06-10 17:58:03.000000000 +0000
+++ chromium-149.0.7827.155/chrome/browser/contextual_tasks/OWNERS 2026-06-16 01:31:02.000000000 +0000
@@ -15,3 +15,6 @@
per-file *.mojom=set noparent
per-file *.mojom=file://ipc/SECURITY_OWNERS
+
+per-file *_mojom_traits*.*=set noparent
+per-file *_mojom_traits*.*=file://ipc/SECURITY_OWNERS
diff -Nru chromium-149.0.7827.114/chrome/browser/contextual_tasks/contextual_tasks.mojom chromium-149.0.7827.155/chrome/browser/contextual_tasks/contextual_tasks.mojom
--- chromium-149.0.7827.114/chrome/browser/contextual_tasks/contextual_tasks.mojom 2026-06-10 17:58:03.000000000 +0000
+++ chromium-149.0.7827.155/chrome/browser/contextual_tasks/contextual_tasks.mojom 2026-06-16 01:31:02.000000000 +0000
@@ -21,6 +21,15 @@
bool submit_after_injection;
};
+struct ContextualTaskId {
+ mojo_base.mojom.Uuid value;
+};
+
+struct ContextualWindowId {
+ mojo_base.mojom.UnguessableToken value;
+};
+
+
// Represents the the visual information needed to display a single context row
// of a tab where title and url will be displayed in the sources menu.
struct TabContext {
@@ -174,6 +183,14 @@
// Unpins the side panel from the toolbar.
UnpinSidePanel();
+
+ // Registers a tracked window with its ID, associated task ID, and URL.
+ RegisterWindow(ContextualTaskId task_id,
+ url.mojom.Url url,
+ ContextualWindowId window_id);
+
+ // Requests the browser to close a tracked window.
+ CloseWindow(ContextualWindowId window_id);
};
// WebUI-side handler for requests from the browser.
@@ -294,6 +311,10 @@
// Called by the browser process (C++) to notify the UI whether the expand
// button should be enabled.
SetExpandButtonEnabled(bool enabled);
+
+ // Called by the browser process (C++) to notify the UI that a tracked window
+ // was closed.
+ OnWindowClosed(ContextualWindowId window_id);
};
// Used to bootstrip bi-directional communication between the browser and webui.
diff -Nru chromium-149.0.7827.114/chrome/browser/contextual_tasks/contextual_tasks_composebox_handler.cc chromium-149.0.7827.155/chrome/browser/contextual_tasks/contextual_tasks_composebox_handler.cc
--- chromium-149.0.7827.114/chrome/browser/contextual_tasks/contextual_tasks_composebox_handler.cc 2026-06-10 17:58:03.000000000 +0000
+++ chromium-149.0.7827.155/chrome/browser/contextual_tasks/contextual_tasks_composebox_handler.cc 2026-06-16 01:31:02.000000000 +0000
@@ -135,7 +135,8 @@
const AutocompleteMatch& alternative_nav_match) {
std::string query_text;
net::GetValueForKeyInQuery(destination_url, "q", &query_text);
- composebox_handler_->CreateAndSendQueryMessage(query_text);
+ composebox_handler_->CreateAndSendQueryMessage(query_text,
+ /*is_voice_search=*/false);
}
lens::LensOverlayDismissalSource ToLensOverlayDismissalSource(
@@ -298,14 +299,16 @@
bool alt_key,
bool ctrl_key,
bool meta_key,
- bool shift_key) {
- CreateAndSendQueryMessage(query_text);
+ bool shift_key,
+ bool is_voice_search) {
+ CreateAndSendQueryMessage(query_text, is_voice_search);
// TODO(crbug.com/469535685): This should reflect the response from the
// webview when PostMessageToWebview provides one.
}
void ContextualTasksComposeboxHandler::CreateAndSendQueryMessage(
- const std::string& query) {
+ const std::string& query,
+ bool is_voice_search) {
// Retrieve the overlay token before closing the overlay, as the controller
// might be destroyed or reset during closure.
std::optional overlay_token = GetLensOverlayToken();
@@ -326,7 +329,8 @@
session_handle->GetUploadedContextTokens().empty();
if (!task_id.has_value() || !contextual_tasks_service ||
is_only_visual_selection) {
- ContinueCreateAndSendQueryMessage(query, task_id, overlay_token);
+ ContinueCreateAndSendQueryMessage(query, task_id, overlay_token,
+ is_voice_search);
return;
}
@@ -369,6 +373,19 @@
// It is safe to use base::Unretained(this) here because `recontextualizer_`
// is owned by `this` and will be destroyed when `this` is destroyed,
// cancelling any pending callbacks.
+ auto callback = base::BindOnce(
+ [](ContextualTasksComposeboxHandler* handler, std::string query,
+ std::optional task_id,
+ std::optional token, bool voice,
+ base::WeakPtr
+ handle) {
+ // The session handle is accessed via GetContextualSessionHandle(),
+ // so we ignore it here.
+ handler->ContinueCreateAndSendQueryMessage(query, task_id, token,
+ voice);
+ },
+ base::Unretained(this), query, task_id, overlay_token, is_voice_search);
+
recontextualizer_->Contextualize(
task_id, query, tabs_to_recontextualize, tabs_to_force_contextualize,
base::BindRepeating(
@@ -377,18 +394,7 @@
base::BindRepeating(&ContextualTasksComposeboxHandler::
OnTabProcessedForQueryContextualization,
base::Unretained(this)),
- base::BindOnce(
- [](ContextualTasksComposeboxHandler* handler, std::string query,
- std::optional task_id,
- std::optional token,
- base::WeakPtr
- handle) {
- // The session handle is accessed via GetContextualSessionHandle(),
- // so we ignore it here.
- handler->ContinueCreateAndSendQueryMessage(query, task_id, token);
- },
- base::Unretained(this), query, task_id, overlay_token),
- IsSmartTabSharingActive());
+ std::move(callback), IsSmartTabSharingActive());
}
contextual_tasks::ContextualTasksService*
@@ -485,7 +491,8 @@
void ContextualTasksComposeboxHandler::ContinueCreateAndSendQueryMessage(
std::string query,
std::optional original_task_id,
- std::optional overlay_token) {
+ std::optional overlay_token,
+ bool is_voice_search) {
if (recontextualization_pending_count_ > 0) {
recontextualization_pending_count_--;
}
@@ -504,7 +511,7 @@
contextual_tasks::PrepareClientToAimRequestInfo(
query, session_handle, web_ui_interface_,
GetInputState().active_tool, GetInputState().active_model,
- GetActiveTabContextId(), overlay_token);
+ GetActiveTabContextId(), overlay_token, is_voice_search);
// Delay submission if context still uploading.
if (IsAnyContextUploading()) {
diff -Nru chromium-149.0.7827.114/chrome/browser/contextual_tasks/contextual_tasks_composebox_handler.h chromium-149.0.7827.155/chrome/browser/contextual_tasks/contextual_tasks_composebox_handler.h
--- chromium-149.0.7827.114/chrome/browser/contextual_tasks/contextual_tasks_composebox_handler.h 2026-06-10 17:58:03.000000000 +0000
+++ chromium-149.0.7827.155/chrome/browser/contextual_tasks/contextual_tasks_composebox_handler.h 2026-06-16 01:31:02.000000000 +0000
@@ -71,7 +71,8 @@
bool alt_key,
bool ctrl_key,
bool meta_key,
- bool shift_key) override;
+ bool shift_key,
+ bool is_voice_search) override;
void DeleteContext(const base::UnguessableToken& file_token,
bool from_automatic_chip) override;
void HandleFileUpload(bool is_image) override;
@@ -99,7 +100,8 @@
const std::optional&
error_type) override;
- void CreateAndSendQueryMessage(const std::string& query);
+ void CreateAndSendQueryMessage(const std::string& query,
+ bool is_voice_search);
void ResetInputStateModel() override;
void UpdateStateFromUrl(const GURL& url) override;
@@ -163,7 +165,8 @@
void ContinueCreateAndSendQueryMessage(
std::string query,
std::optional original_task_id,
- std::optional overlay_token);
+ std::optional overlay_token,
+ bool is_voice_search);
void OnVisualSelectionAdded(
base::UnguessableToken overlay_token,
diff -Nru chromium-149.0.7827.114/chrome/browser/contextual_tasks/contextual_tasks_composebox_handler_unittest.cc chromium-149.0.7827.155/chrome/browser/contextual_tasks/contextual_tasks_composebox_handler_unittest.cc
--- chromium-149.0.7827.114/chrome/browser/contextual_tasks/contextual_tasks_composebox_handler_unittest.cc 2026-06-10 17:58:03.000000000 +0000
+++ chromium-149.0.7827.155/chrome/browser/contextual_tasks/contextual_tasks_composebox_handler_unittest.cc 2026-06-16 01:31:02.000000000 +0000
@@ -499,7 +499,7 @@
CloseLensSync(
lens::LensOverlayDismissalSource::kContextualTasksQuerySubmitted));
- handler_->SubmitQuery("test query", 0, false, false, false, false);
+ handler_->SubmitQuery("test query", 0, false, false, false, false, false);
EXPECT_EQ(session_handle_->previous_query(), "test query");
}
@@ -526,7 +526,7 @@
});
EXPECT_CALL(*mock_ui_, PostMessageToWebview(testing::_));
- handler_->CreateAndSendQueryMessage(kQuery);
+ handler_->CreateAndSendQueryMessage(kQuery, false);
}
TEST_F(ContextualTasksComposeboxHandlerTest,
@@ -551,7 +551,7 @@
});
EXPECT_CALL(*mock_ui_, PostMessageToWebview(testing::_));
- handler_->CreateAndSendQueryMessage(kQuery);
+ handler_->CreateAndSendQueryMessage(kQuery, false);
}
TEST_F(ContextualTasksComposeboxHandlerTest,
@@ -573,7 +573,7 @@
});
EXPECT_CALL(*mock_ui_, PostMessageToWebview(testing::_));
- handler_->CreateAndSendQueryMessage(kQuery);
+ handler_->CreateAndSendQueryMessage(kQuery, false);
}
TEST_F(ContextualTasksComposeboxHandlerTest,
@@ -666,7 +666,7 @@
EXPECT_CALL(*mock_ui_, PostMessageToWebview(testing::_))
.WillOnce(base::test::RunClosure(run_loop.QuitClosure()));
- handler_->CreateAndSendQueryMessage(kQuery);
+ handler_->CreateAndSendQueryMessage(kQuery, false);
run_loop.Run();
}
@@ -766,7 +766,7 @@
EXPECT_CALL(*mock_ui_, PostMessageToWebview(testing::_))
.WillOnce(base::test::RunClosure(run_loop.QuitClosure()));
- handler_->CreateAndSendQueryMessage(kQuery);
+ handler_->CreateAndSendQueryMessage(kQuery, false);
run_loop.Run();
}
@@ -866,7 +866,7 @@
EXPECT_CALL(*mock_ui_, PostMessageToWebview(testing::_))
.WillOnce(base::test::RunClosure(run_loop.QuitClosure()));
- handler_->CreateAndSendQueryMessage(kQuery);
+ handler_->CreateAndSendQueryMessage(kQuery, false);
run_loop.Run();
}
@@ -920,7 +920,7 @@
EXPECT_CALL(*mock_ui_, PostMessageToWebview(testing::_))
.WillOnce(base::test::RunClosure(run_loop.QuitClosure()));
- handler_->CreateAndSendQueryMessage(kQuery);
+ handler_->CreateAndSendQueryMessage(kQuery, false);
run_loop.Run();
}
@@ -979,7 +979,7 @@
EXPECT_CALL(*mock_ui_, PostMessageToWebview(testing::_))
.WillOnce(base::test::RunClosure(run_loop.QuitClosure()));
- handler_->CreateAndSendQueryMessage(kQuery);
+ handler_->CreateAndSendQueryMessage(kQuery, false);
run_loop.Run();
}
@@ -1123,7 +1123,7 @@
EXPECT_CALL(*mock_ui_, PostMessageToWebview(testing::_))
.WillOnce(base::test::RunClosure(run_loop.QuitClosure()));
- handler_->CreateAndSendQueryMessage(kQuery);
+ handler_->CreateAndSendQueryMessage(kQuery, false);
run_loop.Run();
}
@@ -1217,7 +1217,7 @@
EXPECT_CALL(*mock_ui_, PostMessageToWebview(testing::_))
.WillOnce(base::test::RunClosure(run_loop.QuitClosure()));
- handler_->CreateAndSendQueryMessage(kQuery);
+ handler_->CreateAndSendQueryMessage(kQuery, false);
run_loop.Run();
}
@@ -1303,7 +1303,7 @@
});
EXPECT_CALL(*mock_ui_, PostMessageToWebview(testing::_));
- handler_->CreateAndSendQueryMessage("test query");
+ handler_->CreateAndSendQueryMessage("test query", false);
}
INSTANTIATE_TEST_SUITE_P(
@@ -1420,7 +1420,7 @@
EXPECT_CALL(*mock_ui_, PostMessageToWebview(testing::_))
.WillOnce(base::test::RunClosure(run_loop.QuitClosure()));
- handler_->CreateAndSendQueryMessage(kQuery);
+ handler_->CreateAndSendQueryMessage(kQuery, false);
run_loop.Run();
ASSERT_FALSE(handler_->IsAnyContextUploading());
@@ -1502,7 +1502,7 @@
base::RunLoop run_loop;
EXPECT_CALL(*mock_ui_, PostMessageToWebview(testing::_))
.WillOnce(base::test::RunClosure(run_loop.QuitClosure()));
- handler_->CreateAndSendQueryMessage(kQuery);
+ handler_->CreateAndSendQueryMessage(kQuery, false);
run_loop.Run();
}
@@ -1580,7 +1580,8 @@
// Do not submit request to server yet.
EXPECT_CALL(*mock_ui_, PostMessageToWebview(testing::_)).Times(0);
- handler_->SubmitQuery("Summarize the tab", 0, false, false, false, false);
+ handler_->SubmitQuery("Summarize the tab", 0, false, false, false, false,
+ false);
ASSERT_TRUE(handler_->IsAnyContextUploading());
ASSERT_TRUE(handler_->HasPendingQueryForTesting());
@@ -1728,7 +1729,7 @@
ASSERT_FALSE(handler_->HasPendingQueryForTesting());
EXPECT_CALL(*mock_ui_, PostMessageToWebview(testing::_)).Times(0);
- handler_->SubmitQuery("What is this?", 0, false, false, false, false);
+ handler_->SubmitQuery("What is this?", 0, false, false, false, false, false);
ASSERT_TRUE(handler_->IsAnyContextUploading());
ASSERT_TRUE(handler_->HasPendingQueryForTesting());
@@ -1819,7 +1820,8 @@
EXPECT_CALL(*mock_ui_, PostMessageToWebview(testing::_)).Times(0);
// Should stash message instead of submit.
- handler_->SubmitQuery("Summarize the tab", 0, false, false, false, false);
+ handler_->SubmitQuery("Summarize the tab", 0, false, false, false, false,
+ false);
ASSERT_TRUE(handler_->IsAnyContextUploading());
ASSERT_TRUE(handler_->HasPendingQueryForTesting());
@@ -1912,7 +1914,8 @@
// Do not submit request to server yet.
EXPECT_CALL(*mock_ui_, PostMessageToWebview(testing::_)).Times(0);
- handler_->SubmitQuery("Summarize the tab", 0, false, false, false, false);
+ handler_->SubmitQuery("Summarize the tab", 0, false, false, false, false,
+ false);
ASSERT_TRUE(handler_->IsAnyContextUploading());
ASSERT_TRUE(handler_->HasPendingQueryForTesting());
@@ -2032,7 +2035,7 @@
ASSERT_TRUE(handler_->IsAnyContextUploading());
// No pending query yet since have not submitted yet.
ASSERT_FALSE(handler_->HasPendingQueryForTesting());
- handler_->SubmitQuery("What is this?", 0, false, false, false, false);
+ handler_->SubmitQuery("What is this?", 0, false, false, false, false, false);
base::RunLoop().RunUntilIdle();
// Now the delayed tabs should have uploaded.
@@ -2104,7 +2107,7 @@
EXPECT_CALL(*mock_controller_, GetFileInfo(*current_token))
.WillRepeatedly(testing::Return(&uploading_info));
- handler_->SubmitQuery("What is this?", 0, false, false, false, false);
+ handler_->SubmitQuery("What is this?", 0, false, false, false, false, false);
ASSERT_TRUE(handler_->IsAnyContextUploading());
EXPECT_CALL(*mock_ui_, PostMessageToWebview(testing::_)).Times(0);
@@ -2259,7 +2262,7 @@
contextual_search::ContextUploadStatus::kUploadSuccessful);
});
- handler_->SubmitQuery("Combined Test", 0, false, false, false, false);
+ handler_->SubmitQuery("Combined Test", 0, false, false, false, false, false);
base::RunLoop().RunUntilIdle();
// Delayed tabs should be uploaded once submit is run.
@@ -2438,7 +2441,7 @@
.WillRepeatedly(testing::Return(&file_info_rB));
EXPECT_CALL(*mock_ui_, PostMessageToWebview(testing::_)).Times(0);
- handler_->SubmitQuery("Stress Test", 0, false, false, false, false);
+ handler_->SubmitQuery("Stress Test", 0, false, false, false, false, false);
base::RunLoop().RunUntilIdle();
// Delayed tab #2 finishes uploading.
@@ -2515,7 +2518,7 @@
});
EXPECT_CALL(*mock_ui_, PostMessageToWebview(testing::_));
- handler_->CreateAndSendQueryMessage(kQuery);
+ handler_->CreateAndSendQueryMessage(kQuery, false);
}
TEST_F(ContextualTasksComposeboxHandlerTest,
@@ -2581,7 +2584,7 @@
EXPECT_CALL(*mock_controller_, CreateClientToAimRequest(testing::_)).Times(0);
EXPECT_CALL(*mock_ui_, PostMessageToWebview(testing::_)).Times(0);
- handler_->CreateAndSendQueryMessage(kQuery);
+ handler_->CreateAndSendQueryMessage(kQuery, false);
EXPECT_TRUE(handler_->HasPendingQueryForTesting());
@@ -2661,7 +2664,7 @@
});
EXPECT_CALL(*mock_ui_, PostMessageToWebview(testing::_));
- handler_->CreateAndSendQueryMessage(kQuery);
+ handler_->CreateAndSendQueryMessage(kQuery, false);
}
TEST_F(ContextualTasksComposeboxHandlerTest,
@@ -2716,7 +2719,7 @@
});
EXPECT_CALL(*mock_ui_, PostMessageToWebview(testing::_));
- handler_->CreateAndSendQueryMessage(kQuery);
+ handler_->CreateAndSendQueryMessage(kQuery, false);
}
TEST_F(ContextualTasksComposeboxHandlerTest, ClearFiles_Delayed) {
@@ -2784,7 +2787,7 @@
EXPECT_CALL(*mock_ui_, PostMessageToWebview(testing::_))
.WillOnce(base::test::RunClosure(run_loop.QuitClosure()));
- handler_->CreateAndSendQueryMessage(kQuery);
+ handler_->CreateAndSendQueryMessage(kQuery, false);
run_loop.Run();
}
@@ -3253,7 +3256,7 @@
callback) { pending_callback = std::move(callback); });
// 2. Call CreateAndSendQueryMessage.
- handler_->CreateAndSendQueryMessage(kQuery);
+ handler_->CreateAndSendQueryMessage(kQuery, false);
// Verify: recontextualization is pending, so the query is blocked.
ASSERT_TRUE(handler_->IsAnyContextUploading());
@@ -3374,7 +3377,7 @@
EXPECT_CALL(*mock_ui_, PostMessageToWebview(testing::_))
.WillOnce(base::test::RunClosure(run_loop.QuitClosure()));
- handler_->CreateAndSendQueryMessage(kQuery);
+ handler_->CreateAndSendQueryMessage(kQuery, false);
run_loop.Run();
// Verify: No context was uploaded, pending uploads are back to 0.
@@ -3434,7 +3437,7 @@
ASSERT_EQ(handler_->GetNumContextUploading(), 1);
// Submit query manually. It should be stashed.
- handler_->SubmitQuery("Test query", 0, false, false, false, false);
+ handler_->SubmitQuery("Test query", 0, false, false, false, false, false);
ASSERT_TRUE(handler_->HasPendingQueryForTesting());
// Now expect the stashed query to be sent when the chip completes
diff -Nru chromium-149.0.7827.114/chrome/browser/contextual_tasks/contextual_tasks_interactive_uitest.cc chromium-149.0.7827.155/chrome/browser/contextual_tasks/contextual_tasks_interactive_uitest.cc
--- chromium-149.0.7827.114/chrome/browser/contextual_tasks/contextual_tasks_interactive_uitest.cc 2026-06-10 17:58:03.000000000 +0000
+++ chromium-149.0.7827.155/chrome/browser/contextual_tasks/contextual_tasks_interactive_uitest.cc 2026-06-16 01:31:02.000000000 +0000
@@ -3,42 +3,62 @@
// found in the LICENSE file.
#include
+#include "base/base64.h"
#include "base/check_deref.h"
+#include "base/command_line.h"
+#include "base/files/file_util.h"
#include "base/path_service.h"
#include "base/strings/stringprintf.h"
#include "base/strings/utf_string_conversions.h"
#include "base/test/bind.h"
+#include "base/threading/thread_restrictions.h"
#include "chrome/browser/autocomplete/aim_eligibility_service_factory.h"
#include "chrome/browser/contextual_search/contextual_search_service_factory.h"
#include "chrome/browser/contextual_tasks/contextual_tasks_cookie_synchronizer.h"
#include "chrome/browser/contextual_tasks/contextual_tasks_eligibility_manager.h"
#include "chrome/browser/contextual_tasks/contextual_tasks_service_factory.h"
+#include "chrome/browser/contextual_tasks/contextual_tasks_ui_interface.h"
#include "chrome/browser/contextual_tasks/contextual_tasks_ui_service.h"
#include "chrome/browser/contextual_tasks/contextual_tasks_ui_service_factory.h"
+#include "chrome/browser/contextual_tasks/contextual_tasks_utils.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/signin/identity_manager_factory.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/contextual_search/tab_contextualization_controller.h"
#include "chrome/browser/ui/tabs/public/tab_features.h"
-#include "chrome/browser/ui/webui/searchbox/contextual_searchbox_test_utils.h"
#include "chrome/common/chrome_paths.h"
#include "chrome/common/webui_url_constants.h"
+#include "chrome/test/base/ui_test_utils.h"
#include "chrome/test/interaction/interactive_browser_test.h"
#include "components/contextual_search/contextual_search_types.h"
#include "components/contextual_search/pref_names.h"
#include "components/contextual_tasks/public/contextual_tasks_service.h"
#include "components/contextual_tasks/public/features.h"
#include "components/lens/contextual_input.h"
+#include "components/lens/lens_features.h"
#include "components/omnibox/browser/mock_aim_eligibility_service.h"
#include "components/prefs/pref_service.h"
#include "components/sessions/content/session_tab_helper.h"
#include "components/signin/public/identity_manager/identity_test_utils.h"
+#include "components/tabs/public/tab_interface.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/content_mock_cert_verifier.h"
#include "content/public/test/file_system_chooser_test_helpers.h"
+#include "content/public/test/test_navigation_observer.h"
+#include "content/public/test/url_loader_interceptor.h"
+#include "net/base/net_errors.h"
#include "net/dns/mock_host_resolver.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/lens_server_proto/aim_communication.pb.h"
+#include "third_party/lens_server_proto/aim_query.pb.h"
+#include "third_party/lens_server_proto/lens_overlay_cluster_info.pb.h"
+#include "third_party/lens_server_proto/lens_overlay_server.pb.h"
#include "third_party/skia/include/core/SkBitmap.h"
#include "third_party/skia/include/core/SkColor.h"
#include "ui/base/unowned_user_data/user_data_factory.h"
@@ -46,7 +66,13 @@
using testing::_;
namespace {
+constexpr char kMockAimPagePath[] = "chrome/test/data/mock_aim_page.html";
+constexpr char kMockAimPageHost[] = "www.google.com";
+
DEFINE_LOCAL_ELEMENT_IDENTIFIER_VALUE(kPrimaryTab);
+DEFINE_LOCAL_ELEMENT_IDENTIFIER_VALUE(kGenericTab);
+DEFINE_LOCAL_ELEMENT_IDENTIFIER_VALUE(kInnerWebContentsId);
+DEFINE_LOCAL_ELEMENT_IDENTIFIER_VALUE(kNewTabId);
DEFINE_LOCAL_CUSTOM_ELEMENT_EVENT_TYPE(kElementExistsEvent);
DEFINE_LOCAL_CUSTOM_ELEMENT_EVENT_TYPE(kElementDoesNotExistEvent);
@@ -113,6 +139,11 @@
bool IsSignedInToBrowserWithValidCredentials() override { return true; }
bool IsUrlForPrimaryAccount(const GURL& url) override { return true; }
+ void GetAccessToken(
+ GetAccessTokenCallback callback,
+ base::WeakPtr web_contents) override {
+ std::move(callback).Run("fake_access_token");
+ }
};
} // namespace
@@ -122,7 +153,9 @@
class ContextualTasksInteractiveUiTest : public InteractiveBrowserTest {
public:
ContextualTasksInteractiveUiTest() {
- scoped_feature_list_.InitAndEnableFeature(kContextualTasks);
+ scoped_feature_list_.InitWithFeatures(
+ /*enabled_features=*/{kContextualTasks},
+ /*disabled_features=*/{lens::features::kLensSendRawFileMediaTypes});
}
~ContextualTasksInteractiveUiTest() override = default;
@@ -143,9 +176,6 @@
BuildMockContextualTasksUiServiceInstance,
base::Unretained(this)));
- ContextualSearchServiceFactory::GetInstance()->SetTestingFactory(
- context,
- base::BindRepeating(&BuildMockContextualSearchServiceInstance));
}
std::unique_ptr BuildMockAimServiceInstance(
@@ -175,13 +205,51 @@
void SetUpOnMainThread() override {
InteractiveBrowserTest::SetUpOnMainThread();
- signin::MakePrimaryAccountAvailable(
- IdentityManagerFactory::GetForProfile(browser()->profile()),
- "test_user@gmail.com", signin::ConsentLevel::kSignin);
-
host_resolver()->AddRule("*", "127.0.0.1");
ASSERT_TRUE(embedded_test_server()->Start());
+ url_loader_interceptor_ = std::make_unique<
+ content::URLLoaderInterceptor>(base::BindLambdaForTesting(
+ [&](content::URLLoaderInterceptor::RequestParams* params) {
+ const GURL& url = params->url_request.url;
+ if (url.host() == "a.google.com") {
+ content::URLLoaderInterceptor::WriteResponse(
+ "HTTP/1.1 200 OK\nContent-Type: text/html\n\n",
+ "Title 1", params->client.get());
+ return true;
+ }
+ if (url.host() == kMockAimPageHost) {
+ content::URLLoaderInterceptor::WriteResponse(kMockAimPagePath,
+ params->client.get());
+ return true;
+ }
+ GURL cluster_info_url{
+ lens::features::GetLensOverlayClusterInfoEndpointUrl()};
+ GURL upload_url{lens::features::GetLensOverlayEndpointURL()};
+ if (url.host() == cluster_info_url.host()) {
+ if (url.path() == cluster_info_url.path()) {
+ lens::LensOverlayServerClusterInfoResponse response;
+ response.set_search_session_id("test_search_session_id");
+ std::string response_string;
+ CHECK(response.SerializeToString(&response_string));
+ content::URLLoaderInterceptor::WriteResponse(
+ "HTTP/1.1 200 OK\nContent-Type: application/x-protobuf\n\n",
+ response_string, params->client.get());
+ return true;
+ }
+ if (url.path() == upload_url.path()) {
+ lens::LensOverlayServerResponse response;
+ std::string response_string;
+ CHECK(response.SerializeToString(&response_string));
+ content::URLLoaderInterceptor::WriteResponse(
+ "HTTP/1.1 200 OK\nContent-Type: application/x-protobuf\n\n",
+ response_string, params->client.get());
+ return true;
+ }
+ }
+ return false;
+ }));
+
auto* mock_aim = GetMockAimEligibilityService(browser()->profile());
auto* config = &mock_aim->config();
// Configure AimEligibility to recognize Browser Tabs as valid inputs to
@@ -223,6 +291,7 @@
}
void TearDownOnMainThread() override {
+ url_loader_interceptor_.reset();
ui::SelectFileDialog::SetFactory(nullptr);
InteractiveBrowserTest::TearDownOnMainThread();
}
@@ -367,11 +436,90 @@
WaitForInterceptionAndLoad());
}
+ // Verifies the structure and content of the SubmitQuery protobuf message sent
+ // from the browser to the inner WebContents.
+ auto VerifySubmitQueryMessage(
+ lens::LensOverlayRequestId::MediaType expected_media_type,
+ std::optional expected_added_input_name = std::nullopt) {
+ return Steps(
+ // Wait until the inner WebContents receives a message starting with the
+ // SubmitQuery protobuf tag byte (18 / 0x12).
+ WaitForJsResult(kInnerWebContentsId,
+ "() => window.receivedMessages.some(buf => new "
+ "Uint8Array(buf)[0] === 18)"),
+ WithElement(kInnerWebContentsId, [expected_media_type,
+ expected_added_input_name](
+ ui::TrackedElement* el) {
+ auto* web_contents = AsInstrumentedWebContents(el)->web_contents();
+ // Extract the binary protobuf message from JavaScript by converting
+ // the matching ArrayBuffer into a base64 encoded string.
+ std::string base64_msg =
+ content::EvalJs(web_contents,
+ "btoa(Array.from(new "
+ "Uint8Array(window.receivedMessages.find(buf => "
+ "new Uint8Array(buf)[0] === 18))).map(b => "
+ "String.fromCharCode(b)).join(''))")
+ .ExtractString();
+ std::string decoded_msg;
+ ASSERT_TRUE(base::Base64Decode(base64_msg, &decoded_msg));
+ lens::ClientToAimMessage message;
+ ASSERT_TRUE(message.ParseFromString(decoded_msg));
+
+ // Verify core query payload requirements.
+ EXPECT_TRUE(message.has_submit_query());
+ EXPECT_EQ(
+ message.submit_query().payload().lens_image_query_data_size(), 1);
+ EXPECT_EQ(message.submit_query()
+ .payload()
+ .lens_image_query_data(0)
+ .request_id()
+ .media_type(),
+ expected_media_type);
+
+ // Verify additional context inputs if expected by the test case.
+ if (expected_added_input_name.has_value()) {
+ EXPECT_TRUE(message.submit_query().payload().has_added_inputs());
+ EXPECT_EQ(message.submit_query()
+ .payload()
+ .added_inputs()
+ .added_inputs_size(),
+ 1);
+ EXPECT_TRUE(message.submit_query()
+ .payload()
+ .added_inputs()
+ .added_inputs(0)
+ .has_lens_file());
+ if (expected_media_type ==
+ lens::LensOverlayRequestId::MEDIA_TYPE_PDF) {
+ EXPECT_THAT(message.submit_query()
+ .payload()
+ .added_inputs()
+ .added_inputs(0)
+ .lens_file()
+ .file_name(),
+ testing::HasSubstr(*expected_added_input_name));
+ } else {
+ EXPECT_THAT(message.submit_query()
+ .payload()
+ .added_inputs()
+ .added_inputs(0)
+ .lens_file()
+ .page_url(),
+ testing::HasSubstr(*expected_added_input_name));
+ }
+ } else {
+ EXPECT_FALSE(message.submit_query().payload().has_added_inputs());
+ }
+ }));
+ }
+
protected:
base::test::ScopedFeatureList scoped_feature_list_;
std::optional tab_context_override_;
+ std::unique_ptr url_loader_interceptor_;
};
+// TODO(crbug.com/500717050): Parameterize this test suite on the feature flag.
IN_PROC_BROWSER_TEST_F(ContextualTasksInteractiveUiTest,
AddAndRemovePdfChipFromComposebox) {
const GURL kInterceptionUrl("https://www.google.com/search?udm=50");
@@ -461,4 +609,678 @@
WaitForComposeboxFilesCount(0));
}
+IN_PROC_BROWSER_TEST_F(ContextualTasksInteractiveUiTest,
+ AddAndRemoveTabFromComposebox) {
+ const GURL kInterceptionUrl("https://www.google.com/search?udm=50");
+ const GURL kGenericPageUrl = embedded_test_server()->GetURL("/title1.html");
+
+ const DeepQuery kFaviconGroup = {
+ "contextual-tasks-app", "#composebox", "#composebox",
+ "#contextEntrypoint", "#entrypointButton", "composebox-favicon-group"};
+
+ RunTestSequence(InstrumentTab(kPrimaryTab, 0),
+ AddInstrumentedTab(kGenericTab, kGenericPageUrl),
+ SelectTab(kTabStripElementId, 0),
+ OpenContextualTasksInCurrentTab(kInterceptionUrl),
+
+ ForceClickAddContextEntrypoint(kPrimaryTab),
+ ForceClickMenuButton(kPrimaryTab, 0),
+
+ WaitForElementExists(kPrimaryTab, kFaviconGroup),
+ CheckJsResultAt(kPrimaryTab, kFaviconGroup,
+ "el => el.tabs && el.tabs.length === 1 && "
+ "el.tabs[0].title.includes('title1.html')",
+ true),
+ WaitForComposeboxFilesCount(1),
+
+ ForceClickAddContextEntrypoint(kPrimaryTab),
+ ForceClickMenuButton(kPrimaryTab, 0),
+
+ WaitForElementDoesNotExist(kPrimaryTab, kFaviconGroup),
+ WaitForComposeboxFilesCount(0));
+}
+
+IN_PROC_BROWSER_TEST_F(ContextualTasksInteractiveUiTest,
+ AddAndSubmitTabFromComposebox) {
+ const GURL kInterceptionUrl("https://www.google.com/search?udm=50");
+ const GURL kGenericPageUrl = embedded_test_server()->GetURL("/title1.html");
+
+ const DeepQuery kFaviconGroup = {
+ "contextual-tasks-app", "#composebox", "#composebox",
+ "#contextEntrypoint", "#entrypointButton", "composebox-favicon-group"};
+
+ const DeepQuery kSubmitButton = {"contextual-tasks-app", "#composebox",
+ "#composebox", "cr-composebox-submit",
+ "#submitContainer"};
+
+ RunTestSequence(
+ InstrumentTab(kPrimaryTab, 0),
+ AddInstrumentedTab(kGenericTab, kGenericPageUrl),
+ SelectTab(kTabStripElementId, 0),
+ OpenContextualTasksInCurrentTab(kInterceptionUrl),
+ InstrumentInnerWebContents(kInnerWebContentsId, kPrimaryTab, 0),
+ ForceClickAddContextEntrypoint(kPrimaryTab),
+ ForceClickMenuButton(kPrimaryTab, 0),
+ WaitForElementExists(kPrimaryTab, kFaviconGroup),
+ CheckJsResultAt(kPrimaryTab, kFaviconGroup,
+ "el => el.tabs && el.tabs.length === 1 && "
+ "el.tabs[0].title.includes('title1.html')",
+ true),
+ WaitForComposeboxFilesCount(1), ClickButton(kPrimaryTab, kSubmitButton),
+ VerifySubmitQueryMessage(
+ lens::LensOverlayRequestId::MEDIA_TYPE_WEBPAGE_AND_IMAGE,
+ "title1.html"));
+}
+
+IN_PROC_BROWSER_TEST_F(ContextualTasksInteractiveUiTest,
+ AddAndSubmitPdfChipFromComposebox) {
+ const GURL kInterceptionUrl("https://www.google.com/search?udm=50");
+
+ base::FilePath test_data_dir;
+ base::PathService::Get(chrome::DIR_TEST_DATA, &test_data_dir);
+ base::FilePath file_path = test_data_dir.AppendASCII("download.pdf");
+
+ ui::SelectFileDialog::SetFactory(
+ std::make_unique(
+ std::vector{file_path}));
+
+ const DeepQuery kDocumentChip = {"contextual-tasks-app",
+ "#composebox",
+ "#composebox",
+ "#carousel",
+ "cr-composebox-file-thumbnail",
+ "#documentChip"};
+
+ const DeepQuery kSubmitButton = {"contextual-tasks-app", "#composebox",
+ "#composebox", "cr-composebox-submit",
+ "#submitContainer"};
+
+ const DeepQuery kDocumentChipTitle = {"contextual-tasks-app",
+ "#composebox",
+ "#composebox",
+ "#carousel",
+ "cr-composebox-file-thumbnail",
+ "#documentTitle"};
+
+ RunTestSequence(
+ InstrumentTab(kPrimaryTab, 0), SelectTab(kTabStripElementId, 0),
+ OpenContextualTasksInCurrentTab(kInterceptionUrl),
+ InstrumentInnerWebContents(kInnerWebContentsId, kPrimaryTab, 0),
+ ForceClickAddContextEntrypoint(kPrimaryTab),
+ ForceClickMenuButton(kPrimaryTab, "fileUpload"),
+ WaitForElementExists(kPrimaryTab, kDocumentChip),
+ CheckJsResultAt(kPrimaryTab, kDocumentChipTitle,
+ "el => el.textContent.trim()",
+ testing::HasSubstr("download.pdf")),
+ WaitForComposeboxFilesCount(1), ClickButton(kPrimaryTab, kSubmitButton),
+ VerifySubmitQueryMessage(lens::LensOverlayRequestId::MEDIA_TYPE_PDF,
+ "download.pdf"));
+}
+
+IN_PROC_BROWSER_TEST_F(ContextualTasksInteractiveUiTest,
+ AddAndSubmitImageChipFromComposebox) {
+ const GURL kInterceptionUrl("https://www.google.com/search?udm=50");
+
+ base::FilePath test_data_dir;
+ base::PathService::Get(chrome::DIR_TEST_DATA, &test_data_dir);
+ base::FilePath file_path = test_data_dir.AppendASCII("handbag.png");
+
+ ui::SelectFileDialog::SetFactory(
+ std::make_unique(
+ std::vector{file_path}));
+
+ const DeepQuery kImgChip = {
+ "contextual-tasks-app", "#composebox", "#composebox", "#carousel",
+ "cr-composebox-file-thumbnail", "#imgChip"};
+
+ const DeepQuery kSubmitButton = {"contextual-tasks-app", "#composebox",
+ "#composebox", "cr-composebox-submit",
+ "#submitContainer"};
+
+ RunTestSequence(
+ InstrumentTab(kPrimaryTab, 0), SelectTab(kTabStripElementId, 0),
+ OpenContextualTasksInCurrentTab(kInterceptionUrl),
+ InstrumentInnerWebContents(kInnerWebContentsId, kPrimaryTab, 0),
+ ForceClickAddContextEntrypoint(kPrimaryTab),
+ ForceClickMenuButton(kPrimaryTab, "imageUpload"),
+ WaitForElementExists(kPrimaryTab, kImgChip),
+ WaitForComposeboxFilesCount(1), ClickButton(kPrimaryTab, kSubmitButton),
+ VerifySubmitQueryMessage(
+ lens::LensOverlayRequestId::MEDIA_TYPE_DEFAULT_IMAGE));
+}
+
+class ContextualTasksInteractiveUiTestParameterized
+ : public ContextualTasksInteractiveUiTest,
+ public testing::WithParamInterface {
+ public:
+ ContextualTasksInteractiveUiTestParameterized() {
+ if (GetParam()) {
+ scoped_feature_list_.InitAndEnableFeature(kAimTriggeredThreadLinks);
+ } else {
+ scoped_feature_list_.InitAndDisableFeature(kAimTriggeredThreadLinks);
+ }
+ }
+
+ void SetUpCommandLine(base::CommandLine* command_line) override {
+ mock_cert_verifier_.SetUpCommandLine(command_line);
+
+ // Add command line to allow opaque origin post messages to be accepted in
+ // GetGuestForMessage. For some reason, during testing, the
+ // accounts.google.com postMessage is always opaque, so this is needed for
+ // the test to pass.
+ command_line->AppendSwitch(
+ "allow-opaque-origin-for-contextual-tasks-testing");
+ }
+
+ void SetUpInProcessBrowserTestFixture() override {
+ mock_cert_verifier_.SetUpInProcessBrowserTestFixture();
+ }
+
+ void SetUpOnMainThread() override {
+ ContextualTasksInteractiveUiTest::SetUpOnMainThread();
+ mock_cert_verifier_.mock_cert_verifier()->set_default_result(net::OK);
+
+ base::FilePath test_data_dir;
+ base::PathService::Get(chrome::DIR_TEST_DATA, &test_data_dir);
+ base::FilePath file_path = test_data_dir.AppendASCII("mock_aim_page.html");
+ std::string mock_aim_page_content;
+ {
+ base::ScopedAllowBlockingForTesting allow_blocking;
+ ASSERT_TRUE(base::ReadFileToString(file_path, &mock_aim_page_content));
+ }
+
+ https_server_.SetSSLConfig(net::EmbeddedTestServer::CERT_TEST_NAMES);
+ https_server_.RegisterRequestHandler(base::BindRepeating(
+ [](std::string mock_aim_page_content,
+ const net::test_server::HttpRequest& request)
+ -> std::unique_ptr {
+ auto response =
+ std::make_unique();
+ response->set_code(net::HTTP_OK);
+ response->set_content_type("text/html; charset=utf-8");
+ response->AddCustomHeader("Cross-Origin-Opener-Policy",
+ "unsafe-none");
+
+ auto it = request.headers.find("Host");
+ if (it != request.headers.end()) {
+ if (it->second == "myaccount.google.com" &&
+ request.relative_url == "/title1.html") {
+ response->set_content(
+ "Title 1 HTML Page");
+ return response;
+ }
+ if (it->second == kMockAimPageHost) {
+ response->set_content(mock_aim_page_content);
+ return response;
+ }
+ }
+ return nullptr;
+ },
+ mock_aim_page_content));
+
+ ASSERT_TRUE(https_server_.Start());
+ }
+
+ void TearDownInProcessBrowserTestFixture() override {
+ mock_cert_verifier_.TearDownInProcessBrowserTestFixture();
+ }
+
+ protected:
+ base::test::ScopedFeatureList scoped_feature_list_;
+ content::ContentMockCertVerifier mock_cert_verifier_;
+ net::EmbeddedTestServer https_server_{net::EmbeddedTestServer::TYPE_HTTPS};
+};
+
+// CUJ covered by this test:
+// 1) Opens Contextual Tasks in a tab.
+// 2) In the Contextual Tasks , calls window.open() to a URL (not
+// about:blank) with target _blank.
+// 3) Verifies that the call returns a window
+// object.
+// 4) Verifies that the window.open call was successful by verifying the
+// new tab opened.
+// 5) Back in Contextual Tasks, calls window.open with a URL and
+// target _self.
+// 6) Verifies the Contextual Tasks tab navigates to the opened
+// URL.
+IN_PROC_BROWSER_TEST_P(ContextualTasksInteractiveUiTestParameterized,
+ WindowOpenCUJ) {
+ const GURL kInterceptionUrl("https://www.google.com/search?udm=50");
+ const GURL kTargetUrl("https://a.google.com/title1.html");
+
+ auto sequence = Steps(
+ InstrumentTab(kPrimaryTab, 0), SelectTab(kTabStripElementId, 0),
+ // 1) Opens Contextual Tasks in a tab.
+ OpenContextualTasksInCurrentTab(kInterceptionUrl),
+ InstrumentInnerWebContents(kInnerWebContentsId, kPrimaryTab, 0),
+
+ WithElement(kInnerWebContentsId, [kTargetUrl](ui::TrackedElement* el) {
+ auto* web_contents = AsInstrumentedWebContents(el)->web_contents();
+ content::TestNavigationObserver nav_observer(kTargetUrl);
+ nav_observer.StartWatchingNewWebContents();
+
+ // 2) In the Contextual Tasks , calls window.open() to a
+ // URL (not about:blank) with target _blank. 3) Verifies that the
+ // call returns a window object.
+ bool returns_window =
+ content::EvalJs(
+ web_contents,
+ base::StringPrintf("window.open('%s', '_blank') !== null",
+ kTargetUrl.spec().c_str()))
+ .ExtractBool();
+ EXPECT_TRUE(returns_window);
+
+ // 4) Verifies that the window.open call was successful by verifying
+ // the new tab opened.
+ nav_observer.Wait();
+ }));
+
+ if (GetParam()) {
+ // When flag is enabled, the source tab stays open.
+ // Instrument the new tab at index 1.
+ sequence = Steps(
+ std::move(sequence), InstrumentTab(kNewTabId, 1),
+ SelectTab(kTabStripElementId, 0), WaitForShow(kInnerWebContentsId),
+ // 5) Back in Contextual Tasks, calls window.open with a URL and target
+ // _self.
+ Do(base::BindLambdaForTesting([this, kTargetUrl]() {
+ auto* web_contents =
+ browser()->tab_strip_model()->GetActiveWebContents();
+ auto inner_contents = web_contents->GetInnerWebContents();
+ ASSERT_FALSE(inner_contents.empty());
+ auto* guest_contents = inner_contents[0];
+
+ content::ExecuteScriptAsync(
+ guest_contents, base::StringPrintf("window.open('%s', '_self')",
+ kTargetUrl.spec().c_str()));
+ })),
+ // 6) Verifies the Contextual Tasks tab navigates to the opened URL.
+ WaitForWebContentsNavigation(kPrimaryTab, kTargetUrl));
+ } else {
+ // When flag is disabled, the source tab closes and the new tab becomes
+ // index 0.
+ sequence = Steps(
+ std::move(sequence), InstrumentTab(kNewTabId, 0),
+
+ // Verifies the new tab opened and is at the target URL.
+ CheckElement(kNewTabId,
+ base::BindOnce(
+ [](const GURL& expected_url, ui::TrackedElement* el) {
+ return AsInstrumentedWebContents(el)
+ ->web_contents()
+ ->GetLastCommittedURL() == expected_url;
+ },
+ kTargetUrl)));
+ }
+
+ RunTestSequence(std::move(sequence));
+}
+
+// CUJ covered by this test:
+// 1) Opens Contextual Tasks in a tab.
+// 2) In the Contextual Tasks , calls window.open() to a URL (not
+// about:blank) with target _blank.
+// 3) Verifies that the call returns a window
+// object.
+// 4) Verifies that the window.open call was successful by verifying the
+// new tab opened.
+IN_PROC_BROWSER_TEST_P(ContextualTasksInteractiveUiTestParameterized,
+ WindowOpenBlank) {
+ const GURL kInterceptionUrl("https://www.google.com/search?udm=50");
+ const GURL kTargetUrl("https://a.google.com/title1.html");
+
+ RunTestSequence(
+ InstrumentTab(kPrimaryTab, 0), SelectTab(kTabStripElementId, 0),
+ // 1) Opens Contextual Tasks in a tab.
+ OpenContextualTasksInCurrentTab(kInterceptionUrl),
+ InstrumentInnerWebContents(kInnerWebContentsId, kPrimaryTab, 0),
+
+ WithElement(kInnerWebContentsId, [kTargetUrl](ui::TrackedElement* el) {
+ auto* web_contents = AsInstrumentedWebContents(el)->web_contents();
+ content::TestNavigationObserver nav_observer(kTargetUrl);
+ nav_observer.StartWatchingNewWebContents();
+
+ // 2) In the Contextual Tasks , calls window.open() to a URL
+ // (not about:blank) with target _blank. 3) Verifies that the call
+ // returns a window object.
+ bool returns_window =
+ content::EvalJs(
+ web_contents,
+ base::StringPrintf("window.open('%s', '_blank') !== null",
+ kTargetUrl.spec().c_str()))
+ .ExtractBool();
+ EXPECT_TRUE(returns_window);
+
+ // 4) Verifies that the window.open call was successful by verifying the
+ // new tab opened.
+ nav_observer.Wait();
+ }));
+}
+
+// CUJ covered by this test:
+// 1) Opens Contextual Tasks in a tab.
+// 2) Back in Contextual Tasks, calls window.open with a URL and target _self.
+// 3) Verifies the Contextual Tasks tab navigates to the opened URL.
+IN_PROC_BROWSER_TEST_P(ContextualTasksInteractiveUiTestParameterized,
+ WindowOpenSelf) {
+ const GURL kInterceptionUrl("https://www.google.com/search?udm=50");
+ const GURL kTargetUrl("https://a.google.com/title1.html");
+
+ RunTestSequence(
+ InstrumentTab(kPrimaryTab, 0), SelectTab(kTabStripElementId, 0),
+ // 1) Opens Contextual Tasks in a tab.
+ OpenContextualTasksInCurrentTab(kInterceptionUrl),
+ InstrumentInnerWebContents(kInnerWebContentsId, kPrimaryTab, 0),
+
+ // 2) Back in Contextual Tasks, calls window.open with a URL and target
+ // _self.
+ Do(base::BindLambdaForTesting([this, kTargetUrl]() {
+ auto* web_contents =
+ browser()->tab_strip_model()->GetActiveWebContents();
+ auto inner_contents = web_contents->GetInnerWebContents();
+ ASSERT_FALSE(inner_contents.empty());
+ auto* guest_contents = inner_contents[0];
+
+ content::ExecuteScriptAsync(
+ guest_contents, base::StringPrintf("window.open('%s', '_self')",
+ kTargetUrl.spec().c_str()));
+ })),
+
+ // 3) Verifies the Contextual Tasks tab navigates to the opened URL.
+ WaitForWebContentsNavigation(kPrimaryTab, kTargetUrl));
+}
+
+// CUJ covered by this test:
+// 1) Opens Contextual Tasks in a tab.
+// 2) Call window.open from the Contextual Tasks .
+// 3) In the opened window, call window.opener.postMessage.
+// 4) Verify the postMessage is received in the .
+IN_PROC_BROWSER_TEST_P(ContextualTasksInteractiveUiTestParameterized,
+ PostMessageToDummyOpenerRoutedToWebview) {
+ const GURL kActiveTabUrl =
+ https_server_.GetURL("myaccount.google.com", "/title1.html");
+ const GURL clicked_url =
+ https_server_.GetURL("myaccount.google.com", "/title1.html#citation");
+ const GURL kInterceptionUrl =
+ https_server_.GetURL(kMockAimPageHost, "/search?udm=50");
+
+ DEFINE_LOCAL_ELEMENT_IDENTIFIER_VALUE(kOpenedTab);
+ DEFINE_LOCAL_ELEMENT_IDENTIFIER_VALUE(kPrimaryTab2);
+ DEFINE_LOCAL_ELEMENT_IDENTIFIER_VALUE(kInnerWebContentsId2);
+
+ // Step 1: Open Contextual Tasks in a Tab
+ auto sequence =
+ Steps(InstrumentTab(kPrimaryTab, 0), SelectTab(kTabStripElementId, 0),
+ OpenContextualTasksInCurrentTab(kInterceptionUrl),
+ InstrumentInnerWebContents(kInnerWebContentsId, kPrimaryTab, 0));
+
+ sequence = Steps(
+ std::move(sequence),
+ // 1. Inject listener in the to collect
+ // received messages.
+ WithElement(kInnerWebContentsId,
+ base::BindOnce([](ui::TrackedElement* el) {
+ std::string setup_listener = R"(
+ window.receivedMessages = [];
+ window.messagePromiseResolver = null;
+ window.addEventListener('message', (event) => {
+ window.receivedMessages.push(event.data);
+ if (window.messagePromiseResolver &&
+ event.data === 'hello_from_opened_page') {
+ window.messagePromiseResolver(true);
+ }
+ });
+ )";
+ auto* wc = AsInstrumentedWebContents(el)->web_contents();
+ EXPECT_TRUE(content::ExecJs(wc, setup_listener));
+ })),
+
+ // 2. Within the , inject and open a window.
+ WithElement(kInnerWebContentsId,
+ base::BindOnce(
+ [](GURL url, ui::TrackedElement* el) {
+ auto* wc =
+ AsInstrumentedWebContents(el)->web_contents();
+ std::string click_script = content::JsReplace(
+ R"(
+ (() => {
+ window.open($1, '_blank');
+ })();
+ )",
+ url.spec());
+ EXPECT_TRUE(content::ExecJs(wc, click_script));
+ },
+ clicked_url)),
+
+ // 3. Verify the URL opens in a new tab in the tab strip and instrument
+ // it.
+ InstrumentNextTab(kOpenedTab),
+
+ // 4. Wait for the opened tab to finish loading
+ WaitForWebContentsNavigation(kOpenedTab, clicked_url),
+
+ // 5. Verify window.opener is non-null and call postMessage from the
+ // opened tab.
+ WithElement(kOpenedTab, base::BindOnce([](ui::TrackedElement* el) {
+ auto* wc = AsInstrumentedWebContents(el)->web_contents();
+ std::string post_message_script = R"(
+ (async () => {
+ if (!window.opener) {
+ return "no opener";
+ }
+ try {
+ window.opener.postMessage("hello_from_opened_page", "*");
+ return "ok";
+ } catch (e) {
+ return "error: " + e.message;
+ }
+ })();
+ )";
+ EXPECT_EQ("ok", content::EvalJs(wc, post_message_script));
+ })),
+
+ // Switch back to the original tab.
+ SelectTab(kTabStripElementId, 0));
+
+ const std::string check_message_script = R"(
+ new Promise((resolve) => {
+ if (window.receivedMessages &&
+ window.receivedMessages.includes("hello_from_opened_page")) {
+ resolve(true);
+ } else {
+ window.messagePromiseResolver = resolve;
+ }
+ });
+ )";
+
+ // If AimTriggeredThreadLinks is enabled, the window.open call opens in a new
+ // tab, so the original contextual tasks tab still exists.
+ if (GetParam()) {
+ sequence = Steps(
+ std::move(sequence), WaitForShow(kInnerWebContentsId),
+ WithElement(kInnerWebContentsId,
+ base::BindOnce(
+ [](std::string script, ui::TrackedElement* el) {
+ auto* wc =
+ AsInstrumentedWebContents(el)->web_contents();
+ EXPECT_EQ(true, content::EvalJs(wc, script));
+ },
+ check_message_script)));
+ } else {
+ // If AimTriggeredThreadLinks is disabled, the window.open call moves
+ // Contextual Tasks into the side panel. Therefore, instrument the side
+ // panel and ensure the postMessage was received.
+ sequence = Steps(
+ std::move(sequence),
+ WaitForShow(kContextualTasksSidePanelWebViewElementId),
+ InstrumentNonTabWebView(kPrimaryTab2,
+ kContextualTasksSidePanelWebViewElementId),
+ InstrumentInnerWebContents(kInnerWebContentsId2, kPrimaryTab2, 0),
+ WaitForShow(kInnerWebContentsId2),
+ WithElement(kInnerWebContentsId2,
+ base::BindOnce(
+ [](std::string script, ui::TrackedElement* el) {
+ auto* wc =
+ AsInstrumentedWebContents(el)->web_contents();
+ EXPECT_EQ(true, content::EvalJs(wc, script));
+ },
+ check_message_script)));
+ }
+
+ RunTestSequence(std::move(sequence));
+}
+
+// CUJ covered by this test:
+// 1) Opens Contextual Tasks in a tab.
+// 2) Call window.open from the Contextual Tasks .
+// 3) In the opened window, call window.opener.postMessage.
+// 4) Verify the postMessage is received in the .
+IN_PROC_BROWSER_TEST_P(ContextualTasksInteractiveUiTestParameterized,
+ PopupPostMessageToOpenerRoutedToWebview) {
+ const GURL kActiveTabUrl =
+ https_server_.GetURL("myaccount.google.com", "/title1.html");
+ const GURL clicked_url =
+ https_server_.GetURL("myaccount.google.com", "/title1.html#citation");
+ const GURL kInterceptionUrl =
+ https_server_.GetURL(kMockAimPageHost, "/search?udm=50");
+
+ DEFINE_LOCAL_ELEMENT_IDENTIFIER_VALUE(kOpenedPopup);
+ DEFINE_LOCAL_ELEMENT_IDENTIFIER_VALUE(kPrimaryTab2);
+ DEFINE_LOCAL_ELEMENT_IDENTIFIER_VALUE(kInnerWebContentsId2);
+
+ // Step 1: Open Contextual Tasks in a Tab
+ auto sequence =
+ Steps(InstrumentTab(kPrimaryTab, 0), SelectTab(kTabStripElementId, 0),
+ OpenContextualTasksInCurrentTab(kInterceptionUrl),
+ InstrumentInnerWebContents(kInnerWebContentsId, kPrimaryTab, 0));
+
+ sequence = Steps(
+ std::move(sequence),
+ // 1. Inject listener in the to collect
+ // received messages.
+ WithElement(kInnerWebContentsId,
+ base::BindOnce([](ui::TrackedElement* el) {
+ std::string setup_listener = R"(
+ window.receivedMessages = [];
+ window.messagePromiseResolver = null;
+ window.addEventListener('message', (event) => {
+ window.receivedMessages.push(event.data);
+ if (window.messagePromiseResolver &&
+ event.data === 'hello_from_opened_page') {
+ window.messagePromiseResolver(true);
+ }
+ });
+ )";
+ auto* wc = AsInstrumentedWebContents(el)->web_contents();
+ EXPECT_TRUE(content::ExecJs(wc, setup_listener));
+ })),
+
+ // 2. Within the guest view, call window.open with popup=yes
+ WithElement(kInnerWebContentsId,
+ base::BindOnce(
+ [](GURL url, ui::TrackedElement* el) {
+ auto* wc =
+ AsInstrumentedWebContents(el)->web_contents();
+ std::string open_script = content::JsReplace(
+ R"(
+ (() => {
+ window.open($1, '_blank', 'popup=yes,width=400,height=400');
+ })();
+ )",
+ url.spec());
+ EXPECT_TRUE(content::ExecJs(wc, open_script));
+ },
+ clicked_url)),
+
+ // 3. Verify the URL opens in a new window (popup) and instrument it!
+ // We use AnyBrowser() to catch it if it opens in a new window.
+ InstrumentNextTab(kOpenedPopup, AnyBrowser()),
+
+ // 4. Wait for the opened popup to finish loading
+ WaitForWebContentsNavigation(kOpenedPopup, clicked_url),
+
+ // Check that the popup window is focused when it is opened.
+ CheckElement(
+ kOpenedPopup, base::BindOnce([](ui::TrackedElement* el) {
+ auto* wc = AsInstrumentedWebContents(el)->web_contents();
+ tabs::TabInterface* tab = tabs::TabInterface::GetFromContents(wc);
+ Browser* popup_browser =
+ tab->GetBrowserWindowInterface()->GetBrowserForMigrationOnly();
+ EXPECT_TRUE(popup_browser);
+ EXPECT_TRUE(ui_test_utils::IsBrowserActive(popup_browser));
+ return true;
+ })),
+
+ // 5. Verify window.opener is non-null and call postMessage from the
+ // opened popup
+ WithElement(kOpenedPopup, base::BindOnce([](ui::TrackedElement* el) {
+ auto* wc = AsInstrumentedWebContents(el)->web_contents();
+ std::string post_message_script = R"(
+ (async () => {
+ if (!window.opener) {
+ return "no opener";
+ }
+ try {
+ window.opener.postMessage("hello_from_opened_page", "*");
+ return "ok";
+ } catch (e) {
+ return "error: " + e.message;
+ }
+ })();
+ )";
+ EXPECT_EQ("ok", content::EvalJs(wc, post_message_script));
+ })));
+
+ const std::string check_message_script = R"(
+ new Promise((resolve) => {
+ if (window.receivedMessages &&
+ window.receivedMessages.includes("hello_from_opened_page")) {
+ resolve(true);
+ } else {
+ window.messagePromiseResolver = resolve;
+ }
+ });
+ )";
+
+ // If AimTriggeredThreadLinks is enabled, the window.open call opens in a new
+ // tab, so the original contextual tasks tab still exists.
+ if (GetParam()) {
+ sequence = Steps(
+ std::move(sequence), WaitForShow(kInnerWebContentsId),
+ WithElement(kInnerWebContentsId,
+ base::BindOnce(
+ [](std::string script, ui::TrackedElement* el) {
+ auto* wc =
+ AsInstrumentedWebContents(el)->web_contents();
+ EXPECT_EQ(true, content::EvalJs(wc, script));
+ },
+ check_message_script)));
+ } else {
+ // If AimTriggeredThreadLinks is disabled, the window.open call moves
+ // Contextual Tasks into the side panel. Therefore, instrument the side
+ // panel and ensure the postMessage was received.
+ sequence = Steps(
+ std::move(sequence),
+ WaitForShow(kContextualTasksSidePanelWebViewElementId),
+ InstrumentNonTabWebView(kPrimaryTab2,
+ kContextualTasksSidePanelWebViewElementId),
+ InstrumentInnerWebContents(kInnerWebContentsId2, kPrimaryTab2, 0),
+ WaitForShow(kInnerWebContentsId2),
+ WithElement(kInnerWebContentsId2,
+ base::BindOnce(
+ [](std::string script, ui::TrackedElement* el) {
+ auto* wc =
+ AsInstrumentedWebContents(el)->web_contents();
+ EXPECT_EQ(true, content::EvalJs(wc, script));
+ },
+ check_message_script)));
+ }
+
+ RunTestSequence(InAnyContext(std::move(sequence)));
+}
+
+INSTANTIATE_TEST_SUITE_P(All,
+ ContextualTasksInteractiveUiTestParameterized,
+ testing::Bool());
+
} // namespace contextual_tasks
diff -Nru chromium-149.0.7827.114/chrome/browser/contextual_tasks/contextual_tasks_mojom_traits.h chromium-149.0.7827.155/chrome/browser/contextual_tasks/contextual_tasks_mojom_traits.h
--- chromium-149.0.7827.114/chrome/browser/contextual_tasks/contextual_tasks_mojom_traits.h 1970-01-01 00:00:00.000000000 +0000
+++ chromium-149.0.7827.155/chrome/browser/contextual_tasks/contextual_tasks_mojom_traits.h 2026-06-16 01:31:02.000000000 +0000
@@ -0,0 +1,56 @@
+// 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_CONTEXTUAL_TASKS_MOJOM_TRAITS_H_
+#define CHROME_BROWSER_CONTEXTUAL_TASKS_CONTEXTUAL_TASKS_MOJOM_TRAITS_H_
+
+#include "base/uuid.h"
+#include "chrome/browser/contextual_tasks/contextual_tasks.mojom-shared.h"
+#include "chrome/browser/contextual_tasks/contextual_tasks_types.h"
+#include "mojo/public/cpp/base/unguessable_token_mojom_traits.h"
+#include "mojo/public/cpp/base/uuid_mojom_traits.h"
+#include "mojo/public/cpp/bindings/struct_traits.h"
+
+namespace mojo {
+
+template <>
+struct StructTraits {
+ static const base::Uuid& value(const contextual_tasks::ContextualTaskId& id) {
+ return id.value();
+ }
+
+ static bool Read(contextual_tasks::mojom::ContextualTaskIdDataView data,
+ contextual_tasks::ContextualTaskId* out) {
+ base::Uuid uuid;
+ if (!data.ReadValue(&uuid)) {
+ return false;
+ }
+ *out = contextual_tasks::ContextualTaskId(uuid);
+ return true;
+ }
+};
+
+template <>
+struct StructTraits {
+ static const base::UnguessableToken& value(
+ const contextual_tasks::ContextualWindowId& id) {
+ return id.value();
+ }
+
+ static bool Read(contextual_tasks::mojom::ContextualWindowIdDataView data,
+ contextual_tasks::ContextualWindowId* out) {
+ base::UnguessableToken token;
+ if (!data.ReadValue(&token)) {
+ return false;
+ }
+ *out = contextual_tasks::ContextualWindowId(token);
+ return true;
+ }
+};
+
+} // namespace mojo
+
+#endif // CHROME_BROWSER_CONTEXTUAL_TASKS_CONTEXTUAL_TASKS_MOJOM_TRAITS_H_
diff -Nru chromium-149.0.7827.114/chrome/browser/contextual_tasks/contextual_tasks_navigation_throttle.cc chromium-149.0.7827.155/chrome/browser/contextual_tasks/contextual_tasks_navigation_throttle.cc
--- chromium-149.0.7827.114/chrome/browser/contextual_tasks/contextual_tasks_navigation_throttle.cc 2026-06-10 17:58:03.000000000 +0000
+++ chromium-149.0.7827.155/chrome/browser/contextual_tasks/contextual_tasks_navigation_throttle.cc 2026-06-16 01:31:02.000000000 +0000
@@ -15,10 +15,13 @@
#include "chrome/browser/profiles/profile.h"
#include "components/contextual_tasks/public/features.h"
#include "components/omnibox/browser/aim_eligibility_service.h"
+#include "content/public/browser/global_routing_id.h"
#include "content/public/browser/navigation_controller.h"
#include "content/public/browser/navigation_handle.h"
#include "content/public/browser/web_contents.h"
#include "net/http/http_request_headers.h"
+#include "third_party/blink/public/mojom/window_features/window_features.mojom.h"
+#include "url/origin.h"
using ThrottleCheckResult = content::NavigationThrottle::ThrottleCheckResult;
@@ -105,15 +108,25 @@
headers.GetHeader("sec-ch-ua-mobile");
bool is_mobile_ua = sec_ch_ua_mobile.has_value() && *sec_ch_ua_mobile == "?1";
+ std::optional initiator_origin =
+ navigation_handle()->GetInitiatorOrigin();
+
+ std::optional initiator_frame_token;
+ if (navigation_handle()->GetInitiatorFrameToken().has_value()) {
+ initiator_frame_token = content::GlobalRenderFrameHostToken(
+ navigation_handle()->GetInitiatorProcessId(),
+ navigation_handle()->GetInitiatorFrameToken().value());
+ }
+
if (ui_service &&
ui_service->HandleNavigation(
std::move(url_params), web_contents->GetResponsibleWebContents(),
/*is_from_embedded_page=*/web_contents !=
web_contents->GetResponsibleWebContents() ||
navigation_handle()->IsGuestViewMainFrame(),
- /*is_to_new_tab=*/false,
- /*is_same_site_or_from_ui=*/is_same_site_or_from_ui,
- /*is_mobile_ua=*/is_mobile_ua)) {
+ /*from_can_create_window=*/false, is_same_site_or_from_ui,
+ is_mobile_ua, initiator_origin, initiator_frame_token,
+ blink::mojom::WindowFeatures())) {
return CANCEL;
}
return PROCEED;
diff -Nru chromium-149.0.7827.114/chrome/browser/contextual_tasks/contextual_tasks_page_handler.cc chromium-149.0.7827.155/chrome/browser/contextual_tasks/contextual_tasks_page_handler.cc
--- chromium-149.0.7827.114/chrome/browser/contextual_tasks/contextual_tasks_page_handler.cc 2026-06-10 17:58:03.000000000 +0000
+++ chromium-149.0.7827.155/chrome/browser/contextual_tasks/contextual_tasks_page_handler.cc 2026-06-16 01:31:02.000000000 +0000
@@ -465,6 +465,24 @@
web_ui_controller_->GetPageRemote()->LockInput();
} else if (aim_to_client_message.has_unlock_input()) {
web_ui_controller_->GetPageRemote()->UnlockInput();
+ } else if (aim_to_client_message.has_open_link_in_side_panel_mode()) {
+ tabs::TabInterface* tab = tabs::TabInterface::MaybeGetFromContents(
+ web_ui_controller_->GetWebUIWebContents());
+ BrowserWindowInterface* browser = web_ui_controller_->GetBrowser();
+ GURL target_url(aim_to_client_message.open_link_in_side_panel_mode().url());
+ // Only accept valid URLs that are HTTP or HTTPS.
+ if (target_url.is_valid() && target_url.SchemeIsHTTPOrHTTPS()) {
+ ui_service_->OnThreadLinkClicked(
+ target_url, web_ui_controller_->GetTaskId().value_or(base::Uuid()),
+ tab ? tab->GetWeakPtr() : nullptr,
+ browser ? browser->GetWeakPtr() : nullptr,
+ /*initiator_origin=*/
+ web_ui_controller_->GetInnerWebContents()
+ ? web_ui_controller_->GetInnerWebContents()
+ ->GetPrimaryMainFrame()
+ ->GetLastCommittedOrigin()
+ : url::Origin());
+ }
}
}
@@ -689,6 +707,22 @@
prefs::kPinContextualTaskButton, true);
}
+void ContextualTasksPageHandler::RegisterWindow(
+ const contextual_tasks::ContextualTaskId& task_id,
+ const GURL& url,
+ const contextual_tasks::ContextualWindowId& window_id) {
+ if (ui_service_) {
+ ui_service_->RegisterWindow(task_id, url, window_id);
+ }
+}
+
+void ContextualTasksPageHandler::CloseWindow(
+ const contextual_tasks::ContextualWindowId& window_id) {
+ if (ui_service_) {
+ ui_service_->CloseTrackedWindow(window_id);
+ }
+}
+
void ContextualTasksPageHandler::UnpinSidePanel() {
if (!contextual_tasks::IsContextualTasksPinButtonInToolbarEnabled()) {
return;
diff -Nru chromium-149.0.7827.114/chrome/browser/contextual_tasks/contextual_tasks_page_handler.h chromium-149.0.7827.155/chrome/browser/contextual_tasks/contextual_tasks_page_handler.h
--- chromium-149.0.7827.114/chrome/browser/contextual_tasks/contextual_tasks_page_handler.h 2026-06-10 17:58:03.000000000 +0000
+++ chromium-149.0.7827.155/chrome/browser/contextual_tasks/contextual_tasks_page_handler.h 2026-06-16 01:31:02.000000000 +0000
@@ -78,6 +78,12 @@
void ReopenTabs() override;
void PinSidePanel() override;
void UnpinSidePanel() override;
+ void RegisterWindow(
+ const contextual_tasks::ContextualTaskId& task_id,
+ const GURL& url,
+ const contextual_tasks::ContextualWindowId& window_id) override;
+ void CloseWindow(
+ const contextual_tasks::ContextualWindowId& window_id) override;
void PostMessageToWebview(const lens::ClientToAimMessage& message);
// contextual_tasks::ContextualTasksService::Observer:
diff -Nru chromium-149.0.7827.114/chrome/browser/contextual_tasks/contextual_tasks_page_handler_unittest.cc chromium-149.0.7827.155/chrome/browser/contextual_tasks/contextual_tasks_page_handler_unittest.cc
--- chromium-149.0.7827.114/chrome/browser/contextual_tasks/contextual_tasks_page_handler_unittest.cc 2026-06-10 17:58:03.000000000 +0000
+++ chromium-149.0.7827.155/chrome/browser/contextual_tasks/contextual_tasks_page_handler_unittest.cc 2026-06-16 01:31:02.000000000 +0000
@@ -83,6 +83,34 @@
MOCK_METHOD(BrowserWindowInterface*, GetBrowser, (), (override));
};
+class MockContextualTasksUiServiceForThreadLink
+ : public MockContextualTasksUiService {
+ public:
+ MockContextualTasksUiServiceForThreadLink(
+ Profile* profile,
+ ContextualTasksService* service,
+ signin::IdentityManager* identity_manager,
+ AimEligibilityService* aim_eligibility_service,
+ std::unique_ptr eligibility_manager,
+ std::unique_ptr cookie_synchronizer)
+ : MockContextualTasksUiService(profile,
+ service,
+ identity_manager,
+ aim_eligibility_service,
+ std::move(eligibility_manager),
+ std::move(cookie_synchronizer)) {}
+ ~MockContextualTasksUiServiceForThreadLink() override = default;
+
+ MOCK_METHOD(void,
+ OnThreadLinkClicked,
+ (const GURL& url,
+ base::Uuid task_id,
+ base::WeakPtr tab,
+ base::WeakPtr browser,
+ const url::Origin& initiator_origin),
+ (override));
+};
+
class ContextualTasksPageHandlerTest : public ChromeRenderViewHostTestHarness {
public:
void SetUp() override {
@@ -103,7 +131,8 @@
profile(), base::BindOnce([](content::BrowserContext* context) {
Profile* profile = Profile::FromBrowserContext(context);
return std::unique_ptr(
- std::make_unique>(
+ std::make_unique<
+ NiceMock>(
profile,
ContextualTasksServiceFactory::GetForProfile(profile),
/*identity_manager=*/nullptr,
@@ -126,7 +155,7 @@
mock_contextual_tasks_service_ = static_cast(
ContextualTasksServiceFactory::GetForProfile(profile()));
mock_contextual_tasks_ui_service_ =
- static_cast(
+ static_cast(
ContextualTasksUiServiceFactory::GetForBrowserContext(profile()));
profile()->GetPrefs()->SetBoolean(prefs::kPinContextualTaskButton, false);
@@ -156,7 +185,8 @@
std::unique_ptr> contextual_tasks_ui_;
std::unique_ptr page_handler_;
raw_ptr mock_contextual_tasks_service_;
- raw_ptr mock_contextual_tasks_ui_service_;
+ raw_ptr
+ mock_contextual_tasks_ui_service_;
NiceMock page_;
base::test::ScopedFeatureList feature_list_;
std::unique_ptr profile_manager_;
@@ -724,6 +754,44 @@
page_handler_->OnWebviewMessage(serialized);
}
+
+TEST_F(ContextualTasksPageHandlerTest,
+ OnWebviewMessage_OpenLinkInSidePanelMode) {
+ lens::AimToClientMessage message;
+ auto* open_link = message.mutable_open_link_in_side_panel_mode();
+ open_link->set_url("https://example.com");
+
+ size_t size = message.ByteSizeLong();
+ std::vector serialized(size);
+ message.SerializeToArray(serialized.data(), size);
+
+ EXPECT_CALL(*mock_contextual_tasks_ui_service_,
+ OnThreadLinkClicked(GURL("https://example.com"), base::Uuid(),
+ testing::Eq(nullptr), testing::Eq(nullptr),
+ testing::_))
+ .Times(1);
+
+ page_handler_->OnWebviewMessage(serialized);
+}
+
+// Link click events where the URL is not HTTP or HTTPS should not trigger the
+// thread link click event.
+TEST_F(ContextualTasksPageHandlerTest,
+ OnWebviewMessage_NotifyLinkClicked_InvalidScheme) {
+ lens::AimToClientMessage message;
+ auto* open_link = message.mutable_open_link_in_side_panel_mode();
+ open_link->set_url("chrome://settings");
+
+ size_t size = message.ByteSizeLong();
+ std::vector serialized(size);
+ message.SerializeToArray(serialized.data(), size);
+
+ EXPECT_CALL(*mock_contextual_tasks_ui_service_,
+ OnThreadLinkClicked(_, _, _, _, _))
+ .Times(0);
+
+ page_handler_->OnWebviewMessage(serialized);
+}
TEST_F(ContextualTasksPageHandlerTest, OpenMyActivityUi) {
// Navigation smoke test. We provide a null browser to safely exit early
diff -Nru chromium-149.0.7827.114/chrome/browser/contextual_tasks/contextual_tasks_side_panel_coordinator_interactive_uitest.cc chromium-149.0.7827.155/chrome/browser/contextual_tasks/contextual_tasks_side_panel_coordinator_interactive_uitest.cc
--- chromium-149.0.7827.114/chrome/browser/contextual_tasks/contextual_tasks_side_panel_coordinator_interactive_uitest.cc 2026-06-10 17:58:03.000000000 +0000
+++ chromium-149.0.7827.155/chrome/browser/contextual_tasks/contextual_tasks_side_panel_coordinator_interactive_uitest.cc 2026-06-16 01:31:02.000000000 +0000
@@ -31,7 +31,9 @@
#include "components/web_modal/web_contents_modal_dialog_manager.h"
#include "content/public/test/browser_test.h"
#include "content/public/test/browser_test_utils.h"
+#include "content/public/test/url_loader_interceptor.h"
#include "content/public/test/web_contents_tester.h"
+#include "net/dns/mock_host_resolver.h"
#include "ui/base/models/dialog_model.h"
#include "ui/views/widget/widget_deletion_observer.h"
@@ -144,6 +146,27 @@
void SetUpOnMainThread() override {
InteractiveBrowserTest::SetUpOnMainThread();
+ host_resolver()->AddRule("*", "127.0.0.1");
+ ASSERT_TRUE(embedded_test_server()->Start());
+ url_loader_interceptor_ = std::make_unique(
+ base::BindLambdaForTesting(
+ [&](content::URLLoaderInterceptor::RequestParams* params) {
+ const GURL& url = params->url_request.url;
+ LOG(INFO) << "URLLoaderInterceptor intercepted URL: "
+ << url.spec();
+ if (url.host() == "www.google.com") {
+ content::URLLoaderInterceptor::WriteResponse(
+ "chrome/test/data/mock_aim_page.html",
+ params->client.get());
+ return true;
+ }
+ return false;
+ }));
+ }
+
+ void TearDownOnMainThread() override {
+ url_loader_interceptor_.reset();
+ InteractiveBrowserTest::TearDownOnMainThread();
}
ContextualTasksUI* GetContextualTasksUI() {
@@ -171,6 +194,7 @@
private:
base::test::ScopedFeatureList scoped_feature_list_;
+ std::unique_ptr url_loader_interceptor_;
};
MATCHER(IsNullSuggestedTabContext, "is a null TabContextPtr") {
@@ -1030,4 +1054,135 @@
}));
}
+DEFINE_LOCAL_CUSTOM_ELEMENT_EVENT_TYPE(kElementExistsEvent);
+DEFINE_LOCAL_CUSTOM_ELEMENT_EVENT_TYPE(kFrameLoadedEvent);
+DEFINE_LOCAL_CUSTOM_ELEMENT_EVENT_TYPE(kComposeboxFocusedEvent);
+
+IN_PROC_BROWSER_TEST_F(
+ ContextualTasksSidePanelCoordinatorInteractiveUiTest,
+ DISABLED_ComposeboxFocusOnBoundsUpdateWhenComposeboxHidden) {
+ SetUpTasks();
+ ASSERT_TRUE(ui_test_utils::BringBrowserWindowToFront(browser()));
+ ContextualTasksSidePanelCoordinator* coordinator = GetCoordinator();
+
+ DEFINE_LOCAL_ELEMENT_IDENTIFIER_VALUE(kSidePanelWebContentsId);
+
+ StateChange contextual_tasks_app_exists;
+ contextual_tasks_app_exists.type = StateChange::Type::kExists;
+ contextual_tasks_app_exists.where = {"contextual-tasks-app"};
+ contextual_tasks_app_exists.event = kElementExistsEvent;
+
+ StateChange frame_loaded;
+ frame_loaded.type = StateChange::Type::kExistsAndConditionTrue;
+ frame_loaded.where = {"contextual-tasks-app"};
+ frame_loaded.test_function = "(app) => !app.isFrameLoading";
+ frame_loaded.event = kFrameLoadedEvent;
+
+ StateChange composebox_focused;
+ composebox_focused.type = StateChange::Type::kExistsAndConditionTrue;
+ composebox_focused.where = {"contextual-tasks-app"};
+ composebox_focused.test_function =
+ "(app) => app.shadowRoot.activeElement && "
+ "app.shadowRoot.activeElement.id === 'composebox'";
+ composebox_focused.event = kComposeboxFocusedEvent;
+
+ RunTestSequence(
+ Do([&]() {
+ coordinator->Show(
+ false, omnibox::ChromeAimEntryPoint::UNKNOWN_AIM_ENTRY_POINT);
+ }),
+ WaitForShow(kContextualTasksSidePanelWebViewElementId),
+ InstrumentNonTabWebView(kSidePanelWebContentsId,
+ kContextualTasksSidePanelWebViewElementId),
+ FocusWebContents(kSidePanelWebContentsId),
+ WaitForStateChange(kSidePanelWebContentsId, contextual_tasks_app_exists),
+ Do([&]() {
+ content::WebContents* side_panel_contents =
+ coordinator->GetActiveWebContents();
+ ASSERT_NE(side_panel_contents, nullptr);
+ // Use Object.defineProperty to mock the app's state properties. This
+ // freezes the values for the duration of the test and prevents
+ // asynchronous Mojo callbacks or page-load event handlers from
+ // overwriting them in the background, which would cause flakiness.
+ EXPECT_TRUE(content::ExecJs(
+ side_panel_contents,
+ "(() => {"
+ " const app = document.querySelector('contextual-tasks-app');"
+ " Object.defineProperty(app, 'isErrorDialogVisible_', {"
+ " get() { return false; },"
+ " set() {}"
+ " });"
+ " Object.defineProperty(app, 'isAimEligible_', {"
+ " get() { return true; },"
+ " set() {}"
+ " });"
+ " Object.defineProperty(app, 'isZeroState_', {"
+ " get() { return false; },"
+ " set() {}"
+ " });"
+ " Object.defineProperty(app, 'isAiPage_', {"
+ " get() { return true; },"
+ " set() {}"
+ " });"
+ " Object.defineProperty(app, 'enableComposeboxJumpFix_', {"
+ " get() { return true; },"
+ " set() {}"
+ " });"
+ " Object.defineProperty(app, 'isInputHidden_', {"
+ " get() { return false; },"
+ " set() {}"
+ " });"
+ " Object.defineProperty(app, 'enableBasicMode_', {"
+ " get() { return false; },"
+ " set() {}"
+ " });"
+ " Object.defineProperty(app, 'inNlm_', {"
+ " get() { return false; },"
+ " set() {}"
+ " });"
+ " app.forcedComposeboxBounds_ = null;"
+ "})()"));
+ }),
+ WaitForStateChange(kSidePanelWebContentsId, frame_loaded), Do([&]() {
+ content::WebContents* side_panel_contents =
+ coordinator->GetActiveWebContents();
+ ASSERT_NE(side_panel_contents, nullptr);
+ EXPECT_EQ(
+ true,
+ content::EvalJs(
+ side_panel_contents,
+ "(() => {"
+ " const app = document.querySelector('contextual-tasks-app');"
+ " return app.isComposeboxHidden_();"
+ "})()"));
+ }),
+ Do([&]() {
+ content::WebContents* side_panel_contents =
+ coordinator->GetActiveWebContents();
+ ASSERT_NE(side_panel_contents, nullptr);
+ EXPECT_TRUE(content::ExecJs(
+ side_panel_contents,
+ "(() => {"
+ " const app = document.querySelector('contextual-tasks-app');"
+ " const mockRect = {top: 10, left: 10, width: 200, height: "
+ "50, right: 210, bottom: 60};"
+ " app.onInputPlateBoundsUpdateForTesting(mockRect, []);"
+ "})()"));
+ }),
+ WaitForStateChange(kSidePanelWebContentsId, composebox_focused),
+ Do([&]() {
+ content::WebContents* side_panel_contents =
+ coordinator->GetActiveWebContents();
+ ASSERT_NE(side_panel_contents, nullptr);
+ EXPECT_EQ(
+ false,
+ content::EvalJs(
+ side_panel_contents,
+ "(() => {"
+ " const app = document.querySelector('contextual-tasks-app');"
+ " return app.isComposeboxHidden_();"
+ "})()"));
+ }));
+}
+
} // namespace contextual_tasks
diff -Nru chromium-149.0.7827.114/chrome/browser/contextual_tasks/contextual_tasks_types.h chromium-149.0.7827.155/chrome/browser/contextual_tasks/contextual_tasks_types.h
--- chromium-149.0.7827.114/chrome/browser/contextual_tasks/contextual_tasks_types.h 1970-01-01 00:00:00.000000000 +0000
+++ chromium-149.0.7827.155/chrome/browser/contextual_tasks/contextual_tasks_types.h 2026-06-16 01:31:02.000000000 +0000
@@ -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.
+
+#ifndef CHROME_BROWSER_CONTEXTUAL_TASKS_CONTEXTUAL_TASKS_TYPES_H_
+#define CHROME_BROWSER_CONTEXTUAL_TASKS_CONTEXTUAL_TASKS_TYPES_H_
+
+#include "base/types/strong_alias.h"
+#include "base/types/token_type.h"
+#include "base/uuid.h"
+
+namespace contextual_tasks {
+
+// Uniquely identifies a Contextual Task.
+// It is implemented as a base::StrongAlias wrapping base::Uuid to enforce C++
+// type-safety and prevent mix-ups with window IDs, while retaining underlying
+// base::Uuid compatibility for storage and sync layers across components.
+// TODO(crbug.com/515502892): Move all task IDs to use this strongly typed
+// alias.
+using ContextualTaskId =
+ base::StrongAlias;
+
+// Uniquely identifies a guest window/webview being tracked by the Contextual
+// Tasks window tracker.
+// It is implemented as a base::TokenType wrapping base::UnguessableToken
+// because it is a virtual, run-time only bearer token used to securely
+// associate guest webview closure events from the WebUI back to their
+// browser-side tab.
+using ContextualWindowId = base::TokenType;
+
+} // namespace contextual_tasks
+
+#endif // CHROME_BROWSER_CONTEXTUAL_TASKS_CONTEXTUAL_TASKS_TYPES_H_
diff -Nru chromium-149.0.7827.114/chrome/browser/contextual_tasks/contextual_tasks_ui.cc chromium-149.0.7827.155/chrome/browser/contextual_tasks/contextual_tasks_ui.cc
--- chromium-149.0.7827.114/chrome/browser/contextual_tasks/contextual_tasks_ui.cc 2026-06-10 17:58:03.000000000 +0000
+++ chromium-149.0.7827.155/chrome/browser/contextual_tasks/contextual_tasks_ui.cc 2026-06-16 01:31:02.000000000 +0000
@@ -8,6 +8,7 @@
#include "base/check_deref.h"
#include "base/feature_list.h"
#include "base/functional/callback_helpers.h"
+#include "base/logging.h"
#include "base/memory/raw_ref.h"
#include "base/metrics/histogram_functions.h"
#include "base/strings/string_split.h"
@@ -500,6 +501,9 @@
"enableLockAndUnlockInputCapability",
contextual_tasks::ShouldEnableLockAndUnlockInputCapability());
source->AddBoolean("enableFileHint", contextual_tasks::GetEnableFileHint());
+ source->AddBoolean(
+ "windowTrackingEnabled",
+ contextual_tasks::GetIsContextualTasksWindowTrackingEnabled());
source->AddBoolean("enableComposeboxJumpFix",
contextual_tasks::GetEnableComposeboxJumpFix());
source->AddBoolean("roundedClipPathEnabled",
@@ -746,7 +750,7 @@
}
void ContextualTasksUI::OnInitComplete() {
- if (task_id_) {
+ if (task_id_ && ui_service_) {
ui_service_->OnWebUIReady(GetBrowser(), *task_id_,
web_ui()->GetWebContents());
}
@@ -959,7 +963,6 @@
void ContextualTasksUI::MoveTaskUiToNewTab() {
auto* browser = GetBrowser();
if (!task_id_.has_value()) {
- LOG(ERROR) << "Attempted to open in new tab with no valid task ID.";
return;
}
diff -Nru chromium-149.0.7827.114/chrome/browser/contextual_tasks/contextual_tasks_ui_browsertest.cc chromium-149.0.7827.155/chrome/browser/contextual_tasks/contextual_tasks_ui_browsertest.cc
--- chromium-149.0.7827.114/chrome/browser/contextual_tasks/contextual_tasks_ui_browsertest.cc 2026-06-10 17:58:03.000000000 +0000
+++ chromium-149.0.7827.155/chrome/browser/contextual_tasks/contextual_tasks_ui_browsertest.cc 2026-06-16 01:31:02.000000000 +0000
@@ -18,6 +18,7 @@
#include "chrome/browser/contextual_tasks/contextual_tasks_cookie_synchronizer.h"
#include "chrome/browser/contextual_tasks/contextual_tasks_panel_controller.h"
#include "chrome/browser/contextual_tasks/contextual_tasks_service_factory.h"
+#include "chrome/browser/contextual_tasks/contextual_tasks_types.h"
#include "chrome/browser/contextual_tasks/contextual_tasks_ui_service.h"
#include "chrome/browser/contextual_tasks/contextual_tasks_ui_service_delegate_desktop.h"
#include "chrome/browser/contextual_tasks/contextual_tasks_ui_service_factory.h"
@@ -93,6 +94,10 @@
MOCK_METHOD(void, SetInNlm, (bool in_nlm), (override));
MOCK_METHOD(void, OnAiPageStatusChanged, (bool), (override));
MOCK_METHOD(void,
+ OnWindowClosed,
+ (const contextual_tasks::ContextualWindowId& window_id),
+ (override));
+ MOCK_METHOD(void,
OnLensOverlayStateChanged,
(bool is_showing, bool maybe_show_overlay_hint_text),
(override));
diff -Nru chromium-149.0.7827.114/chrome/browser/contextual_tasks/contextual_tasks_ui_service.cc chromium-149.0.7827.155/chrome/browser/contextual_tasks/contextual_tasks_ui_service.cc
--- chromium-149.0.7827.114/chrome/browser/contextual_tasks/contextual_tasks_ui_service.cc 2026-06-10 17:58:03.000000000 +0000
+++ chromium-149.0.7827.155/chrome/browser/contextual_tasks/contextual_tasks_ui_service.cc 2026-06-16 01:31:02.000000000 +0000
@@ -6,6 +6,7 @@
#include
+#include "base/command_line.h"
#include "base/containers/adapters.h"
#include "base/feature_list.h"
#include "base/logging.h"
@@ -33,6 +34,9 @@
#include "chrome/browser/contextual_tasks/contextual_tasks_ui.h"
#include "chrome/browser/contextual_tasks/contextual_tasks_ui_interface.h"
#include "chrome/browser/contextual_tasks/contextual_tasks_utils.h"
+#include "chrome/browser/contextual_tasks/contextual_tasks_window_tracker.h"
+#include "chrome/browser/contextual_tasks/contextual_tasks_window_tracker_manager.h"
+#include "chrome/browser/contextual_tasks/guest_opener_user_data.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/search_engines/template_url_service_factory.h"
#include "chrome/browser/sessions/session_tab_helper_factory.h"
@@ -66,11 +70,16 @@
#include "components/strings/grit/components_strings.h"
#include "components/tabs/public/tab_interface.h"
#include "components/url_formatter/url_formatter.h"
+#include "content/public/browser/global_routing_id.h"
#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/render_process_host.h"
+#include "content/public/browser/site_instance.h"
#include "content/public/browser/web_contents.h"
#include "content/public/browser/web_ui.h"
#include "content/public/common/url_constants.h"
+#include "google_apis/gaia/gaia_urls.h"
#include "net/base/registry_controlled_domains/registry_controlled_domain.h"
#include "net/base/schemeful_site.h"
#include "net/base/url_util.h"
@@ -78,6 +87,7 @@
#include "third_party/omnibox_proto/chrome_aim_entry_point.pb.h"
#include "ui/base/page_transition_types.h"
#include "ui/base/window_open_disposition.h"
+#include "url/origin.h"
#if !BUILDFLAG(IS_ANDROID)
#include "chrome/browser/ui/lens/lens_media_link_handler.h"
@@ -95,14 +105,59 @@
namespace {
+// An allowlist of origins to allow account linking features to work with
+// Contextual Tasks. This list should only contain trusted domains, defined as:
+// must be a google first-party domain, strictly require postMessaging without a
+// safer workaround, and have been approved by Chrome security.
+// TODO(crbug.com/500717050): Consider moving these to GAIA URL constants or a
+// feature param.
+constexpr const char* const kAllowedHostsForGuestMessage[] = {
+ "myaccount.google.com",
+ "oauth-redirect.googleusercontent.com",
+ "payments.google.com",
+};
+
+bool IsAllowedOriginForGuestMessage(const url::Origin& origin) {
+ if (origin.opaque()) {
+ if (base::CommandLine::ForCurrentProcess()->HasSwitch(
+ "allow-opaque-origin-for-contextual-tasks-testing")) {
+ return true;
+ }
+ return false;
+ }
+ if (origin.scheme() != url::kHttpsScheme) {
+ return false;
+ }
+ const std::string& host = origin.host();
+
+ // Allow explicit trusted domains.
+ for (const char* allowed_host : kAllowedHostsForGuestMessage) {
+ if (host == allowed_host) {
+ return true;
+ }
+ }
+
+ // Allow GAIA host from GaiaUrls.
+ if (host == GaiaUrls::GetInstance()->gaia_origin().host()) {
+ return true;
+ }
+
+ // Allow internal corp domains for development.
+ if (origin.GetURL().DomainIs("corp.google.com")) {
+ return true;
+ }
+
+ return false;
+}
+
constexpr net::BackoffEntry::Policy
kIgnoreFirstErrorRequestAccessTokenBackoffPolicy = {
// Number of initial errors (in sequence) to ignore before applying
// exponential back-off rules.
- 1,
+ 2,
// Initial delay for exponential back-off in ms.
- 500,
+ 150,
// Factor by which the waiting time will be multiplied.
2,
@@ -112,7 +167,7 @@
0.2, // 20%
// Maximum amount of time we are willing to delay our request in ms.
- 10000, // 10 seconds.
+ 2000, // 2 seconds.
// Time to keep an entry from being discarded even when it
// has no significant state, -1 to never discard.
@@ -122,6 +177,10 @@
false,
};
+// Heuristic threshold under which an OAuth token fetch is classified as a
+// local cache hit.
+constexpr base::TimeDelta kOAuthCacheHitThreshold = base::Milliseconds(30);
+
constexpr char kAiPageHost[] = "https://google.com";
constexpr char kDebugParam[] = "deb";
constexpr char kDebugNoCobrowseValue[] = "nocobrowse1";
@@ -196,7 +255,9 @@
request_access_token_backoff_(
&kIgnoreFirstErrorRequestAccessTokenBackoffPolicy),
cookie_synchronizer_(std::move(cookie_synchronizer)),
- eligibility_manager_(std::move(eligibility_manager)) {
+ eligibility_manager_(std::move(eligibility_manager)),
+ tracker_manager_(
+ std::make_unique(profile)) {
if (eligibility_manager_ && contextual_tasks::ShouldEnableCookiePrefetch()) {
eligibility_subscription_ =
eligibility_manager_->RegisterEligibilityChangedCallback(
@@ -218,7 +279,12 @@
for (auto& observer : observers_) {
observer.OnContextualTasksUiServiceShutdown(this);
}
+ tracker_manager_.reset();
weak_ptr_factory_.InvalidateWeakPtrs();
+ if (current_oauth_fetch_trigger_.has_value()) {
+ RecordOAuthMetrics(GoogleServiceAuthError::CreateRequestCanceled(),
+ signin::AccessTokenInfo());
+ }
access_token_fetcher_.reset();
token_refresh_timer_.Stop();
}
@@ -240,6 +306,10 @@
<< url;
CHECK(contextual_tasks_service_);
+ // Starts the access token fetch now so it happens in parallel to the WebUI
+ // initializing.
+ StartAccessTokenFetch(OAuthFetchTrigger::kAimNavigationInterception);
+
// Get the session handle from the source web contents, if provided, to
// propagate context from the source WebUI.
std::unique_ptr
@@ -294,7 +364,7 @@
controller->Show();
contextual_task_web_contents = controller->GetActiveWebContents();
}
- } else if (!is_to_new_tab) {
+ } else if (!is_to_new_tab && source_tab) {
OMNIBOX_LOG("nav_trace")
<< "ContextualTasks navigation trace: "
"OnNavigationToAiPageIntercepted loading in source tab";
@@ -361,20 +431,20 @@
token_refresh_timer_.Start(
FROM_HERE, delay,
base::BindOnce(&ContextualTasksUiService::StartAccessTokenFetch,
- weak_ptr_factory_.GetWeakPtr()));
+ weak_ptr_factory_.GetWeakPtr(), std::nullopt));
}
return;
}
- // TODO(crbug.com/470109970): If at this point the token is empty, the error
- // is not transient and a blocking error needs to shown to the user to
- // prevent the user continuing to interact with broken UI.
+ RecordOAuthMetrics(error, access_token_info);
+ request_access_token_backoff_.Reset();
OMNIBOX_LOG("nav_trace")
<< "ContextualTasks navigation trace: OnOAuthTokenReceived "
"non-transient error, running callbacks with empty token";
RunPendingAccessTokenCallbacks("");
return;
}
+ RecordOAuthMetrics(error, access_token_info);
request_access_token_backoff_.Reset();
OMNIBOX_LOG("nav_trace")
<< "ContextualTasks navigation trace: OnOAuthTokenReceived "
@@ -382,6 +452,70 @@
RunPendingAccessTokenCallbacks(access_token_info.token);
}
+void ContextualTasksUiService::RecordOAuthMetrics(
+ GoogleServiceAuthError error,
+ signin::AccessTokenInfo access_token_info) {
+ if (!current_oauth_fetch_trigger_.has_value() ||
+ fetch_start_time_.is_null()) {
+ return;
+ }
+
+ base::TimeDelta latency = base::TimeTicks::Now() - fetch_start_time_;
+ int tries = request_access_token_backoff_.failure_count() + 1;
+ bool success = (error.state() == GoogleServiceAuthError::NONE);
+
+ bool was_cached = false;
+ if (success) {
+ // Heuristic for cache hit.
+ was_cached = (latency < kOAuthCacheHitThreshold);
+ }
+
+ // Record to .All
+ base::UmaHistogramMediumTimes("ContextualTasks.OAuth.Latency.All", latency);
+ base::UmaHistogramExactLinear("ContextualTasks.OAuth.TriesCount.All", tries,
+ 10);
+ if (success) {
+ base::UmaHistogramExactLinear(
+ "ContextualTasks.OAuth.TriesCountBeforeSuccess.All", tries, 10);
+ } else {
+ base::UmaHistogramExactLinear(
+ "ContextualTasks.OAuth.TriesCountBeforeFailure.All", tries, 10);
+ }
+ base::UmaHistogramBoolean("ContextualTasks.OAuth.Success.All", success);
+ if (success) {
+ base::UmaHistogramBoolean("ContextualTasks.OAuth.WasCached.All",
+ was_cached);
+ }
+
+ // Record to .AimNavigation if applicable
+ if (*current_oauth_fetch_trigger_ ==
+ OAuthFetchTrigger::kAimNavigationInterception) {
+ base::UmaHistogramMediumTimes("ContextualTasks.OAuth.Latency.AimNavigation",
+ latency);
+ base::UmaHistogramExactLinear(
+ "ContextualTasks.OAuth.TriesCount.AimNavigation", tries, 10);
+ if (success) {
+ base::UmaHistogramExactLinear(
+ "ContextualTasks.OAuth.TriesCountBeforeSuccess.AimNavigation", tries,
+ 10);
+ } else {
+ base::UmaHistogramExactLinear(
+ "ContextualTasks.OAuth.TriesCountBeforeFailure.AimNavigation", tries,
+ 10);
+ }
+ base::UmaHistogramBoolean("ContextualTasks.OAuth.Success.AimNavigation",
+ success);
+ if (success) {
+ base::UmaHistogramBoolean("ContextualTasks.OAuth.WasCached.AimNavigation",
+ was_cached);
+ }
+ }
+
+ // Reset trigger.
+ current_oauth_fetch_trigger_ = std::nullopt;
+ fetch_start_time_ = base::TimeTicks();
+}
+
void ContextualTasksUiService::ShowOauthErrorDialogForWebContents(
base::WeakPtr web_contents) {
content::WebUI* webui = web_contents->GetWebUI();
@@ -432,52 +566,112 @@
is_eligible_ = eligible;
}
-tabs::TabInterface* ContextualTasksUiService::MaybeFocusExistingOpenTab(
- const GURL& url,
- TabListInterface* tab_list,
- const base::Uuid& task_id) {
+#if BUILDFLAG(ENABLE_PDF)
+// Checks if the current URL is a scroll citation on the active tab. Returns
+// true if this is a scroll citation and should be scrolled using the
+// PDFDocumentHelper rather than opening normally.
+static bool IsPdfCitation(const GURL& url,
+ tabs::TabInterface* active_tab,
+ const base::Uuid& task_id,
+ ContextualTasksService* service) {
+ if (!GetIsContextualTasksPdfCitationsEnabled() || !active_tab) {
+ return false;
+ }
+ content::WebContents* web_contents = active_tab->GetContents();
+ std::optional task = service->GetContextualTaskForTab(
+ SessionTabHelper::IdForTab(web_contents));
+ if (!task || task->GetTaskId() != task_id) {
+ return false;
+ }
+ const GURL& page_url = web_contents->GetLastCommittedURL();
+ if (page_url.GetScheme() != url.GetScheme() ||
+ page_url.GetHost() != url.GetHost() ||
+ page_url.GetPath() != url.GetPath() ||
+ page_url.GetQuery() != url.GetQuery() || url.GetRef().empty()) {
+ return false;
+ }
+ return pdf::PDFDocumentHelper::MaybeGetForWebContents(web_contents) !=
+ nullptr;
+}
+#endif
+
+#if !BUILDFLAG(IS_ANDROID)
+// Checks if the current URL is a video citation on the active tab. Returns
+// true if this is a video citation and should be handled by scrolling the
+// video to the timestamp in the URL.
+static bool IsVideoCitation(const GURL& url,
+ tabs::TabInterface* active_tab,
+ const base::Uuid& task_id,
+ ContextualTasksService* service) {
+ if (!base::FeatureList::IsEnabled(kContextualTasksVideoCitations) ||
+ !active_tab) {
+ return false;
+ }
+ content::WebContents* web_contents = active_tab->GetContents();
+ std::optional task = service->GetContextualTaskForTab(
+ SessionTabHelper::IdForTab(web_contents));
+ if (task && task->GetTaskId() == task_id) {
+ return url.GetHost() == "www.youtube.com";
+ }
+ return false;
+}
+#endif
+
+// Checks if the current URL is already open in the tab list. If so, returns a
+// pointer to that tab. If there is not existing tab with the given url, returns
+// nullptr.
+static tabs::TabInterface* GetExistingTab(const GURL& url,
+ TabListInterface* tab_list,
+ const base::Uuid& task_id,
+ ContextualTasksService* service) {
+ if (!tab_list) {
+ return nullptr;
+ }
GURL url_no_fragments =
shared_highlighting::RemoveFragmentSelectorDirectives(url);
for (int i = 0; i < tab_list->GetTabCount(); ++i) {
content::WebContents* web_contents = tab_list->GetTab(i)->GetContents();
- std::optional task =
- contextual_tasks_service_->GetContextualTaskForTab(
- SessionTabHelper::IdForTab(web_contents));
- // Remove any text selection directives when trying to match an existing
- // URL. The directives don't meaningfully change the page content, so it's
- // ok to match them.
+ std::optional task = service->GetContextualTaskForTab(
+ SessionTabHelper::IdForTab(web_contents));
GURL tab_url_no_fragments =
shared_highlighting::RemoveFragmentSelectorDirectives(
web_contents->GetLastCommittedURL());
if (tab_url_no_fragments == url_no_fragments && task &&
task->GetTaskId() == task_id) {
- tab_list->ActivateTab(tab_list->GetTab(i)->GetHandle());
return tab_list->GetTab(i);
}
}
return nullptr;
}
+// Moves the tab focus to the tab with `url` already open. If a tab is focused,
+// returns a pointer to that tab. If no such tab exists and no focus was
+// changed, this method returns nullptr.
+tabs::TabInterface* ContextualTasksUiService::MaybeFocusExistingOpenTab(
+ const GURL& url,
+ TabListInterface* tab_list,
+ const base::Uuid& task_id) {
+ tabs::TabInterface* existing_tab =
+ GetExistingTab(url, tab_list, task_id, contextual_tasks_service_);
+ if (existing_tab) {
+ tab_list->ActivateTab(existing_tab->GetHandle());
+ }
+ return existing_tab;
+}
+
bool ContextualTasksUiService::MaybeHandleVideoCitation(
const GURL& url,
tabs::TabInterface* tab,
const base::Uuid& task_id) {
#if !BUILDFLAG(IS_ANDROID)
- if (!base::FeatureList::IsEnabled(kContextualTasksVideoCitations)) {
+ if (!IsVideoCitation(url, tab, task_id, contextual_tasks_service_)) {
return false;
}
- if (tab) {
- content::WebContents* web_contents = tab->GetContents();
- std::optional task =
- contextual_tasks_service_->GetContextualTaskForTab(
- SessionTabHelper::IdForTab(web_contents));
- if (task && task->GetTaskId() == task_id) {
- auto handler = CreateMediaLinkHandler(web_contents);
- if (handler && handler->MaybeReplaceNavigation(url)) {
- return true;
- }
- }
+ content::WebContents* web_contents = tab->GetContents();
+ auto handler = CreateMediaLinkHandler(web_contents);
+ if (handler && handler->MaybeReplaceNavigation(url)) {
+ return true;
}
#endif
return false;
@@ -488,45 +682,11 @@
tabs::TabInterface* tab,
const base::Uuid& task_id) {
#if BUILDFLAG(ENABLE_PDF)
- if (!GetIsContextualTasksPdfCitationsEnabled()) {
- return false;
- }
-
- if (!tab) {
+ if (!IsPdfCitation(url, tab, task_id, contextual_tasks_service_)) {
return false;
}
content::WebContents* web_contents = tab->GetContents();
- std::optional task =
- contextual_tasks_service_->GetContextualTaskForTab(
- SessionTabHelper::IdForTab(web_contents));
-
- if (!task || task->GetTaskId() != task_id) {
- return false;
- }
-
- const GURL& page_url = web_contents->GetLastCommittedURL();
- // The citation URL must match the current page URL (ignoring the fragment).
- if (page_url.GetScheme() != url.GetScheme() ||
- page_url.GetHost() != url.GetHost() ||
- page_url.GetPath() != url.GetPath() ||
- page_url.GetQuery() != url.GetQuery()) {
- return false;
- }
-
- // The citation URL must have a fragment. The fragment is not parsed here
- // because the PDF viewer supports various open parameters (page, zoom, view,
- // etc.).
- if (url.GetRef().empty()) {
- return false;
- }
-
- auto* pdf_helper =
- pdf::PDFDocumentHelper::MaybeGetForWebContents(web_contents);
- if (!pdf_helper) {
- return false;
- }
-
// Dispatch an event to the PDF viewer to update the viewport.
pdf_extension_util::DispatchShouldUpdateViewportEvent(
web_contents->GetPrimaryMainFrame(), url);
@@ -540,7 +700,8 @@
const GURL& url,
base::Uuid task_id,
base::WeakPtr tab,
- base::WeakPtr browser) {
+ base::WeakPtr browser,
+ const url::Origin& initiator_origin) {
OMNIBOX_LOG("nav_trace")
<< "ContextualTasks navigation trace: OnThreadLinkClicked called "
"for URL: "
@@ -578,12 +739,31 @@
}
}
+ content::WebContents::CreateParams create_params(profile_);
+ std::unique_ptr message_proxy_web_contents;
+
+ if (GetIsContextualTasksWindowTrackingEnabled() && tracker_manager_) {
+ message_proxy_web_contents =
+ CreateMessageProxyWebContents(initiator_origin);
+ create_params.opener_render_process_id =
+ message_proxy_web_contents->GetPrimaryMainFrame()
+ ->GetProcess()
+ ->GetID()
+ .value();
+ create_params.opener_render_frame_id =
+ message_proxy_web_contents->GetPrimaryMainFrame()->GetRoutingID();
+ }
+
std::unique_ptr new_contents =
- content::WebContents::Create(
- content::WebContents::CreateParams(profile_));
+ content::WebContents::Create(create_params);
content::WebContents* new_contents_ptr = new_contents.get();
CreateSessionServiceTabHelper(new_contents_ptr);
+ if (GetIsContextualTasksWindowTrackingEnabled() && tracker_manager_) {
+ tracker_manager_->MatchAndAssociatePendingTracker(
+ url, new_contents_ptr, std::move(message_proxy_web_contents));
+ }
+
// Copy navigation entries from the current tab to the new tab to support back
// button navigation. See crbug.com/467042329 for detail.
if (tab) {
@@ -594,13 +774,6 @@
&tab->GetContents()->GetController(), /*needs_reload=*/false);
}
- OMNIBOX_LOG("nav_trace")
- << "ContextualTasks navigation trace: OnThreadLinkClicked "
- "loading URL: "
- << url;
- new_contents->GetController().LoadURLWithParams(
- content::NavigationController::LoadURLParams(url));
-
// If the source contents is the panel, open the AI page in a new foreground
// tab.
// TODO(crbug.com/458139141): Split this API so we can assume `tab` non-null.
@@ -626,11 +799,21 @@
active_tab_index + 1, std::move(new_contents),
/*should_pin=*/false,
/*group=*/active_tab ? active_tab->GetGroup() : std::nullopt);
+
+ // Set the opener relationship so that if the new tab is closed, focus
+ // returns to the active tab.
if (active_tab) {
tab_list->SetOpenerForTab(new_tab->GetHandle(),
active_tab->GetHandle());
}
tab_list->ActivateTab(new_tab->GetHandle());
+
+ OMNIBOX_LOG("nav_trace")
+ << "ContextualTasks navigation trace: OnThreadLinkClicked "
+ "loading URL in inserted tab: "
+ << url;
+ new_tab->GetContents()->GetController().LoadURLWithParams(
+ content::NavigationController::LoadURLParams(url));
} else {
OMNIBOX_LOG("nav_trace")
<< "ContextualTasks navigation trace: OnThreadLinkClicked "
@@ -679,10 +862,20 @@
AssociateWebContentsToTask(new_contents_ptr, task_id);
- DCHECK(new_tab);
+ // Set the opener relationship so that if the new tab is closed, focus
+ // returns to the source tab.
+ tab_list->SetOpenerForTab(new_tab->GetHandle(), tab->GetHandle());
+
tab_list->ActivateTab(new_tab->GetHandle());
CHECK(new_contents_ptr == tab_list->GetActiveTab()->GetContents());
+ OMNIBOX_LOG("nav_trace")
+ << "ContextualTasks navigation trace: OnThreadLinkClicked "
+ "loading URL in inserted tab: "
+ << url;
+ new_tab->GetContents()->GetController().LoadURLWithParams(
+ content::NavigationController::LoadURLParams(url));
+
// Detach the WebContents from tab.
std::unique_ptr contextual_task_contents =
tab_list->DetachWebContents(tab->GetHandle());
@@ -773,6 +966,36 @@
text_directives);
}
+bool ContextualTasksUiService::ShouldAllowNewTabOpen(
+ const GURL& url,
+ BrowserWindowInterface* browser,
+ const base::Uuid& task_id) {
+ TabListInterface* tab_list =
+ browser ? TabListInterface::From(browser) : nullptr;
+#if BUILDFLAG(ENABLE_PDF) || !BUILDFLAG(IS_ANDROID)
+ tabs::TabInterface* active_tab =
+ tab_list ? tab_list->GetActiveTab() : nullptr;
+#endif
+
+#if BUILDFLAG(ENABLE_PDF)
+ if (IsPdfCitation(url, active_tab, task_id, contextual_tasks_service_)) {
+ return false;
+ }
+#endif
+
+#if !BUILDFLAG(IS_ANDROID)
+ if (IsVideoCitation(url, active_tab, task_id, contextual_tasks_service_)) {
+ return false;
+ }
+#endif
+
+ if (GetExistingTab(url, tab_list, task_id, contextual_tasks_service_)) {
+ return false;
+ }
+
+ return true;
+}
+
void ContextualTasksUiService::InitializeTaskInSidePanel(
content::WebContents* web_contents,
const base::Uuid& task_id,
@@ -787,12 +1010,12 @@
}
void ContextualTasksUiService::OnNonThreadNavigationInTab(
- const GURL& url,
+ content::OpenURLParams url_params,
base::WeakPtr tab) {
OMNIBOX_LOG("nav_trace")
<< "ContextualTasks navigation trace: OnNonThreadNavigationInTab "
"called for URL: "
- << url;
+ << url_params.url;
if (!tab || !tab->GetContents()) {
OMNIBOX_LOG("nav_trace")
<< "ContextualTasks navigation trace: OnNonThreadNavigationInTab "
@@ -800,8 +1023,9 @@
return;
}
- content::NavigationController::LoadURLParams params(url);
+ content::NavigationController::LoadURLParams params(url_params);
params.transition_type = ::ui::PAGE_TRANSITION_AUTO_TOPLEVEL;
+ params.frame_tree_node_id = content::FrameTreeNodeId();
tab->GetContents()->GetController().LoadURLWithParams(params);
}
@@ -819,14 +1043,18 @@
content::OpenURLParams url_params,
content::WebContents* source_contents,
bool is_from_embedded_page,
- bool is_to_new_tab,
+ bool from_can_create_window,
bool is_same_site_or_from_ui,
- bool is_mobile_ua) {
+ bool is_mobile_ua,
+ const std::optional& initiator_origin,
+ const std::optional&
+ initiator_frame_token,
+ const blink::mojom::WindowFeatures& window_features) {
return HandleNavigationImpl(
std::move(url_params), source_contents,
tabs::TabInterface::MaybeGetFromContents(source_contents),
- is_from_embedded_page, is_to_new_tab, is_same_site_or_from_ui,
- is_mobile_ua);
+ is_from_embedded_page, from_can_create_window, is_same_site_or_from_ui,
+ is_mobile_ua, initiator_origin, initiator_frame_token, window_features);
}
void ContextualTasksUiService::GetAccessToken(
@@ -836,25 +1064,33 @@
<< "ContextualTasks navigation trace: GetAccessToken called";
pending_access_token_callbacks_.emplace_back(std::move(callback),
web_contents);
+ StartAccessTokenFetch(OAuthFetchTrigger::kFetchRequest);
+}
+
+void ContextualTasksUiService::StartAccessTokenFetch(
+ std::optional trigger) {
+ OMNIBOX_LOG("nav_trace")
+ << "ContextualTasks navigation trace: StartAccessTokenFetch called";
// If a request is already in progress, or we are waiting to retry, do
// nothing.
if (access_token_fetcher_ || token_refresh_timer_.IsRunning()) {
OMNIBOX_LOG("nav_trace")
- << "ContextualTasks navigation trace: GetAccessToken returning "
+ << "ContextualTasks navigation trace: StartAccessTokenFetch returning "
"early because fetch is in progress or waiting to retry";
return;
}
- OMNIBOX_LOG("nav_trace")
- << "ContextualTasks navigation trace: GetAccessToken starting "
- "access token fetch";
- StartAccessTokenFetch();
-}
+ if (trigger.has_value() && !current_oauth_fetch_trigger_.has_value()) {
+ current_oauth_fetch_trigger_ = trigger;
+ fetch_start_time_ = base::TimeTicks::Now();
+ base::UmaHistogramBoolean("ContextualTasks.OAuth.Start.All", true);
+ if (*trigger == OAuthFetchTrigger::kAimNavigationInterception) {
+ base::UmaHistogramBoolean("ContextualTasks.OAuth.Start.AimNavigation",
+ true);
+ }
+ }
-void ContextualTasksUiService::StartAccessTokenFetch() {
- OMNIBOX_LOG("nav_trace")
- << "ContextualTasks navigation trace: StartAccessTokenFetch called";
token_refresh_timer_.Stop();
if (!identity_manager_ ||
@@ -862,6 +1098,10 @@
OMNIBOX_LOG("nav_trace")
<< "ContextualTasks navigation trace: StartAccessTokenFetch "
"returning early due to no primary account";
+ if (current_oauth_fetch_trigger_.has_value()) {
+ RecordOAuthMetrics(GoogleServiceAuthError::CreateAccountNotFound(),
+ signin::AccessTokenInfo());
+ }
RunPendingAccessTokenCallbacks("");
return;
}
@@ -879,14 +1119,91 @@
signin::AccessTokenFetcher::Mode::kWaitUntilRefreshTokenAvailable);
}
-void ContextualTasksUiService::OnShareUrlNavigation(const GURL& url) {
+std::unique_ptr
+ContextualTasksUiService::CreateMessageProxyWebContents(
+ const url::Origin& origin) {
+ content::WebContents::CreateParams dummy_params(profile_);
+ dummy_params.site_instance =
+ content::SiteInstance::CreateForURL(profile_, origin.GetURL());
+ auto message_proxy_web_contents = content::WebContents::Create(dummy_params);
+
+ GuestOpenerUserData::CreateForWebContents(message_proxy_web_contents.get());
+
+ content::NavigationController::LoadURLParams load_params(GURL("about:blank"));
+ // A CHECK will fail if setting the origin to an invalid URL.
+ if (origin.GetURL().is_valid()) {
+ load_params.initiator_origin = origin;
+ load_params.initiator_base_url = origin.GetURL();
+ }
+ message_proxy_web_contents->GetController().LoadURLWithParams(load_params);
+ return message_proxy_web_contents;
+}
+
+void ContextualTasksUiService::OpenUrl(
+ const content::OpenURLParams& url_params,
+ const blink::mojom::WindowFeatures& window_features,
+ BrowserWindowInterface* browser) {
+ const GURL& url = url_params.url;
OMNIBOX_LOG("nav_trace")
- << "ContextualTasks navigation trace: OnShareUrlNavigation called "
+ << "ContextualTasks navigation trace: OpenUrl called "
"for URL: "
- << url;
- NavigateParams params(profile_, url, ui::PAGE_TRANSITION_AUTO_TOPLEVEL);
- params.disposition = WindowOpenDisposition::NEW_FOREGROUND_TAB;
- Navigate(¶ms);
+ << url << ", disposition: " << static_cast(url_params.disposition);
+
+ NavigateParams nav_params(profile_, url, url_params.transition);
+ nav_params.FillNavigateParamsFromOpenURLParams(url_params);
+
+ // Browser and tab index have no equivalent in OpenURLParams, so add them to
+ // ensure the tab opens in the right tab strip position.
+ if (browser) {
+ nav_params.browser = browser;
+ TabListInterface* tab_list = TabListInterface::From(browser);
+ if (tab_list) {
+ nav_params.tabstrip_index = tab_list->GetActiveIndex() + 1;
+ }
+ }
+ // Reset frame_tree_node_id to avoid targeting the source frame when opening
+ // a new tab/window.
+ nav_params.frame_tree_node_id = content::FrameTreeNodeId();
+
+ // Apply window features (bounds).
+ nav_params.window_features = window_features;
+ const auto& bounds = nav_params.window_features.bounds;
+ OMNIBOX_LOG("nav_trace") << "OpenUrl: applied window features: bounds=["
+ << bounds.x() << "," << bounds.y() << " "
+ << bounds.width() << "x" << bounds.height() << "]";
+
+ std::unique_ptr message_proxy_web_contents;
+ if (GetIsContextualTasksWindowTrackingEnabled() && tracker_manager_ &&
+ base::FeatureList::IsEnabled(kAimTriggeredThreadLinks) &&
+ url_params.initiator_origin.has_value()) {
+ OMNIBOX_LOG("nav_trace") << "ContextualTasks navigation trace: OpenUrl "
+ "setting opener for initiator_origin: "
+ << url_params.initiator_origin->Serialize();
+
+ // Create the dummy contents to be used as opener.
+ message_proxy_web_contents =
+ CreateMessageProxyWebContents(*url_params.initiator_origin);
+
+ nav_params.opener = message_proxy_web_contents->GetPrimaryMainFrame();
+ }
+
+ // Perform the navigation
+ Navigate(&nav_params);
+
+ // Grab the web contents that was associated with tis navigation and begin
+ // tracking it.
+ content::WebContents* new_contents_ptr =
+ nav_params.navigated_or_inserted_contents;
+
+ if (new_contents_ptr) {
+ if (GetIsContextualTasksWindowTrackingEnabled() && tracker_manager_) {
+ tracker_manager_->MatchAndAssociatePendingTracker(
+ url, new_contents_ptr, std::move(message_proxy_web_contents));
+ }
+ } else {
+ OMNIBOX_LOG("nav_trace") << "ContextualTasks navigation trace: "
+ "OpenUrl failed to get new WebContents";
+ }
}
bool ContextualTasksUiService::HandleNavigationImpl(
@@ -894,13 +1211,24 @@
content::WebContents* source_contents,
tabs::TabInterface* tab,
bool is_from_embedded_page,
- bool is_to_new_tab,
+ bool from_can_create_window,
bool is_same_site_or_from_ui,
- bool is_mobile_ua) {
+ bool is_mobile_ua,
+ const std::optional& initiator_origin,
+ const std::optional&
+ initiator_frame_token,
+ const blink::mojom::WindowFeatures& window_features) {
OMNIBOX_LOG("nav_trace")
<< "ContextualTasks navigation trace: HandleNavigationImpl called "
"for URL: "
- << url_params.url;
+ << url_params.url << ", is_from_embedded_page=" << is_from_embedded_page
+ << ", from_can_create_window=" << from_can_create_window
+ << ", has_tab=" << (tab != nullptr)
+ << ", disposition=" << static_cast(url_params.disposition)
+ << ", initiator_origin="
+ << (url_params.initiator_origin.has_value()
+ ? url_params.initiator_origin->Serialize()
+ : "null");
// Make sure the user is eligible to use the feature before attempting to
// intercept.
if (!eligibility_manager_ ||
@@ -1079,21 +1407,70 @@
ui::PageTransitionCoreTypeIs(page_transition,
ui::PAGE_TRANSITION_RELOAD));
+ // Retrieve the stored open url params from the pending tracker. If found, use
+ // those params for this navigation. This is required as the second call to
+ // HandleNavigationImpl after CanCreateWindow has the navigation params for
+ // the navigation in the opened window, rather than the original params that
+ // started the chain of naivgations.
+ ContextualTasksWindowTracker* pending_tracker =
+ (GetIsContextualTasksWindowTrackingEnabled() && tracker_manager_)
+ ? tracker_manager_->GetPendingTracker(url_params.url, source_contents)
+ : nullptr;
+ if (pending_tracker && pending_tracker->open_url_params()) {
+ // When the navigation goes through CanCreateWindow, the first navigation
+ // has the correct url params to trigger the new window, where as the second
+ // has the correct initiator and other url params. Therefore, copy the
+ // disposition and other params that dictate where the navigation goes into
+ // the url params for the second navigation to make sure the navigation
+ // opens in the correct place.
+ const auto& stored_params = *pending_tracker->open_url_params();
+ url_params.disposition = stored_params.disposition;
+ url_params.user_gesture = stored_params.user_gesture;
+ url_params.referrer = stored_params.referrer;
+ url_params.transition = stored_params.transition;
+ OMNIBOX_LOG("nav_trace")
+ << "ContextualTasks navigation trace: HandleNavigationImpl "
+ "overriding url_params with stored params from CanCreateWindow";
+ }
+ // Determine if this is a navigation to a new tab. Its considered new tab
+ // if the disposition will be a new tab/window, or if there is a tracked
+ // window for this navigation. The tracked window covers cases that go through
+ // CanCreateWindow which doesn't get intercepted, then ends up here without
+ // the disposition.
+ bool is_to_new_tab =
+ from_can_create_window || source_contents->GetOpener() != nullptr ||
+ url_params.disposition == WindowOpenDisposition::NEW_FOREGROUND_TAB ||
+ url_params.disposition == WindowOpenDisposition::NEW_BACKGROUND_TAB ||
+ url_params.disposition == WindowOpenDisposition::NEW_WINDOW ||
+ (pending_tracker != nullptr);
+ OMNIBOX_LOG("nav_trace")
+ << "ContextualTasks navigation trace: HandleNavigationImpl "
+ "is_to_new_tab="
+ << is_to_new_tab;
+
// Intercept any navigation where the wrapping WebContents is the WebUI host
// unless it is the embedded page.
if ((is_from_embedded_page || is_forward_link_navigation) &&
IsContextualTasksUrl(source_contents->GetLastCommittedURL())) {
- if (IsShareUrl(url_params.url)) {
+ // If aim is telling the browser what to do with links, the share case
+ // doesn't explicitly need to be handled. If aim doesn't communicate this,
+ // handle share links specifically to avoid ambiguity with other special-
+ // case, host-based navigation.
+ if (!base::FeatureList::IsEnabled(kAimTriggeredThreadLinks) &&
+ IsShareUrl(url_params.url)) {
OMNIBOX_LOG("nav_trace")
<< "ContextualTasks navigation trace: HandleNavigationImpl "
- "posting OnShareUrlNavigation";
+ "posting OpenUrl";
// Since the web content will no longer be hosted in the side panel, make
// sure to remove the param that makes the page render for it.
+ content::OpenURLParams new_url_params = url_params;
+ new_url_params.url = lens::RemoveSidePanelURLParameters(url_params.url);
+ new_url_params.disposition = WindowOpenDisposition::NEW_FOREGROUND_TAB;
base::SequencedTaskRunner::GetCurrentDefault()->PostTask(
- FROM_HERE,
- base::BindOnce(&ContextualTasksUiService::OnShareUrlNavigation,
- weak_ptr_factory_.GetWeakPtr(),
- lens::RemoveSidePanelURLParameters(url_params.url)));
+ FROM_HERE, base::BindOnce(&ContextualTasksUiService::OpenUrl,
+ weak_ptr_factory_.GetWeakPtr(),
+ std::move(new_url_params),
+ blink::mojom::WindowFeatures(), browser));
return true;
}
@@ -1139,7 +1516,7 @@
FROM_HERE,
base::BindOnce(
&ContextualTasksUiService::OnNonThreadNavigationInTab,
- weak_ptr_factory_.GetWeakPtr(), url_params.url,
+ weak_ptr_factory_.GetWeakPtr(), std::move(url_params),
tab->GetWeakPtr()));
return true;
} else {
@@ -1175,18 +1552,155 @@
}
}
- OMNIBOX_LOG("nav_trace")
- << "ContextualTasks navigation trace: HandleNavigationImpl "
- "posting OnThreadLinkClicked";
- // This needs to be posted in case the called method triggers a navigation
- // in the same WebContents, invalidating the nav handle used up the chain.
- base::SequencedTaskRunner::GetCurrentDefault()->PostTask(
- FROM_HERE,
- base::BindOnce(&ContextualTasksUiService::OnThreadLinkClicked,
- weak_ptr_factory_.GetWeakPtr(), url_params.url, task_id,
- tab ? tab->GetWeakPtr() : nullptr,
- browser ? browser->GetWeakPtr() : nullptr));
- return true;
+ // If this is a navigation CanCreateWindow, check to see if this
+ // navigation should open in a new tab, or needs to be cancelled. If the
+ // former, return true to allow the window to be created. Afterwards, the
+ // new tab will be created and the navigation throttle will be called
+ // again, which will then handle the `OnThreadLinkClick` via the call
+ // below. Allowing the window to open is a requirement to allow
+ // `window.open` calls to receive a Window object.
+ if (GetIsContextualTasksWindowTrackingEnabled() && from_can_create_window &&
+ ShouldAllowNewTabOpen(url_params.url, browser, task_id)) {
+ OMNIBOX_LOG("nav_trace")
+ << "ContextualTasks navigation trace: HandleNavigationImpl "
+ "allowing natural opening for new tab";
+
+ if (tracker_manager_) {
+ // Create a tracker to associate this navigation with the task.
+ // Store the initiator's RFH ID to prevent matching different contents.
+ // At this point, `source_contents` is known to be the WebUI page. So we
+ // can pass `source_contents->GetWeakPtr()` as the webui_contents.
+ // Additionally, initiator_frame_token is used for tracking, so if its
+ // missing, fallback to the WebUI primary frame token.
+ auto tracker = std::make_unique(
+ ContextualTaskId(task_id), url_params.url,
+ initiator_frame_token.has_value()
+ ? initiator_frame_token.value()
+ : source_contents->GetPrimaryMainFrame()->GetGlobalFrameToken(),
+ /*webui_contents=*/source_contents->GetWeakPtr(),
+ base::BindOnce(&ContextualTasksUiService::RemoveWindowTracker,
+ weak_ptr_factory_.GetWeakPtr()));
+ tracker->SetOpenURLParams(url_params);
+ tracker->SetWindowFeatures(window_features);
+ OMNIBOX_LOG("window_tracker")
+ << "Stored window features for URL " << url_params.url.spec()
+ << ": bounds=[" << window_features.bounds.x() << ","
+ << window_features.bounds.y() << " "
+ << window_features.bounds.width() << "x"
+ << window_features.bounds.height() << "]";
+ tracker_manager_->AddTracker(std::move(tracker));
+ tabs::TabInterface* source_tab =
+ tabs::TabInterface::MaybeGetFromContents(source_contents);
+ if (source_tab) {
+ BrowserWindowInterface* current_browser =
+ source_tab->GetBrowserWindowInterface();
+ if (current_browser) {
+ TabListInterface* tab_list =
+ TabListInterface::From(current_browser);
+ if (tab_list) {
+ tracker_manager_->ObserveTabList(tab_list);
+ }
+ }
+ }
+ }
+ return false;
+ }
+
+ std::vector inner_contents =
+ source_contents->GetInnerWebContents();
+ bool is_nav_from_embedded_non_ai_page = false;
+ // For this feature there should only ever be a single inner WebContents if
+ // one exists at all.
+ if (inner_contents.size() > 0) {
+ is_nav_from_embedded_non_ai_page =
+ is_from_embedded_page &&
+ !IsAiUrl(inner_contents[0]->GetLastCommittedURL());
+ }
+
+ // A message about how to treat links is only received from aim pages. In
+ // lens flows, link behavior is still treated as best-guess since the SRP
+ // doesn't message the browser when a link is clicked - search results
+ // links still need to be opened as a thread link.
+ if (base::FeatureList::IsEnabled(kAimTriggeredThreadLinks) &&
+ !is_nav_from_embedded_non_ai_page) {
+ if (is_forward_link_navigation) {
+ base::SequencedTaskRunner::GetCurrentDefault()->PostTask(
+ FROM_HERE,
+ base::BindOnce(
+ &ContextualTasksUiService::OnThreadLinkClicked,
+ weak_ptr_factory_.GetWeakPtr(), url_params.url, task_id,
+ tab ? tab->GetWeakPtr() : nullptr,
+ browser ? browser->GetWeakPtr() : nullptr,
+ initiator_origin.value_or(source_contents->GetPrimaryMainFrame()
+ ->GetLastCommittedOrigin())));
+ return true;
+ }
+
+ if (!is_to_new_tab) {
+ if (tab) {
+ // Links from the embedded page that would need to navigate the tab
+ // need special treatment so they navigate the tab rather than the
+ // embedded page.
+ OMNIBOX_LOG("nav_trace")
+ << "ContextualTasks navigation trace: HandleNavigationImpl "
+ "posting OnNonThreadNavigationInTab";
+ base::SequencedTaskRunner::GetCurrentDefault()->PostTask(
+ FROM_HERE,
+ base::BindOnce(
+ &ContextualTasksUiService::OnNonThreadNavigationInTab,
+ weak_ptr_factory_.GetWeakPtr(), std::move(url_params),
+ tab->GetWeakPtr()));
+ } else {
+ // In-tab links from within the side panel shouldn't navigate the side
+ // panel. Some lans cases can, but that is handled earlier in this
+ // method. For this case open a new tab instead.
+ url_params.disposition = WindowOpenDisposition::NEW_FOREGROUND_TAB;
+ base::SequencedTaskRunner::GetCurrentDefault()->PostTask(
+ FROM_HERE, base::BindOnce(&ContextualTasksUiService::OpenUrl,
+ weak_ptr_factory_.GetWeakPtr(),
+ std::move(url_params),
+ pending_tracker
+ ? pending_tracker->window_features()
+ : blink::mojom::WindowFeatures(),
+ browser));
+ }
+ return true;
+ } else {
+ // Allow the link to open to its intended destination as deemed by the
+ // url_params.
+ base::SequencedTaskRunner::GetCurrentDefault()->PostTask(
+ FROM_HERE,
+ base::BindOnce(&ContextualTasksUiService::OpenUrl,
+ weak_ptr_factory_.GetWeakPtr(),
+ std::move(url_params),
+ pending_tracker ? pending_tracker->window_features()
+ : blink::mojom::WindowFeatures(),
+ browser));
+ return true;
+ }
+ } else {
+ OMNIBOX_LOG("nav_trace")
+ << "ContextualTasks navigation trace: HandleNavigationImpl "
+ "posting OnThreadLinkClicked";
+ // This needs to be posted in case the called method triggers a navigation
+ // in the same WebContents, invalidating the nav handle used up the chain.
+ url::Origin final_initiator_origin =
+ initiator_origin.value_or(url_params.initiator_origin.value_or(
+ url::Origin::Create(source_contents->GetLastCommittedURL())));
+
+ base::SequencedTaskRunner::GetCurrentDefault()->PostTask(
+ FROM_HERE,
+ base::BindOnce(
+ static_cast,
+ base::WeakPtr, const url::Origin&)>(
+ &ContextualTasksUiService::OnThreadLinkClicked),
+ weak_ptr_factory_.GetWeakPtr(), url_params.url, task_id,
+ tab ? tab->GetWeakPtr() : nullptr,
+ browser ? browser->GetWeakPtr() : nullptr,
+ final_initiator_origin));
+ return true;
+ }
}
// Navigations to the AI URL in the topmost frame should always be
@@ -1392,6 +1906,44 @@
return "";
}
+void ContextualTasksUiService::RemoveWindowTracker(
+ base::WeakPtr tracker) {
+ if (!GetIsContextualTasksWindowTrackingEnabled()) {
+ return;
+ }
+ if (!tracker) {
+ return;
+ }
+ if (tracker->window_id().has_value()) {
+ if (tracker->webui_contents()) {
+ content::WebContents* webui_contents = tracker->webui_contents().get();
+ auto* web_ui_interface = GetWebUiInterface(webui_contents);
+ if (web_ui_interface) {
+ if (web_ui_interface->GetPageRemote().is_bound()) {
+ web_ui_interface->GetPageRemote()->OnWindowClosed(
+ tracker->window_id().value());
+ }
+ }
+ }
+ }
+ tracker_manager_->RemoveTracker(tracker.get());
+}
+
+void ContextualTasksUiService::RegisterWindow(ContextualTaskId task_id,
+ const GURL& url,
+ ContextualWindowId window_id) {
+ if (GetIsContextualTasksWindowTrackingEnabled() && tracker_manager_) {
+ tracker_manager_->RegisterWindow(task_id, url, window_id);
+ }
+}
+
+void ContextualTasksUiService::CloseTrackedWindow(
+ ContextualWindowId window_id) {
+ if (GetIsContextualTasksWindowTrackingEnabled() && tracker_manager_) {
+ tracker_manager_->CloseTrackedWindow(window_id);
+ }
+}
+
bool ContextualTasksUiService::IsTrustedHost(const std::string& host) {
if (base::EndsWith(host, ".corp.google.com") ||
base::EndsWith(host, ".c.googlers.com") ||
@@ -1478,6 +2030,22 @@
return AppendAimEntryPointParams(url, entry_point);
}
+bool ContextualTasksUiService::IsTrackedWindow(
+ content::WebContents* web_contents) {
+ if (GetIsContextualTasksWindowTrackingEnabled() && tracker_manager_ &&
+ tracker_manager_->IsTrackedWindow(web_contents)) {
+ return true;
+ }
+
+ // Also considered tracked if the opener is a guest (e.g. in a webview).
+ content::WebContents* opener_wc =
+ web_contents->GetOpener()
+ ? content::WebContents::FromRenderFrameHost(web_contents->GetOpener())
+ : nullptr;
+ return opener_wc && opener_wc != opener_wc->GetResponsibleWebContents() &&
+ IsContextualTasksUrl(opener_wc->GetLastCommittedURL());
+}
+
void ContextualTasksUiService::OnTaskChanged(
BrowserWindowInterface* browser_window_interface,
content::WebContents* web_contents,
@@ -1894,6 +2462,58 @@
}
}
+content::RenderFrameHost* ContextualTasksUiService::GetGuestForMessage(
+ content::RenderFrameHost* target_rfh,
+ const url::Origin& source_origin) {
+ if (!GetIsContextualTasksWindowTrackingEnabled() || !tracker_manager_) {
+ return nullptr;
+ }
+ // 1. Verify that the target of postMessage is one of our message proxy web
+ // contents.
+ content::WebContents* target_contents =
+ content::WebContents::FromRenderFrameHost(target_rfh);
+ if (!GuestOpenerUserData::IsGuestOpener(target_contents)) {
+ OMNIBOX_LOG("route_message")
+ << "GetGuestForMessage: target is not a message proxy";
+ return nullptr;
+ }
+
+ // 2. Origin check: Only allow trusted Google origins to send messages.
+ if (!IsAllowedOriginForGuestMessage(source_origin)) {
+ OMNIBOX_LOG("route_message") << "GetGuestForMessage: origin not allowed "
+ << source_origin.Serialize();
+ return nullptr;
+ }
+
+ // 3. Find the tracker that manages the sender's tab.
+ ContextualTasksWindowTracker* tracker =
+ tracker_manager_->FindTrackerByMessageProxy(target_contents);
+ if (!tracker) {
+ OMNIBOX_LOG("route_message")
+ << "GetGuestForMessage: no tracker found for message proxy";
+ return nullptr;
+ }
+
+ // 4. Return the main frame that opened the frame this postMessage is coming
+ // from. The postMessage will then be routed to this frame. This is most
+ // likely the , but could also be an